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 {}