Initial commit

This commit is contained in:
Michael Pfaff 2022-05-19 12:27:06 -04:00
commit c0cfc8eb82
Signed by: michael
GPG Key ID: CF402C4A012AA9D4
4 changed files with 177 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/target
/Cargo.lock

11
Cargo.toml Normal file
View File

@ -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" ] }

33
src/lib.rs Normal file
View File

@ -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),
}
}
}

131
src/macros.rs Normal file
View File

@ -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)+));
)
+
}
}
)
*
};
}