#[macro_export(local_inner_macros)]
macro_rules! query {
(@$util:ident $($args:tt)*) => ( _query_impl!(@$util $($args)*) );
($($tokens:tt)+) => ( _query_impl!(@query _query_native, $($tokens)+) );
}
#[macro_export]
#[doc(hidden)]
macro_rules! _query_native {
(@index $coll:expr, [ $($indexes:tt),+ ]) => ( $coll.set_indexes(&[$($indexes),+]) );
(@index $coll:expr, $($indexes:tt)+) => ( $coll.set_indexes($($indexes)+) );
(@find $type:tt, $coll:expr, $filter:expr, $order:expr) => ( $coll.find::<$type>($filter, $order) );
(@insert $coll:expr, $doc:expr) => ( $coll.insert(&$doc) );
(@update $coll:expr, $filter:expr, $modify:expr) => ( $coll.update($filter, $modify) );
(@remove $coll:expr, $filter:expr) => ( $coll.remove($filter) );
}
#[macro_export(local_inner_macros)]
#[doc(hidden)]
macro_rules! _query_impl {
(@query $out:ident, index $type:tt for $coll:tt) => ( _query_impl!(@index_document ($out, $type, $coll)) );
(@query $out:ident, index for $coll:tt $($tokens:tt)+) => ( _query_impl!(@index ($out, $coll), $($tokens)+) );
(@query $out:ident, find $type:tt in $coll:tt $($tokens:tt)*) => ( _query_impl!(@find ($out, $type, $coll), $($tokens)*) );
(@query $out:ident, find in $coll:tt $($tokens:tt)*) => ( _query_impl!(@find ($out, _, $coll), $($tokens)*) );
(@query $out:ident, insert into $coll:tt $($tokens:tt)+) => ( _query_impl!(@insert ($out, $coll), $($tokens)+) );
(@query $out:ident, update in $coll:tt $($tokens:tt)+) => ( _query_impl!(@update ($out, $coll), $($tokens)+) );
(@query $out:ident, remove from $coll:tt $($tokens:tt)*) => ( _query_impl!(@remove ($out, $coll), $($tokens)+) );
(@index_document ($out:ident, $type:tt, $coll:expr)) => (
_query_impl!(@call $out, @index $coll, <$type as $crate::Document>::key_fields())
);
(@index $args:tt, $($tokens:tt)+) => (
_query_impl!(@index_list $args, [], $($tokens)+)
);
(@index_field [ $($field:tt)+ ], $type:ident, $kind:ident) => (
(_query_impl!(@field $($field)+), _query_impl!(@key_type $type), _query_impl!(@index_kind $kind))
);
(@index_list $args:tt, [ $($index:tt)* ], $field:ident $($tokens:tt)*) => (
_query_impl!(@index_list_field $args, [ $($index)* ], [ $field ], $($tokens)*)
);
(@index_list_field $args:tt, $index:tt, [ $($path:tt)+ ], . * $($tokens:tt)*) => (
_query_impl!(@index_list_field $args, $index, [ $($path)+ . * ], $($tokens)*)
);
(@index_list_field $args:tt, $index:tt, [ $($path:tt)+ ], . $field:ident $($tokens:tt)*) => (
_query_impl!(@index_list_field $args, $index, [ $($path)+ . $field ], $($tokens)*)
);
(@index_list_field $args:tt, $index:tt, $path:tt, $($tokens:tt)*) => (
_query_impl!(@index_list_key $args, $index, $path, $($tokens)*)
);
(@index_list_key $args:tt, $index:tt, $field:tt, $type:ident $($tokens:tt)*) => (
_query_impl!(@index_list_kind $args, $index, $field, $type, $($tokens)*)
);
(@index_list_kind $args:tt, $index:tt, $path:tt, $type:ident, index $($tokens:tt)*) => (
_query_impl!(@index_list_out $args, $index, $path, $type, index, $($tokens)*)
);
(@index_list_kind $args:tt, $index:tt, $path:tt, $type:ident, unique $($tokens:tt)*) => (
_query_impl!(@index_list_out $args, $index, $path, $type, unique, $($tokens)*)
);
(@index_list_kind $args:tt, $index:tt, $path:tt, $type:ident, $($tokens:tt)*) => (
_query_impl!(@index_list_out $args, $index, $path, $type, index, $($tokens)*)
);
(@index_list_out $args:tt, [ $($index:tt)* ], $field:tt, $type:ident, $kind:ident, $($tokens:tt)*) => (
_query_impl!(@index_list_next $args, [ $($index)* {_query_impl!(@index_field $field, $type, $kind)} ], $($tokens)*)
);
(@index_list_next $args:tt, $index:tt, $(,)+ $($tokens:tt)*) => (
_query_impl!(@index_list $args, $index, $($tokens)*)
);
(@index_list_next $args:tt, $index:tt, $(;)+ $($tokens:tt)*) => (
_query_impl!(@index_list $args, $index, $($tokens)*)
);
(@index_list_next $args:tt, $index:tt, $($tokens:tt)*) => (
_query_impl!(@index_list $args, $index, $($tokens)*)
);
(@index_list ($out:ident, $coll:expr), [ $($index:tt)+ ], ) => (
_query_impl!(@call $out, @index $coll, [ $($index),+ ])
);
(@index_kind index) => ( $crate::IndexKind::Index );
(@index_kind unique) => ( $crate::IndexKind::Unique );
(@key_type integer) => ( $crate::KeyType::Int );
(@key_type int) => ( $crate::KeyType::Int );
(@key_type float) => ( $crate::KeyType::Float );
(@key_type boolean) => ( $crate::KeyType::Bool );
(@key_type bool) => ( $crate::KeyType::Bool );
(@key_type string) => ( $crate::KeyType::String );
(@key_type str) => ( $crate::KeyType::String );
(@key_type text) => ( $crate::KeyType::String );
(@key_type binary) => ( $crate::KeyType::Binary );
(@key_type bin) => ( $crate::KeyType::Binary );
(@key_type bytes) => ( $crate::KeyType::Binary );
(@find $args:tt,) => (
_query_impl!(@find_impl $args, [], [])
);
(@find $args:tt, order $($order:tt)+) => (
_query_impl!(@find_impl $args, [], [ $($order)+ ])
);
(@find $args:tt, where $($tokens:tt)+) => (
_query_impl!(@find_filter $args, [], $($tokens)+)
);
(@find_filter $args:tt, $filter:tt, order by $($field:ident).+ >) => (
_query_impl!(@find_impl $args, $filter, [ by $($field).+ > ])
);
(@find_filter $args:tt, $filter:tt, order by $($field:ident).+ <) => (
_query_impl!(@find_impl $args, $filter, [ by $($field).+ < ])
);
(@find_filter $args:tt, $filter:tt, order by $($field:ident).+ asc) => (
_query_impl!(@find_impl $args, $filter, [ by $($field).+ asc ])
);
(@find_filter $args:tt, $filter:tt, order by $($field:ident).+ desc) => (
_query_impl!(@find_impl $args, $filter, [ by $($field).+ desc ])
);
(@find_filter $args:tt, $filter:tt, order by $($field:ident).+) => (
_query_impl!(@find_impl $args, $filter, [ by $($field).+ ])
);
(@find_filter $args:tt, $filter:tt, order >) => (
_query_impl!(@find_impl $args, $filter, [ > ])
);
(@find_filter $args:tt, $filter:tt, order <) => (
_query_impl!(@find_impl $args, $filter, [ < ])
);
(@find_filter $args:tt, $filter:tt, order asc) => (
_query_impl!(@find_impl $args, $filter, [ > ])
);
(@find_filter $args:tt, $filter:tt, order desc) => (
_query_impl!(@find_impl $args, $filter, [ < ])
);
(@find_filter $args:tt, $filter:tt, ) => (
_query_impl!(@find_impl $args, $filter, [])
);
(@find_filter $args:tt, [ $($filter:tt)* ], $token:tt $($tokens:tt)*) => (
_query_impl!(@find_filter $args, [ $($filter)* $token ], $($tokens)*)
);
(@find_impl ($out:ident, $type:tt, $coll:expr), [ $($filter:tt)* ], [ $($order:tt)* ]) => (
_query_impl!(@call $out, @find $type, $coll, _query_impl!(@filter $($filter)*), _query_impl!(@order $($order)*))
);
(@insert $args:tt, { $($json:tt)* }) => ( _query_impl!(@insert_impl $args, _query_impl!(@json { $($json)* }))
);
(@insert $args:tt, $($data:tt)+) => ( _query_impl!(@insert_impl $args, $($data)+)
);
(@insert_impl ($out:ident, $coll:expr), $doc:expr) => (
_query_impl!(@call $out, @insert $coll, $doc)
);
(@update $args:tt, modify $($tokens:tt)+) => (
_query_impl!(@update_modify $args, [], $($tokens)+)
);
(@update_modify $args:tt, $modify:tt, where ( $($conds:tt)* ) $($tokens:tt)*) => (
_query_impl!(@update_impl $args, [ ( $($conds)* ) $($tokens)* ], $modify)
);
(@update_modify $args:tt, $modify:tt, where $($field:ident).+ $($tokens:tt)+) => (
_query_impl!(@update_impl $args, [ $($field).+ $($tokens)+ ], $modify)
);
(@update_modify $args:tt, $modify:tt, where ! $($tokens:tt)+) => (
_query_impl!(@update_impl $args, [ ! $($tokens)+ ], $modify)
);
(@update_modify $args:tt, $modify:tt, ) => (
_query_impl!(@update_impl $args, [], $modify)
);
(@update_modify $args:tt, [ $($modify:tt)* ], $token:tt $($tokens:tt)*) => (
_query_impl!(@update_modify $args, [ $($modify)* $token ], $($tokens)*)
);
(@update_impl ($out:ident, $coll:expr), [ $($filter:tt)* ], [ $($modify:tt)* ]) => (
_query_impl!(@call $out, @update $coll, _query_impl!(@filter $($filter)*), _query_impl!(@modify $($modify)*))
);
(@remove $args:tt, ) => (
_query_impl!(@remove_impl $args, [])
);
(@remove $args:tt, where $($filter:tt)+) => (
_query_impl!(@remove_impl $args, [ $($filter)+ ])
);
(@remove_impl ($out:ident, $coll:expr), [ $($filter:tt)* ]) => (
_query_impl!(@call $out, @remove $coll, _query_impl!(@filter $($filter)*))
);
(@order_kind >) => ( $crate::OrderKind::Asc );
(@order_kind <) => ( $crate::OrderKind::Desc );
(@order_kind asc) => ( $crate::OrderKind::Asc );
(@order_kind desc) => ( $crate::OrderKind::Desc );
(@order_kind ) => ( $crate::OrderKind::default() );
(@order by $field:ident $($tokens:tt)*) => ( _query_impl!(@order_field [ $field ] $($tokens)*) );
(@order $order:tt) => ( $crate::Order::primary(_query_impl!(@order_kind $order)) );
(@order ) => ( $crate::Order::primary(_query_impl!(@order_kind )) );
(@order_field [ $($path:tt)+ ] . * $($tokens:tt)*) => ( _query_impl!(@order_field [ $($path)+ . * ] $($tokens)*) );
(@order_field [ $($path:tt)+ ] . $field:ident $($tokens:tt)*) => ( _query_impl!(@order_field [ $($path)+ . $field ] $($tokens)*) );
(@order_field [ $($path:tt)+ ] $($tokens:tt)*) => ( _query_impl!(@order_field_impl [ $($path)+ ], $($tokens)*) );
(@order_field_impl [ $($field:tt)+ ], $($order:tt)*) => (
$crate::Order::field(_query_impl!(@field $($field)+), _query_impl!(@order_kind $($order)*))
);
(@filter ( $($tokens:tt)+ )) => (
Some(_query_impl!(@filter_or [] $($tokens)+))
);
(@filter $($tokens:tt)+) => (
Some(_query_impl!(@filter_or [] $($tokens)+))
);
(@filter ) => (
(None as Option<$crate::Filter>)
);
(@filter_or [ $($conds:tt)* ] $token:tt $($tokens:tt)*) => (
_query_impl!(@filter_or_cond [ $($conds)* ] [ $token ] $($tokens)*)
);
(@filter_or [ $($conds:tt)+ ]) => (
_query_impl!(@filter_or_proc $($conds)+)
);
(@filter_or_cond [ $($conds:tt)* ] [ $($cond:tt)+ ] || $($tokens:tt)*) => (
_query_impl!(@filter_or [ $($conds)* [ $($cond)+ ] ] $($tokens)*)
);
(@filter_or_cond [ $($conds:tt)* ] [ $($cond:tt)+ ]) => (
_query_impl!(@filter_or [ $($conds)* [ $($cond)+ ] ])
);
(@filter_or_cond [ $($conds:tt)* ] [ $($cond:tt)+ ] $token:tt $($tokens:tt)*) => (
_query_impl!(@filter_or_cond [ $($conds)* ] [ $($cond)+ $token ] $($tokens)*)
);
(@filter_or_proc [ $($tokens:tt)+ ]) => (
_query_impl!(@filter_and [] $($tokens)+)
);
(@filter_or_proc $($cond:tt)+) => (
$crate::Filter::Cond($crate::Cond::Or(_query_impl!(@vec $(_query_impl!(@filter_and_wrap $cond)),+)))
);
(@filter_and_wrap [ $($tokens:tt)+ ]) => (
_query_impl!(@filter_and [] $($tokens)+)
);
(@filter_and [ $($conds:tt)* ] $token:tt $($tokens:tt)*) => (
_query_impl!(@filter_and_cond [ $($conds)* ] [ $token ] $($tokens)*)
);
(@filter_and [ $($conds:tt)+ ]) => (
_query_impl!(@filter_and_proc $($conds)+)
);
(@filter_and_cond [ $($conds:tt)* ] [ $($cond:tt)+ ] && $($tokens:tt)*) => (
_query_impl!(@filter_and [ $($conds)* [ $($cond)+ ] ] $($tokens)*)
);
(@filter_and_cond [ $($conds:tt)* ] [ $($cond:tt)+ ]) => (
_query_impl!(@filter_and [ $($conds)* [ $($cond)+ ] ])
);
(@filter_and_cond [ $($conds:tt)* ] [ $($cond:tt)+ ] $token:tt $($tokens:tt)*) => (
_query_impl!(@filter_and_cond [ $($conds)* ] [ $($cond)+ $token ] $($tokens)*)
);
(@filter_and_proc [ $($tokens:tt)+ ]) => (
_query_impl!(@filter_not $($tokens)+)
);
(@filter_and_proc $($cond:tt)+) => (
$crate::Filter::Cond($crate::Cond::And(_query_impl!(@vec $(_query_impl!(@filter_not_wrap $cond)),+)))
);
(@filter_not_wrap [ $($tokens:tt)+ ]) => (
_query_impl!(@filter_not $($tokens)+)
);
(@filter_not ! $($tokens:tt)+) => (
$crate::Filter::Cond($crate::Cond::Not(Box::new(_query_impl!(@filter_nest $($tokens)+))))
);
(@filter_not $($tokens:tt)+) => (
_query_impl!(@filter_nest $($tokens)+)
);
(@filter_nest ( $($tokens:tt)+ )) => (
_query_impl!(@filter_or [] $($tokens)+)
);
(@filter_nest $($tokens:tt)+) => (
_query_impl!(@filter_comp_init $($tokens)+)
);
(@filter_comp_init $field:ident $($tokens:tt)+) => (
_query_impl!(@filter_comp_field [ $field ] $($tokens)+)
);
(@filter_comp_field [ $($path:tt)+ ] . * $($tokens:tt)+) => (
_query_impl!(@filter_comp_field [ $($path)+ . * ] $($tokens)+)
);
(@filter_comp_field [ $($path:tt)+ ] . $field:ident $($tokens:tt)+) => (
_query_impl!(@filter_comp_field [ $($path)+ . $field ] $($tokens)+)
);
(@filter_comp_field [ $($path:tt)+ ] $($tokens:tt)+) => (
_query_impl!(@filter_comp [ $($path)+ ] $($tokens)+)
);
(@filter_comp $field:tt == $value:expr) => (
_query_impl!(@filter_comp_impl $field, Eq, $crate::KeyData::from($value))
);
(@filter_comp $field:tt != $value:expr) => (
_query_impl!(@filter_comp_impl ! $field, Eq, $crate::KeyData::from($value))
);
(@filter_comp $field:tt !of [$($value:expr),*]) => (
_query_impl!(@filter_comp_impl ! $field, In, _query_impl!(@vec $($crate::KeyData::from($value)),*))
);
(@filter_comp $field:tt of [$($value:expr),*]) => (
_query_impl!(@filter_comp_impl $field, In, _query_impl!(@vec $($crate::KeyData::from($value)),*))
);
(@filter_comp $field:tt !of $value:expr) => (
_query_impl!(@filter_comp_impl ! $field, In, $value.iter().map($crate::KeyData::from).collect())
);
(@filter_comp $field:tt of $value:expr) => (
_query_impl!(@filter_comp_impl $field, In, $value.iter().map($crate::KeyData::from).collect())
);
(@filter_comp $field:tt < $value:expr) => (
_query_impl!(@filter_comp_impl $field, Lt, $crate::KeyData::from($value))
);
(@filter_comp $field:tt <= $value:expr) => (
_query_impl!(@filter_comp_impl $field, Le, $crate::KeyData::from($value))
);
(@filter_comp $field:tt > $value:expr) => (
_query_impl!(@filter_comp_impl $field, Gt, $crate::KeyData::from($value))
);
(@filter_comp $field:tt >= $value:expr) => (
_query_impl!(@filter_comp_impl $field, Ge, $crate::KeyData::from($value))
);
(@filter_comp $field:tt in $range:expr) => (
_query_impl!(@filter_comp_impl $field, Bw, $crate::KeyData::from($range.start), true, $crate::KeyData::from($range.end), true)
);
(@filter_comp $field:tt <in> $range:expr) => (
_query_impl!(@filter_comp_impl $field, Bw, $crate::KeyData::from($range.start), false, $crate::KeyData::from($range.end), false)
);
(@filter_comp $field:tt <in $range:expr) => (
_query_impl!(@filter_comp_impl $field, Bw, $crate::KeyData::from($range.start), false, $crate::KeyData::from($range.end), true)
);
(@filter_comp $field:tt in> $range:expr) => (
_query_impl!(@filter_comp_impl $field, Bw, $crate::KeyData::from($range.start), true, $crate::KeyData::from($range.end), false)
);
(@filter_comp $field:tt ?) => (
_query_impl!(@filter_comp_impl $field, Has)
);
(@filter_comp_impl ! $($tokens:tt)+) => (
$crate::Filter::cond($crate::Cond::Not(Box::new(_query_impl!(@filter_comp_impl $($tokens)+))))
);
(@filter_comp_impl [ $($field:tt)+ ], $op:ident) => (
$crate::Filter::comp(_query_impl!(@field $($field)+), $crate::Comp::$op)
);
(@filter_comp_impl [ $($field:tt)+ ], $op:ident, $($args:expr),+) => (
$crate::Filter::comp(_query_impl!(@field $($field)+), $crate::Comp::$op($($args),+))
);
(@modify $($tokens:tt)+) => ( _query_impl!(@modify_parse [] $($tokens)*) );
(@modify ) => ( $crate::Modify::default() );
(@modify_parse [ $($actions:tt)* ] , $($tokens:tt)*) => ( _query_impl!(@modify_parse [ $($actions)* ] $($tokens)*)
);
(@modify_parse [ $($actions:tt)* ] ; $($tokens:tt)*) => ( _query_impl!(@modify_parse [ $($actions)* ] $($tokens)*)
);
(@modify_parse [ $($actions:tt)* ] ) => ( _query_impl!(@modify_apply $($actions)*)
);
(@modify_parse [ $($actions:tt)* ] $token:tt $($tokens:tt)*) => ( _query_impl!(@modify_action_parse [ $($actions)* ] { $token } $($tokens)*)
);
(@modify_action_parse [ $($actions:tt)* ] { $($action:tt)+ } , $($tokens:tt)*) => ( _query_impl!(@modify_parse [ $($actions)* { $($action)+ } ] $($tokens)*)
);
(@modify_action_parse [ $($actions:tt)* ] { $($action:tt)+ } ; $($tokens:tt)*) => ( _query_impl!(@modify_parse [ $($actions)* { $($action)+ } ] $($tokens)*)
);
(@modify_action_parse [ $($actions:tt)* ] { $($action:tt)+ } ) => ( _query_impl!(@modify_parse [ $($actions)* { $($action)+ } ])
);
(@modify_action_parse [ $($actions:tt)* ] { $($action:tt)+ } $token:tt $($tokens:tt)*) => ( _query_impl!(@modify_action_parse [ $($actions)* ] { $($action)+ $token } $($tokens)*)
);
(@modify_apply $($actions:tt)*) => ({ let mut m = $crate::Modify::default();
_query_impl!(@modify_actions_apply m $($actions)*);
m
});
(@modify_actions_apply $m:ident { $($action:tt)+ } $($actions:tt)*) => (
_query_impl!(@modify_action_apply $m $($action)+);
_query_impl!(@modify_actions_apply $m $($actions)*)
);
(@modify_actions_apply $m:ident ) => (
);
(@modify_action_apply $m:ident $($field:ident).+ = $val:expr) => (
$m.add(_query_impl!(@field $($field).+), $crate::Action::Set($crate::to_value($val).unwrap()))
);
(@modify_action_apply $m:ident $($field:ident).+ ~) => (
$m.add(_query_impl!(@field $($field).+), $crate::Action::Delete)
);
(@modify_action_apply $m:ident $($field:ident).+ += $val:expr) => (
$m.add(_query_impl!(@field $($field).+), $crate::Action::Add($crate::to_value($val).unwrap()))
);
(@modify_action_apply $m:ident $($field:ident).+ -= $val:expr) => (
$m.add(_query_impl!(@field $($field).+), $crate::Action::Sub($crate::to_value($val).unwrap()))
);
(@modify_action_apply $m:ident $($field:ident).+ *= $val:expr) => (
$m.add(_query_impl!(@field $($field).+), $crate::Action::Mul($crate::to_value($val).unwrap()))
);
(@modify_action_apply $m:ident $($field:ident).+ /= $val:expr) => (
$m.add(_query_impl!(@field $($field).+), $crate::Action::Div($crate::to_value($val).unwrap()))
);
(@modify_action_apply $m:ident $($field:ident).+ !) => (
$m.add(_query_impl!(@field $($field).+), $crate::Action::Toggle)
);
(@modify_add_splice $m:ident $($field:ident).+, [ $range:expr ], $($insert:tt)*) => (
{
use std::ops::{Bound, RangeBounds};
$m.add(_query_impl!(@field $($field).+), $crate::Action::Splice(match $range.start_bound() {
Bound::Unbounded => 0,
Bound::Included(start) => *start,
Bound::Excluded(start) => *start + 1,
}, match $range.end_bound() {
Bound::Unbounded => -1,
Bound::Included(end) => *end + 1,
Bound::Excluded(end) => *end,
}, _query_impl!(@modify_insert_splice $($insert)*)))
}
);
(@modify_insert_splice $insert:expr) => (
$insert.iter().map(|elm| $crate::to_value(elm).unwrap()).collect()
);
(@modify_insert_splice ) => (
Vec::new()
);
(@modify_action_apply $m:ident $($field:ident).+ [ $($range:tt)+ ] ~) => (
_query_impl!(@modify_add_splice $m $($field).+, [ $($range)+ ], )
);
(@modify_action_apply $m:ident $($field:ident).+ [ $($range:tt)+ ] = $insert:expr) => (
_query_impl!(@modify_add_splice $m $($field).+, [ $($range)+ ], $insert)
);
(@modify_action_apply $m:ident $($field:ident).+ ~= { $($obj:tt)+ }) => (
$m.add(_query_impl!(@field $($field).+), $crate::Action::Merge($crate::to_value(_query_impl!(@json { $($obj)+ })).unwrap()))
);
(@modify_action_apply $m:ident $($field:ident).+ ~= $obj:expr) => (
$m.add(_query_impl!(@field $($field).+), $crate::Action::Merge($crate::to_value($obj).unwrap()))
);
(@modify_action_apply $m:ident $($field:ident).+ ~= $pat:tt $sub:expr) => (
$m.add(_query_impl!(@field $($field).+), $crate::Action::Replace($crate::WrappedRegex($pat.parse().unwrap()), String::from($sub)))
);
(@field $($part:tt)+) => (
_query_impl!(@concat $(_query_impl!(@stringify $part)),+)
);
(@tts $($x:tt)+) => (
_query_impl!(@vec $(_query_impl!(@stringify $x)),+)
);
(@call $cb:ident, $($args:tt)*) => ( query_extr!(@call $cb, $($args)*) );
(@vec $($content:tt)*) => ( query_extr!(@vec $($content)*) );
(@stringify $($content:tt)*) => ( query_extr!(@stringify $($content)*) );
(@concat $($content:tt)*) => ( query_extr!(@concat $($content)*) );
(@json $($content:tt)*) => ( query_extr!(@json $($content)*) );
}
#[macro_export]
#[doc(hidden)]
macro_rules! query_extr {
(@call $cb:ident, $($args:tt)*) => ( $cb!($($args)*) );
(@vec $($content:tt)*) => ( vec! [ $($content)* ] );
(@stringify $($content:tt)*) => ( stringify! { $($content)* } );
(@concat $($content:tt)*) => ( concat! { $($content)* } );
(@json $($content:tt)*) => ( json! { $($content)* } );
}
#[cfg(test)]
mod test {
mod filter {
use serde_json::from_value;
#[test]
fn empty() {
assert_eq!(query!(@filter ), json_val!(null));
}
#[test]
fn comp_eq() {
assert_eq!(query!(@filter f == 123), json_val!({ "f": { "$eq": 123 } }));
assert_eq!(
query!(@filter f != 123),
json_val!({ "$not": { "f": { "$eq": 123 } } })
);
assert_eq!(
query!(@filter f != "abc"),
json_val!({ "$not": { "f": { "$eq": "abc" } } })
);
let v = 123;
assert_eq!(query!(@filter f == v), json_val!({ "f": { "$eq": v } }));
assert_eq!(query!(@filter f == (v + 1)), json_val!({ "f": { "$eq": (v + 1) } }));
assert_eq!(query!(@filter f == {v * 2}), json_val!({ "f": { "$eq": (v * 2) } }));
}
#[test]
fn comp_in() {
assert_eq!(
query!(@filter f of [1, 2, 3]),
json_val!({ "f": { "$in": [1, 2, 3] } })
);
assert_eq!(
query!(@filter f !of [1, 2, 3]),
json_val!({ "$not": { "f": { "$in": [1, 2, 3] } } })
);
assert_eq!(
query!(@filter f of ["a", "b", "c"]),
json_val!({ "f": { "$in": ["a", "b", "c"] } })
);
let v = [1, 2, 3];
assert_eq!(
query!(@filter f of v),
json_val!({ "f": { "$in": v } })
);
}
#[test]
fn comp_lt() {
assert_eq!(query!(@filter f < 123), json_val!({ "f": { "$lt": 123 } }));
}
#[test]
fn comp_le() {
assert_eq!(query!(@filter f <= 123), json_val!({ "f": { "$le": 123 } }));
}
#[test]
fn comp_gt() {
assert_eq!(query!(@filter f > 123), json_val!({ "f": { "$gt": 123 } }));
}
#[test]
fn comp_ge() {
assert_eq!(query!(@filter f >= 123), json_val!({ "f": { "$ge": 123 } }));
}
#[test]
fn comp_bw() {
assert_eq!(
query!(@filter f in 12..34),
json_val!({ "f": { "$bw": [12, true, 34, true] } })
);
assert_eq!(
query!(@filter f <in> 12..34),
json_val!({ "f": { "$bw": [12, false, 34, false] } })
);
assert_eq!(
query!(@filter f <in 12..34),
json_val!({ "f": { "$bw": [12, false, 34, true] } })
);
assert_eq!(
query!(@filter f in> 12..34),
json_val!({ "f": { "$bw": [12, true, 34, false] } })
);
assert_eq!(
query!(@filter f in -12..-34),
json_val!({ "f": { "$bw": [-12, true, -34, true] } })
);
assert_eq!(
query!(@filter f <in> -12..-34),
json_val!({ "f": { "$bw": [-12, false, -34, false] } })
);
assert_eq!(
query!(@filter f <in -12..-34),
json_val!({ "f": { "$bw": [-12, false, -34, true] } })
);
assert_eq!(
query!(@filter f in> -12..-34),
json_val!({ "f": { "$bw": [-12, true, -34, false] } })
);
}
#[test]
fn comp_has() {
assert_eq!(query!(@filter f?), json_val!({ "f": "$has" }));
}
#[test]
fn cond_not() {
assert_eq!(query!(@filter !a?), json_val!({ "$not": { "a": "$has" } }));
}
#[test]
fn cond_and() {
assert_eq!(
query!(@filter a == 3 && b >= 1),
json_val!({ "$and": [ { "a": { "$eq": 3 } }, { "b": { "$ge": 1 } } ] })
);
}
#[test]
fn cond_or() {
assert_eq!(
query!(@filter a == "abc" || b <in> 12..34),
json_val!({ "$or": [ { "a": { "$eq": "abc" } }, { "b": { "$bw": [12, false, 34, false] } } ] })
);
}
#[test]
fn cond_not_and() {
assert_eq!(
query!(@filter !(a == "abc" && b <in> 12..34)),
json_val!({ "$not": { "$and": [ { "a": { "$eq": "abc" } }, { "b": { "$bw": [12, false, 34, false] } } ] } })
);
}
#[test]
fn cond_and_not() {
assert_eq!(
query!(@filter a != "abc" && !(b <in> 12..34)),
json_val!({ "$and": [ { "$not": { "a": { "$eq": "abc" } } }, { "$not": { "b": { "$bw": [12, false, 34, false] } } } ] })
);
}
#[test]
fn cond_and_or() {
let b_edge = 10;
assert_eq!(
query!(@filter a of [1, 2, 3] && (b > b_edge || b < -b_edge)),
json_val!({ "$and": [ { "a": { "$in": [1, 2, 3] } }, { "$or": [ { "b": { "$gt": 10 } }, { "b": { "$lt": -10 } } ] } ] })
);
}
#[test]
fn comp_sub_fields() {
assert_eq!(
query!(@filter a.b.c == 1),
json_val!({ "a.b.c": { "$eq": 1 } })
);
}
#[test]
fn or_nested_and() {
assert_eq!(
query!(@filter a == 1 || !b == "abc" && c < 5),
json_val!({ "$or": [
{ "a": { "$eq": 1 } },
{ "$and": [
{ "$not": { "b": { "$eq": "abc" } } },
{ "c": { "$lt": 5 } }
] }
] })
);
assert_eq!(
query!(@filter a == 1 && !b == "abc" || c < 5),
json_val!({ "$or": [
{ "$and": [
{ "a": { "$eq": 1 } },
{ "$not": { "b": { "$eq": "abc" } } }
] },
{ "c": { "$lt": 5 } }
] })
);
}
#[test]
fn and_nested_or() {
assert_eq!(
query!(@filter (a == 1 || !b == "abc") && c < 5),
json_val!({ "$and": [
{ "$or": [
{ "a": { "$eq": 1 } },
{ "$not": { "b": { "$eq": "abc" } } }
] },
{ "c": { "$lt": 5 } }
] })
);
assert_eq!(
query!(@filter a == 1 && (!b == "abc" || c < 5)),
json_val!({ "$and": [
{ "a": { "$eq": 1 } },
{ "$or": [
{ "$not": { "b": { "$eq": "abc" } } },
{ "c": { "$lt": 5 } }
] }
] })
);
}
#[test]
fn pattern_map() {
assert_eq!(query!(@filter field.* == "abc"), json_val!({ "field.*": { "$eq": "abc" } }));
assert_eq!(query!(@filter field.*.subfield == 1), json_val!({ "field.*.subfield": { "$eq": 1 } }));
}
}
mod order {
use serde_json::from_value;
#[test]
fn default() {
assert_eq!(query!(@order ), json_val!("$asc"));
}
#[test]
fn primary_asc() {
assert_eq!(query!(@order >), json_val!("$asc"));
assert_eq!(query!(@order asc), json_val!("$asc"));
}
#[test]
fn primary_desc() {
assert_eq!(query!(@order <), json_val!("$desc"));
assert_eq!(query!(@order desc), json_val!("$desc"));
}
#[test]
fn field_asc() {
assert_eq!(query!(@order by field >), json_val!({ "field": "$asc" }));
assert_eq!(query!(@order by field asc), json_val!({ "field": "$asc" }));
}
#[test]
fn field_desc() {
assert_eq!(query!(@order by a.b.c <), json_val!({ "a.b.c": "$desc" }));
assert_eq!(
query!(@order by a.b.c desc),
json_val!({ "a.b.c": "$desc" })
);
}
#[test]
fn pattern_map() {
assert_eq!(query!(@order by field.*), json_val!({ "field.*": "$asc" }));
assert_eq!(
query!(@order by field.*.subfield desc),
json_val!({ "field.*.subfield": "$desc" })
);
}
}
mod modify {
use serde_json::{from_value, json};
#[test]
fn empty() {
assert_eq!(query!(@modify ), json_val!({}));
}
#[test]
fn set() {
assert_eq!(query!(@modify a = 1u32), json_val!({ "a": { "$set": 1 } }));
assert_eq!(
query!(@modify a = 123u32, b.c = "abc"),
json_val!({ "a": { "$set": 123 }, "b.c": { "$set": "abc" } })
);
assert_eq!(
query!(@modify
a = 123u32;
b.c = "abc";
),
json_val!({ "a": { "$set": 123 }, "b.c": { "$set": "abc" } })
);
}
#[test]
fn delete() {
assert_eq!(query!(@modify field ~), json_val!({ "field": "$delete" }));
assert_eq!(
query!(@modify field ~, other.field ~),
json_val!({ "field": "$delete", "other.field": "$delete" })
);
}
#[test]
fn add() {
assert_eq!(
query!(@modify field += 123u32),
json_val!({ "field": { "$add": 123 } })
);
assert_eq!(
query!(@modify field += 123u32, other.field += "abc"),
json_val!({ "field": { "$add": 123 }, "other.field": { "$add": "abc" } })
);
}
#[test]
fn sub() {
assert_eq!(
query!(@modify field -= 123u32),
json_val!({ "field": { "$sub": 123 } })
);
assert_eq!(
query!(@modify field -= 123u32, other.field -= "abc"),
json_val!({ "field": { "$sub": 123 }, "other.field": { "$sub": "abc" } })
);
}
#[test]
fn mul() {
assert_eq!(
query!(@modify field *= 123u32),
json_val!({ "field": { "$mul": 123 } })
);
assert_eq!(
query!(@modify field *= 123u32, other.field *= "abc"),
json_val!({ "field": { "$mul": 123 }, "other.field": { "$mul": "abc" } })
);
}
#[test]
fn div() {
assert_eq!(
query!(@modify field /= 123u32),
json_val!({ "field": { "$div": 123 } })
);
assert_eq!(
query!(@modify field /= 123u32, other.field /= "abc"),
json_val!({ "field": { "$div": 123 }, "other.field": { "$div": "abc" } })
);
}
#[test]
fn toggle() {
assert_eq!(query!(@modify field!), json_val!({ "field": "$toggle" }));
assert_eq!(
query!(@modify field!, other.field!),
json_val!({ "field": "$toggle", "other.field": "$toggle" })
);
assert_eq!(
query!(@modify field!; field!),
json_val!({ "field": ["$toggle", "$toggle"] })
);
}
#[test]
fn replace() {
assert_eq!(
query!(@modify field ~= "abc" "def"),
json_val!({ "field": { "$replace": ["abc", "def"] } })
);
assert_eq!(
query!(@modify field ~= "abc" "def", other.field ~= "april" "may"),
json_val!({ "field": { "$replace": ["abc", "def"] }, "other.field": { "$replace": ["april", "may"] } })
);
}
#[test]
fn splice() {
assert_eq!(
query!(@modify field[1..2]~),
json_val!({ "field": { "$splice": [1, 2] } })
);
let a = 3;
let b = 6;
assert_eq!(
query!(@modify field[a..b]~),
json_val!({ "field": { "$splice": [3, 6] } })
);
let range = 2..7;
assert_eq!(
query!(@modify field[range]~),
json_val!({ "field": { "$splice": [2, 7] } })
);
assert_eq!(
query!(@modify field[1..2] = ["a", "b", "c"]),
json_val!({ "field": { "$splice": [1, 2, "a", "b", "c"] } })
);
let ins = [1u8, 2, 3];
assert_eq!(
query!(@modify other.field[-1..0] = ins),
json_val!({ "other.field": { "$splice": [-1, 0, 1, 2, 3] } })
);
assert_eq!(
query!(@modify field[..]~),
json_val!({ "field": { "$splice": [0, -1] } })
);
}
#[test]
fn merge() {
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
struct Extra {
subfield: bool,
other: u8,
}
let extra = Extra {
subfield: true,
other: 123,
};
assert_eq!(
query!(@modify field ~= extra),
json_val!({ "field": { "$merge": { "subfield": true, "other": 123 } } })
);
assert_eq!(
query!(@modify field ~= { "subfield": true, "other": 123 }),
json_val!({ "field": { "$merge": { "subfield": true, "other": 123 } } })
);
}
}
}