diff --git a/Cargo.toml b/Cargo.toml index 0c84e5d..299259e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = [".", "venndb-usage"] +members = [".", "venndb-macros", "venndb-usage"] [package] description = "an in-memory database in Rust for rows queried using bit (flag) columns" @@ -20,9 +20,5 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [dependencies] -proc-macro2 = "1.0" -quote = "1.0" -syn = "2.0" - -[lib] -proc-macro = true +hashbrown = "0.14.3" +venndb-macros = { version = "0.1.0", path = "venndb-macros" } diff --git a/src/lib.rs b/src/lib.rs index 7f31395..3cf5a52 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,95 +1,9 @@ -#![forbid(unsafe_code)] +pub use venndb_macros::VennDB; -mod errors; -mod field; -mod generate_db; -mod parse_attrs; +#[doc(hidden)] +pub mod internal { + //! Hidden thirdparty dependencies for venndb, + //! not to be relied upon directly, as they may change at any time. -use errors::Errors; -use field::StructField; -use parse_attrs::{FieldAttrs, TypeAttrs}; -use proc_macro2::TokenStream; -use quote::{format_ident, quote, ToTokens}; - -/// Derive macro generating VennDB functionality for this struct. -#[proc_macro_derive(VennDB, attributes(venndb))] -pub fn venndb(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let ast = syn::parse_macro_input!(input as syn::DeriveInput); - let gen = impl_from_args(&ast); - gen.into() -} - -/// Transform the input into a token stream containing any generated implementations, -/// as well as all errors that occurred. -fn impl_from_args(input: &syn::DeriveInput) -> TokenStream { - let errors = &Errors::default(); - let type_attrs = &TypeAttrs::parse(errors, input); - let mut output_tokens = match &input.data { - syn::Data::Struct(ds) => impl_from_args_struct( - errors, - &input.vis, - &input.ident, - type_attrs, - &input.generics, - ds, - ), - syn::Data::Enum(_) => { - errors.err(input, "`#[derive(VennDB)]` cannot be applied to enums"); - TokenStream::new() - } - syn::Data::Union(_) => { - errors.err(input, "`#[derive(VennDB)]` cannot be applied to unions"); - TokenStream::new() - } - }; - errors.to_tokens(&mut output_tokens); - output_tokens -} - -/// Implements `VennDB` for a `#[derive(VennDB)]` struct. -fn impl_from_args_struct( - errors: &Errors, - vis: &syn::Visibility, - name: &syn::Ident, - type_attrs: &TypeAttrs, - _generic_args: &syn::Generics, - ds: &syn::DataStruct, -) -> TokenStream { - let fields = match &ds.fields { - syn::Fields::Named(fields) => fields, - syn::Fields::Unnamed(_) => { - errors.err( - &ds.struct_token, - "`#![derive(VennDB)]` is not currently supported on tuple structs", - ); - return TokenStream::new(); - } - syn::Fields::Unit => { - errors.err( - &ds.struct_token, - "#![derive(VennDB)]` cannot be applied to unit structs", - ); - return TokenStream::new(); - } - }; - - let fields: Vec<_> = fields - .named - .iter() - .filter_map(|field| { - let attrs = FieldAttrs::parse(errors, field); - StructField::new(errors, field, attrs) - }) - .collect(); - - let name_db = match &type_attrs.name { - Some(name) => format_ident!("{}", name.value()), - None => format_ident!("{}DB", name), - }; - - let db_code = generate_db::generate_db(name, &name_db, vis, &fields[..]); - - quote! { - #db_code - } + pub use hashbrown::HashMap; } diff --git a/venndb-macros/Cargo.toml b/venndb-macros/Cargo.toml new file mode 100644 index 0000000..5d6b1af --- /dev/null +++ b/venndb-macros/Cargo.toml @@ -0,0 +1,25 @@ +[package] +description = "macros for venndb, cannot be used directly" +edition = "2021" +homepage = "https://venndb.rs" +license = "MIT OR Apache-2.0" +name = "venndb-macros" +readme = "README.md" +repository = "https://github.com/plabayo/venndb" +keywords = ["database", "db", "memory", "bits"] +categories = ["database", "db"] +authors = ["Glen De Cauwsemaecker "] +version = "0.1.0" +rust-version = "1.75.0" + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[dependencies] +proc-macro2 = "1.0" +quote = "1.0" +syn = "2.0" + +[lib] +proc-macro = true diff --git a/src/errors.rs b/venndb-macros/src/errors.rs similarity index 100% rename from src/errors.rs rename to venndb-macros/src/errors.rs diff --git a/src/field.rs b/venndb-macros/src/field.rs similarity index 100% rename from src/field.rs rename to venndb-macros/src/field.rs diff --git a/src/generate_db.rs b/venndb-macros/src/generate_db.rs similarity index 93% rename from src/generate_db.rs rename to venndb-macros/src/generate_db.rs index 1151567..e84f9c7 100644 --- a/src/generate_db.rs +++ b/venndb-macros/src/generate_db.rs @@ -42,7 +42,7 @@ fn generate_db_struct( let field_name = field.map_name(); let ty: &syn::Type = field.ty(); quote! { - #field_name: std::collections::HashMap<#ty, usize>, + #field_name: ::venndb::internal::HashMap<#ty, usize>, } } }) @@ -124,7 +124,7 @@ pub fn generate_db_struct_method_new( FieldInfo::Key(field) => { let name = field.map_name(); quote! { - #name: std::collections::HashMap::new(), + #name: ::venndb::internal::HashMap::new(), } } }) @@ -196,7 +196,7 @@ pub fn generate_db_struct_method_with_capacity( FieldInfo::Key(field) => { let name = field.map_name(); quote! { - #name: std::collections::HashMap::with_capacity(capacity), + #name: ::venndb::internal::HashMap::with_capacity(capacity), } } }) @@ -227,10 +227,10 @@ pub fn generate_db_struct_methods_key( let ty = field.ty(); let method_name = field.method_name(); quote! { - #vis fn #method_name(&self, key: &Q) -> std::option::Option<&#name> + #vis fn #method_name(&self, key: &Q) -> ::std::option::Option<&#name> where - #ty: std::borrow::Borrow, - Q: std::hash::Hash + std::cmp::Eq + ?std::marker::Sized, + #ty: ::std::borrow::Borrow, + Q: ::std::hash::Hash + ::std::cmp::Eq + ?::std::marker::Sized, { self.#map_name.get(key).and_then(|index| self.rows.get(*index)) } diff --git a/venndb-macros/src/lib.rs b/venndb-macros/src/lib.rs new file mode 100644 index 0000000..5e20d3e --- /dev/null +++ b/venndb-macros/src/lib.rs @@ -0,0 +1,95 @@ +#![forbid(unsafe_code)] + +mod errors; +mod field; +mod generate_db; +mod parse_attrs; + +use errors::Errors; +use field::StructField; +use parse_attrs::{FieldAttrs, TypeAttrs}; +use proc_macro2::TokenStream; +use quote::{format_ident, quote, ToTokens}; + +/// Derive macro generating VennDB functionality for this struct. +#[proc_macro_derive(VennDB, attributes(venndb))] +pub fn venndb(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let ast = syn::parse_macro_input!(input as syn::DeriveInput); + let gen: TokenStream = impl_from_args(&ast); + gen.into() +} + +/// Transform the input into a token stream containing any generated implementations, +/// as well as all errors that occurred. +fn impl_from_args(input: &syn::DeriveInput) -> TokenStream { + let errors = &Errors::default(); + let type_attrs = &TypeAttrs::parse(errors, input); + let mut output_tokens = match &input.data { + syn::Data::Struct(ds) => impl_from_args_struct( + errors, + &input.vis, + &input.ident, + type_attrs, + &input.generics, + ds, + ), + syn::Data::Enum(_) => { + errors.err(input, "`#[derive(VennDB)]` cannot be applied to enums"); + TokenStream::new() + } + syn::Data::Union(_) => { + errors.err(input, "`#[derive(VennDB)]` cannot be applied to unions"); + TokenStream::new() + } + }; + errors.to_tokens(&mut output_tokens); + output_tokens +} + +/// Implements `VennDB` for a `#[derive(VennDB)]` struct. +fn impl_from_args_struct( + errors: &Errors, + vis: &syn::Visibility, + name: &syn::Ident, + type_attrs: &TypeAttrs, + _generic_args: &syn::Generics, + ds: &syn::DataStruct, +) -> TokenStream { + let fields = match &ds.fields { + syn::Fields::Named(fields) => fields, + syn::Fields::Unnamed(_) => { + errors.err( + &ds.struct_token, + "`#![derive(VennDB)]` is not currently supported on tuple structs", + ); + return TokenStream::new(); + } + syn::Fields::Unit => { + errors.err( + &ds.struct_token, + "#![derive(VennDB)]` cannot be applied to unit structs", + ); + return TokenStream::new(); + } + }; + + let fields: Vec<_> = fields + .named + .iter() + .filter_map(|field| { + let attrs = FieldAttrs::parse(errors, field); + StructField::new(errors, field, attrs) + }) + .collect(); + + let name_db = match &type_attrs.name { + Some(name) => format_ident!("{}", name.value()), + None => format_ident!("{}DB", name), + }; + + let db_code = generate_db::generate_db(name, &name_db, vis, &fields[..]); + + quote! { + #db_code + } +} diff --git a/src/parse_attrs.rs b/venndb-macros/src/parse_attrs.rs similarity index 84% rename from src/parse_attrs.rs rename to venndb-macros/src/parse_attrs.rs index 44b1217..1d7b306 100644 --- a/src/parse_attrs.rs +++ b/venndb-macros/src/parse_attrs.rs @@ -21,20 +21,31 @@ impl FieldAttrs { continue; }; + let mut skipped = false; + let mut is_key = false; + for meta in ml { let name = meta.path(); if name.is_ident("key") { - this.kind = Some(FieldKind::Key); + is_key = true; + } else if name.is_ident("skip") { + skipped = true; } else { errors.err( &meta, concat!( - "Invalid field-level `argh` attribute\n", + "Invalid field-level `venndb` attribute\n", "Expected one of: `key`", ), ); } } + + if skipped { + this.kind = None; + } else if is_key { + this.kind = Some(FieldKind::Key); + } } this @@ -86,7 +97,7 @@ fn venndb_attr_to_meta_list( errors: &Errors, attr: &syn::Attribute, ) -> Option> { - if !is_argh_attr(attr) { + if !is_venndb_attr(attr) { return None; } let ml = errors.expect_meta_list(&attr.meta)?; @@ -101,6 +112,6 @@ fn is_matching_attr(name: &str, attr: &syn::Attribute) -> bool { } /// Checks for `#[venndb ...]` -fn is_argh_attr(attr: &syn::Attribute) -> bool { +fn is_venndb_attr(attr: &syn::Attribute) -> bool { is_matching_attr("venndb", attr) }