Macro optics::declare_affine_traversal
source · macro_rules! declare_affine_traversal { ( $(#[$m:meta])* $vis:vis $name:ident as $base:ty => $target:ty $(, for<$($p:ident),+ $(,)?>)?, ($s:ident) => by_val: $by_val:expr, by_ref: $by_ref:expr, by_mut: $by_mut:expr $(,)? ) => { ... }; ( $(#[$m:meta])* $vis:vis $name:ident as $base:ty => $target:ty $(, for<$($p:ident),+ $(,)?>)?, ($s:ident) $(reused($wrap:ident))? => $reused:expr $(,)? ) => { ... }; }
Expand description
Declare an AffineTraversal
from an accessor expression.
Normally we obtain AffineTraversal
s by composing Lens
es and Prism
s. However, due to
the ownership system in Rust, and the lack of enum variants as standalone types, it is generally
difficult to define prisms for enum variants with more than one field.
Use this macro (as a workaround for the said problem) to directly define AffineTraversal
s.
Similar to the example for declare_lens
, the match
expression below is reused 3 times in
AffineFold::preview
, AffineFoldRef::preview_ref
, and AffineFoldMut::preview_mut
;
this relies on the fact that (shared, mutable) borrow of fields is implicit in match
expressions. Again, the reused(wrap)
syntax is available for complicated use cases.
#[derive(Debug, Copy, Clone, PartialEq)]
enum Color {
Rgba { red: f32, green: f32, blue: f32, alpha: f32 },
Hsla { hue: f32, saturation: f32, lightness: f32, alpha: f32 },
}
declare_affine_traversal! {
/// Alpha component for colors.
#[derive(Debug)]
_Green as Color => f32,
(c) => match c {
Color::Rgba { green, .. } => Ok(green),
_ => Err(_Green),
}
}
let mut rgba = Color::Rgba { red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0 };
let mut hsla = Color::Hsla { hue: 1.0, saturation: 1.0, lightness: 1.0, alpha: 1.0 };
// AffineFold(Ref,Mut)
assert_eq!(_Green.preview(rgba), Ok(1.0));
assert_eq!(_Green.preview_ref(&rgba), Ok(&1.0));
assert_eq!(_Green.preview_mut(&mut rgba), Ok(&mut 1.0));
assert_eq!(_Green.preview(hsla), Err(_Green));
assert_eq!(_Green.preview_ref(&hsla), Err(_Green));
assert_eq!(_Green.preview_mut(&mut hsla), Err(_Green));
// Setter
_Green.over(&mut rgba, &mut |green| *green *= 0.5);
assert_eq!(rgba, Color::Rgba { red: 1.0, green: 0.5, blue: 1.0, alpha: 1.0 });
_Green.over(&mut hsla, &mut |green| *green *= 0.5);
assert_eq!(hsla, Color::Hsla { hue: 1.0, saturation: 1.0, lightness: 1.0, alpha: 1.0 });
// AffineTraversal
_Green.set(&mut rgba, 0.0);
assert_eq!(rgba, Color::Rgba { red: 1.0, green: 0.0, blue: 1.0, alpha: 1.0 });
_Green.set(&mut hsla, 0.0);
assert_eq!(hsla, Color::Hsla { hue: 1.0, saturation: 1.0, lightness: 1.0, alpha: 1.0 });