Compare commits
9 Commits
Author | SHA1 | Date |
---|---|---|
Michael Pfaff | 5ce0f242f6 | |
Michael Pfaff | df882cb64c | |
Michael Pfaff | 694ce4d66f | |
Michael Pfaff | 566886563e | |
Michael Pfaff | 6f46786e8d | |
Michael Pfaff | af358d7e62 | |
Michael Pfaff | 5ea54133f5 | |
Michael Pfaff | 1ea5a510a7 | |
Michael Pfaff | 4c6aadf634 |
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "ql"
|
||||
version = "0.1.1"
|
||||
version = "0.3.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
|
@ -9,6 +9,7 @@ default = [ ]
|
|||
serde = [ "dep:serde" ]
|
||||
|
||||
[dependencies]
|
||||
paste = "1"
|
||||
serde = { version = "1", optional = true, features = [ "derive" ] }
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
167
src/lib.rs
167
src/lib.rs
|
@ -1,9 +1,106 @@
|
|||
/// A dynamic query structure.
|
||||
//! 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,
|
||||
|
@ -13,17 +110,24 @@ pub trait DynamicQuery {
|
|||
}
|
||||
}
|
||||
|
||||
// needed for macros
|
||||
#[doc(hidden)]
|
||||
pub use paste;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
#[doc(hidden)]
|
||||
pub use serde;
|
||||
|
||||
mod macros;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serde::{Deserialize, Serialize};
|
||||
use super::DynamicQuery;
|
||||
|
||||
// just tests that the macro compiles
|
||||
crate::query_def! {
|
||||
/// Foo doc.
|
||||
Foo(FooDef) {
|
||||
/// Field doc.
|
||||
#[derive(PartialEq)]
|
||||
struct Foo {
|
||||
#[serde(flatten)]
|
||||
bar: (Bar),
|
||||
baz: (bool),
|
||||
|
@ -31,22 +135,67 @@ mod tests {
|
|||
bars_by_bazs: 'map (bool, Bar),
|
||||
}
|
||||
|
||||
/// Bar doc.
|
||||
Bar(BarDef) {
|
||||
#[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() {
|
||||
assert_eq!(serde_json::to_string(&Foo {
|
||||
let x = Foo {
|
||||
bar: Some(Bar {
|
||||
baz_inner: Some(true),
|
||||
}),
|
||||
baz: Some(false),
|
||||
bars: Some(vec![]),
|
||||
bars_by_bazs: Some(std::collections::HashMap::new()),
|
||||
}).ok(), Some(r#"{"baz_inner":true,"baz":false,"bars":[],"bars_by_bazs":{}}"#.to_owned()));
|
||||
};
|
||||
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,
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
421
src/macros.rs
421
src/macros.rs
|
@ -1,43 +1,47 @@
|
|||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! query_def_type {
|
||||
(@ ($type:ty)) => {
|
||||
(($type:ty)) => {
|
||||
$type
|
||||
};
|
||||
(@ 'dyn ($type:ty)) => {
|
||||
('dyn ($type:ty)) => {
|
||||
$type
|
||||
};
|
||||
(@ 'vec ($type:ty)) => {
|
||||
Vec<$type>
|
||||
('vec ($type:ty)) => {
|
||||
::std::vec::Vec<$type>
|
||||
};
|
||||
(@ 'map ($key:ty, $value:ty)) => {
|
||||
('map ($key:ty, $value:ty)) => {
|
||||
::std::collections::HashMap<$key, $value>
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! query_def_field {
|
||||
(@dyn $type:ty) => {
|
||||
Option<<$type as $crate::DynamicQuery>::Definition>
|
||||
};
|
||||
(@ ($type:ty)) => {
|
||||
(($type:ty)) => {
|
||||
bool
|
||||
};
|
||||
(@ 'dyn ($type:ty)) => {
|
||||
('dyn ($type:ty)) => {
|
||||
$crate::query_def_field!(@dyn $type)
|
||||
};
|
||||
(@ 'vec ($type:ty)) => {
|
||||
('vec ($type:ty)) => {
|
||||
$crate::query_def_field!(@dyn $type)
|
||||
};
|
||||
(@ 'map ($key:ty, $value:ty)) => {
|
||||
('map ($key:ty, $value:ty)) => {
|
||||
$crate::query_def_field!(@dyn $value)
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[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)) => {
|
||||
// special handling is not necessary in the first place (i.e. you can just do `(Vec<Type>)`
|
||||
// instead of `'vec (Type)`.
|
||||
($query:expr, $value:ident, 'vec ($_type:path)) => {
|
||||
match $query {
|
||||
Some(query) => {
|
||||
if let Some(ref mut value) = $value {
|
||||
|
@ -51,7 +55,7 @@ macro_rules! query_def_filter {
|
|||
}
|
||||
}
|
||||
};
|
||||
(@ $query:expr, $value:ident, 'map ($_key:path, $_value:path)) => {
|
||||
($query:expr, $value:ident, 'map ($_key:path, $_value:path)) => {
|
||||
match $query {
|
||||
Some(query) => {
|
||||
if let Some(ref mut value) = $value {
|
||||
|
@ -65,164 +69,355 @@ macro_rules! query_def_filter {
|
|||
}
|
||||
}
|
||||
};
|
||||
(@ $query:expr, $value:ident, 'dyn ($($_type:tt)+)) => {
|
||||
($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)+)) => {
|
||||
($query:expr, $value:ident, ($($_type:tt)+)) => {
|
||||
if !*$query {
|
||||
*$value = None;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! query_def_internal {
|
||||
(@
|
||||
macro_rules! query_def_internal_struct {
|
||||
(
|
||||
$( #[$meta:meta] )*
|
||||
$vis:vis $data:ident$((
|
||||
$( #[$def_meta:meta] )+
|
||||
))? {
|
||||
$(
|
||||
$(#[$meta:meta])
|
||||
*
|
||||
$vis:vis $data:ident(
|
||||
$(#[$def_meta:meta])
|
||||
*
|
||||
$def:ident
|
||||
) {
|
||||
$(
|
||||
$(#[$field_meta:meta])
|
||||
*
|
||||
$field:ident: $($qualifier:lifetime)? ($($type:tt)+),
|
||||
)+
|
||||
$( #[$field_meta:meta] )*
|
||||
$field:ident: $( $qualifier:lifetime )? ($( $type:tt )+)
|
||||
),+
|
||||
}
|
||||
)
|
||||
*
|
||||
) => {
|
||||
$(
|
||||
#[derive(Debug, Clone)]
|
||||
$(#[$meta])
|
||||
*
|
||||
$( #[$meta] )*
|
||||
$vis struct $data {
|
||||
$(
|
||||
$(#[$field_meta])
|
||||
*
|
||||
pub $field: Option<$crate::query_def_type!(@ $($qualifier)? ($($type)+))>,
|
||||
)+
|
||||
$( #[$field_meta] )*
|
||||
pub $field: Option<$crate::query_def_type!($( $qualifier )? ($( $type )+))>
|
||||
),+
|
||||
}
|
||||
|
||||
$crate::paste::paste! {
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
$(#[$def_meta])
|
||||
*
|
||||
$vis struct $def {
|
||||
$(
|
||||
pub $field: $crate::query_def_field!(@ $($qualifier)? ($($type)+)),
|
||||
)+
|
||||
$( $( #[$def_meta] )+ )?
|
||||
$vis struct [<$data Def>] {
|
||||
$( pub $field: $crate::query_def_field!($( $qualifier )? ($( $type )+)) ),+
|
||||
}
|
||||
}
|
||||
|
||||
impl $crate::DynamicQuery for $data {
|
||||
type Definition = $def;
|
||||
type Definition = $crate::paste::paste! { [<$data 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)+));
|
||||
)
|
||||
+
|
||||
$crate::query_def_filter!(query, value, $( $qualifier )? ($( $type )+));
|
||||
)+
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! query_def_internal_enum_variant_def {
|
||||
(
|
||||
$( #[$variant_def_meta:meta] )*
|
||||
$vis:vis $name:ident
|
||||
) => {
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
$( #[$variant_def_meta] )*
|
||||
$vis struct $name;
|
||||
};
|
||||
(
|
||||
$( #[$variant_def_meta:meta] )*
|
||||
$vis:vis $name:ident {
|
||||
$( $field:ident$((
|
||||
$( #[$variant_def_field_meta:meta] )+
|
||||
))?: $( $qualifier:lifetime )? ($( $type:tt )+) ),+
|
||||
}
|
||||
) => {
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
$( #[$variant_def_meta] )*
|
||||
$vis struct $name {
|
||||
$(
|
||||
$( $( #[$variant_def_field_meta] )+ )?
|
||||
pub $field: $crate::query_def_field!($( $qualifier )? ($( $type )+))
|
||||
),+
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! query_def_internal_enum {
|
||||
(
|
||||
$( #[$meta:meta] )*
|
||||
$vis:vis $data:ident$((
|
||||
$( #[$def_meta:meta] )+
|
||||
))? {
|
||||
$(
|
||||
$( #[$variant_meta:meta] )*
|
||||
$variant:ident$((
|
||||
$( #[$variant_def_meta:meta] )+
|
||||
))? $({
|
||||
$(
|
||||
$( #[$field_meta:meta] )*
|
||||
$field:ident$((
|
||||
$( #[$variant_def_field_meta:meta] )+
|
||||
))?: $( $qualifier:lifetime )? ($( $type:tt )+)
|
||||
),+
|
||||
})?
|
||||
),+
|
||||
}
|
||||
) => {
|
||||
#[derive(Debug, Clone)]
|
||||
$( #[$meta] )*
|
||||
$vis enum $data {
|
||||
$(
|
||||
$( #[$variant_meta] )*
|
||||
$variant $({
|
||||
$(
|
||||
$( #[$field_meta] )*
|
||||
$field: Option<$crate::query_def_type!($( $qualifier )? ($( $type )+))>
|
||||
),*
|
||||
})?
|
||||
),+
|
||||
}
|
||||
|
||||
$crate::paste::paste! {
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
$( $( #[$def_meta] )+ )?
|
||||
$vis struct [<$data Def>] {
|
||||
$( pub [<$variant:snake:lower>]: [<$data Def Variant $variant>] ),+
|
||||
}
|
||||
}
|
||||
|
||||
// $crate::paste::paste! {
|
||||
// impl [<$data Def>] {
|
||||
// $(
|
||||
// pub type $variant = [<$data Def Variant $variant>];
|
||||
// )+
|
||||
// }
|
||||
// }
|
||||
|
||||
$crate::paste::paste! {
|
||||
$(
|
||||
$crate::query_def_internal_enum_variant_def! {
|
||||
$( $( #[$variant_def_meta] )+ )?
|
||||
$vis [<$data Def Variant $variant>] $({
|
||||
$( $field$((
|
||||
$( #[$variant_def_field_meta] )+
|
||||
))?: $( $qualifier )? ($( $type )+) ),+
|
||||
})?
|
||||
}
|
||||
)+
|
||||
}
|
||||
|
||||
impl $crate::DynamicQuery for $data {
|
||||
type Definition = $crate::paste::paste! { [<$data Def>] };
|
||||
|
||||
fn filter_ref(&mut self, def: &Self::Definition) {
|
||||
match self {
|
||||
$(
|
||||
Self::$variant $({ $( ref mut $field ),* })? => {
|
||||
$($(
|
||||
let query = &$crate::paste::paste! { &def.[<$variant:snake:lower>] }.$field;
|
||||
$crate::query_def_filter!(query, $field, $( $qualifier )? ($( $type )+));
|
||||
)*)?
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
*
|
||||
};
|
||||
}
|
||||
|
||||
/// Defines a dynamic query structure.
|
||||
#[cfg(not(feature = "serde"))]
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! query_def {
|
||||
macro_rules! query_def_0 {
|
||||
(
|
||||
$( #[$meta:meta] )*
|
||||
$vis:vis struct $data:ident$((
|
||||
$( #[$def_meta:meta] )+
|
||||
))? {
|
||||
$(
|
||||
$(#[$meta:meta])
|
||||
*
|
||||
$vis:vis $data:ident(
|
||||
$(#[$def_meta:meta])
|
||||
*
|
||||
$def:ident
|
||||
) {
|
||||
$(
|
||||
$(#[$field_meta:meta])
|
||||
*
|
||||
$field:ident: $($qualifier:lifetime)? ($($type:tt)+),
|
||||
)+
|
||||
$( #[$field_meta:meta] )*
|
||||
$field:ident: $( $qualifier:lifetime )? ($( $type:tt )+)
|
||||
),+
|
||||
|
||||
$(,)?
|
||||
}
|
||||
)
|
||||
*
|
||||
) => {
|
||||
$crate::query_def_internal!(@
|
||||
$crate::query_def_internal_struct! {
|
||||
$( #[$meta] )*
|
||||
$vis $data$((
|
||||
$( #[$def_meta] )+
|
||||
))? {
|
||||
$(
|
||||
$(#[$meta])
|
||||
*
|
||||
$vis $data(
|
||||
$(#[$def_meta])
|
||||
*
|
||||
$def
|
||||
) {
|
||||
$(
|
||||
$(#[$field_meta])
|
||||
*
|
||||
$field: $($qualifier)? ($($type)+),
|
||||
)+
|
||||
$( #[$field_meta] )*
|
||||
$field: $( $qualifier )? ($( $type )+)
|
||||
),+
|
||||
}
|
||||
}
|
||||
};
|
||||
(
|
||||
$( #[$meta:meta] )*
|
||||
$vis:vis enum $data:ident$((
|
||||
$( #[$def_meta:meta] )+
|
||||
))? {
|
||||
$(
|
||||
$( #[$variant_meta:meta] )*
|
||||
$variant:ident$((
|
||||
$( #[$variant_def_meta:meta] )+
|
||||
))? $({
|
||||
$(
|
||||
$( #[$field_meta:meta] )*
|
||||
$field:ident: $( $qualifier:lifetime )? ($( $type:tt )+)
|
||||
),+
|
||||
|
||||
$(,)?
|
||||
})?
|
||||
),+
|
||||
|
||||
$(,)?
|
||||
}
|
||||
) => {
|
||||
$crate::query_def_internal_enum! {
|
||||
$( #[$meta] )*
|
||||
$vis $data$((
|
||||
$( #[$def_meta] )+
|
||||
))? {
|
||||
$(
|
||||
$( #[$variant_meta] )*
|
||||
$variant$((
|
||||
$( #[$variant_def_meta] )?
|
||||
))? $({
|
||||
$(
|
||||
$( #[$field_meta] )*
|
||||
$field: $( $qualifier )? ($( $type )+)
|
||||
),+
|
||||
})?
|
||||
),+
|
||||
}
|
||||
}
|
||||
)
|
||||
*
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
/// Defines a dynamic query structure.
|
||||
#[cfg(feature = "serde")]
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! query_def {
|
||||
macro_rules! query_def_0 {
|
||||
(
|
||||
$( #[$meta:meta] )*
|
||||
$vis:vis struct $data:ident$((
|
||||
$( #[$def_meta:meta] )+
|
||||
))? {
|
||||
$(
|
||||
$(#[$meta:meta])
|
||||
*
|
||||
$vis:vis $data:ident(
|
||||
$(#[$def_meta:meta])
|
||||
*
|
||||
$def:ident
|
||||
) {
|
||||
$(
|
||||
$(#[$field_meta:meta])
|
||||
*
|
||||
$field:ident: $($qualifier:lifetime)? ($($type:tt)+),
|
||||
)+
|
||||
$( #[$field_meta:meta] )*
|
||||
$field:ident: $( $qualifier:lifetime )? ($( $type:tt )+)
|
||||
),+
|
||||
|
||||
$(,)?
|
||||
}
|
||||
)
|
||||
*
|
||||
) => {
|
||||
$crate::query_def_internal!(@
|
||||
$(
|
||||
#[derive(Serialize, Deserialize)]
|
||||
$(#[$meta])
|
||||
*
|
||||
$crate::query_def_internal_struct! {
|
||||
#[derive($crate::serde::Serialize, $crate::serde::Deserialize)]
|
||||
$( #[$meta] )*
|
||||
$vis $data(
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive($crate::serde::Serialize, $crate::serde::Deserialize)]
|
||||
#[serde(default)]
|
||||
$(#[$def_meta])
|
||||
*
|
||||
$def
|
||||
$( $( #[$def_meta] )+ )?
|
||||
) {
|
||||
$(
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
$(#[$field_meta])
|
||||
*
|
||||
$field: $($qualifier)? ($($type)+),
|
||||
)+
|
||||
$( #[$field_meta] )*
|
||||
$field: $( $qualifier )? ($( $type )+)
|
||||
),+
|
||||
}
|
||||
}
|
||||
};
|
||||
(
|
||||
$( #[$meta:meta] )*
|
||||
$vis:vis enum $data:ident$((
|
||||
$( #[$def_meta:meta] )+
|
||||
))? {
|
||||
$(
|
||||
$( #[$variant_meta:meta] )*
|
||||
$variant:ident$((
|
||||
$( #[$variant_def_meta:meta] )+
|
||||
))? $({
|
||||
$(
|
||||
$( #[$field_meta:meta] )*
|
||||
$field:ident: $( $qualifier:lifetime )? ($( $type:tt )+)
|
||||
),+
|
||||
|
||||
$(,)?
|
||||
})?
|
||||
),+
|
||||
|
||||
$(,)?
|
||||
}
|
||||
) => {
|
||||
$crate::query_def_internal_enum! {
|
||||
#[derive($crate::serde::Serialize, $crate::serde::Deserialize)]
|
||||
$( #[$meta] )*
|
||||
$vis $data(
|
||||
#[derive($crate::serde::Serialize, $crate::serde::Deserialize)]
|
||||
#[serde(default)]
|
||||
$( $( #[$def_meta] )+ )?
|
||||
) {
|
||||
$(
|
||||
$( #[$variant_meta] )*
|
||||
$variant(
|
||||
#[derive($crate::serde::Serialize, $crate::serde::Deserialize)]
|
||||
$( $( #[$variant_def_meta] )+ )?
|
||||
) $({
|
||||
$(
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
$( #[$field_meta] )*
|
||||
$field(
|
||||
#[serde(default)]
|
||||
): $( $qualifier )? ($( $type )+)
|
||||
),+
|
||||
})?
|
||||
),+
|
||||
}
|
||||
}
|
||||
)
|
||||
*
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
/// Defines a dynamic query structure.
|
||||
///
|
||||
/// See the crate documentation for details.
|
||||
#[macro_export]
|
||||
macro_rules! query_def {
|
||||
($(
|
||||
$( #[$meta:meta] )*
|
||||
$vis:vis $type:ident $data:ident$((
|
||||
$( #[$def_meta:meta] )+
|
||||
))? {
|
||||
$($body:tt)+
|
||||
}
|
||||
)*) => {
|
||||
$($crate::query_def_0! {
|
||||
$( #[$meta] )*
|
||||
$vis $type $data$((
|
||||
$( #[$def_meta] )+
|
||||
))? {
|
||||
$($body)+
|
||||
}
|
||||
})*
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue