sum_type/
lib.rs

1//! A convenience macro for creating a wrapper enum which may be one of several
2//! distinct types. In type theory, this is often referred to as a [sum type].
3//!
4//! This crate will work with `no_std` code.
5//!
6//! # Examples
7//!
8//! Using the `sum_type!()` macro is rather straightforward. You just define a
9//! normal `enum` inside it and the macro will automatically add a bunch of
10//! handy trait implementations.
11//!
12//! For convenience, all attributes are passed through and the macro will
13//! derive `From` for each variant.
14//!
15//! ```rust
16//! #[macro_use]
17//! extern crate sum_type;
18//!
19//! sum_type! {
20//!     #[derive(Debug, Clone, PartialEq)]
21//!     pub enum MySumType {
22//!         /// The first variant.
23//!         First(u32),
24//!         /// The second variant.
25//!         Second(String),
26//!         /// A list of bytes.
27//!         Third(Vec<u8>),
28//!     }
29//! }
30//!
31//! # fn main() {
32//! let first: MySumType = 52.into();
33//! assert_eq!(first, MySumType::First(52));
34//! # }
35//! ```
36//!
37//! You can also be lazy and omit the variant name. This will name the variant
38//! the same thing as its type.
39//!
40//! ```rust
41//! # #[macro_use]
42//! # extern crate sum_type;
43//! sum_type!{
44//!     pub enum Lazy {
45//!         f32, u32, String,
46//!     }
47//! }
48//! # fn main() {
49//! let s = Lazy::String("Hello World!".to_string());
50//! # }
51//! ```
52//!
53//! The [`SumType`] trait is also implemented, allowing a basic level of
54//! introspection and dynamic typing.
55//!
56//! ```rust
57//! # #[macro_use]
58//! # extern crate sum_type;
59//! use sum_type::SumType;
60//! # sum_type! { #[derive(Debug, Clone, PartialEq)] pub enum MySumType {
61//! #         First(u32), Second(String), Third(Vec<u8>), } }
62//!
63//! # fn main() {
64//! let first = MySumType::First(52);
65//!
66//! assert_eq!(first.variant(), "First");
67//! assert_eq!(first.variants(), &["First", "Second", "Third"]);
68//! assert!(first.variant_is::<u32>());
69//! assert_eq!(first.downcast_ref::<u32>(), Some(&52));
70//! # }
71//! ```
72//!
73//! # Assumptions
74//!
75//! You need to make sure your type has more than one variant, meaning the
76//! following example will fail to compile.
77//!
78//! ```rust,compile_fail
79//! # fn main() {}
80//! #[macro_use]
81//! extern crate sum_type;
82//!
83//! sum_type!{
84//!     pub enum OneVariant {
85//!         First(String),
86//!     }
87//! }
88//! ```
89//!
90//! The `compile_error!()` macro is used to give a (hopefully) useful error
91//! message.
92//!
93//! ```text
94//! error: The `OneVariant` type must have more than one variant
95//!   --> src/lib.rs:37:1
96//!    |
97//! 7  | / sum_type!{
98//! 8  | |     pub enum OneVariant {
99//! 9  | |         First(String),
100//! 10 | |     }
101//! 11 | | }
102//!    | |_^
103//!    |
104//!    = note: this error originates in a macro outside of the current crate
105//! ```
106//!
107//! Sum types containing generics, including lifetimes, or which are using
108//! visibility modifiers (e.g. `pub(crate)`) aren't (yet!) supported. That
109//! means this will fail:
110//!
111//! ```rust,compile_fail
112//! # fn main() {}
113//! # #[macro_use]
114//! # extern crate sum_type;
115//! sum_type!{
116//!     TypeWithLifetime<'a> {
117//!         First(&'a str),
118//!         Second(usize),
119//!     }
120//! }
121//! ```
122//!
123//! And so will this:
124//!
125//! ```rust,compile_fail
126//! # fn main() {}
127//! # #[macro_use]
128//! # extern crate sum_type;
129//! sum_type!{
130//!     pub(crate) ModifiedVisibility {
131//!         First(u32),
132//!         Second(String),
133//!     }
134//! }
135//! ```
136//!
137//! # Try From
138//!
139//! `TryFrom` is automatically implemented on your sum type to convert it back to one of its variant types.
140//!
141//! ```rust
142//! #[macro_use]
143//! extern crate sum_type;
144//! # fn main() {
145//! # sum_type! { #[derive(Debug, Clone, PartialEq)] pub enum MySumType {
146//! #         First(u32), Second(String), Third(Vec<u8>), } }
147//! use std::convert::TryFrom;
148//!
149//! let first = MySumType::First(52);
150//!
151//! let as_u32 = u32::try_from(first);
152//! assert_eq!(as_u32, Ok(52));
153//!
154//! let second = MySumType::Second(String::from("Not a Vec<u8>"));
155//! let as_vec_u8 = Vec::<u8>::try_from(second);
156//! assert!(as_vec_u8.is_err());
157//!
158//! let err = as_vec_u8.unwrap_err();
159//! assert_eq!(err.expected_variant, "Third");
160//! assert_eq!(err.actual_variant, "Second");
161//! # }
162//! ```
163//!
164//! The `generated_example` feature flag will create an example of our
165//! `MySumType` which can be viewed using `rustdoc`.
166//!
167//! [sum type]: https://www.schoolofhaskell.com/school/to-infinity-and-beyond/pick-of-the-week/sum-types
168//! [`SumType`]: trait.SumType.html
169
170#![no_std]
171#![deny(
172    missing_docs,
173    missing_copy_implementations,
174    missing_debug_implementations,
175    unsafe_code
176)]
177
178// re-export so users of our macro have a stable way to import the standard
179// library (as `$crate::_core`).
180#[doc(hidden)]
181pub extern crate core as _core;
182
183use core::any::Any;
184
185/// The result of a failed conversion from `TryFrom`.
186#[derive(Debug, Copy, Clone, PartialEq)]
187pub struct InvalidType {
188    /// The variant this conversion is valid for.
189    pub expected_variant: &'static str,
190    /// The actual variant.
191    pub actual_variant: &'static str,
192    /// All possible variants.
193    pub all_variants: &'static [&'static str],
194    #[doc(hidden)]
195    pub __non_exhaustive: (),
196}
197
198/// Various methods for introspection and dynamic typing.
199///
200/// # Note
201///
202/// This trait is automatically implemented for all types generated by the
203/// `sum_type!()` macro. You should never need to implement it manually.
204pub trait SumType {
205    /// The name of the current variant.
206    fn variant(&self) -> &'static str;
207    /// A list of all possible variants.
208    fn variants(&self) -> &'static [&'static str];
209    /// Try to get a reference to the inner field if it is a `T`.
210    fn downcast_ref<T: Any>(&self) -> Option<&T>;
211    /// Return a mutable reference to the inner field if it is a `T`.
212    fn downcast_mut<T: Any>(&mut self) -> Option<&mut T>;
213    /// Is the underlying variant an instance of `T`?
214    fn variant_is<T: Any>(&self) -> bool;
215}
216
217#[doc(hidden)]
218#[macro_export]
219macro_rules! __sum_type_try_from {
220    ($enum_name:ident, $( $name:ident => $variant_type:ty ),*) => {
221       $(
222            impl $crate::_core::convert::TryFrom<$enum_name> for $variant_type {
223                type Error = $crate::InvalidType;
224
225                fn try_from(other: $enum_name) -> Result<$variant_type, Self::Error> {
226                    let variant = $crate::SumType::variant(&other);
227                    let variants = $crate::SumType::variants(&other);
228
229                    if let $enum_name::$name(value) = other {
230                        Ok(value)
231                    } else {
232                        Err($crate::InvalidType {
233                            expected_variant: stringify!($name),
234                            actual_variant: variant,
235                            all_variants: variants,
236                            __non_exhaustive: (),
237                        })
238                    }
239                }
240
241            }
242       )*
243    }
244}
245
246#[doc(hidden)]
247#[macro_export]
248macro_rules! __sum_type_from {
249    ($enum_name:ident, $( $name:ident => $variant_type:ty ),*) => {
250       $(
251            impl From<$variant_type> for $enum_name {
252                fn from(other: $variant_type) -> $enum_name {
253                    $enum_name::$name(other)
254                }
255            }
256        )*
257    }
258}
259
260#[doc(hidden)]
261#[macro_export]
262macro_rules! __sum_type_trait {
263    ($enum_name:ident, $( $name:ident => $variant_type:ty ),*) => {
264        impl $crate::SumType for $enum_name {
265            fn variants(&self) -> &'static [ &'static str] {
266                &[
267                    $( stringify!($name) ),*
268                ]
269            }
270
271            fn variant(&self) ->  &'static str {
272                match *self {
273                    $(
274                        $enum_name::$name(_) => stringify!($name),
275                    )*
276                }
277            }
278
279            fn downcast_ref<T: $crate::_core::any::Any>(&self) -> Option<&T> {
280                use $crate::_core::any::Any;
281
282                match *self {
283                    $(
284                        $enum_name::$name(ref value) => (value as &Any).downcast_ref::<T>(),
285                    )*
286                }
287            }
288
289            fn downcast_mut<T: $crate::_core::any::Any>(&mut self) -> Option<&mut T> {
290                use $crate::_core::any::Any;
291
292                match *self {
293                    $(
294                        $enum_name::$name(ref mut value) => (value as &mut Any).downcast_mut::<T>(),
295                    )*
296                }
297            }
298
299            fn variant_is<T: $crate::_core::any::Any>(&self) -> bool {
300                self.downcast_ref::<T>().is_some()
301            }
302        }
303    }
304}
305
306#[doc(hidden)]
307#[macro_export]
308macro_rules! __assert_multiple_variants {
309    ($enum_name:ident, $name:ident => $variant_type:ty) => {
310        compile_error!(concat!(
311            "The `",
312            stringify!($enum_name),
313            "` type must have more than one variant"
314        ));
315    };
316    ($enum_name:ident, $( $name:ident => $variant_type:ty ),*) => {};
317}
318
319#[doc(hidden)]
320#[macro_export]
321macro_rules! __sum_type_impls {
322    ($enum_name:ident, $( $name:ident => $variant_type:ty ),*) => (
323        $crate::__assert_multiple_variants!($enum_name, $( $name => $variant_type ),*);
324
325        $crate::__sum_type_from!($enum_name, $($name => $variant_type),*);
326        $crate::__sum_type_try_from!($enum_name, $($name => $variant_type),*);
327        $crate::__sum_type_trait!($enum_name, $($name => $variant_type),*);
328    )
329}
330
331/// The entire point.
332#[macro_export]
333macro_rules! sum_type {
334    (
335        $( #[$outer:meta] )*
336        pub enum $name:ident {
337            $(
338                $( #[$inner:meta] )*
339                $var_name:ident($var_ty:ty),
340                )*
341        }) => {
342       $( #[$outer] )*
343        pub enum $name {
344            $(
345                $( #[$inner] )*
346                $var_name($var_ty),
347            )*
348        }
349
350        $crate::__sum_type_impls!($name, $( $var_name => $var_ty),*);
351    };
352    (
353        $( #[$outer:meta] )*
354        enum $name:ident {
355            $(
356                $( #[$inner:meta] )*
357                $var_name:ident($var_ty:ty),
358                )*
359        }) => {
360       $( #[$outer] )*
361        enum $name {
362            $(
363                $( #[$inner] )*
364                $var_name($var_ty),
365            )*
366        }
367
368        $crate::__sum_type_impls!($name, $( $var_name => $var_ty),*);
369    };
370
371    // "lazy" variations which reuse give the variant the same name as its type.
372    (
373        $( #[$outer:meta] )*
374        pub enum $name:ident {
375            $(
376                $( #[$inner:meta] )*
377                $var_name:ident,
378                )*
379        }) => {
380            $crate::sum_type!($(#[$outer])* pub enum $name { $( $(#[$inner])* $var_name($var_name), )* });
381    };
382    (
383        $( #[$outer:meta] )*
384        enum $name:ident {
385            $(
386                $( #[$inner:meta] )*
387                $var_name:ident($var_ty:ty),
388                )*
389        }) => {
390            $crate::sum_type!($(#[$outer])* enum $name { $( $(#[$inner])* $var_name($var_name), )* });
391    };
392}
393
394/// Execute an operation on each enum variant.
395///
396/// This macro is short-hand for matching on each variant in an enum and
397/// performing the same operation to each.
398///
399/// It will expand to roughly the following:
400///
401/// ```rust
402/// sum_type::sum_type! {
403///     #[derive(Debug, PartialEq)]
404///     pub enum Foo {
405///         First(u32),
406///         Second(f64),
407///         Third(String),
408///     }
409/// }
410///
411/// let third = Foo::Third(String::from("Hello World"));
412///
413/// let got = match third {
414///     Foo::First(ref item) => item.to_string(),
415///     Foo::Second(ref item) => item.to_string(),
416///     Foo::Third(ref item) => item.to_string(),
417/// };
418/// ```
419///
420/// # Examples
421///
422/// ```rust
423/// sum_type::sum_type! {
424///     #[derive(Debug, PartialEq)]
425///     pub enum Foo {
426///         First(u32),
427///         Second(f64),
428///         Third(String),
429///     }
430/// }
431///
432/// let mut third = Foo::Third(String::from("Hello World"));
433///
434/// // Execute some operation on each variant (skipping Second) and get the
435/// // return value
436/// let mut got = sum_type::defer!(Foo as third; First | Third => |ref item| item.to_string());
437///
438/// assert_eq!(got, "Hello World");
439///
440/// // mutate the variant in place
441/// sum_type::defer!(Foo as third;
442///     First | Second | Third => |ref mut item| {
443///         *item = Default::default();
444///     }
445/// );
446/// assert_eq!(third, Foo::Third(String::new()));
447/// ```
448///
449/// The `defer!()` macro will panic if it encounters an unhandled variant.
450///
451/// ```rust,should_panic
452/// sum_type::sum_type! {
453///     #[derive(Debug, PartialEq)]
454///     pub enum Foo {
455///         First(u32),
456///         Second(f64),
457///         Third(String),
458///     }
459/// }
460///
461/// let mut first = Foo::First(42);
462///
463/// sum_type::defer!(Foo as first; Second | Third => |ref _dont_care| ());
464/// ```
465#[macro_export]
466macro_rules! defer {
467    ($kind:ident as $variable:expr; $( $variant:ident )|* => |ref $item:ident| $exec:expr) => {
468        $crate::defer!(@foreach_variant $kind, $variable;
469            $(
470                $kind::$variant(ref $item) => $exec
471            ),*
472        )
473    };
474    ($kind:ident as $variable:expr; $( $variant:ident )|* => |ref mut $item:ident| $exec:expr) => {
475        $crate::defer!(@foreach_variant $kind, $variable;
476            $(
477                $kind::$variant(ref mut $item) => $exec
478            ),*
479        )
480    };
481    (@foreach_variant $kind:ident, $variable:expr; $( $pattern:pat => $exec:expr ),*) => {
482        match $variable {
483            $(
484                $pattern => $exec,
485            )*
486            #[allow(unreachable_patterns)]
487            _ => unreachable!("Unexpected variant, {}, for {}",
488                <_ as $crate::SumType>::variant(&$variable),
489                stringify!($kind)),
490        }
491    }
492}
493
494/// An example of the generated sum type.
495#[cfg(feature = "generated_example")]
496#[allow(missing_docs)]
497pub mod generated_example {
498    sum_type! {
499        #[derive(Debug, Copy, Clone, PartialEq)]
500        pub enum MySumType {
501            /// The first variant.
502            First(u32),
503            /// The second variant.
504            Second(&'static str),
505            /// A list of bytes.
506            Third(&'static [u8]),
507        }
508    }
509}