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}