Skip to content

Commit

Permalink
implement filter map and ensures it compiles
Browse files Browse the repository at this point in the history
TODO: add functional test to ensure logic is correct as well
  • Loading branch information
GlenDC committed Apr 8, 2024
1 parent 3e122da commit 2f417ad
Show file tree
Hide file tree
Showing 11 changed files with 265 additions and 26 deletions.
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ pub mod __internal {
//! Hidden thirdparty dependencies for venndb,
//! not to be relied upon directly, as they may change at any time.

pub use bitvec::{bitvec, order::Lsb0, slice::IterOnes, vec::BitVec};
pub use bitvec::{order::Lsb0, slice::IterOnes, vec::BitVec};
pub use hashbrown::HashMap;

/// Generate a random `usize`.
Expand Down
28 changes: 28 additions & 0 deletions venndb-macros/src/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub struct StructField<'a> {
pub enum FieldInfo<'a> {
Key(KeyField<'a>),
Filter(FilterField<'a>),
FilterMap(FilterMapField<'a>),
}

pub struct KeyField<'a> {
Expand Down Expand Up @@ -83,6 +84,33 @@ impl<'a> StructField<'a> {
ty: &self.field.ty,
}),
FieldKind::Filter => FieldInfo::Filter(FilterField { name: self.name }),
FieldKind::FilterMap => FieldInfo::FilterMap(FilterMapField {
name: self.name,
ty: &self.field.ty,
}),
})
}
}

pub struct FilterMapField<'a> {
pub name: &'a Ident,
pub ty: &'a syn::Type,
}

impl<'a> FilterMapField<'a> {
pub fn name(&'a self) -> &'a Ident {
self.name
}

pub fn ty(&'a self) -> &'a syn::Type {
self.ty
}

pub fn filter_map_name(&self) -> Ident {
format_ident!("filter_map_{}", self.name)
}

pub fn filter_vec_name(&self) -> Ident {
format_ident!("filter_vec_{}", self.name)
}
}
108 changes: 102 additions & 6 deletions venndb-macros/src/generate_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,15 @@ fn generate_db_struct(
#field_name_not: ::venndb::__internal::BitVec,
}
}
FieldInfo::FilterMap(field) => {
let filter_map_name = field.filter_map_name();
let filter_vec_name = field.filter_vec_name();
let ty: &syn::Type = field.ty();
quote! {
#filter_map_name: ::venndb::__internal::HashMap<#ty, usize>,
#filter_vec_name: ::std::vec::Vec<::venndb::__internal::BitVec>,
}
}
})
.collect();

Expand Down Expand Up @@ -122,7 +131,7 @@ fn generate_db_struct_methods(
#method_append

/// Consumes the database and returns the rows.
#vis fn into_rows(self) -> Vec<#name> {
#vis fn into_rows(self) -> ::std::vec::Vec<#name> {
self.rows
}
}
Expand Down Expand Up @@ -157,6 +166,14 @@ fn generate_db_struct_method_new(
#name_not: ::venndb::__internal::BitVec::new(),
}
}
FieldInfo::FilterMap(field) => {
let filter_map_name = field.filter_map_name();
let filter_vec_name = field.filter_vec_name();
quote! {
#filter_map_name: ::venndb::__internal::HashMap::new(),
#filter_vec_name: ::std::vec::Vec::new(),
}
}
})
.collect();

Expand Down Expand Up @@ -199,6 +216,14 @@ fn generate_db_struct_method_with_capacity(
#name_not: ::venndb::__internal::BitVec::with_capacity(capacity),
}
}
FieldInfo::FilterMap(field) => {
let filter_map_name = field.filter_map_name();
let filter_vec_name = field.filter_vec_name();
quote! {
#filter_map_name: ::venndb::__internal::HashMap::with_capacity(capacity),
#filter_vec_name: ::std::vec::Vec::with_capacity(capacity),
}
}
})
.collect();

Expand All @@ -225,7 +250,8 @@ fn generate_db_struct_method_from_rows(
name
);

