202 lines
4.5 KiB
Rust
202 lines
4.5 KiB
Rust
//! A crate providing a (mostly) straighforward syntax for defining dynamic structures for
|
|
//! efficient querying.
|
|
//!
|
|
//! # Features
|
|
//!
|
|
//! - Filter a struct or enum according to a query definition.
|
|
//! - Inspect definitions manually to avoid unnecessary computations before filtering.
|
|
//!
|
|
//! # Attributes
|
|
//!
|
|
//! Attributes are fully supported on structs, struct fields, enums, enum variants, and enum
|
|
//! variant fields. Additionally, attributes can be applied to some aspects of definitions, though
|
|
//! this may be supported for all aspects in the future.
|
|
//!
|
|
//! # Structs
|
|
//!
|
|
//! Only struct structs (yes, that is what they're [called](https://doc.rust-lang.org/reference/items/structs.html)),
|
|
//! are supported. Struct structs with no fields are also not supported.
|
|
//!
|
|
//! This fails:
|
|
//! ```compile_fail
|
|
//! # #[macro_use] extern crate ql;
|
|
//! #
|
|
//! query_def! {
|
|
//! struct Foo {
|
|
//! }
|
|
//! }
|
|
//! #
|
|
//! # fn main() {
|
|
//! # }
|
|
//! ```
|
|
//!
|
|
//! This works:
|
|
//! ```
|
|
//! # #[macro_use] extern crate ql;
|
|
//! #
|
|
//! query_def! {
|
|
//! struct Foo {
|
|
//! a: (bool),
|
|
//! b: (usize),
|
|
//! c: (String),
|
|
//! d: (Vec<String>),
|
|
//! }
|
|
//! }
|
|
//! #
|
|
//! # fn main() {
|
|
//! # }
|
|
//! ```
|
|
//!
|
|
//! # Enums
|
|
//!
|
|
//! Fieldless and struct enums are supported, but struct enums with no fields are not.
|
|
//!
|
|
//! This fails:
|
|
//! ```compile_fail
|
|
//! # #[macro_use] extern crate ql;
|
|
//! #
|
|
//! query_def! {
|
|
//! enum Foo {
|
|
//! A { }
|
|
//! }
|
|
//! }
|
|
//! #
|
|
//! # fn main() {
|
|
//! # }
|
|
//! ```
|
|
//!
|
|
//! This works:
|
|
//! ```
|
|
//! # #[macro_use] extern crate ql;
|
|
//! #
|
|
//! query_def! {
|
|
//! enum Foo {
|
|
//! A
|
|
//! }
|
|
//! }
|
|
//! #
|
|
//! # fn main() {
|
|
//! # }
|
|
//! ```
|
|
//!
|
|
//! Incorrect syntax in the macro invocation can lead to very confusing errors.
|
|
//!
|
|
//! For example, the incorrect enum syntax shown earlier will fail with this message:
|
|
//!
|
|
//! ```text
|
|
//! error: no rules expected the token `struct`
|
|
//! ```
|
|
|
|
|
|
/// A dynamic struct whose fields can be recursively filtered according to a
|
|
/// [definition](`Self::Definition`).
|
|
pub trait DynamicQuery {
|
|
/// A type defining the fields to include in the filtered structure.
|
|
type Definition;
|
|
|
|
/// Filters the fields of this structure in place according to the `def`.
|
|
fn filter_ref(&mut self, def: &Self::Definition);
|
|
|
|
/// Filters the fields of this structure in place according to the `def`.
|
|
/// This method takes ownership of the structure, making it more suitable
|
|
/// for pipelines.
|
|
#[inline(always)]
|
|
fn filter(mut self, def: &Self::Definition) -> Self
|
|
where
|
|
Self: Sized,
|
|
{
|
|
self.filter_ref(def);
|
|
self
|
|
}
|
|
}
|
|
|
|
// needed for macros
|
|
#[doc(hidden)]
|
|
pub use paste;
|
|
|
|
#[cfg(feature = "serde")]
|
|
#[doc(hidden)]
|
|
pub use serde;
|
|
|
|
mod macros;
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::DynamicQuery;
|
|
|
|
// just tests that the macro compiles
|
|
crate::query_def! {
|
|
#[derive(PartialEq)]
|
|
struct Foo {
|
|
#[serde(flatten)]
|
|
bar: (Bar),
|
|
baz: (bool),
|
|
bars: 'vec (Bar),
|
|
bars_by_bazs: 'map (bool, Bar),
|
|
}
|
|
|
|
#[derive(PartialEq)]
|
|
struct Bar {
|
|
baz_inner: (bool),
|
|
}
|
|
|
|
enum FooOrBar {
|
|
Foo {
|
|
value: 'dyn (Foo),
|
|
},
|
|
|
|
Bar {
|
|
value: 'dyn (Bar),
|
|
}
|
|
}
|
|
|
|
enum OptionalFooOrBarDyn {
|
|
Some {
|
|
value: 'dyn (FooOrBar),
|
|
},
|
|
|
|
None,
|
|
}
|
|
|
|
enum OptionalFooOrBar {
|
|
Some {
|
|
value: (FooOrBar),
|
|
},
|
|
|
|
None
|
|
}
|
|
}
|
|
|
|
// make sure A. serde is working and B. we get expected structure
|
|
#[test]
|
|
fn test_json() {
|
|
let x = Foo {
|
|
bar: Some(Bar {
|
|
baz_inner: Some(true),
|
|
}),
|
|
baz: Some(false),
|
|
bars: Some(vec![]),
|
|
bars_by_bazs: Some(std::collections::HashMap::new()),
|
|
};
|
|
assert_eq!(
|
|
serde_json::to_string(&x).ok(),
|
|
Some(r#"{"baz_inner":true,"baz":false,"bars":[],"bars_by_bazs":{}}"#.to_owned())
|
|
);
|
|
|
|
assert_eq!(
|
|
x.clone().filter(&FooDef {
|
|
bar: false,
|
|
baz: true,
|
|
bars: Some(BarDef::default()),
|
|
bars_by_bazs: None,
|
|
}),
|
|
Foo {
|
|
bar: None,
|
|
baz: Some(false),
|
|
bars: Some(vec![]),
|
|
bars_by_bazs: None,
|
|
}
|
|
)
|
|
}
|
|
}
|