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 AffineTraversals by composing Lenses and Prisms. 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 AffineTraversals. 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 });