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}