let return_type = db_error.generate_fn_output(name_db, quote! { Vec<#name> }, quote! { Self });
let return_type =
db_error.generate_fn_output(name_db, quote! { ::std::vec::Vec<#name> }, quote! { Self });
let append_internal_call = db_error.generate_fn_error_kind_usage(
name_db,
quote! {
Expand All @@ -239,7 +265,7 @@ fn generate_db_struct_method_from_rows(

quote! {
#[doc=#method_doc]
#vis fn from_rows(rows: Vec<#name>) -> #return_type {
#vis fn from_rows(rows: ::std::vec::Vec<#name>) -> #return_type {
let mut db = Self::with_capacity(rows.len());
for (index, row) in rows.iter().enumerate() {
#append_internal_call
Expand Down Expand Up @@ -278,6 +304,7 @@ fn generate_db_struct_method_append(
})
}
FieldInfo::Filter(_) => None,
FieldInfo::FilterMap(_) => None,
})
.collect();

Expand All @@ -301,6 +328,27 @@ fn generate_db_struct_method_append(
self.#field_name_not.push(!data.#name);
}
}
FieldInfo::FilterMap(field) => {
let name = field.name();
let filter_map_name = field.filter_map_name();
let filter_vec_name = field.filter_vec_name();
let filter_index = format_ident!("{}_index", filter_vec_name);
quote! {
let #filter_index = match self.#filter_map_name.entry(data.#name.clone()) {
::venndb::__internal::hash_map::Entry::Occupied(entry) => *entry.get(),
::venndb::__internal::hash_map::Entry::Vacant(entry) => {
let index = self.#filter_vec_name.len();
entry.insert(index);
let bv = ::venndb::__internal::BitVec::repeat(false, self.rows.len());
self.#filter_vec_name.push(bv);
index
}
};
for (i, row) in self.#filter_vec_name.iter_mut().enumerate() {
row.push(i == #filter_index);
}
}
}
})
.collect();

Expand Down Expand Up @@ -364,6 +412,7 @@ fn generate_db_struct_field_methods(
})
}
FieldInfo::Filter(_) => None,
FieldInfo::FilterMap(_) => None,
})
.collect();

