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