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);