//! 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), //! } //! } //! # //! # 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, } ) } }