Expand All @@ -389,6 +438,13 @@ fn generate_query_struct(
#name: Option<bool>,
})
}
FieldInfo::FilterMap(field) => {
let name = field.name();
let ty = field.ty();
Some(quote! {
#name: Option<#ty>,
})
}
FieldInfo::Key(_) => None,
})
.collect();
Expand All @@ -406,6 +462,12 @@ fn generate_query_struct(
#name: None,
})
}
FieldInfo::FilterMap(field) => {
let name = field.name();
Some(quote! {
#name: None,
})
}
FieldInfo::Key(_) => None,
})
.collect();
Expand Down Expand Up @@ -471,6 +533,21 @@ fn generate_query_struct_impl(
}
})
}
FieldInfo::FilterMap(field) => {
let name = field.name();
let ty = field.ty();
let doc = format!(
"Enable and set the `{}` filter-map with the given option.",
name
);
Some(quote! {
#[doc=#doc]
#vis fn #name(&mut self, value: #ty) -> &mut Self {
self.#name = Some(value);
self
}
})
}
FieldInfo::Key(_) => None,
})
.collect();
Expand All @@ -484,6 +561,12 @@ fn generate_query_struct_impl(
self.#name = None;
})
}
FieldInfo::FilterMap(field) => {
let name = field.name();
Some(quote! {
self.#name = None;
})
}
FieldInfo::Key(_) => None,
})
.collect();
Expand All @@ -503,6 +586,19 @@ fn generate_query_struct_impl(
};
})
}
FieldInfo::FilterMap(field) => {
let name = field.name();
let filter_map_name: Ident = field.filter_map_name();
let filter_vec_name: Ident = field.filter_vec_name();
Some(quote! {
if let Some(value) = &self.#name {
match self.db.#filter_map_name.get(value) {
Some(index) => filter &= &self.db.#filter_vec_name[*index],
None => filter.fill(false),
};
}
})
}
FieldInfo::Key(_) => None,
})
.collect();
Expand Down Expand Up @@ -550,7 +646,7 @@ fn generate_query_struct_impl(

/// Execute the query on the database, returning an iterator over the results.
#vis fn execute(&self) -> Option<#name_query_result<'a>> {
let mut filter = ::venndb::__internal::bitvec![1; self.db.rows.len()];
let mut filter = ::venndb::__internal::BitVec::repeat(true, self.db.rows.len());

#(#filters)*

Expand All @@ -575,7 +671,7 @@ fn generate_query_struct_impl(
#[derive(Debug)]
enum #name_query_result_kind {
Bits(::venndb::__internal::BitVec),
Indices(Vec<usize>),
Indices(::std::vec::Vec<usize>),
}

impl<'a> #name_query_result<'a> {
Expand Down Expand Up @@ -619,7 +715,7 @@ fn generate_query_struct_impl(
where
F: Fn(&#name) -> bool,
{
let indices: Vec<usize> = match &self.references {
let indices: ::std::vec::Vec<usize> = match &self.references {
#name_query_result_kind::Bits(v) => v.iter_ones().filter(|index| predicate(&self.rows[*index])).collect(),
#name_query_result_kind::Indices(i) => i.iter().filter(|&index| predicate(&self.rows[*index])).map(|index| *index).collect(),
};
Expand Down
62 changes: 46 additions & 16 deletions venndb-macros/src/parse_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub struct FieldAttrs {
pub enum FieldKind {
Key,
Filter,
FilterMap,
}

impl FieldAttrs {
Expand All @@ -19,28 +20,54 @@ impl FieldAttrs {

let mut skipped = false;
let mut is_key = false;
let mut is_filter = false;

for attr in &field.attrs {
let ml = if let Some(ml) = venndb_attr_to_meta_list(errors, attr) {
ml
let ml: Vec<_> = if let Some(ml) = venndb_attr_to_meta_list(errors, attr) {
ml.into_iter().collect()
} else {
continue;
};

for meta in ml {
let name = meta.path();
if name.is_ident("key") {
is_key = true;
} else if name.is_ident("skip") {
skipped = true;
} else {
errors.err(
&meta,
concat!(
"Invalid field-level `venndb` attribute\n",
"Expected one of: `key`",
),
);
if ml.iter().any(|meta| meta.path().is_ident("skip")) {
// check first to avoid any other invalid combinations
skipped = true;
} else {
for meta in ml {
let name = meta.path();
if name.is_ident("key") {
if is_filter {
errors.err(
&meta,
concat!(
"Invalid field-level `venndb` attribute\n",
"Cannot have both `key` and `filter`",
),
);
} else {
is_key = true;
}
} else if name.is_ident("filter") {
if is_key {
errors.err(
&meta,
concat!(
"Invalid field-level `venndb` attribute\n",
"Cannot have both `key` and `filter`",
),
);
} else {
is_filter = true;
}
} else {
errors.err(
&meta,
concat!(
"Invalid field-level `venndb` attribute\n",
"Expected one of: `key`",
),
);
}
}
}
}
Expand All @@ -51,6 +78,9 @@ impl FieldAttrs {
this.kind = Some(FieldKind::Key);
} else if is_bool(&field.ty) {
this.kind = Some(FieldKind::Filter);
} else if is_filter {
// bool filters are to be seen as regular filters, even when made explicitly so!
this.kind = Some(FieldKind::FilterMap);
}

this
Expand Down
3 changes: 2 additions & 1 deletion venndb-usage/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ pub struct Employee {
is_manager: bool,
is_admin: bool,
is_active: bool,
#[venndb(filter)]
department: Department,
}

#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub enum Department {
Engineering,
Sales,
Expand Down

0 comments on commit 2f417ad

Please sign in to comment.