diff --git a/Cargo.toml b/Cargo.toml index 21a758f..7f94c1c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,9 +18,15 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [features] +default = ["derive"] map = ["indexmap"] serde = ["dep:serde", "indexmap/serde"] +derive = ["implicit-clone-derive"] [dependencies] +implicit-clone-derive = { version = "0.1", optional = true, path = "./implicit-clone-derive" } indexmap = { version = ">= 1, <= 2", optional = true } serde = { version = "1", optional = true } + +[workspace] +members = ["implicit-clone-derive"] diff --git a/implicit-clone-derive/Cargo.toml b/implicit-clone-derive/Cargo.toml new file mode 100644 index 0000000..347736c --- /dev/null +++ b/implicit-clone-derive/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "implicit-clone-derive" +version = "0.1.0" +authors = ["Cecile Tonglet "] +license = "MIT OR Apache-2.0" +edition = "2021" +description = "Immutable types and ImplicitClone trait similar to Copy" +repository = "https://github.com/yewstack/implicit-clone" +homepage = "https://github.com/yewstack/implicit-clone" +documentation = "https://docs.rs/implicit-clone" +readme = "README.md" +keywords = ["immutable", "cheap-clone", "copy", "rc"] +categories = ["rust-patterns"] +rust-version = "1.64" + +[lib] +proc-macro = true + +[dependencies] +quote = "1" +syn = { version = "2", features = ["full"] } + +[dev-dependencies] +implicit-clone = { path = ".." } +trybuild = "1" +rustversion = "1" diff --git a/implicit-clone-derive/src/lib.rs b/implicit-clone-derive/src/lib.rs new file mode 100644 index 0000000..f613f07 --- /dev/null +++ b/implicit-clone-derive/src/lib.rs @@ -0,0 +1,10 @@ +use quote::quote; + +#[proc_macro_derive(ImplicitClone)] +pub fn derive_implicit_clone(item: proc_macro::TokenStream) -> proc_macro::TokenStream { + let syn::ItemStruct { ident, .. } = syn::parse_macro_input!(item as syn::ItemStruct); + let res = quote! { + impl ::implicit_clone::ImplicitClone for #ident {} + }; + res.into() +} diff --git a/implicit-clone-derive/tests/function_attr_test.rs b/implicit-clone-derive/tests/function_attr_test.rs new file mode 100644 index 0000000..0c4d894 --- /dev/null +++ b/implicit-clone-derive/tests/function_attr_test.rs @@ -0,0 +1,13 @@ +#[allow(dead_code)] +#[test] +fn tests_pass() { + let t = trybuild::TestCases::new(); + t.pass("tests/function_component_attr/*-pass.rs"); +} + +#[allow(dead_code)] +#[rustversion::attr(stable(1.64), test)] +fn tests_fail() { + let t = trybuild::TestCases::new(); + t.compile_fail("tests/function_component_attr/*-fail.rs"); +} diff --git a/implicit-clone-derive/tests/function_component_attr/derive-fail.rs b/implicit-clone-derive/tests/function_component_attr/derive-fail.rs new file mode 100644 index 0000000..b0b30fc --- /dev/null +++ b/implicit-clone-derive/tests/function_component_attr/derive-fail.rs @@ -0,0 +1,6 @@ +use implicit_clone::ImplicitClone; + +#[derive(ImplicitClone)] +pub struct NotClonableStruct; + +fn main() {} diff --git a/implicit-clone-derive/tests/function_component_attr/derive-fail.stderr b/implicit-clone-derive/tests/function_component_attr/derive-fail.stderr new file mode 100644 index 0000000..070f5ba --- /dev/null +++ b/implicit-clone-derive/tests/function_component_attr/derive-fail.stderr @@ -0,0 +1,16 @@ +error[E0277]: the trait bound `NotClonableStruct: Clone` is not satisfied + --> tests/function_component_attr/derive-fail.rs:3:10 + | +3 | #[derive(ImplicitClone)] + | ^^^^^^^^^^^^^ the trait `Clone` is not implemented for `NotClonableStruct` + | +note: required by a bound in `ImplicitClone` + --> $WORKSPACE/src/lib.rs + | + | pub trait ImplicitClone: Clone { + | ^^^^^ required by this bound in `ImplicitClone` + = note: this error originates in the derive macro `ImplicitClone` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider annotating `NotClonableStruct` with `#[derive(Clone)]` + | +4 | #[derive(Clone)] + | diff --git a/implicit-clone-derive/tests/function_component_attr/derive-pass.rs b/implicit-clone-derive/tests/function_component_attr/derive-pass.rs new file mode 100644 index 0000000..47b77ae --- /dev/null +++ b/implicit-clone-derive/tests/function_component_attr/derive-pass.rs @@ -0,0 +1,44 @@ +#![no_implicit_prelude] + +// Shadow primitives +#[allow(non_camel_case_types)] +pub struct bool; +#[allow(non_camel_case_types)] +pub struct char; +#[allow(non_camel_case_types)] +pub struct f32; +#[allow(non_camel_case_types)] +pub struct f64; +#[allow(non_camel_case_types)] +pub struct i128; +#[allow(non_camel_case_types)] +pub struct i16; +#[allow(non_camel_case_types)] +pub struct i32; +#[allow(non_camel_case_types)] +pub struct i64; +#[allow(non_camel_case_types)] +pub struct i8; +#[allow(non_camel_case_types)] +pub struct isize; +#[allow(non_camel_case_types)] +pub struct str; +#[allow(non_camel_case_types)] +pub struct u128; +#[allow(non_camel_case_types)] +pub struct u16; +#[allow(non_camel_case_types)] +pub struct u32; +#[allow(non_camel_case_types)] +pub struct u64; +#[allow(non_camel_case_types)] +pub struct u8; +#[allow(non_camel_case_types)] +pub struct usize; + +#[derive(Clone, ::implicit_clone::ImplicitClone)] +struct ExampleStruct; + +fn main() { + let _ = ::implicit_clone::ImplicitClone::implicit_clone(&ExampleStruct); +} diff --git a/src/lib.rs b/src/lib.rs index 5e11309..cee6e4d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -51,6 +51,9 @@ pub mod sync; /// Single-threaded version of immutable types. pub mod unsync; +#[cfg(feature = "implicit-clone-derive")] +pub use implicit_clone_derive::*; + /// Marker trait for cheap-to-clone types that should be allowed to be cloned implicitly. /// /// Enables host libraries to have the same syntax as [`Copy`] while calling the [`Clone`]