//! Traits for generic composition. /// Trait to get part of composed type. pub trait AsPart { /// 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 { /// 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` automatically implement `TryAsPart`. impl TryAsPart for T where T: AsPart, { 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`. 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`. 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 } } } }})* }; }