fluent_comparisons_macros/
lib.rs

1//! This crate contains the macros for the fluent-comparisons crate
2
3#[macro_export]
4#[doc(hidden)]
5/// # Internal Macro
6/// This macro checks that the comparison operator in the expression is indeed allowed. If it is
7/// allowed this macro evaluates to a unit/void statement. Otherwise it will give a compile error
8/// explaining which operators are allowed
9//#[macro_export]
10#[doc(hidden)]
11macro_rules! __check_operator {
12    // these are the allowed comparison operators
13    (==) => {};
14    (<=) => {};
15    (>=) => {};
16    (!=) => {};
17    (<) => {};
18    (>) => {};
19    // everything else is not allowed, including &&, ||, and such
20    ($other:tt) => {
21        std::compile_error!(
22            "This operator is not allowed. The only allowed operators are ==, !=, <=, >=, <, >"
23        );
24    };
25}
26
27/// Compare all values in a set to a common right hand side and decide whether the comparison returns `true` for *any of the values* in the set.
28///
29/// # Lazy Evaluation
30///
31/// If we write `any_of!({a,b}<c)`, this is equivalent to the hand coded `a<c && b<c`. That means that the comparisons are
32/// evaluated [lazily](https://doc.rust-lang.org/reference/expressions/operator-expr.html#lazy-boolean-operators) from left to right. Once
33/// the truth value of the expression can be determined, the evaluation stops. That means that e.g. for the expression `any_of!({1,some_func()}<5)`,
34/// the function `some_func()` is not invoked.
35///
36/// # Usage
37///
38/// ## Basic Usage
39///
40/// For the basic use case we compare a set of values against a common right hand side. Invoke the macro using
41/// `any_of!({/*list of expressions*/} operator rhs)`, where operator can be any of the binary comparison operators, i.e.
42/// `==`, `!=`, `<=`, `<`, `>`, and `>=`. The list of expressions on the left hand side is comma separated. The right hand side
43/// is an expression as well.
44///
45/// The list of expressions can have a variadic number of elements but must have at least one. It must always be enclosed in
46/// curly braces. The expressions on the left hand side need not be of the same type, but the comparison with the right hand side must be valid. In particular,
47/// the expressions need not be numeric.
48///
49/// ```
50/// # use fluent_comparisons_macros::any_of;
51/// use rand::prelude::*;
52/// // given:
53/// let square = |val|val*val;
54/// let v = vec![1, 2,3];
55/// let mut rng = rand::thread_rng();
56/// // the following assertions hold
57/// assert!(any_of!({1,2,3}>2));
58/// assert!(any_of!({4+4+1,square(7*2),120_i32.pow(2)}>8));
59/// assert!(any_of!( {rng.gen::<usize>(),v.len(),2,1+1,"hello world".len()} == v.len()));
60/// ```
61///
62/// ## Usage with Transformations
63///
64/// We can also apply a transformation to the list on the left hand side before comparing to the right hand side.
65/// For that, simply append `.map(...)` to the list and give an argument that transforms the values. The argument
66/// to map can be any kind of invokable of a single argument, like a function or closure. Here the type requirements
67/// are a bit stricter and all values on the left hand side must be of the same type.
68///
69/// ```
70/// # use fluent_comparisons_macros::any_of;
71/// // given
72/// let square = |x|x*x;
73/// // the following assertions hold
74/// assert!(any_of!({4,square(2),2_i32.pow(2)}.map(|x|x+5)>8));
75/// assert!(any_of!({4+1,3,5}.map(square)==9));
76/// ```
77///
78/// ## Usage with Predicates
79///
80/// This is a special case where the transformation maps to a boolean predicate. Instead of writing
81/// `any_of!({...}.map(/*predicate f:x -> bool*/)==true)`, we can use the syntax `any_of!({...}.satisfy(/*predicate f:x -> bool*/))`,
82/// which saves us the comparison with `true` on the right hand side. Don't use a predicate which
83/// compares values with one of the comparison operators, because then you are better served with the
84/// syntax above. Rather use it for more complex predicates:
85///
86/// ```
87/// # use fluent_comparisons_macros::any_of;
88/// fn is_prime_number(x:i32) -> bool {
89///     /*some interesting math*/
90/// # true
91/// };
92/// //this assertion holds
93/// assert!(any_of!({12,14,5}.satisfy(is_prime_number)));
94/// ```
95///
96///
97#[macro_export]
98macro_rules! any_of {
99    // variant with a predicate (does not use a comparison operator and rhs)
100    ( {$($lh_sides:expr),+ $(,)?}.satisfy($($func:tt)+) ) => {
101        any_of!({$($lh_sides),+}.map($($func)+)==true)
102    };
103
104    // variant with a transformation of the set
105    ( {$($lh_sides:expr),+ $(,)?}.map($($func:tt)+) $operator:tt $rhs:expr) => {
106        {
107            $crate::__check_operator!($operator);
108            //by fixing this here, we have more type deduction powers but also less
109            //flexibility in generic arguments. We could also pass the expanded tt func to a single
110            //tt in a submacro (by putting (...) around it) and then use that function, which is more
111            //powerful when passing generic functions, but less intuitive when passing lambdas
112            //so this map is more akin to a map in a collection. The other is more akin to a C++
113            //transform of a heterogeneous collection. For that we might want to pass a path or ident
114            //instead of the token tree. Because the token tree is just a trick to get lambdas
115            //as well. But since the other way isn't great for lambdas anyway we can just skip it.
116            let map_func = $($func)+;
117            $( (map_func($lh_sides) $operator $rhs) )||+
118        }
119    };
120
121    //variant without map (requires a comparison operator and rhs)
122    ( {$($lh_sides:expr),+ $(,)?} $operator:tt $rhs:expr)=> {
123        {
124            $crate::__check_operator!($operator);
125            $( ($lh_sides $operator $rhs) )||+
126        }
127    };
128}
129
130/// Compare all values in a set to a common right hand side and decide whether the comparison returns `true` for *all of the values* in the set.
131///
132/// # Usage
133/// The usage is analogous to the [any_of](crate::any_of) macro and is documented in more detail there.
134/// Just like `any_of`, this macro also performs lazy evaluation.
135///
136/// ## Examples
137/// The following examples show how to use the macro.
138/// ```
139/// # use fluent_comparisons_macros::all_of;
140///
141/// let square = |val|val*val;
142/// // the following assertion holds
143/// assert!(all_of!({4+4+1,square(7*2),120_i32.pow(2)}>0));
144///
145/// let v = vec![1, 2,3,4,5];
146/// // the following assertion holds
147/// assert!(all_of!( {square(2),v.len() as i32,2,1+1,"hello".len() as i32} <= v.len() as i32));
148/// ```
149#[macro_export]
150macro_rules! all_of {
151
152    ( {$($lh_sides:expr),+ $(,)?}.satisfy($($func:tt)+) ) => {
153        all_of!({$($lh_sides),+}.map($($func)+)==true)
154    };
155
156    ( {$($lh_sides:expr),+ $(,)?}.map($($func:tt)+) $operator:tt $rhs:expr) => {
157        {
158            $crate::__check_operator!($operator);
159            let map_func = $($func)+;
160            $( (map_func($lh_sides) $operator $rhs) )&&+
161        }
162    };
163
164    ( {$($lh_sides:expr),+ $(,)?} $operator:tt $rhs:expr)=> {
165        {
166            $crate::__check_operator!($operator);
167            $( ($lh_sides $operator $rhs) )&&+
168        }
169    };
170}
171
172/// Compare all values in a set to a common right hand side and decide whether the comparison returns `true` for *none of the values* in the set.
173///
174/// # Usage
175/// The usage is analogous to the [any_of](crate::any_of) macro and is documented in more detail there.
176/// Just like `any_of`, this macro also performs lazy evaluation.
177///
178/// ## Examples
179/// The following examples show how to use the macro.
180/// ```
181/// # use fluent_comparisons_macros::none_of;
182///
183/// let square = |val|val*val;
184/// // the following assertion holds
185/// assert!(none_of!({4+4+1,square(7*2),120_i32.pow(2)}<0));
186///
187/// let v = vec![1, 2,3,4,5];
188/// // the following assertion holds
189/// assert!(none_of!( {square(2),v.len() as i32,2,1+1,"hello".len() as i32} > v.len() as i32));
190/// ```
191#[macro_export]
192macro_rules! none_of {
193    ( {$($lh_sides:expr),+ $(,)?}.satisfy($($func:tt)+) ) => {
194        none_of!({$($lh_sides),+}.map($($func)+)==true)
195    };
196
197    ( {$($lh_sides:expr),+ $(,)?}.map($($func:tt)+) $operator:tt $rhs:expr) => {
198        {
199            $crate::__check_operator!($operator);
200            let map_func = $($func)+;
201            $( !(map_func($lh_sides) $operator $rhs) )&&+
202        }
203    };
204
205    ( {$($lh_sides:expr),+ $(,)?} $operator:tt $rhs:expr)=> {
206        {
207            $crate::__check_operator!($operator);
208            $( !($lh_sides $operator $rhs) )&&+
209        }
210    };
211}