Skip to content

Commit

Permalink
hello VennDB derive macro
Browse files Browse the repository at this point in the history
  • Loading branch information
GlenDC committed Mar 31, 2024
1 parent 8964124 commit ef1ee2f
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 199 deletions.
13 changes: 12 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -11,6 +14,14 @@ categories = ["database", "db"]
authors = ["Glen De Cauwsemaecker <glen@plabayo.tech>"]
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
188 changes: 0 additions & 188 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,191 +129,3 @@ and other open source frameworks such as <https://github.com/plabayo/rama>.

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<String> = 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<Employee>,
is_manager: venndb::BitVec,
is_admin: venndb::BitVec,
is_active: venndb::BitVec,
departments: venndb::BitMap<EmployeeDepartment>,
}

struct EmployeeDBQueryBuilder<'a> {
db: &'a EmployeeDB,

...
}

impl EmployeeDB {
fn new() -> Self { ... }
fn with_capacity(capacity: usize) -> Self { ... }

fn extend(&mut self, employees: impl IntoIterator<Item = Employee>) { ... }

fn query(&self) -> EmployeeDBQueryBuilder { ... }

fn iter(&self) -> impl Iterator<Item = &Employee> {
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<Item = &Employee> { ... }
}

impl Iterator for EmployeeDB<'_> {
type Item = &'_ Employee;

fn next(&mut self) -> Option<Self::Item> { ... }
}

impl IntoIterator for EmployeeDB {
type Item = Employee;
type IntoIter = std::vec::IntoIter<Self::Item>;

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<u32, usize>`
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);
```
46 changes: 36 additions & 10 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -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()
}
8 changes: 8 additions & 0 deletions venndb-usage/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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 = ".." }
35 changes: 35 additions & 0 deletions venndb-usage/src/main.rs
Original file line number Diff line number Diff line change
@@ -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();
}

0 comments on commit ef1ee2f

Please sign in to comment.