sketchbook 0.0.2

Interactive visual applications in Rust
Documentation
//! Traits for generic composition.

/// Trait to get part of composed type.
pub trait AsPart<T> {
    /// Borrow part of composed type.
    fn as_part(&self) -> &T;

    /// Mutably borrow part of composed type.
    fn as_part_mut(&mut self) -> &mut T;
}

/// Trait to try to get part of composed type.
pub trait TryAsPart<T> {
    /// Try to borrow part of composed type.
    fn try_as_part(&self) -> Option<&T>;

    /// Try to mutably borrow part of composed type.
    fn try_as_part_mut(&mut self) -> Option<&mut T>;
}

/// Types that implement `AsPart<T>` automatically implement `TryAsPart<T>`.
impl<T, X> TryAsPart<X> for T
where
    T: AsPart<X>,
{
    fn try_as_part(&self) -> Option<&X> {
        Some(self.as_part())
    }

    fn try_as_part_mut(&mut self) -> Option<&mut X> {
        Some(self.as_part_mut())
    }
}

/// Helper macro.
#[doc(hidden)]
#[macro_export]
macro_rules! enable_if_part {
    {(part) {$($b:tt)*}} => {$($b)*};
    {() {$($b:tt)*}} => {};
}

/// Create type composed of other types.
///
/// For structs, this macro is used in the following way.
/// When the `#[part]` attribute is applied to a field, that field will be available using
/// `AsPart<T>`. Only one field can have a `#[part]` of a particular type.
///
/// ```
/// # use sketchbook::compose;
/// # use sketchbook::compose::AsPart;
/// compose! {
///     struct Test {
///         a: i32,
///         
///         #[part]
///         b: i32,
///                   
///         #[part]
///         c: f32,
///     }
/// }
///
/// let x = Test { a: 123, b: 456, c: 1.23 };
/// let y: &f32 = x.as_part();
/// assert_eq!(y, &1.23);
/// ```
///
/// For enums, this macro is used in the following way.
/// When the `#[part]` attribute is applied to a variant, that variant will be available using
/// `TryAsPart<T>`. Only one variant can have a `#[part]` of a particular type. Additionally, the
/// variant must be a tuple of a single value.
/// ```
/// # use sketchbook::compose;
/// # use sketchbook::compose::TryAsPart;
/// compose! {
///     enum Test {
///         A,
///         
///         #[part]
///         B(i32),
///                                            
///         #[part]
///         C(f32),
///     }
/// }
///                                            
/// let x = Test::B(123);
/// let y: Option<&i32> = x.try_as_part();
/// assert_eq!(y, Some(&123));
/// ```
#[macro_export]
macro_rules! compose {
    {
        $(#[$($meta_code:tt)*])?
        $v:vis struct $name:ident {
            $(
                $(#[$attr:ident])? $vi:vis $field:ident: $field_type:ty
            ),*$(,)?
        }
    } => {
        $(#[$($meta_code)*])?
        $v struct $name {
            $($vi $field: $field_type),*
        }

        $($crate::enable_if_part!{($($attr)?) {
            impl $crate::compose::AsPart<$field_type> for $name {
                fn as_part(&self) -> &$field_type {
                    &self.$field
                }

                fn as_part_mut(&mut self) -> &mut $field_type {
                    &mut self.$field
                }
            }
        }})*
    };
    {
        $(#[$($meta_code:tt)*])?
        $v:vis enum $name:ident {
            $($(#[$attr:ident])? $variant:ident$(($variant_type:ty))?),*$(,)?
        }
    } => {
        $(#[$($meta_code)*])?
        $v enum $name {
            $($variant$(($variant_type))?),*
        }

        $($crate::enable_if_part!{($($attr)?) {
            impl $crate::compose::TryAsPart<$($variant_type)?> for $name {
                fn try_as_part(&self) -> Option<&$($variant_type)?> {
                    match self {
                        Self::$variant(value) => Some(value),
                        #[allow(unreachable_patterns)]
                        _ => None
                    }
                }

                fn try_as_part_mut(&mut self) -> Option<&mut $($variant_type)?> {
                    match self {
                        Self::$variant(value) => Some(value),
                        #[allow(unreachable_patterns)]
                        _ => None
                    }
                }
            }
        }})*
    };
}