diff --git a/Cargo.lock b/Cargo.lock index 5e91d72..cbe7482 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1009,6 +1009,7 @@ dependencies = [ "digest", "serde", "serde_json", + "vanth_derive", ] [[package]] @@ -1019,6 +1020,15 @@ dependencies = [ "vanth", ] +[[package]] +name = "vanth_derive" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "variadics_please" version = "1.1.0" diff --git a/Cargo.toml b/Cargo.toml index 693ea0d..48172e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,3 +15,6 @@ serde = { version = "1.0.219", features = ["derive"] } serde_json = "1.0.140" digest = "0.10.7" blake3 = { version = "1.8.2", features = ["traits-preview"] } +quote = "1.0" +syn = { version = "2.0", features = ["full"] } +proc-macro2 = "1.0" \ No newline at end of file diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index a973166..79a19de 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -7,4 +7,4 @@ use cli::Cli; fn main() { let cli = Cli::parse(); cli::execute(cli); -} \ No newline at end of file +} diff --git a/crates/vanth/Cargo.toml b/crates/vanth/Cargo.toml index 41c10fd..0aa85c2 100644 --- a/crates/vanth/Cargo.toml +++ b/crates/vanth/Cargo.toml @@ -13,3 +13,4 @@ digest.workspace = true serde.workspace = true serde_json.workspace = true blake3.workspace = true +vanth_derive = { path = "../vanth_derive" } diff --git a/crates/vanth/src/lib.rs b/crates/vanth/src/lib.rs index b2eace2..8796aa6 100644 --- a/crates/vanth/src/lib.rs +++ b/crates/vanth/src/lib.rs @@ -12,6 +12,7 @@ pub mod entity; pub mod hashing_serializer; pub use hashing_serializer::hash; +pub use vanth_derive::Vanth; pub type Result = std::result::Result; @@ -77,10 +78,22 @@ pub struct Value { #[derive(Clone, Debug, Deserialize, Serialize)] pub struct Ty { - path: Vec, + pub path: Vec, } -pub trait Vanth: Serialize + DeserializeOwned { +impl PartialEq for Ty { + fn eq(&self, other: &Self) -> bool { + self.path == other.path + } +} + +impl > PartialEq for Ty { + fn eq(&self, other: &T) -> bool { + self.path.join("::") == *other.as_ref() + } +} + +pub trait Vanth { fn ty() -> Ty; } diff --git a/crates/vanth/src/vanth.rs b/crates/vanth/src/vanth.rs deleted file mode 100644 index c403fee..0000000 --- a/crates/vanth/src/vanth.rs +++ /dev/null @@ -1,18 +0,0 @@ -#![allow(unused)] - -use std::path::PathBuf; - -pub struct Value {} - -pub struct Ty {} - -pub enum Primitive { - Bool(bool), - Int(i32), - Float(f32), - String(String), - Path(PathBuf), - Char(char), - Byte(u8), - ByteArray(Vec), -} diff --git a/crates/vanth/tests/integration/derive.rs b/crates/vanth/tests/integration/derive.rs index 3b9fe46..58c5e23 100644 --- a/crates/vanth/tests/integration/derive.rs +++ b/crates/vanth/tests/integration/derive.rs @@ -1,5 +1,5 @@ use bevy_ecs::component::Component; -use serde::{Deserialize, Serialize}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; use vanth::Vanth; // TODO: derive `Vanth` @@ -10,5 +10,29 @@ struct Foo { #[test] fn test_derive() { - println!("yeet"); + #[derive(Deserialize, Serialize, Vanth)] + struct Foo { + field_a: i32, + field_b: String, + inner: T, + } + + #[derive(Deserialize, Serialize, Vanth)] + struct Bar { + field_a: i32, + } + + #[derive(Deserialize, Serialize, Vanth)] + struct Qux { + field_a: i32, + field_b: String, + inner: T, + inner_2: S, + } + + let base = "integration::derive::"; + + assert_eq!(Bar::ty(), format!("{base}Bar")); + assert_eq!(Foo::::ty(), format!("{base}Foo<{base}Bar>")); + assert_eq!(Qux::>::ty(), format!("{base}Qux<{base}Bar,{base}Foo<{base}Bar>>")); } diff --git a/crates/vanth_derive/Cargo.toml b/crates/vanth_derive/Cargo.toml new file mode 100644 index 0000000..4c10d21 --- /dev/null +++ b/crates/vanth_derive/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "vanth_derive" +version.workspace = true +edition.workspace = true + +[lib] +proc-macro = true + +[dependencies] +quote.workspace = true +syn.workspace = true +proc-macro2.workspace = true \ No newline at end of file diff --git a/crates/vanth_derive/src/lib.rs b/crates/vanth_derive/src/lib.rs new file mode 100644 index 0000000..3e84f23 --- /dev/null +++ b/crates/vanth_derive/src/lib.rs @@ -0,0 +1,51 @@ +use proc_macro::TokenStream; +use quote::quote; +use syn::{parse_macro_input, DeriveInput, GenericParam, Generics}; +use syn::parse_quote; + +#[proc_macro_derive(Vanth)] +pub fn vanth_derive(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + + let name = input.ident.clone(); + + let mut generics = input.generics.clone(); + + let type_params: Vec = generics.params.iter().filter_map(|param| { + if let GenericParam::Type(type_param) = param { + Some(type_param.ident.clone()) + } else { + None + } + }).collect(); + + let mut where_clause = generics.where_clause.clone().unwrap_or_else(|| parse_quote!(where )); + for tp in &type_params { + where_clause.predicates.push(parse_quote!(#tp : vanth::Vanth)); + } + + let (impl_generics, ty_generics, _) = generics.split_for_impl(); + + let generics_code = if type_params.is_empty() { + quote! { String::new() } + } else { + quote! { + format!("<{}>", [#(#type_params::ty().path.join("::")),*].join(",")) + } + }; + + let expanded = quote! { + impl #impl_generics vanth::Vanth for #name #ty_generics #where_clause { + fn ty() -> vanth::Ty { + let module_path = module_path!(); + let mut path: Vec = module_path.split("::").map(|s| s.to_string()).collect(); + let base_name = stringify!(#name); + let generics_str = #generics_code; + path.push(format!("{}{}", base_name, generics_str)); + vanth::Ty { path } + } + } + }; + + TokenStream::from(expanded) +}