diff --git a/Cargo.toml b/Cargo.toml index 23bcf3d..7d3596a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,6 @@ +[workspace] +members = [".", "venndb-usage"] + [package] description = "an in-memory database in Rust for rows queried using bit (flag) columns" edition = "2021" @@ -11,6 +14,14 @@ categories = ["database", "db"] authors = ["Glen De Cauwsemaecker "] version = "0.1.0" rust-version = "1.75.0" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] [dependencies] +quote = "1" +syn = "2" + +[lib] +proc-macro = true diff --git a/README.md b/README.md index e043407..6710ced 100644 --- a/README.md +++ b/README.md @@ -129,191 +129,3 @@ and other open source frameworks such as . Sponsors receive perks and depending on your regular contribution it also allows you to rely on us for support and consulting. - -## Example - -### Read Only Database Example - -```rust -use venndb::venndb; - -venndb! { - struct Employee { - id: u32, - name: String, - is_manager: bool, - is_admin: bool, - is_active: bool, - department: enum { - Sales, - Marketing, - Engineering, - HumanResources, - Accounting, - } - } -} - -fn main() { - let db = EmployeeDB::with_capacity(100).extend(vec![ - Employee { - id: 1, - name: "John".to_string(), - is_manager: true, - is_admin: false, - is_active: true, - department: Department::Sales, - }, - Employee { - id: 2, - name: "Jane".to_string(), - is_manager: false, - is_admin: true, - is_active: true, - department: Department::Marketing, - }, - Employee { - id: 3, - name: "Bob".to_string(), - is_manager: false, - is_admin: false, - is_active: false, - department: Department::Engineering, - }, - ]); - - let active_managers: Vec = db.query() - .is_manager() - .is_active() - .run() - .map(|employee| employee.name) - .collect(); - - assert_eq!(active_managers, vec!["John".to_string()]); -} -``` - -The above example show how easy it is to create an in-memory database -with little effort while still being powerful and easy to use. - -What's going om under the hood? High level the following is generated -(use `cargo expand` to see the fully generated code): - -```rust -#[derive(Debug, venndb::Serialize, venndb::Deserialize)] -struct Employee { - pub id: u32, - pub name: String, - pub is_manager: bool, - pub is_admin: bool, - pub is_active: bool, - pub department: EmployeeDepartment, -} - -#[derive(Debug, venndb::Serialize, venndb::Deserialize)] -enum EmployeeDepartment { - Sales, - Marketing, - Engineering, - HumanResources, - Accounting, -} - -#[derive(Debug)] -struct EmployeeDB { - employees: Vec, - is_manager: venndb::BitVec, - is_admin: venndb::BitVec, - is_active: venndb::BitVec, - departments: venndb::BitMap, -} - -struct EmployeeDBQueryBuilder<'a> { - db: &'a EmployeeDB, - - ... -} - -impl EmployeeDB { - fn new() -> Self { ... } - fn with_capacity(capacity: usize) -> Self { ... } - - fn extend(&mut self, employees: impl IntoIterator) { ... } - - fn query(&self) -> EmployeeDBQueryBuilder { ... } - - fn iter(&self) -> impl Iterator { - self.employees.iter() - } -} - -impl EmployeeDBQueryBuilder<'_> { - fn is_manager(&mut self) -> &mut Self { ... } - fn is_admin(&mut self) -> &mut Self { ... } - fn is_active(&mut self) -> &mut Self { ... } - fn department(&mut self, department: EmployeeDepartment) -> &mut Self { ... } - - fn run(self) -> impl Iterator { ... } -} - -impl Iterator for EmployeeDB<'_> { - type Item = &'_ Employee; - - fn next(&mut self) -> Option { ... } -} - -impl IntoIterator for EmployeeDB { - type Item = Employee; - type IntoIter = std::vec::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.employees.into_iter() - } -} -``` - -### Mutate Example - -Should you need to mutate the database, you can do so with -a couple of changes. - -First the venndb creation has to be change a bit: - -venndb! { - struct Employee { - #[venndb::key] - id: u32, - name: String, - is_manager: bool, - is_admin: bool, - is_active: bool, - department: enum { - Sales, - Marketing, - Engineering, - HumanResources, - Accounting, - } - } -} - -Note the use of the `#[venndb::key]` attribute on the `id` field, -which will ensure that internally we generate a `venndb::FxHashMap` -property in the actual `EmployeeDB` struct. - -Then we can mutate previously queried employees as follows: - -```rust -db.get_mut(1).unwrap().is_manager = false; // John is no longer a manager -``` - -Without modifications we can however also query mutable: - -```rust -// fire all the managers -let active_managers: Vec<&mut Employee> = db.mutate() - .is_manager() - .is_active() - .run() - .map(|employee| employee.is_active = false); -``` diff --git a/src/lib.rs b/src/lib.rs index 152f541..f7e4e89 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,16 +1,42 @@ #![forbid(unsafe_code)] -pub fn add(left: usize, right: usize) -> usize { - left + right -} +use proc_macro::TokenStream; +use quote::{format_ident, quote}; +use syn::{Data::Struct, DataStruct, DeriveInput, Fields::Named, FieldsNamed}; + +#[proc_macro_derive(VennDB)] +pub fn venndb(item: TokenStream) -> TokenStream { + let ast: DeriveInput = syn::parse2(item.into()).unwrap(); + + let name_db = format_ident!("{}VennDB", ast.ident); + + let _fields = match ast.data { + Struct(DataStruct { + fields: Named(FieldsNamed { ref named, .. }), + .. + }) => named, + _ => { + return syn::Error::new_spanned(ast, "Only Structs with named fields are supported") + .to_compile_error() + .into() + } + }; + + quote! { + #[non_exhaustive] + struct #name_db; -#[cfg(test)] -mod tests { - use super::*; + impl #name_db { + fn new() -> Self { + Self + } + } - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); + impl Default for #name_db { + fn default() -> Self { + Self::new() + } + } } + .into() } diff --git a/venndb-usage/Cargo.toml b/venndb-usage/Cargo.toml new file mode 100644 index 0000000..4de52af --- /dev/null +++ b/venndb-usage/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "venndb-usage" +version = "0.1.0" +edition = "2021" +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +venndb = { path = ".." } diff --git a/venndb-usage/src/main.rs b/venndb-usage/src/main.rs new file mode 100644 index 0000000..7cd5b72 --- /dev/null +++ b/venndb-usage/src/main.rs @@ -0,0 +1,35 @@ +#![allow(dead_code)] + +use venndb::VennDB; + +#[derive(Debug, VennDB)] +struct Employee { + id: u32, + name: String, + is_manager: bool, + is_admin: bool, + is_active: bool, + department: Department, +} + +#[derive(Debug)] +pub enum Department { + Engineering, + Sales, + Marketing, + HR, +} + +fn main() { + let employee = Employee { + id: 1, + name: "Alice".to_string(), + is_manager: true, + is_admin: false, + is_active: true, + department: Department::Engineering, + }; + println!("employee: {:#?}", employee); + + let _db = EmployeeVennDB::default(); +}