diff --git a/Cargo.toml b/Cargo.toml index bf22563..121566d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "0.2.2" authors = ["CodeChain Team "] license = "MIT OR Apache-2.0" description = "Allow for inter-trait casting" -edition = "2018" +edition = "2021" repository = "https://github.com/CodeChain-io/intertrait" documentation = "https://docs.rs/intertrait" readme = "README.md" @@ -14,7 +14,7 @@ include = ["src/**/*", "Cargo.toml", "LICENSE-*", "README.md"] [dependencies] once_cell = "1.4" -linkme = "0.2" +linkme = "0.3" intertrait-macros = { version = "=0.2.2", path = "macros" } [dev-dependencies] diff --git a/README.md b/README.md index f304f44..6e63cd5 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,6 @@ Add the following two dependencies to your `Cargo.toml`: ```toml [dependencies] intertrait = "0.2" -linkme = "0.2" ``` The `linkme` dependency is required due to the use of `linkme` macro in the output of `intertrait` macros. @@ -129,7 +128,7 @@ To use with `Arc`, the following steps should be taken: # How it works First of all, [`CastFrom`] trait makes it possible to retrieve an object of [`std::any::Any`] -from an object for a sub-trait of [`CastFrom`]. +from an object for a sub-trait of [`CastFrom`]. And the macros provided by `intertrait` generates trampoline functions for downcasting a trait object for [`std::any::Any`] back to its concrete type and then creating a trait object for the target trait from it. diff --git a/macros/Cargo.toml b/macros/Cargo.toml index c7ae783..8552d46 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -4,7 +4,7 @@ description = "Macros for intertrait crate, which allows for direct casting betw version = "0.2.2" authors = ["CodeChain Team "] license = "MIT OR Apache-2.0" -edition = "2018" +edition = "2021" repository = "https://github.com/CodeChain-io/intertrait" include = ["src/**/*", "Cargo.toml", "LICENSE-*"] @@ -15,8 +15,7 @@ proc-macro = true proc-macro2 = "1.0" syn = { version = "1.0", features = ["full"] } quote = "1.0" -uuid = { version = "0.8", features = ["v4"] } +uuid = { version = "1", features = ["v4"] } [dev-dependencies] intertrait = { version = "=0.2.2", path = ".." } -linkme = "0.2" diff --git a/macros/src/args.rs b/macros/src/args.rs index 002175d..09890a0 100644 --- a/macros/src/args.rs +++ b/macros/src/args.rs @@ -62,16 +62,26 @@ impl Parse for Targets { pub struct Casts { pub ty: Type, pub targets: Targets, + pub intertrait_path: Path, } impl Parse for Casts { fn parse(input: ParseStream) -> Result { + let intertrait_path = if input.peek(Token![@]) { + input.parse::()?; + input.parse()? + } else { + syn::parse(quote::quote!(::intertrait).into())? + }; + let ty: Type = input.parse()?; input.parse::]>()?; + let targets: Targets = input.parse()?; Ok(Casts { ty, - targets: input.parse()?, + targets, + intertrait_path, }) } } diff --git a/macros/src/attr.rs b/macros/src/attr.rs new file mode 100644 index 0000000..416d1ba --- /dev/null +++ b/macros/src/attr.rs @@ -0,0 +1,31 @@ +use syn::parse::{Error, ParseStream, Result}; +use syn::{parse_quote, Attribute, Path, Token}; + +// #[intertrait(crate = path::to::intertrait)] +pub(crate) fn intertrait_path(attrs: &mut Vec) -> Result { + let mut intertrait_path = None; + let mut errors: Option = None; + + attrs.retain(|attr| { + if !attr.path.is_ident("intertrait") { + return true; + } + match attr.parse_args_with(|input: ParseStream| { + input.parse::()?; + input.parse::()?; + input.call(Path::parse_mod_style) + }) { + Ok(path) => intertrait_path = Some(path), + Err(err) => match &mut errors { + None => errors = Some(err), + Some(errors) => errors.combine(err), + }, + } + false + }); + + match errors { + None => Ok(intertrait_path.unwrap_or_else(|| parse_quote!(::intertrait))), + Some(errors) => Err(errors), + } +} diff --git a/macros/src/gen_caster.rs b/macros/src/gen_caster.rs index 7ef02ff..3a82cda 100644 --- a/macros/src/gen_caster.rs +++ b/macros/src/gen_caster.rs @@ -1,19 +1,26 @@ use std::str::from_utf8_unchecked; use proc_macro2::TokenStream; -use uuid::adapter::Simple; +use uuid::fmt::Simple; use uuid::Uuid; use quote::format_ident; use quote::quote; use quote::ToTokens; -pub fn generate_caster(ty: &impl ToTokens, trait_: &impl ToTokens, sync: bool) -> TokenStream { +use syn::Path; + +pub fn generate_caster( + ty: &impl ToTokens, + trait_: &impl ToTokens, + sync: bool, + intertrait_path: &Path, +) -> TokenStream { let mut fn_buf = [0u8; FN_BUF_LEN]; let fn_ident = format_ident!("{}", new_fn_name(&mut fn_buf)); let new_caster = if sync { quote! { - ::intertrait::Caster::::new_sync( + #intertrait_path::Caster::::new_sync( |from| from.downcast_ref::<#ty>().unwrap(), |from| from.downcast_mut::<#ty>().unwrap(), |from| from.downcast::<#ty>().unwrap(), @@ -23,7 +30,7 @@ pub fn generate_caster(ty: &impl ToTokens, trait_: &impl ToTokens, sync: bool) - } } else { quote! { - ::intertrait::Caster::::new( + #intertrait_path::Caster::::new( |from| from.downcast_ref::<#ty>().unwrap(), |from| from.downcast_mut::<#ty>().unwrap(), |from| from.downcast::<#ty>().unwrap(), @@ -33,8 +40,9 @@ pub fn generate_caster(ty: &impl ToTokens, trait_: &impl ToTokens, sync: bool) - }; quote! { - #[::linkme::distributed_slice(::intertrait::CASTERS)] - fn #fn_ident() -> (::std::any::TypeId, ::intertrait::BoxedCaster) { + #[#intertrait_path::linkme::distributed_slice(#intertrait_path::CASTERS)] + #[linkme(crate = #intertrait_path::linkme)] + fn #fn_ident() -> (::std::any::TypeId, #intertrait_path::BoxedCaster) { (::std::any::TypeId::of::<#ty>(), Box::new(#new_caster)) } } @@ -46,7 +54,7 @@ const FN_BUF_LEN: usize = FN_PREFIX.len() + Simple::LENGTH; fn new_fn_name(buf: &mut [u8]) -> &str { buf[..FN_PREFIX.len()].copy_from_slice(FN_PREFIX); Uuid::new_v4() - .to_simple() + .as_simple() .encode_lower(&mut buf[FN_PREFIX.len()..]); unsafe { from_utf8_unchecked(&buf[..FN_BUF_LEN]) } } diff --git a/macros/src/item_impl.rs b/macros/src/item_impl.rs index 868ef40..9a701f4 100644 --- a/macros/src/item_impl.rs +++ b/macros/src/item_impl.rs @@ -14,14 +14,17 @@ use PathArguments::AngleBracketed; use crate::args::Flag; use crate::gen_caster::generate_caster; -pub fn process(flags: &HashSet, input: ItemImpl) -> TokenStream { +pub fn process(flags: &HashSet, mut input: ItemImpl) -> TokenStream { let ItemImpl { + ref mut attrs, ref self_ty, ref trait_, ref items, .. } = input; + let intertrait_path = crate::attr::intertrait_path(attrs).unwrap(); + let generated = match trait_ { None => quote_spanned! { self_ty.span() => compile_error!("#[cast_to] should only be on an impl of a trait"); @@ -32,7 +35,12 @@ pub fn process(flags: &HashSet, input: ItemImpl) -> TokenStream { }, (None, path, _) => { let path = fully_bound_trait(path, items); - generate_caster(self_ty, &path, flags.contains(&Flag::Sync)) + generate_caster( + self_ty, + &path, + flags.contains(&Flag::Sync), + &intertrait_path, + ) } }, }; diff --git a/macros/src/item_type.rs b/macros/src/item_type.rs index b3d1ac4..4413243 100644 --- a/macros/src/item_type.rs +++ b/macros/src/item_type.rs @@ -9,12 +9,16 @@ use quote::{quote, quote_spanned}; use crate::args::Flag; use crate::gen_caster::generate_caster; -pub fn process(flags: &HashSet, paths: Vec, input: DeriveInput) -> TokenStream { +pub fn process(flags: &HashSet, paths: Vec, mut input: DeriveInput) -> TokenStream { let DeriveInput { + ref mut attrs, ref ident, ref generics, .. } = input; + + let intertrait_path = crate::attr::intertrait_path(attrs).unwrap(); + let generated = if generics.lt_token.is_some() { quote_spanned! { generics.span() => compile_error!("#[cast_to(..)] can't be used on a generic type definition"); @@ -22,7 +26,7 @@ pub fn process(flags: &HashSet, paths: Vec, input: DeriveInput) -> T } else { paths .into_iter() - .flat_map(|t| generate_caster(ident, &t, flags.contains(&Flag::Sync))) + .flat_map(|t| generate_caster(ident, &t, flags.contains(&Flag::Sync), &intertrait_path)) .collect() }; quote! { diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 5a43390..8269045 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -8,6 +8,7 @@ use args::{Casts, Flag, Targets}; use gen_caster::generate_caster; mod args; +mod attr; mod gen_caster; mod item_impl; mod item_type; @@ -134,11 +135,12 @@ pub fn castable_to(input: TokenStream) -> TokenStream { let Casts { ty, targets: Targets { flags, paths }, + intertrait_path, } = parse_macro_input!(input); paths .iter() - .map(|t| generate_caster(&ty, t, flags.contains(&Flag::Sync))) + .map(|t| generate_caster(&ty, t, flags.contains(&Flag::Sync), &intertrait_path)) .collect::() .into() } diff --git a/src/lib.rs b/src/lib.rs index 932608c..1f5d7f8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -61,7 +61,8 @@ use std::collections::HashMap; use std::rc::Rc; use std::sync::Arc; -use linkme::distributed_slice; +// re-export linkme +pub use linkme; use once_cell::sync::Lazy; pub use intertrait_macros::*; @@ -84,7 +85,7 @@ doc_comment::doctest!("../README.md"); /// /// [`Caster`]: ./struct.Caster.html #[doc(hidden)] -#[distributed_slice] +#[linkme::distributed_slice] pub static CASTERS: [fn() -> (TypeId, BoxedCaster)] = [..]; /// A `HashMap` mapping `TypeId` of a [`Caster`] to an instance of it. @@ -287,14 +288,12 @@ mod tests { use std::any::{Any, TypeId}; use std::fmt::{Debug, Display}; - use linkme::distributed_slice; - use crate::{BoxedCaster, CastFromSync}; use super::cast::*; use super::*; - #[distributed_slice(super::CASTERS)] + #[linkme::distributed_slice(super::CASTERS)] static TEST_CASTER: fn() -> (TypeId, BoxedCaster) = create_test_caster; #[derive(Debug)] diff --git a/tests/ui/on-generic-type.stderr b/tests/ui/on-generic-type.stderr index 8df0e29..5544e39 100644 --- a/tests/ui/on-generic-type.stderr +++ b/tests/ui/on-generic-type.stderr @@ -2,4 +2,4 @@ error: #[cast_to(..)] can't be used on a generic type definition --> $DIR/on-generic-type.rs:6:12 | 6 | struct Data { - | ^ + | ^^^^^^^^^^^^