structural/
convert.rs

1//! Traits for converting between structural types.
2//!
3//! The reason these traits exist instead of using `From` and `TryFrom` is,
4//! because structural types are usually converted from
5//! generic types bounded by the `Foo_SI` traits generated by the `Structural` derive macro,
6//! and the `Foo_SI` trait is implemented by the `Foo` type,
7//! which means that those impls would overlap with
8//! the `impl From<T> for T` impl in the standard lbirary.
9
10use std_::fmt::{self, Display};
11
12#[cfg(feature = "std")]
13use std::error::Error as StdError;
14
15/// For conversions between structural types.
16///
17/// For enums,usually the from enum has to have same variants and
18/// at least the same fields as the into enum.<br>
19/// For structs,usually the from struct has to have at least the
20/// same fields as the into struct.
21///
22/// # Implementations
23///
24/// ### Structs
25///
26/// This trait is usually implemented for structs by:
27///
28/// - Bounding the trait type parameter with the `*_SI` structural alias generated for the
29/// struct by the `Structural` derive.
30///
31/// - Calling `into_fields` on the parameter to convert it into the fields in common with
32/// the `Self` struct.
33///
34/// ### Enums
35///
36/// This trait is usually implemented for enums by:
37///
38/// - Bounding the trait type parameter with the `*_ESI` structural alias generated for the
39/// enum by the `Structural` derive.
40///
41/// - Matching on all the variants of the enum with the switch macro,
42/// returning the same variant.
43///
44/// # Struct Examples
45///
46/// ### Derivation
47///
48/// This example demonstrates how this trait can be derived for structs.
49///
50/// ```rust
51/// use structural::{convert::FromStructural, StructuralExt, Structural, fp, make_struct};
52///
53/// {
54///     let this = Point{x: 33, y:45};
55///     assert_eq!(this.into_struc::<Entity>(), Entity{x: 33, y: 45, health: 100});
56/// }
57/// {
58///     let this = make_struct!{x: 100, y: 200, foo: "hello"};
59///     assert_eq!(this.into_struc::<Entity>(), Entity{x: 100, y: 200, health: 100});
60/// }
61#[cfg_attr(
62    feature = "alloc",
63    doc = r###"
64{
65    // The `Point_SI` trait was generated by the Structural derive on `Point`,
66    // aliasing it's accessor trait impls.
67    let this: Box<dyn Point_SI<u32>> = Box::new(Point{x: 2016, y: 2080});
68    assert_eq!(this.into_struc::<Entity>(), Entity{x: 2016, y: 2080, health: 100});
69}
70"###
71)]
72///
73/// #[derive(Structural)]
74/// struct Point<T>{
75///     pub x: T,
76///     pub y: u32,
77/// }
78///
79/// #[derive(Debug,PartialEq,Structural)]
80/// #[struc(from_structural)]
81/// struct Entity{
82///     #[struc(public)]
83///     x: u32,
84///
85///     #[struc(public)]
86///     y: u32,
87///
88///     #[struc(init_with_lit = 100)]
89///     health: u32,
90/// }
91/// ```
92///
93///
94/// ### Semi-manual impl
95///
96/// You can implement the `FromStructural` trait manually by using the `z_impl_from_structural`
97/// macro.
98///
99/// ```rust
100///
101/// use structural::{convert::FromStructural, StructuralExt, Structural, fp, make_struct};
102///
103/// {
104///     let this = Point{x: 33, y:45};
105///     assert_eq!(this.into_struc::<Entity>(), Entity{x: 33, y: 45, health: 100});
106/// }
107///
108/// #[derive(Structural)]
109/// struct Point<T>{
110///     pub x: T,
111///     pub y: u32,
112/// }
113///
114/// #[derive(Debug,PartialEq,Structural)]
115/// struct Entity{
116///     #[struc(public)]
117///     x: u32,
118///
119///     #[struc(public)]
120///     y: u32,
121///
122///     health: u32,
123/// }
124///
125/// // This macro implements TryFromStructural for Entity by
126/// // delegating to the passed-in implementation of `FromStructural`.
127/// //
128/// // `TryFromStructural` cannot have a blanket impl because it would collide
129/// // with user implementations.
130/// structural::z_impl_from_structural!{
131///     impl[F] FromStructural<F> for Entity
132///     where[ F: Entity_SI ]
133///     {
134///         fn from_structural(from){
135///             let (x, y) = from.into_fields(fp!(x, y));
136///             Entity{ x, y, health: 100 }
137///         }    
138///     }
139/// }
140///
141/// ```
142///
143/// # Enum Examples
144///
145/// ### Derivation
146///
147/// This example demonstrates how this trait can be derived for an enum with one
148/// private field.
149///
150/// ```rust
151/// use structural::{
152///     convert::FromStructural,
153///     for_examples::OptionLike,
154///     StructuralExt, Structural, switch,
155/// };
156///
157/// assert_eq!(Some(100).into_struc::<UberOption<_>>(), UberOption::Some(100, 0));
158/// assert_eq!(None.into_struc::<UberOption<u32>>(), UberOption::None);
159///
160/// assert_eq!(OptionLike::Some(3).into_struc::<UberOption<_>>(), UberOption::Some(3, 0));
161/// assert_eq!(OptionLike::None.into_struc::<UberOption<u32>>(), UberOption::None);
162///
163/// // Converting an UberOption back to itself with `FromStructural` drops the
164/// // `#[struct(not_public)]` field,because it has no accessor impls.
165/// assert_eq!(
166///     UberOption::Some(5, 200).into_struc::<UberOption<_>>(),
167///     UberOption::Some(5, 0)
168/// );
169/// assert_eq!(
170///     UberOption::None.into_struc::<UberOption<u32>>(),
171///     UberOption::None,
172/// );
173///
174/// #[derive(Debug,Structural,PartialEq)]
175/// #[struc(from_structural(bound = "T: Default"))]
176/// enum UberOption<T>{
177///     Some(
178///         T,
179///         #[struc(init_with_default)]
180///         T,
181///     ),
182///     None,
183/// }
184///
185/// ```
186///
187/// ### Semi-manual
188///
189/// This example demonstrates how this trait can be semi-manually
190/// implemented for an enum with one
191/// private field (as in a field that doesn't have accessor impls to get it).
192///
193/// ```rust
194/// use structural::{
195///     convert::FromStructural,
196///     for_examples::OptionLike,
197///     structural_aliases::OptionMove_ESI,
198///     StructuralExt, Structural, switch,
199/// };
200///
201/// assert_eq!(Some(100).into_struc::<UberOption<_>>(), UberOption::Some(100, 100));
202/// assert_eq!(None.into_struc::<UberOption<u32>>(), UberOption::None);
203///
204/// assert_eq!(OptionLike::Some(3).into_struc::<UberOption<_>>(), UberOption::Some(3, 3));
205/// assert_eq!(OptionLike::None.into_struc::<UberOption<u32>>(), UberOption::None);
206///
207/// // Converting an UberOption back to itself with `FromStructural` drops the
208/// // `#[struct(not_public)]` field,because it has no accessor impls.
209/// assert_eq!(
210///     UberOption::Some(5, 200).into_struc::<UberOption<_>>(),
211///     UberOption::Some(5, 5)
212/// );
213/// assert_eq!(
214///     UberOption::None.into_struc::<UberOption<u32>>(),
215///     UberOption::None,
216/// );
217///
218/// #[derive(Debug,Structural,PartialEq)]
219/// enum UberOption<T>{
220///     Some(
221///         T,
222///         #[struc(not_public)]
223///         T,
224///     ),
225///     None,
226/// }
227///
228/// // This macro implements TryFromStructural for Entity by
229/// // delegating to the passed-in implementation of `FromStructural`.
230/// //
231/// // `TryFromStructural` cannot have a blanket impl because it would collide
232/// // with user implementations.
233/// structural::z_impl_from_structural!{
234///     impl[F, T] FromStructural<F> for UberOption<T>
235///     where[
236///         F: OptionMove_ESI<T>,
237///         T: Clone,
238///     ]{
239///         fn from_structural(from){
240///             switch!{from;
241///                 Some(x) => Self::Some(x.clone(), x),
242///                 None => Self::None,
243///             }
244///         }
245///     }
246/// }
247///
248/// ```
249///
250pub trait FromStructural<T>: TryFromStructural<T> {
251    /// Performs the conversion
252    fn from_structural(from: T) -> Self;
253}
254
255/// For conversions between structural types.
256///
257/// This trait has a blanket implementations for all types that implement `FromStructural`.
258///
259/// # Example
260///
261/// This example demonstrates how you can use `IntoStructural` as a bound.
262///
263/// ```rust
264/// use structural::convert::IntoStructural;
265///
266/// assert_eq!( into_other((0, 1, 2), [3, 4]), [[0, 1], [3, 4]] );
267///
268/// fn into_other<T, U>(left: T, right: U)-> [U;2]
269/// where
270///     T: IntoStructural<U>,
271/// {
272///     [left.into_structural(), right]
273/// }
274///
275/// ```
276///
277pub trait IntoStructural<T>: Sized {
278    /// Performs the conversion
279    fn into_structural(self) -> T;
280}
281
282impl<This, T> IntoStructural<T> for This
283where
284    T: FromStructural<This>,
285{
286    fn into_structural(self) -> T {
287        T::from_structural(self)
288    }
289}
290
291///////////////////////////////////////////////////////////////////////////////
292
293/// For fallible conversions between structural types.
294///
295/// Usually conversions between enums require the from enum to have at least the
296/// fields and variants of the into enum.
297///
298/// All the examples in this crate are for enums,
299/// since converting from an enum to another with a subset of the variants in the
300/// first is the motivating usecase for defining this trait.
301///
302/// # Implementations
303///
304/// ### Enums
305///
306/// This trait is usually implemented for enums by:
307///
308/// - Bounding the trait's type parameter with the `*_SI` structural alias generated for the
309/// enum by the `Structural` derive.
310///
311/// - Matching on all the variants of the enum with the switch macro,
312/// returning `Ok` with a variant if the parameter matches that enum variant.
313/// If the parameter doesn't match any of the enum variants,an error is returned.
314///
315/// # Enum Examples
316///
317/// ### Derived
318///
319/// This example demonstrates how this trait can be derived.
320///
321/// ```rust
322/// use structural::{
323///     convert::{EmptyTryFromError, TryFromError},
324///     for_examples::{Enum3, Enum4},
325///     Structural, StructuralExt, switch,
326/// };
327///
328/// use std::cmp::Ordering;
329///
330/// assert_eq!(
331///     Enum3::Foo(3, 5).try_into_struc::<Variants>(),
332///     Ok(Variants::Foo(3)),
333/// );
334/// assert_eq!(
335///     Enum3::Bar(Ordering::Less, None).try_into_struc::<Variants>(),
336///     Ok(Variants::Bar),
337/// );
338/// assert_eq!(
339///     Enum3::Baz{foom: "hi"}.try_into_struc::<Variants>(),
340///     Ok(Variants::Baz{foom: "hi"}),
341/// );
342///
343/// let qux=Enum4::Qux { uh: [0; 4], what: (false, false) };
344/// assert_eq!(
345///     qux.try_into_struc::<Variants>(),
346///     Err(TryFromError::with_empty_error(qux)),
347/// );
348///
349///
350/// #[derive(Structural, Copy, Clone, Debug, PartialEq)]
351/// // This attribute tells the `Structural` derive to generate the
352/// // `TryFromStructural` and `FromStructural` impls
353/// #[struc(from_structural)]
354/// enum Variants {
355///     Foo(u8),
356///     Bar,
357///     Baz { foom: &'static str },
358/// }
359///
360///  
361/// ```
362///
363/// ### Semi-manual impl
364///
365/// This example demonstrates how this trait can be implemented with the
366/// `z_impl_try_from_structural_for_enum` macro.
367///
368/// ```rust
369/// use structural::{
370///     convert::{EmptyTryFromError, TryFromError},
371///     for_examples::{Enum3, Enum4},
372///     Structural, StructuralExt, switch,
373/// };
374///
375/// use std::cmp::Ordering;
376///
377/// assert_eq!(
378///     Enum3::Foo(3, 5).try_into_struc::<Variants>(),
379///     Ok(Variants::Foo(3)),
380/// );
381/// assert_eq!(
382///     Enum3::Bar(Ordering::Less, None).try_into_struc::<Variants>(),
383///     Ok(Variants::Bar),
384/// );
385/// assert_eq!(
386///     Enum3::Baz{foom: "hi"}.try_into_struc::<Variants>(),
387///     Ok(Variants::Baz{foom: "hi"}),
388/// );
389///
390/// let qux=Enum4::Qux { uh: [0; 4], what: (false, false) };
391/// assert_eq!(
392///     qux.try_into_struc::<Variants>(),
393///     Err(TryFromError::with_empty_error(qux)),
394/// );
395///
396///
397/// #[derive(Structural, Copy, Clone, Debug, PartialEq)]
398/// enum Variants {
399///     Foo(u8),
400///     Bar,
401///     Baz { foom: &'static str },
402/// }
403///
404/// // This macro implements FromStructural in terms of TryFromStructural,
405/// //
406/// // In order to implement FromStructural,
407/// // this macro assumes that the TryFromStructural implementation written by users:
408/// //   - Matches on all the variants of the enum
409/// //   - Returns `Ok` for all the variants of the enum that were matches by name.
410/// structural::z_impl_try_from_structural_for_enum!{
411///     impl[F] TryFromStructural<F> for Variants
412///     // `Variants_SI` was generated by the `Structural` derive for `Variants`
413///     // aliasing its accessor trait impls,
414///     // and allows `F` to have more variants than `Foo`,`Bar`,and `Baz`.
415///     where[ F: Variants_SI, ]
416///     {
417///         type Error = EmptyTryFromError;
418///
419///         fn try_from_structural(this){
420///             switch! {this;
421///                 Foo(x) => Ok(Self::Foo(x)),
422///                 Bar => Ok(Self::Bar),
423///                 Baz{foom} => Ok(Self::Baz{foom}),
424///                 _ => Err(TryFromError::with_empty_error(this)),
425///             }
426///         }
427///     }
428///
429///     // `Variants_ESI` was generated by the `Structural` derive for `Variants`
430///     // aliasing its accessor trait impls,
431///     // and requires `F` to only have the `Foo`,`Bar`,and `Baz` variants.
432///     FromStructural
433///     where[ F: Variants_ESI, ]
434/// }
435///  
436/// ```
437///
438/// # Example: Manual implementation
439///
440/// ```rust
441/// use structural::{
442///     convert::{EmptyTryFromError, FromStructural, TryFromError, TryFromStructural},
443///     for_examples::{Enum3, Enum4},
444///     Structural, StructuralExt, switch,
445/// };
446///
447/// use std::cmp::Ordering;
448///
449/// assert_eq!(
450///     Enum3::Foo(3, 5).try_into_struc::<Variants>(),
451///     Ok(Variants::Foo(3)),
452/// );
453/// assert_eq!(
454///     Enum3::Bar(Ordering::Less, None).try_into_struc::<Variants>(),
455///     Ok(Variants::Bar),
456/// );
457/// assert_eq!(
458///     Enum3::Baz{foom: "hi"}.try_into_struc::<Variants>(),
459///     Ok(Variants::Baz{foom: "hi"}),
460/// );
461///
462/// let qux=Enum4::Qux { uh: [0; 4], what: (false, false) };
463/// assert_eq!(
464///     qux.try_into_struc::<Variants>(),
465///     Err(TryFromError::with_empty_error(qux)),
466/// );
467///
468///
469/// #[derive(Structural, Copy, Clone, Debug, PartialEq)]
470/// enum Variants {
471///     Foo(u8),
472///     Bar,
473///     Baz { foom: &'static str },
474/// }
475///  
476/// impl<F> FromStructural<F> for Variants
477/// where
478///     // `Variants_ESI` was generated by the `Structural` derive for `Variants`
479///     // aliasing its accessor trait impls,
480///     // and requires `F` to only have the `Foo`,`Bar`,and `Baz` variants.
481///     F: Variants_ESI,
482/// {
483///     fn from_structural(this: F) -> Self {
484///         switch! {this;
485///             Foo(x) => Self::Foo(x),
486///             Bar => Self::Bar,
487///             Baz{foom} => Self::Baz{foom},
488///         }
489///     }
490/// }
491///
492/// impl<F> TryFromStructural<F> for Variants
493/// where
494///     // `Variants_SI` was generated by the `Structural` derive for `Variants`
495///     // aliasing its accessor trait impls,
496///     // and allows `F` to have more variants than `Foo`,`Bar`,and `Baz`.
497///     F: Variants_SI,
498/// {
499///     type Error = EmptyTryFromError;
500///
501///     fn try_from_structural(this: F) -> Result<Self, TryFromError<F, Self::Error>> {
502///         switch! {this;
503///             Foo(x) => Ok(Self::Foo(x)),
504///             Bar => Ok(Self::Bar),
505///             Baz{foom} => Ok(Self::Baz{foom}),
506///             _ => Err(TryFromError::with_empty_error(this)),
507///         }
508///     }
509/// }
510///  
511/// ```
512///
513pub trait TryFromStructural<T>: Sized {
514    /// The error parameter of `TryFromError`,
515    /// returned from `try_into_structural` on conversion error.
516    type Error;
517
518    /// Performs the conversion
519    fn try_from_structural(from: T) -> Result<Self, TryFromError<T, Self::Error>>;
520}
521
522/// For fallible conversions between structural types.
523///
524/// This trait has a blanket implementations for all types that implement `TryFromStructural`.
525///
526/// # Example
527///
528/// This example demonstrates how you can use `TryIntoStructural` as a bound.
529///
530/// ```rust
531/// use structural::{
532///     convert::TryIntoStructural,
533///     for_examples::{Enum2, Enum3}
534/// };
535///
536/// use std::cmp::{Ordering::{Less, Equal, Greater}, PartialEq};
537///
538/// compare_eq(Enum3::Foo(3, 5), Enum2::Foo(3, 5)      , true  );
539/// compare_eq(Enum3::Foo(5, 8), Enum2::Foo(5, 8)      , true  );
540/// compare_eq(Enum3::Foo(3, 5), Enum2::Foo(4, 5)      , false );
541/// compare_eq(Enum3::Foo(3, 5), Enum2::Bar(Less, None), false );
542///
543/// compare_eq(Enum3::Bar(Less, None), Enum2::Foo(3, 5)       , false );
544/// compare_eq(Enum3::Bar(Less, None), Enum2::Bar(Less, None) , true  );
545/// compare_eq(Enum3::Bar(Equal,None), Enum2::Bar(Equal, None), true  );
546/// compare_eq(Enum3::Bar(Less, None), Enum2::Bar(Equal, None), false );
547///
548/// // `Enum3::Baz{..}` is never equal to an `Enum2`,because it doesn't have a `Baz` variant.
549/// compare_eq(Enum3::Baz{foom: "hello"}, Enum2::Foo(3, 5)       , false );
550/// compare_eq(Enum3::Baz{foom: "hello"}, Enum2::Bar(Less, None) , false );
551///
552/// fn compare_eq<T, U>(left: T, right: U, expected: bool)
553/// where
554///     T: TryIntoStructural<U>,
555///     U: PartialEq,
556/// {
557///     let is_equal = match left.try_into_structural() {
558///         Ok(left) => left==right,
559///         Err(_) => false,
560///     };
561///
562///     assert_eq!( is_equal, expected );
563/// }
564///
565/// ```
566///
567pub trait TryIntoStructural<T>: Sized {
568    /// The error parameter of `TryFromError`,
569    /// returned from `try_into_structural` on conversion error.
570    type Error;
571
572    /// Performs the conversion
573    fn try_into_structural(self) -> Result<T, TryFromError<Self, Self::Error>>;
574}
575
576impl<This, T> TryIntoStructural<T> for This
577where
578    T: TryFromStructural<This>,
579{
580    type Error = T::Error;
581
582    fn try_into_structural(self) -> Result<T, TryFromError<Self, Self::Error>> {
583        T::try_from_structural(self)
584    }
585}
586
587///////////////////////////////////////////////////////////////////////////////
588
589/// The error type returned by  `TryFromStructural` and `TryIntoStructural`.
590#[derive(Debug, Clone, PartialEq)]
591pub struct TryFromError<T, E> {
592    /// The paramter of `TryFromStructural::try_from_structural`
593    /// (or self in `TryIntoStructural::try_into_structural`)
594    /// that failed to be conevrted.
595    pub from: T,
596    /// The `TryFromStructural::Error` associated type.
597    pub error: E,
598}
599
600impl<T, E> TryFromError<T, E> {
601    /// Constructs this error.
602    pub fn new(from: T, error: E) -> Self {
603        Self { from, error }
604    }
605}
606
607impl<T> TryFromError<T, EmptyTryFromError> {
608    /// Constructs this error,with `EmptyTryFromError` as the error.
609    pub fn with_empty_error(from: T) -> Self {
610        Self {
611            from,
612            error: EmptyTryFromError,
613        }
614    }
615}
616
617impl<T, E> Display for TryFromError<T, E>
618where
619    E: Display,
620{
621    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
622        Display::fmt(&self.error, f)
623    }
624}
625
626#[cfg(feature = "std")]
627#[allow(deprecated)]
628impl<T, E> StdError for TryFromError<T, E>
629where
630    T: fmt::Debug,
631    E: StdError,
632{
633    fn source(&self) -> Option<&(dyn StdError + 'static)> {
634        self.error.source()
635    }
636    fn description(&self) -> &str {
637        self.error.description()
638    }
639    fn cause(&self) -> Option<&dyn StdError> {
640        self.error.cause()
641    }
642}
643
644///////////////////////////////////////////////////////////////////////////////
645
646/// The "default" error for the `TryFromStructural::Error` associated type,
647/// for when there are no details for how the conversion failed.
648#[derive(Debug, Clone, PartialEq)]
649pub struct EmptyTryFromError;
650
651impl Display for EmptyTryFromError {
652    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
653        Display::fmt("failed TryIntoStructural conversion", f)
654    }
655}
656
657#[cfg(feature = "std")]
658impl StdError for EmptyTryFromError {}