one_of/
lib.rs

1//! Macro to represent a type that can be converted either [`From`] or [`TryInto`] the given types
2//!
3//! This crate only works on the nightly version of Rust
4//!
5//!
6//! ## Usage
7//!
8//! ```
9//! use one_of::{case, one_of};
10//!
11//! // either `u32` or `char`
12//! let x: one_of!(u32, char) = 42.into();
13//! assert_eq!(Some(42u32), x.into());
14//! assert_eq!(Option::<char>::None, x.into());
15//!
16//! // some type of integer
17//! let x: one_of!(i8, i16, i32, i64, u8, u16, u32, u64) = 42.into();
18//! assert_eq!(Option::<i8>::None, x.into());
19//! assert_eq!(Option::<i16>::None, x.into());
20//! assert_eq!(Some(42i32), x.into());
21//! assert_eq!(Option::<i64>::None, x.into());
22//! assert_eq!(Option::<u8>::None, x.into());
23//! assert_eq!(Option::<u16>::None, x.into());
24//! assert_eq!(Option::<u32>::None, x.into());
25//! assert_eq!(Option::<u64>::None, x.into());
26//!
27//! // case macro is the `match` keyword for `one_of` types
28//! case!(<one_of!(bool, &str, i64)>::from("Hello, world!"),
29//!     // bool
30//!     _ => {
31//!         panic!("not bool");
32//!     };
33//!
34//!     // &str
35//!     s if s.starts_with("Hello, ") => {
36//!         assert_eq!(&s[7 ..], "world!");
37//!     }
38//!     _ => {
39//!         panic!("not other strings");
40//!     };
41//!
42//!     // i64
43//!     _ => {
44//!         panic!("not i64");
45//!     };
46//! );
47//! ```
48//!
49//!
50//! ## Changelog
51//!
52//! See [CHANGELOG.md](https://github.com/figsoda/one-of/blob/main/CHANGELOG.md)
53
54#![feature(auto_traits, negative_impls)]
55#![forbid(unsafe_code)]
56#![no_std]
57
58use core::{
59    convert::TryInto,
60    fmt::{self, Display, Formatter},
61};
62
63// https://github.com/rust-lang/rust/issues/30905#issuecomment-173327799
64mod internal {
65    pub auto trait Different {}
66    impl<T> !Different for (T, T) {}
67}
68use crate::internal::Different;
69
70macro_rules! gen_types {
71    ($($n:ident { $($v:ident: $($l:ident,)* @ $(,$r:ident)*;)+ })*) => {
72        $(
73            #[doc(hidden)]
74            #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
75            pub enum $n<$($v),+> {
76                $($v($v)),+
77            }
78
79            impl<$($v: Display),+> Display for $n<$($v),+> {
80                fn fmt(&self, f: &mut Formatter) -> fmt::Result {
81                    match self {
82                        $(Self::$v(x) => write!(f, "{}", x)),+
83                    }
84                }
85            }
86
87            $(
88                impl<$($l,)* $v $(,$r)*> From<$v> for $n<$($l,)* $v $(,$r)*> where
89                    $(($v, $l): Different,)* $(($v, $r): Different,)* {
90                    fn from(x: $v) -> Self {
91                        Self::$v(x)
92                    }
93                }
94
95                impl<$($l,)* $v $(,$r)*> From<$n<$($l,)* $v $(,$r)*>> for Option<$v> where
96                    $(($v, $l): Different,)* $(($v, $r): Different,)* {
97                    fn from(x: $n<$($l,)* $v $(,$r)*>) -> Self {
98                        match x {
99                            $n::$v(x) => Some(x),
100                            _ => None,
101                        }
102                    }
103                }
104
105                impl<$($l,)* $v $(,$r)*> TryInto<$v> for $n<$($l,)* $v $(,$r)*> where
106                    $(($v, $l): Different,)* $(($v, $r): Different,)* {
107                    type Error = ();
108
109                    fn try_into(self) -> Result<$v, Self::Error> {
110                        match self {
111                            $n::$v(x) => Ok(x),
112                            _ => Err(()),
113                        }
114                    }
115                }
116            )+
117        )*
118    }
119}
120
121gen_types!(
122    OneOf2 {
123        A: @, B;
124        B: A, @;
125    }
126    OneOf3 {
127        A: @, B, C;
128        B: A, @, C;
129        C: A, B, @;
130    }
131    OneOf4 {
132        A: @, B, C, D;
133        B: A, @, C, D;
134        C: A, B, @, D;
135        D: A, B, C, @;
136    }
137    OneOf5 {
138        A: @, B, C, D, E;
139        B: A, @, C, D, E;
140        C: A, B, @, D, E;
141        D: A, B, C, @, E;
142        E: A, B, C, D, @;
143    }
144    OneOf6 {
145        A: @, B, C, D, E, F;
146        B: A, @, C, D, E, F;
147        C: A, B, @, D, E, F;
148        D: A, B, C, @, E, F;
149        E: A, B, C, D, @, F;
150        F: A, B, C, D, E, @;
151    }
152    OneOf7 {
153        A: @, B, C, D, E, F, G;
154        B: A, @, C, D, E, F, G;
155        C: A, B, @, D, E, F, G;
156        D: A, B, C, @, E, F, G;
157        E: A, B, C, D, @, F, G;
158        F: A, B, C, D, E, @, G;
159        G: A, B, C, D, E, F, @;
160    }
161    OneOf8 {
162        A: @, B, C, D, E, F, G, H;
163        B: A, @, C, D, E, F, G, H;
164        C: A, B, @, D, E, F, G, H;
165        D: A, B, C, @, E, F, G, H;
166        E: A, B, C, D, @, F, G, H;
167        F: A, B, C, D, E, @, G, H;
168        G: A, B, C, D, E, F, @, H;
169        H: A, B, C, D, E, F, G, @;
170    }
171    OneOf9 {
172        A: @, B, C, D, E, F, G, H, I;
173        B: A, @, C, D, E, F, G, H, I;
174        C: A, B, @, D, E, F, G, H, I;
175        D: A, B, C, @, E, F, G, H, I;
176        E: A, B, C, D, @, F, G, H, I;
177        F: A, B, C, D, E, @, G, H, I;
178        G: A, B, C, D, E, F, @, H, I;
179        H: A, B, C, D, E, F, G, @, I;
180        I: A, B, C, D, E, F, G, H, @;
181    }
182    OneOf10 {
183        A: @, B, C, D, E, F, G, H, I, J;
184        B: A, @, C, D, E, F, G, H, I, J;
185        C: A, B, @, D, E, F, G, H, I, J;
186        D: A, B, C, @, E, F, G, H, I, J;
187        E: A, B, C, D, @, F, G, H, I, J;
188        F: A, B, C, D, E, @, G, H, I, J;
189        G: A, B, C, D, E, F, @, H, I, J;
190        H: A, B, C, D, E, F, G, @, I, J;
191        I: A, B, C, D, E, F, G, H, @, J;
192        J: A, B, C, D, E, F, G, H, I, @;
193    }
194    OneOf11 {
195        A: @, B, C, D, E, F, G, H, I, J, K;
196        B: A, @, C, D, E, F, G, H, I, J, K;
197        C: A, B, @, D, E, F, G, H, I, J, K;
198        D: A, B, C, @, E, F, G, H, I, J, K;
199        E: A, B, C, D, @, F, G, H, I, J, K;
200        F: A, B, C, D, E, @, G, H, I, J, K;
201        G: A, B, C, D, E, F, @, H, I, J, K;
202        H: A, B, C, D, E, F, G, @, I, J, K;
203        I: A, B, C, D, E, F, G, H, @, J, K;
204        J: A, B, C, D, E, F, G, H, I, @, K;
205        K: A, B, C, D, E, F, G, H, I, J, @;
206    }
207    OneOf12 {
208        A: @, B, C, D, E, F, G, H, I, J, K, L;
209        B: A, @, C, D, E, F, G, H, I, J, K, L;
210        C: A, B, @, D, E, F, G, H, I, J, K, L;
211        D: A, B, C, @, E, F, G, H, I, J, K, L;
212        E: A, B, C, D, @, F, G, H, I, J, K, L;
213        F: A, B, C, D, E, @, G, H, I, J, K, L;
214        G: A, B, C, D, E, F, @, H, I, J, K, L;
215        H: A, B, C, D, E, F, G, @, I, J, K, L;
216        I: A, B, C, D, E, F, G, H, @, J, K, L;
217        J: A, B, C, D, E, F, G, H, I, @, K, L;
218        K: A, B, C, D, E, F, G, H, I, J, @, L;
219        L: A, B, C, D, E, F, G, H, I, J, K, @;
220    }
221);
222
223/// Represents a type that can be converted either [`From`] or [`TryInto`] the given types
224///
225/// Also conditionally implements [`Clone`], [`Copy`], [`Debug`](core::fmt::Debug), [`Display`], [`Eq`], [`Hash`](core::hash::Hash) and [`PartialEq`]
226///
227/// Accepts at least 2 types, up to 12 types
228///
229/// ## Examples
230///
231/// ### either `u32` or `char`
232/// ```
233/// # use one_of::one_of;
234/// let x: one_of!(u32, char) = 42.into();
235/// assert_eq!(Some(42u32), x.into());
236/// assert_eq!(Option::<char>::None, x.into());
237/// ```
238///
239/// ### some type of integer
240/// ```
241/// # use one_of::one_of;
242/// let x: one_of!(i8, i16, i32, i64, u8, u16, u32, u64) = 42.into();
243/// assert_eq!(Option::<i8>::None, x.into());
244/// assert_eq!(Option::<i16>::None, x.into());
245/// assert_eq!(Some(42i32), x.into());
246/// assert_eq!(Option::<i64>::None, x.into());
247/// assert_eq!(Option::<u8>::None, x.into());
248/// assert_eq!(Option::<u16>::None, x.into());
249/// assert_eq!(Option::<u32>::None, x.into());
250/// assert_eq!(Option::<u64>::None, x.into());
251/// ```
252#[macro_export]
253macro_rules! one_of {
254    ($a:ty, $b:ty) => {
255        $crate::OneOf2<$a, $b>
256    };
257    ($a:ty, $b:ty, $c:ty) => {
258        $crate::OneOf3<$a, $b, $c>
259    };
260    ($a:ty, $b:ty, $c:ty, $d:ty) => {
261        $crate::OneOf4<$a, $b, $c, $d>
262    };
263    ($a:ty, $b:ty, $c:ty, $d:ty, $e:ty) => {
264        $crate::OneOf5<$a, $b, $c, $d, $e>
265    };
266    ($a:ty, $b:ty, $c:ty, $d:ty, $e:ty, $f:ty) => {
267        $crate::OneOf6<$a, $b, $c, $d, $e, $f>
268    };
269    ($a:ty, $b:ty, $c:ty, $d:ty, $e:ty, $f:ty, $g:ty) => {
270        $crate::OneOf7<$a, $b, $c, $d, $e, $f, $g>
271    };
272    ($a:ty, $b:ty, $c:ty, $d:ty, $e:ty, $f:ty, $g:ty, $h:ty) => {
273        $crate::OneOf8<$a, $b, $c, $d, $e, $f, $g, $h>
274    };
275    ($a:ty, $b:ty, $c:ty, $d:ty, $e:ty, $f:ty, $g:ty, $h:ty, $i:ty) => {
276        $crate::OneOf9<$a, $b, $c, $d, $e, $f, $g, $h, $i>
277    };
278    ($a:ty, $b:ty, $c:ty, $d:ty, $e:ty, $f:ty, $g:ty, $h:ty, $i:ty, $j:ty) => {
279        $crate::OneOf10<$a, $b, $c, $d, $e, $f, $g, $h, $i, $j>
280    };
281    ($a:ty, $b:ty, $c:ty, $d:ty, $e:ty, $f:ty, $g:ty, $h:ty, $i:ty, $j:ty, $k:ty) => {
282        $crate::OneOf11<$a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k>
283    };
284    ($a:ty, $b:ty, $c:ty, $d:ty, $e:ty, $f:ty, $g:ty, $h:ty, $i:ty, $j:ty, $k:ty, $l:ty) => {
285        $crate::OneOf12<$a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k, $l>
286    };
287}
288
289macro_rules! gen_case {
290    ($d:tt $($t:ty { $($v:ident $p:ident $g:ident $b:ident,)* })+) => {
291        /// Pattern matching against [`one_of`] types, similar to `match`
292        ///
293        /// Groups of match arms are separated by `;`
294        ///
295        /// Unlike `match`, [`case`] only accepts blocks as values of match arms
296        ///
297        /// ## Examples
298        ///
299        /// ### matching against one of the integer types
300        /// ```
301        /// use one_of::{case, one_of};
302        /// case!(<one_of!(i8, i64, u8, u64)>::from(42u64),
303        ///     // i8
304        ///     _ => {
305        ///         panic!("not i8");
306        ///     };
307        ///
308        ///     // i64
309        ///     _ => {
310        ///         panic!("not i64");
311        ///     };
312        ///
313        ///     // u8
314        ///     _ => {
315        ///         panic!("not u8");
316        ///     };
317        ///
318        ///     // u64
319        ///     0 ..= 41 => {
320        ///         panic!("not less than 42");
321        ///     }
322        ///     n => {
323        ///         assert_eq!(n, 42);
324        ///     };
325        /// );
326        /// ```
327        ///
328        /// ### gaurds are also supported, just like `match`
329        /// ```
330        /// # use one_of::{case, one_of};
331        /// case!(<one_of!(bool, &str, i64)>::from("Hello, world!"),
332        ///     // bool
333        ///     _ => {
334        ///         panic!("not bool");
335        ///     };
336        ///
337        ///     // &str
338        ///     s if s.starts_with("Hello, ") => {
339        ///         assert_eq!(&s[7 ..], "world!");
340        ///     }
341        ///     _ => {
342        ///         panic!("not other strings");
343        ///     };
344        ///
345        ///     // i64
346        ///     _ => {
347        ///         panic!("not i64");
348        ///     };
349        /// );
350        /// ```
351        /// this is the equivalent using `match` and a custom enum
352        /// ```
353        /// enum BoolStrOrInt<'a> {
354        ///     Bool(bool),
355        ///     Str(&'a str),
356        ///     Int(i64),
357        /// }
358        ///
359        /// match BoolStrOrInt::Str("Hello, world!") {
360        ///     // bool
361        ///     BoolStrOrInt::Bool(_) => {
362        ///         panic!("not bool");
363        ///     }
364        ///
365        ///     // &str
366        ///     BoolStrOrInt::Str(s) if s.starts_with("Hello, ") => {
367        ///         assert_eq!(&s[7 ..], "world!");
368        ///     }
369        ///     BoolStrOrInt::Str(_) => {
370        ///         panic!("not other strings");
371        ///     }
372        ///
373        ///     // i64
374        ///     BoolStrOrInt::Int(_) => {
375        ///         panic!("not i64");
376        ///     }
377        /// }
378        /// ```
379        #[macro_export]
380        macro_rules! case {
381            $(
382                ($d x:expr, $($d ($d $p:pat $d (if $d $g:expr)? => $d $b:block)+ ;)+) => {
383                    match $d x {
384                        $($d ($crate::$t::$v($d $p) $d (if $d $g)? => $d $b)+)+
385                    }
386                };
387            )+
388        }
389    };
390}
391
392gen_case!($
393    OneOf2 {
394        A pa ga ba,
395        B pb gb bb,
396    }
397    OneOf3 {
398        A pa ga ba,
399        B pb gb bb,
400        C pc gc bc,
401    }
402    OneOf4 {
403        A pa ga ba,
404        B pb gb bb,
405        C pc gc bc,
406        D pd gd bd,
407    }
408    OneOf5 {
409        A pa ga ba,
410        B pb gb bb,
411        C pc gc bc,
412        D pd gd bd,
413        E pe ge be,
414    }
415    OneOf6 {
416        A pa ga ba,
417        B pb gb bb,
418        C pc gc bc,
419        D pd gd bd,
420        E pe ge be,
421        F pf gf bf,
422    }
423    OneOf7 {
424        A pa ga ba,
425        B pb gb bb,
426        C pc gc bc,
427        D pd gd bd,
428        E pe ge be,
429        F pf gf bf,
430        G pg gg bg,
431    }
432    OneOf8 {
433        A pa ga ba,
434        B pb gb bb,
435        C pc gc bc,
436        D pd gd bd,
437        E pe ge be,
438        F pf gf bf,
439        G pg gg bg,
440        H ph gh bh,
441    }
442    OneOf9 {
443        A pa ga ba,
444        B pb gb bb,
445        C pc gc bc,
446        D pd gd bd,
447        E pe ge be,
448        F pf gf bf,
449        G pg gg bg,
450        H ph gh bh,
451        I pi gi bi,
452    }
453    OneOf10 {
454        A pa ga ba,
455        B pb gb bb,
456        C pc gc bc,
457        D pd gd bd,
458        E pe ge be,
459        F pf gf bf,
460        G pg gg bg,
461        H ph gh bh,
462        I pi gi bi,
463        J pj gj bj,
464    }
465    OneOf11 {
466        A pa ga ba,
467        B pb gb bb,
468        C pc gc bc,
469        D pd gd bd,
470        E pe ge be,
471        F pf gf bf,
472        G pg gg bg,
473        H ph gh bh,
474        I pi gi bi,
475        J pj gj bj,
476        K pk gk bk,
477    }
478    OneOf12 {
479        A pa ga ba,
480        B pb gb bb,
481        C pc gc bc,
482        D pd gd bd,
483        E pe ge be,
484        F pf gf bf,
485        G pg gg bg,
486        H ph gh bh,
487        I pi gi bi,
488        J pj gj bj,
489        K pk gk bk,
490        L pl gl bl,
491    }
492);