Skip to main content

approx_derive/
lib.rs

1#![deny(missing_docs)]
2//! This crate provides derive macros for the
3//! [AbsDiffEq](https://docs.rs/approx/latest/approx/trait.AbsDiffEq.html) and
4//! [RelativeEq](https://docs.rs/approx/latest/approx/trait.RelativeEq.html) traits of the
5//! [approx](https://docs.rs/approx/latest/approx/) crate.
6//!
7//! These derive macros only implement both traits with `...<Rhs = Self>`.
8//! The macros infer the `EPSILON` type of the [AbsDiffEq] trait by looking
9//! at the type of the first struct or enum field or any type specified by the user.
10//!
11//! This table lists all attributes which can be used to customize the derived traits.
12//! They are ordered in descending priority, meaning setting the `#[approx(equal)]` will overwrite
13//! any specifications made in the `#[approx(map = ...)]` attribute.
14//!
15//! | Field Attribute | Functionality |
16//! |:--- | --- |
17//! | [`#[approx(skip)]`](#skipping-fields) | Skips the field entirely |
18//! | [`#[approx(equal)]`](#testing-for-equality) | Checks this field with `==` for Equality |
19//! | [`#[approx(cast_field)]`](#casting-fields) | Casts the field with `.. as ..` syntax. |
20//! | [`#[approx(cast_value)]`](#casting-fields) | Casts the epsilon value with `.. as ..` syntax. |
21//! | [`#[approx(map = ..)]`](#mapping-values) | Maps values before comparing them. |
22//! | [`#[approx(epsilon_map = ..)]`](#mapping-epsilon-values) | Maps epsilon values before using them. |
23//! | [`#[approx(static_epsilon = ..)]`](#static-values) | Defines a static epsilon value for this particular field. |
24//! | [`#[approx(into_iter)]`](#into-iterator) | Tries to use the `into_iterator` method to compare fields. |
25//! | | |
26//! | **Object Attribute** | |
27//! | [`#[approx(default_epsilon = ...)]`](#default-epsilon) | Sets the default epsilon value |
28//! | [`#[approx(default_max_relative = ...)]`](#default-max-relative) | Sets the default `max_relative` value. |
29//! | [`#[approx(epsilon_type = ...)]`](#epsilon-type) | Sets the type of the epsilon value |
30//!
31//! # Usage
32//!
33//! ```
34//! # use approx::*;
35//! use approx_derive::AbsDiffEq;
36//!
37//! // Define a new type and derive the AbsDiffEq trait
38//! #[derive(AbsDiffEq, PartialEq, Debug)]
39//! struct Position {
40//!     x: f64,
41//!     y: f64
42//! }
43//!
44//! // Compare if two given positions match
45//! // with respect to geiven epsilon.
46//! let p1 = Position { x: 1.01, y: 2.36 };
47//! let p2 = Position { x: 0.99, y: 2.38 };
48//! assert_abs_diff_eq!(p1, p2, epsilon = 0.021);
49//! ```
50//! In this case, the generated code looks something like this:
51//! ```ignore
52//! const _ : () =
53//! {
54//!     #[automatically_derived] impl approx :: AbsDiffEq for Position
55//!     {
56//!         type Epsilon = <f64 as approx::AbsDiffEq>::Epsilon;
57//!
58//!         fn default_epsilon() -> Self :: Epsilon {
59//!             <f64 as approx::AbsDiffEq>::default_epsilon()
60//!         }
61//!
62//!         fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
63//!             <f64 as approx::AbsDiffEq>::abs_diff_eq(
64//!                 &self.x,
65//!                 & other.x,
66//!                 epsilon.clone()
67//!             ) &&
68//!             <f64 as approx::AbsDiffEq>::abs_diff_eq(
69//!                 &self.y,
70//!                 &other.y,
71//!                 epsilon.clone()
72//!             ) && true
73//!         }
74//!     }
75//! };
76//! ```
77//! The [AbsDiffEq] derive macro calls the `abs_diff_eq` method repeatedly on all fields
78//! to determine if all are matching.
79//!
80//! ## Enums
81//! Since `approx-derive` supports enums since `0.2`
82//!
83//! ```
84//! # use approx::*;
85//! use approx_derive::AbsDiffEq;
86//!
87//! #[derive(AbsDiffEq, PartialEq, Debug)]
88//! enum Position {
89//!     Smooth { x: f32, y: f32, },
90//!     #[approx(cast_value)]
91//!     Lattice { x: isize, y: isize },
92//! }
93//!
94//! let p1 = Position::Smooth { x: 1.0, y: 1.1 };
95//! let p2 = Position::Smooth { x: 1.1, y: 1.0};
96//! let p3 = Position::Lattice { x: 1, y: 1 };
97//!
98//! assert_abs_diff_eq!(p1, p2, epsilon=0.2);
99//! ```
100//!
101//! ```should_panic
102//! # use approx::*;
103//! # use approx_derive::AbsDiffEq;
104//! # #[derive(AbsDiffEq, PartialEq, Debug)]
105//! # enum Position {
106//! #     Smooth { x: f32, y: f32, },
107//! #     #[approx(cast_value)]
108//! #     Lattice { x: isize, y: isize },
109//! # }
110//! # let p1 = Position::Smooth { x: 1.0, y: 1.1 };
111//! # let p3 = Position::Lattice { x: 1, y: 1 };
112//! // Note! Different enum variants can never be equal!
113//! assert_abs_diff_eq!(p1, p3, epsilon = 1000.0);
114//! ```
115//!
116//!
117//! # Field Attributes
118//! ## Skipping Fields
119//!
120//! Sometimes, we only want to compare certain fields and omit others completely.
121//! ```
122//! # use approx::*;
123//! # use approx_derive::*;
124//! #[derive(AbsDiffEq, PartialEq, Debug)]
125//! struct Player {
126//!     hit_points: f32,
127//!     pos_x: f32,
128//!     pos_y: f32,
129//!     #[approx(skip)]
130//!     id: (usize, usize),
131//! }
132//!
133//! let player1 = Player {
134//!     hit_points: 100.0,
135//!     pos_x: 2.0,
136//!     pos_y: -650.345,
137//!     id: (0, 1),
138//! };
139//!
140//! let player2 = Player {
141//!     hit_points: 99.9,
142//!     pos_x: 2.001,
143//!     pos_y: -649.898,
144//!     id: (22, 0),
145//! };
146//!
147//! assert_abs_diff_eq!(player1, player2, epsilon = 0.5);
148//! ```
149//!
150//! ## Testing for [Equality](core::cmp::Eq)
151//!
152//! When identical equality is desired, we can specify this with the `#[approx(equal)]` attribute.
153//!
154//! ```
155//! # use approx::*;
156//! # use approx_derive::*;
157//! #[derive(AbsDiffEq, PartialEq, Debug)]
158//! struct Prediction {
159//!     confidence: f64,
160//!     #[approx(equal)]
161//!     category: String,
162//! }
163//! ```
164//!
165//! Note that in this case, the type of the epsilon value for the implementation of
166//! [AbsDiffEq](https://docs.rs/approx/latest/approx/trait.AbsDiffEq.html) is inferred from the
167//! first field of the `Prediction` struct.
168//! This means if we reorder the arguments of the struct, we need to manually set the epsilon type.
169//!
170//! ```
171//! # use approx_derive::*;
172//! #[derive(AbsDiffEq, PartialEq, Debug)]
173//! #[approx(epsilon_type = f64)]
174//! struct Prediction {
175//!     #[approx(equal)]
176//!     category: String,
177//!     confidence: f64,
178//! }
179//! ```
180//!
181//! ## Casting Fields
182//!
183//! Structs which consist of multiple fields with different
184//! numeric types, can not be derived without additional hints.
185//! After all, we should specify how this type mismatch will be handled.
186//!
187//! ```compile_fail
188//! # use approx::*;
189//! # use approx_derive::*;
190//! #[derive(AbsDiffEq, PartialEq, Debug)]
191//! struct MyStruct {
192//!     v1: f32,
193//!     v2: f64,
194//! }
195//! ```
196//!
197//! We can use the `#[approx(cast_field)]` and `#[approx(cast_value)]`
198//! attributes to achieve this goal.
199//!
200//! ### Example 1
201//! Here, the second field will be casted to the type of the inferred epsilon value (`f32`).
202//! We can check this by testing if a change in the size of `f64::MIN_POSITIVE` would get lost by
203//! this procedure.
204//! ```
205//! # use approx::*;
206//! # use approx_derive::*;
207//! # #[derive(RelativeEq, PartialEq, Debug)]
208//! # struct MyStruct {
209//! #   v1: f32,
210//! #   #[approx(cast_field)]
211//! #   v2: f64,
212//! # }
213//! let ms1 = MyStruct {
214//!     v1: 1.0,
215//!     v2: 3.0,
216//! };
217//! let ms2 = MyStruct {
218//!     v1: 1.0,
219//!     v2: 3.0 + f64::MIN_POSITIVE,
220//! };
221//! assert_relative_eq!(ms1, ms2);
222//! ```
223//!
224//! ### Example 2
225//! In this example, we cast the `f64` type to `isize`.
226//! ```
227//! # use approx::*;
228//! # use approx_derive::*;
229//! #[derive(AbsDiffEq, PartialEq, Debug)]
230//! struct MyStruct {
231//!     v1: isize,
232//!     #[approx(cast_field)]
233//!     v2: f64,
234//! }
235//! let ms1 = MyStruct { v1: 1, v2: 2.0 };
236//! let ms2 = MyStruct { v1: 1, v2: 2.1 };
237//! assert_abs_diff_eq!(ms1, ms2);
238//!
239//! // The underlying generated code performs
240//! assert!(isize::abs_diff_eq(
241//!     &(ms1.v2 as isize),
242//!     &(ms2.v2 as isize),
243//!     0,
244//! ));
245//! ```
246//! When we use the `#[approx(cast_value)]` syntax, we get a different result.
247//! ```
248//! # use approx::*;
249//! # use approx_derive::*;
250//! #[derive(AbsDiffEq, PartialEq, Debug)]
251//! struct MyStruct2 {
252//!     v1: isize,
253//!     #[approx(cast_value)]
254//!     v2: f64,
255//! }
256//! let ms1 = MyStruct2 { v1: 1, v2: 2.0 };
257//! let ms2 = MyStruct2 { v1: 1, v2: 2.1 };
258//! assert_abs_diff_ne!(ms1, ms2);
259//!
260//! // Here, the epsilon value for isize is casted to f64
261//! assert!(!f64::abs_diff_eq(
262//!     &ms1.v2,
263//!     &ms2.v2,
264//!     0isize as f64
265//! ));
266//! ```
267//!
268//! ## Mapping Values
269//!
270//! We can map values before comparing them.
271//! By default, we need to return an option of the value in question.
272//! This allows to do computations where error can occur.
273//! Although this error is not caught, the comparison will fail if any of the two compared objects
274//! return a `None` value.
275//! ```
276//! # use approx_derive::*;
277//! # use approx::*;
278//! #[derive(AbsDiffEq, PartialEq, Debug)]
279//! struct Tower {
280//!     height_in_meters: f32,
281//!     #[approx(map = |x: &f32| Some(x.sqrt()))]
282//!     area_in_meters_squared: f32,
283//! }
284//! # let t1 = Tower {
285//! #   height_in_meters: 100.0,
286//! #   area_in_meters_squared: 30.1,
287//! # };
288//! # let t2 = Tower {
289//! #   height_in_meters: 100.0,
290//! #   area_in_meters_squared: 30.5,
291//! # };
292//! # assert_abs_diff_ne!(t1, t2, epsilon = 0.03);
293//! ```
294//!
295//! This functionality can also be useful when having more complex datatypes.
296//! ```
297//! # use approx::*;
298//! # use approx_derive::*;
299//! #[derive(PartialEq, Debug)]
300//! enum Time {
301//!     Years(u16),
302//!     Months(u16),
303//!     Weeks(u16),
304//!     Days(u16),
305//! }
306//!
307//! fn time_to_days(time: &Time) -> Option<u16> {
308//!     match time {
309//!         Time::Years(y) => Some(365 * y),
310//!         Time::Months(m) => Some(30 * m),
311//!         Time::Weeks(w) => Some(7 * w),
312//!         Time::Days(d) => Some(*d),
313//!     }
314//! }
315//!
316//! #[derive(AbsDiffEq, PartialEq, Debug)]
317//! #[approx(epsilon_type = u16)]
318//! struct Dog {
319//!     #[approx(map = time_to_days)]
320//!     age: Time,
321//!     #[approx(map = time_to_days)]
322//!     next_doctors_appointment: Time,
323//! }
324//! ```
325//!
326//! ## Mapping Epsilon Values
327//!
328//! We can also map `epsilon` values before using them. This is usefull i.e. for tuples or arrays.
329//!
330//! ```
331//! # use approx::*;
332//! # use approx_derive::*;
333//! #[derive(AbsDiffEq, PartialEq, Debug)]
334//! struct DifferentialEvolution {
335//!     recombination: f32,
336//!     #[approx(epsilon_map = |x| (x, x))]
337//!     mutation: (f32, f32),
338//! }
339//! # let d1 = DifferentialEvolution {
340//! #   recombination: 0.7,
341//! #   mutation: (0.5, 1.5),
342//! # };
343//! # let d2 = DifferentialEvolution {
344//! #   recombination: 0.7001,
345//! #   mutation: (0.501, 1.499),
346//! # };
347//! # assert_abs_diff_eq!(d1, d2, epsilon = 0.02);
348//! ```
349//!
350//! ## Static Values
351//! We can force a static `EPSILON` or `max_relative` value for individual fields.
352//! ```
353//! # use approx::*;
354//! # use approx_derive::*;
355//! #[derive(AbsDiffEq, PartialEq, Debug)]
356//! struct Rectangle {
357//!     #[approx(static_epsilon = 5e-2)]
358//!     a: f64,
359//!     b: f64,
360//!     #[approx(static_epsilon = 7e-2)]
361//!     c: f64,
362//! }
363//!
364//! let r1 = Rectangle {
365//!     a: 100.01,
366//!     b: 40.0001,
367//!     c: 30.055,
368//! };
369//! let r2 = Rectangle {
370//!     a: 99.97,
371//!     b: 40.0005,
372//!     c: 30.049,
373//! };
374//!
375//! // This is always true although the epsilon is smaller than the
376//! // difference between fields a and b respectively.
377//! assert_abs_diff_eq!(r1, r2, epsilon = 1e-1);
378//! assert_abs_diff_eq!(r1, r2, epsilon = 1e-2);
379//! assert_abs_diff_eq!(r1, r2, epsilon = 1e-3);
380//!
381//! // Here, the epsilon value has become larger than the difference between the
382//! // b field values.
383//! assert_abs_diff_ne!(r1, r2, epsilon = 1e-4);
384//! ```
385//! # Object Attributes
386//! ## Default Epsilon
387//! The [AbsDiffEq] trait allows to specify a default value for its `EPSILON` associated type.
388//! We can control this value by specifying it on an object level.
389//!
390//! ```
391//! # use approx::*;
392//! # use approx_derive::*;
393//! #[derive(AbsDiffEq, PartialEq, Debug)]
394//! #[approx(default_epsilon = 10)]
395//! struct Benchmark {
396//!     cycles: u64,
397//!     warm_up: u64,
398//! }
399//!
400//! let benchmark1 = Benchmark {
401//!     cycles: 248,
402//!     warm_up: 36,
403//! };
404//! let benchmark2 = Benchmark {
405//!     cycles: 239,
406//!     warm_up: 28,
407//! };
408//!
409//! // When testing with not additional arguments, the results match
410//! assert_abs_diff_eq!(benchmark1, benchmark2);
411//! // Once we specify a lower epsilon, the values do not agree anymore.
412//! assert_abs_diff_ne!(benchmark1, benchmark2, epsilon = 5);
413//! ```
414//!
415//! ## Default Max Relative
416//! Similarly to [Default Epsilon], we can also choose a default max_relative devaition.
417//! ```
418//! # use approx_derive::*;
419//! # use approx::*;
420//! #[derive(RelativeEq, PartialEq, Debug)]
421//! #[approx(default_max_relative = 0.1)]
422//! struct Benchmark {
423//!     time: f32,
424//!     warm_up: f32,
425//! }
426//!
427//! let bench1 = Benchmark {
428//!     time: 3.502785781,
429//!     warm_up: 0.58039458,
430//! };
431//! let bench2 = Benchmark {
432//!     time: 3.7023458,
433//!     warm_up: 0.59015897,
434//! };
435//!
436//! assert_relative_eq!(bench1, bench2);
437//! assert_relative_ne!(bench1, bench2, max_relative = 0.05);
438//! ```
439//! ## Epsilon Type
440//! When specifying nothing, the macros will infer the `EPSILON` type from the type of the
441//! first struct/enum field (the order in which it is parsed).
442//! This can be problematic in certain scenarios which is why we can also manually specify this
443//! type.
444//!
445//! ```
446//! # use approx::*;
447//! # use approx_derive::*;
448//! #[derive(RelativeEq, PartialEq, Debug)]
449//! #[approx(epsilon_type = f32)]
450//! struct Car {
451//!     #[approx(cast_field)]
452//!     produced_year: u32,
453//!     horse_power: f32,
454//! }
455//!
456//! let car1 = Car {
457//!     produced_year: 1992,
458//!     horse_power: 122.87,
459//! };
460//! let car2 = Car {
461//!     produced_year: 2000,
462//!     horse_power: 117.45,
463//! };
464//!
465//! assert_relative_eq!(car1, car2, max_relative = 0.05);
466//! assert_relative_ne!(car1, car2, max_relative = 0.01);
467//! ```
468//!
469//! # Into Iterator
470//! To compare two fields which consist of a iterable list of values, we can use the
471//! `#[approx(into_iter)]` field attribute.
472//!
473//! ```
474//! # use approx::*;
475//! # use approx_derive::*;
476//! #[derive(AbsDiffEq, PartialEq, Debug)]
477//! struct Parameter {
478//!     value: f32,
479//!     #[approx(into_iter)]
480//!     bounds: [f32; 2],
481//! }
482//! let p1 = Parameter { value: 3.144, bounds: [0.0, 10.0] };
483//! let p2 = Parameter { value: 3.145, bounds: [0.1, 10.2] };
484//!
485//! assert_abs_diff_ne!(p1, p2);
486//! assert_abs_diff_eq!(p1, p2, epsilon = 0.21);
487//! ```
488//! It has to be noted that whenever both iterator are not of the same length, that the comparison
489//! will fail.
490//!
491//! ```should_panic
492//! # use approx::*;
493//! # use approx_derive::*;
494//! #[derive(AbsDiffEq, PartialEq, Debug)]
495//! #[approx(epsilon_type = f64)]
496//! struct Polynomial {
497//!     #[approx(into_iter)]
498//!     coefficients: Vec<f64>,
499//! }
500//! let poly1 = Polynomial { coefficients: vec![1.0, 0.5] };
501//! let poly2 = Polynomial { coefficients: vec![1.0, 0.5, 1.0/6.0] };
502//! assert_abs_diff_eq!(poly1, poly2);
503//! ```
504
505mod abs_diff_eq;
506mod args_parsing;
507mod base_types;
508mod rel_diff_eq;
509
510use args_parsing::*;
511use base_types::*;
512
513struct AbsDiffEqParser {
514    pub base_type: BaseType,
515    pub struct_args: StructArgs,
516}
517
518/// See the [crate] level documentation for a guide.
519#[proc_macro_derive(AbsDiffEq, attributes(approx))]
520pub fn derive_abs_diff_eq(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
521    let parsed = syn::parse_macro_input!(input as AbsDiffEqParser);
522    parsed.implement_derive_abs_diff_eq().into()
523}
524
525/// See the [crate] level documentation for a guide.
526#[proc_macro_derive(RelativeEq, attributes(approx))]
527pub fn derive_rel_diff_eq(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
528    let parsed = syn::parse_macro_input!(input as AbsDiffEqParser);
529    let mut output = quote::quote!();
530    output.extend(parsed.implement_derive_abs_diff_eq());
531    output.extend(parsed.implement_derive_rel_diff_eq());
532    output.into()
533}