commit c0cfc8eb82516ca1588d418e1225cb29c62adf29 Author: Michael Pfaff Date: Thu May 19 12:27:06 2022 -0400 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4fffb2f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..68d5719 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "ql" +version = "0.1.0" +edition = "2021" + +[features] +default = [ ] +serde = [ "dep:serde" ] + +[dependencies] +serde = { version = "1", optional = true, features = [ "derive" ] } diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..b70804f --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,33 @@ +/// A dynamic query structure. +pub trait DynamicQuery { + type Definition; + + fn filter_ref(&mut self, def: &Self::Definition); + + fn filter(mut self, def: &Self::Definition) -> Self + where + Self: Sized, + { + self.filter_ref(def); + self + } +} + +mod macros; + +#[cfg(test)] +mod tests { + // just tests that the macro compiles + crate::query_def! { + Foo(FooDef) { + bar: 'dyn (Bar), + baz: (bool), + bars: 'vec (Bar), + bars_by_bazs: 'map (bool, Bar), + } + + Bar(BarDef) { + baz: (bool), + } + } +} diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 0000000..6fb9f58 --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,131 @@ +#[macro_export] +macro_rules! query_def_type { + (@ ($type:ty)) => { + $type + }; + (@ 'dyn ($type:ty)) => { + $type + }; + (@ 'vec ($type:ty)) => { + Vec<$type> + }; + (@ 'map ($key:ty, $value:ty)) => { + ::std::collections::HashMap<$key, $value> + }; +} + +#[macro_export] +macro_rules! query_def_field { + (@dyn $type:ty) => { + Option<<$type as $crate::DynamicQuery>::Definition> + }; + (@ ($type:ty)) => { + bool + }; + (@ 'dyn ($type:ty)) => { + $crate::query_def_field!(@dyn $type) + }; + (@ 'vec ($type:ty)) => { + $crate::query_def_field!(@dyn $type) + }; + (@ 'map ($key:ty, $value:ty)) => { + $crate::query_def_field!(@dyn $value) + }; +} + +#[macro_export] +macro_rules! query_def_filter { + // note about this: 'dyn is implied for 'vec and 'map because if the type is not 'dyn, then the + // special handling is not necessary in the first place. + (@ $query:expr, $value:ident, 'vec ($_type:path)) => { + match $query { + Some(query) => { + if let Some(ref mut value) = $value { + for value in value.iter_mut() { + value.filter_ref(&query); + } + } + } + None => { + *$value = None; + } + } + }; + (@ $query:expr, $value:ident, 'map ($_key:path, $_value:path)) => { + match $query { + Some(query) => { + if let Some(ref mut value) = $value { + for (_, value) in value.iter_mut() { + value.filter_ref(&query); + } + } + } + None => { + *$value = None; + } + } + }; + (@ $query:expr, $value:ident, 'dyn ($($_type:tt)+)) => { + if let Some(ref mut value) = $value { + if let Some(ref query) = $query { + value.filter_ref(&query); + } + } + }; + (@ $query:expr, $value:ident, ($($_type:tt)+)) => { + if !*$query { + *$value = None; + } + }; +} + +/// Defines a dynamic query structure. +#[macro_export] +macro_rules! query_def { + ( + $($vis:vis $data:ident($def:ident) { + $( + $(#[$field_meta:meta]) + * + $field:ident: $($qualifier:lifetime)? ($($type:tt)+), + )+ + }) + * + ) => { + $( + #[derive(Debug, Clone)] + #[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))] + $vis struct $data { + $( + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] + $(#[$field_meta]) + * + pub $field: Option<$crate::query_def_type!(@ $($qualifier)? ($($type)+))>, + )+ + } + + #[derive(Debug, Clone)] + #[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))] + $vis struct $def { + $( + #[cfg_attr(feature = "serde", serde(default))] + pub $field: $crate::query_def_field!(@ $($qualifier)? ($($type)+)), + )+ + } + + impl $crate::DynamicQuery for $data { + type Definition = $def; + + fn filter_ref(&mut self, def: &Self::Definition) { + $( + let query = &def.$field; + let value = &mut self.$field; + $crate::query_def_filter!(@ query, value, $($qualifier)? ($($type)+)); + ) + + + } + } + ) + * + }; +}