diff --git a/Cargo.lock b/Cargo.lock index 8c1b02e..c87e175 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,4 +4,104 @@ version = 3 [[package]] name = "newt" -version = "0.2.0" +version = "0.3.1" +dependencies = [ + "newt-derive", + "newt-derive-core", +] + +[[package]] +name = "newt-derive" +version = "0.1.0" +dependencies = [ + "newt-derive-core", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "newt-derive-core" +version = "0.1.0" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" diff --git a/Cargo.toml b/Cargo.toml index 32222b1..0a40836 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,23 @@ [package] name = "newt" -version = "0.3.0" +version = "0.3.1" edition = "2021" +[workspace] +resolver = "2" +members = ["derive", "derive/core"] + +[workspace.dependencies] +proc-macro-error = "1.0" +proc-macro2 = "1" +syn = { version = "2", features = ["full"] } +quote = "1" + [dependencies] +newt-derive = { path = "derive", optional = true } + +[dev-dependencies] +newt-derive-core = { path = "derive/core" } + +[features] +derive = ["dep:newt-derive"] diff --git a/derive/Cargo.toml b/derive/Cargo.toml new file mode 100644 index 0000000..dd08fd0 --- /dev/null +++ b/derive/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "newt-derive" +version = "0.1.0" +edition = "2021" + +[dependencies] +proc-macro-error = { workspace = true } +proc-macro2 = { workspace = true } +syn = { workspace = true } +quote = { workspace = true } + +newt-derive-core = { path = "./core" } + +[lib] +proc-macro = true diff --git a/derive/core/Cargo.toml b/derive/core/Cargo.toml new file mode 100644 index 0000000..42ae4af --- /dev/null +++ b/derive/core/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "newt-derive-core" +version = "0.1.0" +edition = "2021" + +[dependencies] +proc-macro-error = { workspace = true } +proc-macro2 = { workspace = true } +syn = { workspace = true } +quote = { workspace = true } diff --git a/derive/core/src/lib.rs b/derive/core/src/lib.rs new file mode 100644 index 0000000..e066f6c --- /dev/null +++ b/derive/core/src/lib.rs @@ -0,0 +1,83 @@ +use proc_macro2::TokenStream; +use quote::quote; +use syn::{Fields, ItemStruct}; + +pub fn lookup_core(input: TokenStream) -> TokenStream { + let input: ItemStruct = match syn::parse2(input) { + Ok(i) => i, + Err(e) => return e.to_compile_error(), + }; + + let struct_name = input.ident; + let (ipl, ty, _) = input.generics.split_for_impl(); + + match input.fields { + Fields::Named(n) => { + let mut exprs = Vec::new(); + + for field in n.named { + let ident = field.ident.expect("named field should have name"); + let ident_str = format!(r#"{ident}"#); + exprs.push(quote! { + #ident_str => Some(&self.#ident), + }); + } + + let output = quote! { + impl #ipl #struct_name #ty { + fn __lookup(&self, name: &str) -> Option<&dyn ::newt::Value> { + match name { + #(#exprs)* + _ => None, + } + } + } + }; + + output + }, + Fields::Unnamed(_u) => { + todo!() + }, + Fields::Unit => quote! { + todo!() + }, + } +} + +#[cfg(test)] +mod test { + use quote::quote; + + use crate::lookup_core; + + macro_rules! go { + ($in:expr, $exp:expr $(,)?) => { + assert_eq!(lookup_core($in).to_string(), $exp.to_string()); + }; + } + + #[test] + pub fn named_fields_struct() { + go! { + quote! { + #[derive(Lookup)] + struct Test { + foo: String, + bar: String, + } + }, + quote! { + impl Test { + fn __lookup(&self, name: &str) -> Option<&dyn ::newt::Value> { + match name { + "foo" => Some(&self.foo), + "bar" => Some(&self.bar), + _ => None, + } + } + } + }, + }; + } +} diff --git a/derive/src/lib.rs b/derive/src/lib.rs new file mode 100644 index 0000000..3e48a0c --- /dev/null +++ b/derive/src/lib.rs @@ -0,0 +1,9 @@ +use newt_derive_core::lookup_core; +use proc_macro::TokenStream; +use proc_macro_error::proc_macro_error; + +#[proc_macro_error] +#[proc_macro_derive(Lookup)] +pub fn lookup(input: TokenStream) -> TokenStream { + lookup_core(input.into()).into() +} diff --git a/src/lib.rs b/src/lib.rs index c471ee7..0b267b5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -478,3 +478,6 @@ macro_rules! values { ] }} } + +#[cfg(feature = "derive")] +pub use newt_derive::Lookup;