frunk_core/
coproduct.rs

1//! Module that holds Coproduct data structures, traits, and implementations
2//!
3//! Think of "Coproduct" as ad-hoc enums; allowing you to do something like this
4//!
5//! ```
6//! # fn main() {
7//! # use frunk_core::Coprod;
8//! // For simplicity, assign our Coproduct type to a type alias
9//! // This is purely optional.
10//! type I32Bool = Coprod!(i32, bool);
11//! // Inject things into our Coproduct type
12//! let co1 = I32Bool::inject(3);
13//! let co2 = I32Bool::inject(true);
14//!
15//! // Getting stuff
16//! let get_from_1a: Option<&i32> = co1.get();
17//! let get_from_1b: Option<&bool> = co1.get();
18//! assert_eq!(get_from_1a, Some(&3));
19//! assert_eq!(get_from_1b, None);
20//!
21//! let get_from_2a: Option<&i32> = co2.get();
22//! let get_from_2b: Option<&bool> = co2.get();
23//! assert_eq!(get_from_2a, None);
24//! assert_eq!(get_from_2b, Some(&true));
25//!
26//! // *Taking* stuff (by value)
27//! let take_from_1a: Option<i32> = co1.take();
28//! assert_eq!(take_from_1a, Some(3));
29//!
30//! // Or with a Result
31//! let uninject_from_1a: Result<i32, _> = co1.uninject();
32//! let uninject_from_1b: Result<bool, _> = co1.uninject();
33//! assert_eq!(uninject_from_1a, Ok(3));
34//! assert!(uninject_from_1b.is_err());
35//! # }
36//! ```
37//!
38//! Or, if you want to "fold" over all possible values of a coproduct
39//!
40//! ```
41//! # use frunk_core::{hlist, poly_fn, Coprod};
42//! # fn main() {
43//! # type I32Bool = Coprod!(i32, bool);
44//! # let co1 = I32Bool::inject(3);
45//! # let co2 = I32Bool::inject(true);
46//! // In the below, we use unreachable!() to make it obvious hat we know what type of
47//! // item is inside our coproducts co1 and co2 but in real life, you should be writing
48//! // complete functions for all the cases when folding coproducts
49//! //
50//! // to_ref borrows every item so that we can fold without consuming the coproduct.
51//! assert_eq!(
52//!     co1.to_ref().fold(hlist![|&i| format!("i32 {}", i),
53//!                              |&b| unreachable!() /* we know this won't happen for co1 */ ]),
54//!     "i32 3".to_string());
55//! assert_eq!(
56//!     co2.to_ref().fold(hlist![|&i| unreachable!() /* we know this won't happen for co2 */,
57//!                              |&b| String::from(if b { "t" } else { "f" })]),
58//!     "t".to_string());
59//!
60//! // Here, we use the poly_fn! macro to declare a polymorphic function to avoid caring
61//! // about the order in which declare handlers for the types in our coproduct
62//! let folded = co1.fold(
63//!       poly_fn![
64//!         |_b: bool| -> String { unreachable!() }, /* we know this won't happen for co1 */
65//!         |i:  i32 | -> String { format!("i32 {}", i) },
66//!       ]
67//!      );
68//! assert_eq!(folded, "i32 3".to_string());
69//! # }
70//! ```
71
72use crate::hlist::{HCons, HNil};
73use crate::indices::{Here, There};
74use crate::traits::{Func, Poly, ToMut, ToRef};
75#[cfg(feature = "serde")]
76use serde::{Deserialize, Serialize};
77
78/// Enum type representing a Coproduct. Think of this as a Result, but capable
79/// of supporting any arbitrary number of types instead of just 2.
80///
81/// To construct a Coproduct, you would typically declare a type using the `Coprod!` type
82/// macro and then use the `inject` method.
83///
84/// # Examples
85///
86/// ```
87/// # fn main() {
88/// use frunk_core::Coprod;
89///
90/// type I32Bool = Coprod!(i32, bool);
91/// let co1 = I32Bool::inject(3);
92/// let get_from_1a: Option<&i32> = co1.get();
93/// let get_from_1b: Option<&bool> = co1.get();
94/// assert_eq!(get_from_1a, Some(&3));
95/// assert_eq!(get_from_1b, None);
96/// # }
97/// ```
98#[derive(PartialEq, Debug, Eq, Clone, Copy, PartialOrd, Ord, Hash)]
99#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
100pub enum Coproduct<H, T> {
101    /// Coproduct is either H or T, in this case, it is H
102    Inl(H),
103    /// Coproduct is either H or T, in this case, it is T
104    Inr(T),
105}
106
107/// Phantom type for signature purposes only (has no value)
108///
109/// Used by the macro to terminate the Coproduct type signature
110#[derive(PartialEq, Debug, Eq, Clone, Copy, PartialOrd, Ord, Hash)]
111#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
112pub enum CNil {}
113
114// Inherent methods
115impl<Head, Tail> Coproduct<Head, Tail> {
116    /// Instantiate a coproduct from an element.
117    ///
118    /// This is generally much nicer than nested usage of `Coproduct::{Inl, Inr}`.
119    /// The method uses a trick with type inference to automatically build the correct variant
120    /// according to the input type.
121    ///
122    /// In standard usage, the `Index` type parameter can be ignored,
123    /// as it will typically be solved for using type inference.
124    ///
125    /// # Rules
126    ///
127    /// If the type does not appear in the coproduct, the conversion is forbidden.
128    ///
129    /// If the type appears multiple times in the coproduct, type inference will fail.
130    ///
131    /// # Example
132    ///
133    /// ```
134    /// # fn main() {
135    /// use frunk::Coproduct;
136    /// use frunk_core::Coprod;
137    ///
138    /// type I32F32 = Coprod!(i32, f32);
139    ///
140    /// // Constructing coproducts using inject:
141    /// let co1_nice: I32F32 = Coproduct::inject(1i32);
142    /// let co2_nice: I32F32 = Coproduct::inject(42f32);
143    ///
144    /// // Compare this to the "hard way":
145    /// let co1_ugly: I32F32 = Coproduct::Inl(1i32);
146    /// let co2_ugly: I32F32 = Coproduct::Inr(Coproduct::Inl(42f32));
147    ///
148    /// assert_eq!(co1_nice, co1_ugly);
149    /// assert_eq!(co2_nice, co2_ugly);
150    ///
151    /// // Feel free to use `inject` on a type alias, or even directly on the
152    /// // `Coprod!` macro. (the latter requires wrapping the type in `<>`)
153    /// let _ = I32F32::inject(42f32);
154    /// let _ = <Coprod!(i32, f32)>::inject(42f32);
155    ///
156    /// // You can also use a turbofish to specify the type of the input when
157    /// // it is ambiguous (e.g. an empty `vec![]`).
158    /// // The Index parameter should be left as `_`.
159    /// type Vi32Vf32 = Coprod!(Vec<i32>, Vec<f32>);
160    /// let _: Vi32Vf32 = Coproduct::inject::<Vec<i32>, _>(vec![]);
161    /// # }
162    /// ```
163    #[inline(always)]
164    pub fn inject<T, Index>(to_insert: T) -> Self
165    where
166        Self: CoprodInjector<T, Index>,
167    {
168        CoprodInjector::inject(to_insert)
169    }
170
171    /// Borrow an element from a coproduct by type.
172    ///
173    /// # Example
174    ///
175    /// ```
176    /// # fn main() {
177    /// use frunk_core::Coprod;
178    ///
179    /// type I32F32 = Coprod!(i32, f32);
180    ///
181    /// // You can let type inference find the desired type:
182    /// let co1 = I32F32::inject(42f32);
183    /// let co1_as_i32: Option<&i32> = co1.get();
184    /// let co1_as_f32: Option<&f32> = co1.get();
185    /// assert_eq!(co1_as_i32, None);
186    /// assert_eq!(co1_as_f32, Some(&42f32));
187    ///
188    /// // You can also use turbofish syntax to specify the type.
189    /// // The Index parameter should be left as `_`.
190    /// let co2 = I32F32::inject(1i32);
191    /// assert_eq!(co2.get::<i32, _>(), Some(&1));
192    /// assert_eq!(co2.get::<f32, _>(), None);
193    /// # }
194    /// ```
195    #[inline(always)]
196    pub fn get<S, Index>(&self) -> Option<&S>
197    where
198        Self: CoproductSelector<S, Index>,
199    {
200        CoproductSelector::get(self)
201    }
202
203    /// Retrieve an element from a coproduct by type, ignoring all others.
204    ///
205    /// # Example
206    ///
207    /// ```
208    /// # fn main() {
209    /// use frunk_core::Coprod;
210    ///
211    /// type I32F32 = Coprod!(i32, f32);
212    ///
213    /// // You can let type inference find the desired type:
214    /// let co1 = I32F32::inject(42f32);
215    /// let co1_as_i32: Option<i32> = co1.take();
216    /// let co1_as_f32: Option<f32> = co1.take();
217    /// assert_eq!(co1_as_i32, None);
218    /// assert_eq!(co1_as_f32, Some(42f32));
219    ///
220    /// // You can also use turbofish syntax to specify the type.
221    /// // The Index parameter should be left as `_`.
222    /// let co2 = I32F32::inject(1i32);
223    /// assert_eq!(co2.take::<i32, _>(), Some(1));
224    /// assert_eq!(co2.take::<f32, _>(), None);
225    /// # }
226    /// ```
227    #[inline(always)]
228    pub fn take<T, Index>(self) -> Option<T>
229    where
230        Self: CoproductTaker<T, Index>,
231    {
232        CoproductTaker::take(self)
233    }
234
235    /// Attempt to extract a value from a coproduct (or get the remaining possibilities).
236    ///
237    /// By chaining calls to this, one can exhaustively match all variants of a coproduct.
238    ///
239    /// # Examples
240    ///
241    /// Basic usage:
242    ///
243    /// ```
244    /// # fn main() {
245    /// use frunk_core::Coprod;
246    ///
247    /// type I32F32 = Coprod!(i32, f32);
248    /// type I32 = Coprod!(i32); // remainder after uninjecting f32
249    /// type F32 = Coprod!(f32); // remainder after uninjecting i32
250    ///
251    /// let co1 = I32F32::inject(42f32);
252    ///
253    /// // You can let type inference find the desired type.
254    /// let co1 = I32F32::inject(42f32);
255    /// let co1_as_i32: Result<i32, F32> = co1.uninject();
256    /// let co1_as_f32: Result<f32, I32> = co1.uninject();
257    /// assert_eq!(co1_as_i32, Err(F32::inject(42f32)));
258    /// assert_eq!(co1_as_f32, Ok(42f32));
259    ///
260    /// // It is not necessary to annotate the type of the remainder:
261    /// let res: Result<i32, _> = co1.uninject();
262    /// assert!(res.is_err());
263    ///
264    /// // You can also use turbofish syntax to specify the type.
265    /// // The Index parameter should be left as `_`.
266    /// let co2 = I32F32::inject(1i32);
267    /// assert_eq!(co2.uninject::<i32, _>(), Ok(1));
268    /// assert_eq!(co2.uninject::<f32, _>(), Err(I32::inject(1)));
269    /// # }
270    /// ```
271    ///
272    /// Chaining calls for an exhaustive match:
273    ///
274    /// ```rust
275    /// # fn main() {
276    /// use frunk_core::Coprod;
277    ///
278    /// type I32F32 = Coprod!(i32, f32);
279    ///
280    /// // Be aware that this particular example could be
281    /// // written far more succinctly using `fold`.
282    /// fn handle_i32_f32(co: I32F32) -> &'static str {
283    ///     // Remove i32 from the coproduct
284    ///     let co = match co.uninject::<i32, _>() {
285    ///         Ok(x) => return "integer!",
286    ///         Err(co) => co,
287    ///     };
288    ///
289    ///     // Remove f32 from the coproduct
290    ///     let co = match co.uninject::<f32, _>() {
291    ///         Ok(x) => return "float!",
292    ///         Err(co) => co,
293    ///     };
294    ///
295    ///     // Now co is empty
296    ///     match co { /* unreachable */ }
297    /// }
298    ///
299    /// assert_eq!(handle_i32_f32(I32F32::inject(3)), "integer!");
300    /// assert_eq!(handle_i32_f32(I32F32::inject(3.0)), "float!");
301    /// # }
302    #[inline(always)]
303    pub fn uninject<T, Index>(self) -> Result<T, <Self as CoprodUninjector<T, Index>>::Remainder>
304    where
305        Self: CoprodUninjector<T, Index>,
306    {
307        CoprodUninjector::uninject(self)
308    }
309
310    /// Extract a subset of the possible types in a coproduct (or get the remaining possibilities)
311    ///
312    /// This is basically [`uninject`] on steroids.  It lets you remove a number
313    /// of types from a coproduct at once, leaving behind the remainder in an `Err`.
314    /// For instance, one can extract `Coprod!(C, A)` from `Coprod!(A, B, C, D)`
315    /// to produce `Result<Coprod!(C, A), Coprod!(B, D)>`.
316    ///
317    /// Each type in the extracted subset is required to be part of the input coproduct.
318    ///
319    /// [`uninject`]: #method.uninject
320    ///
321    /// # Example
322    ///
323    /// Basic usage:
324    ///
325    /// ```
326    /// # fn main() {
327    /// use frunk_core::Coprod;
328    ///
329    /// type I32BoolF32 = Coprod!(i32, bool, f32);
330    /// type I32F32 = Coprod!(i32, f32);
331    ///
332    /// let co1 = I32BoolF32::inject(42_f32);
333    /// let co2 = I32BoolF32::inject(true);
334    ///
335    /// let sub1: Result<Coprod!(i32, f32), _> = co1.subset();
336    /// let sub2: Result<Coprod!(i32, f32), _> = co2.subset();
337    /// assert!(sub1.is_ok());
338    /// assert!(sub2.is_err());
339    ///
340    /// // Turbofish syntax for specifying the target subset is also supported.
341    /// // The Indices parameter should be left to type inference using `_`.
342    /// assert!(co1.subset::<Coprod!(i32, f32), _>().is_ok());
343    /// assert!(co2.subset::<Coprod!(i32, f32), _>().is_err());
344    ///
345    /// // Order doesn't matter.
346    /// assert!(co1.subset::<Coprod!(f32, i32), _>().is_ok());
347    /// # }
348    /// ```
349    ///
350    /// Like `uninject`, `subset` can be used for exhaustive matching,
351    /// with the advantage that it can remove more than one type at a time:
352    ///
353    /// ```
354    /// # fn main() {
355    /// use frunk_core::{Coprod, hlist};
356    /// use frunk_core::coproduct::Coproduct;
357    ///
358    /// fn handle_stringly_things(co: Coprod!(&'static str, String)) -> String {
359    ///     co.fold(hlist![
360    ///         |s| format!("&str {}", s),
361    ///         |s| format!("String {}", s),
362    ///     ])
363    /// }
364    ///
365    /// fn handle_countly_things(co: Coprod!(u32)) -> String {
366    ///     co.fold(hlist![
367    ///         |n| vec!["."; n as usize].concat(),
368    ///     ])
369    /// }
370    ///
371    /// fn handle_all(co: Coprod!(String, u32, &'static str)) -> String {
372    ///     // co is currently Coprod!(String, u32, &'static str)
373    ///     let co = match co.subset().map(handle_stringly_things) {
374    ///         Ok(s) => return s,
375    ///         Err(co) => co,
376    ///     };
377    ///
378    ///     // Now co is Coprod!(u32).
379    ///     let co = match co.subset().map(handle_countly_things) {
380    ///         Ok(s) => return s,
381    ///         Err(co) => co,
382    ///     };
383    ///
384    ///     // Now co is empty.
385    ///     match co { /* unreachable */ }
386    /// }
387    ///
388    /// assert_eq!(handle_all(Coproduct::inject("hello")), "&str hello");
389    /// assert_eq!(handle_all(Coproduct::inject(String::from("World!"))), "String World!");
390    /// assert_eq!(handle_all(Coproduct::inject(4)), "....");
391    /// # }
392    /// ```
393    #[inline(always)]
394    pub fn subset<Targets, Indices>(
395        self,
396    ) -> Result<Targets, <Self as CoproductSubsetter<Targets, Indices>>::Remainder>
397    where
398        Self: CoproductSubsetter<Targets, Indices>,
399    {
400        CoproductSubsetter::subset(self)
401    }
402
403    /// Convert a coproduct into another that can hold its variants.
404    ///
405    /// This converts a coproduct into another one which is capable of holding each
406    /// of its types. The most well-supported use-cases (i.e. those where type inference
407    /// is capable of solving for the indices) are:
408    ///
409    /// * Reordering variants: `Coprod!(C, A, B) -> Coprod!(A, B, C)`
410    /// * Embedding into a superset: `Coprod!(B, D) -> Coprod!(A, B, C, D, E)`
411    /// * Coalescing duplicate inputs: `Coprod!(B, B, B, B) -> Coprod!(A, B, C)`
412    ///
413    /// and of course any combination thereof.
414    ///
415    /// # Rules
416    ///
417    /// If any type in the input does not appear in the output, the conversion is forbidden.
418    ///
419    /// If any type in the input appears multiple times in the output, type inference will fail.
420    ///
421    /// All of these rules fall naturally out of its fairly simple definition,
422    /// which is equivalent to:
423    ///
424    /// ```text
425    /// coprod.fold(hlist![
426    ///     |x| Coproduct::inject(x),
427    ///     |x| Coproduct::inject(x),
428    ///             ...
429    ///     |x| Coproduct::inject(x),
430    /// ])
431    /// ```
432    ///
433    /// # Example
434    ///
435    /// ```
436    /// # fn main() {
437    /// use frunk_core::Coprod;
438    ///
439    /// type I32BoolF32 = Coprod!(i32, bool, f32);
440    /// type BoolI32 = Coprod!(bool, i32);
441    ///
442    /// let co = BoolI32::inject(true);
443    /// let embedded: I32BoolF32 = co.embed();
444    /// assert_eq!(embedded, I32BoolF32::inject(true));
445    ///
446    /// // Turbofish syntax for specifying the output type is also supported.
447    /// // The Indices parameter should be left to type inference using `_`.
448    /// let embedded = co.embed::<I32BoolF32, _>();
449    /// assert_eq!(embedded, I32BoolF32::inject(true));
450    /// # }
451    /// ```
452    #[inline(always)]
453    pub fn embed<Targets, Indices>(self) -> Targets
454    where
455        Self: CoproductEmbedder<Targets, Indices>,
456    {
457        CoproductEmbedder::embed(self)
458    }
459
460    /// Borrow each variant of the Coproduct.
461    ///
462    /// # Example
463    ///
464    /// Composing with `subset` to match a subset of variants without
465    /// consuming the coproduct:
466    ///
467    /// ```
468    /// # fn main() {
469    /// use frunk::Coproduct;
470    /// use frunk_core::Coprod;
471    ///
472    /// let co: Coprod!(i32, bool, String) = Coproduct::inject(true);
473    ///
474    /// assert!(co.to_ref().subset::<Coprod!(&bool, &String), _>().is_ok());
475    /// # }
476    /// ```
477    #[inline(always)]
478    pub fn to_ref<'a>(&'a self) -> <Self as ToRef<'a>>::Output
479    where
480        Self: ToRef<'a>,
481    {
482        ToRef::to_ref(self)
483    }
484
485    /// Borrow each variant of the `Coproduct` mutably.
486    ///
487    /// # Example
488    ///
489    /// Composing with `subset` to match a subset of variants without
490    /// consuming the coproduct:
491    ///
492    /// ```
493    /// # fn main() {
494    /// use frunk::Coproduct;
495    /// use frunk_core::Coprod;
496    ///
497    /// let mut co: Coprod!(i32, bool, String) = Coproduct::inject(true);
498    ///
499    /// assert!(co.to_mut().subset::<Coprod!(&mut bool, &mut String), _>().is_ok());
500    /// # }
501    /// ```
502    #[inline(always)]
503    pub fn to_mut<'a>(&'a mut self) -> <Self as ToMut<'a>>::Output
504    where
505        Self: ToMut<'a>,
506    {
507        ToMut::to_mut(self)
508    }
509
510    /// Use functions to transform a Coproduct into a single value.
511    ///
512    /// A variety of types are supported for the `Folder` argument:
513    ///
514    /// * An `hlist![]` of closures (one for each type, in order).
515    /// * A single closure (for a Coproduct that is homogenous).
516    /// * A single [`Poly`].
517    ///
518    /// [`Poly`]: ../traits/struct.Poly.html
519    ///
520    /// # Example
521    ///
522    /// ```
523    /// # fn main() {
524    /// use frunk_core::{Coprod, hlist};
525    ///
526    /// type I32F32Bool = Coprod!(i32, f32, bool);
527    ///
528    /// let co1 = I32F32Bool::inject(3);
529    /// let co2 = I32F32Bool::inject(true);
530    /// let co3 = I32F32Bool::inject(42f32);
531    ///
532    /// let folder = hlist![|&i| format!("int {}", i),
533    ///                     |&f| format!("float {}", f),
534    ///                     |&b| (if b { "t" } else { "f" }).to_string()];
535    ///
536    /// assert_eq!(co1.to_ref().fold(folder), "int 3".to_string());
537    /// # }
538    /// ```
539    ///
540    /// Using a polymorphic function type has the advantage of not
541    /// forcing you to care about the order in which you declare
542    /// handlers for the types in your Coproduct.
543    ///
544    /// ```
545    /// # fn main() {
546    /// use frunk::{Poly, Func};
547    /// use frunk_core::Coprod;
548    ///
549    /// type I32F32Bool = Coprod!(i32, f32, bool);
550    ///
551    /// impl Func<i32> for P {
552    ///     type Output = bool;
553    ///     fn call(args: i32) -> Self::Output {
554    ///         args > 100
555    ///     }
556    /// }
557    /// impl Func<bool> for P {
558    ///     type Output = bool;
559    ///     fn call(args: bool) -> Self::Output {
560    ///         args
561    ///     }
562    /// }
563    /// impl Func<f32> for P {
564    ///     type Output = bool;
565    ///     fn call(args: f32) -> Self::Output {
566    ///         args > 9000f32
567    ///     }
568    /// }
569    /// struct P;
570    ///
571    /// let co1 = I32F32Bool::inject(3);
572    /// let folded = co1.fold(Poly(P));
573    /// # }
574    /// ```
575    #[inline(always)]
576    pub fn fold<Output, Folder>(self, folder: Folder) -> Output
577    where
578        Self: CoproductFoldable<Folder, Output>,
579    {
580        CoproductFoldable::fold(self, folder)
581    }
582
583    /// Apply a function to each variant of a Coproduct.
584    ///
585    /// The transforms some `Coprod!(A, B, C, ..., E)` into some
586    /// `Coprod!(T, U, V, ..., Z)`. A variety of types are supported for the
587    /// mapper argument:
588    ///
589    /// * An `hlist![]` of closures (one for each variant).
590    /// * A single closure (for mapping a Coproduct that is homogenous).
591    /// * A single [`Poly`].
592    ///
593    /// # Examples
594    ///
595    /// ```
596    /// use frunk::{hlist, Coprod};
597    ///
598    /// type I32F32Bool = Coprod!(i32, f32, bool);
599    /// type BoolStrU8 = Coprod!(bool, &'static str, u8);
600    ///
601    /// let co1 = I32F32Bool::inject(3);
602    /// let co2 = I32F32Bool::inject(42f32);
603    /// let co3 = I32F32Bool::inject(true);
604    ///
605    /// let mapper = hlist![
606    ///     |n| n > 0,
607    ///     |f| if f == 42f32 { "๐Ÿ˜€" } else { "๐Ÿคจ" },
608    ///     |b| if b { 1u8 } else { 0u8 },
609    /// ];
610    ///
611    /// assert_eq!(co1.map(&mapper), BoolStrU8::inject(true));
612    /// assert_eq!(co2.map(&mapper), BoolStrU8::inject("๐Ÿ˜€"));
613    /// assert_eq!(co3.map(&mapper), BoolStrU8::inject(1u8));
614    /// ```
615    ///
616    /// Using a polymorphic function type has the advantage of not forcing you
617    /// to care about the order in which you declare handlers for the types in
618    /// your Coproduct.
619    ///
620    /// ```
621    /// use frunk::{poly_fn, Coprod};
622    ///
623    /// type I32F32Bool = Coprod!(i32, f32, bool);
624    ///
625    /// let co1 = I32F32Bool::inject(3);
626    /// let co2 = I32F32Bool::inject(42f32);
627    /// let co3 = I32F32Bool::inject(true);
628    ///
629    /// let mapper = poly_fn![
630    ///     |b: bool| -> bool { !b },
631    ///     |n: i32| -> i32 { n + 3 },
632    ///     |f: f32| -> f32 { -f },
633    /// ];
634    ///
635    /// assert_eq!(co1.map(&mapper), I32F32Bool::inject(6));
636    /// assert_eq!(co2.map(&mapper), I32F32Bool::inject(-42f32));
637    /// assert_eq!(co3.map(&mapper), I32F32Bool::inject(false));
638    /// ```
639    ///
640    /// You can also use a singular closure if the Coproduct variants are all
641    /// the same.
642    ///
643    /// ```
644    /// use frunk::Coprod;
645    ///
646    /// type IntInt = Coprod!(i32, i32);
647    /// type BoolBool = Coprod!(bool, bool);
648    ///
649    /// let mapper = |n| n > 0;
650    ///
651    /// let co = IntInt::Inl(42);
652    /// assert_eq!(co.map(mapper), BoolBool::Inl(true));
653    /// ```
654    #[inline(always)]
655    pub fn map<F>(self, mapper: F) -> <Self as CoproductMappable<F>>::Output
656    where
657        Self: CoproductMappable<F>,
658    {
659        CoproductMappable::map(self, mapper)
660    }
661}
662
663impl<T> Coproduct<T, CNil> {
664    /// Extract the value from a coproduct with only one variant.
665    ///
666    /// # Example
667    ///
668    /// ```
669    /// # fn main() {
670    /// use frunk_core::Coprod;
671    ///
672    /// type I32Only = Coprod!(i32);
673    /// let co = I32Only::inject(5);
674    ///
675    /// assert_eq!(co.extract(), 5);
676    /// # }
677    /// ```
678    #[inline(always)]
679    pub fn extract(self) -> T {
680        match self {
681            Coproduct::Inl(v) => v,
682            Coproduct::Inr(never) => match never {},
683        }
684    }
685}
686
687/// Trait for instantiating a coproduct from an element
688///
689/// This trait is part of the implementation of the inherent static method
690/// [`Coproduct::inject`]. Please see that method for more information.
691///
692/// You only need to import this trait when working with generic
693/// Coproducts of unknown type. In most code, `Coproduct::inject` will
694/// "just work," with or without this trait.
695///
696/// [`Coproduct::inject`]: enum.Coproduct.html#method.inject
697pub trait CoprodInjector<InjectType, Index> {
698    /// Instantiate a coproduct from an element.
699    ///
700    /// Please see the [inherent static method] for more information.
701    ///
702    /// The only difference between that inherent method and this
703    /// trait method is the location of the type parameters.
704    /// (here, they are on the trait rather than the method)
705    ///
706    /// [inherent static method]: enum.Coproduct.html#method.inject
707    fn inject(to_insert: InjectType) -> Self;
708}
709
710impl<I, Tail> CoprodInjector<I, Here> for Coproduct<I, Tail> {
711    fn inject(to_insert: I) -> Self {
712        Coproduct::Inl(to_insert)
713    }
714}
715
716impl<Head, I, Tail, TailIndex> CoprodInjector<I, There<TailIndex>> for Coproduct<Head, Tail>
717where
718    Tail: CoprodInjector<I, TailIndex>,
719{
720    fn inject(to_insert: I) -> Self {
721        let tail_inserted = <Tail as CoprodInjector<I, TailIndex>>::inject(to_insert);
722        Coproduct::Inr(tail_inserted)
723    }
724}
725
726// For turning something into a Coproduct -->
727
728/// Trait for borrowing a coproduct element by type
729///
730/// This trait is part of the implementation of the inherent method
731/// [`Coproduct::get`]. Please see that method for more information.
732///
733/// You only need to import this trait when working with generic
734/// Coproducts of unknown type. If you have a Coproduct of known type,
735/// then `co.get()` should "just work" even without the trait.
736///
737/// [`Coproduct::get`]: enum.Coproduct.html#method.get
738pub trait CoproductSelector<S, I> {
739    /// Borrow an element from a coproduct by type.
740    ///
741    /// Please see the [inherent method] for more information.
742    ///
743    /// The only difference between that inherent method and this
744    /// trait method is the location of the type parameters.
745    /// (here, they are on the trait rather than the method)
746    ///
747    /// [inherent method]: enum.Coproduct.html#method.get
748    fn get(&self) -> Option<&S>;
749}
750
751impl<Head, Tail> CoproductSelector<Head, Here> for Coproduct<Head, Tail> {
752    fn get(&self) -> Option<&Head> {
753        use self::Coproduct::*;
754        match *self {
755            Inl(ref thing) => Some(thing),
756            _ => None, // Impossible
757        }
758    }
759}
760
761impl<Head, FromTail, Tail, TailIndex> CoproductSelector<FromTail, There<TailIndex>>
762    for Coproduct<Head, Tail>
763where
764    Tail: CoproductSelector<FromTail, TailIndex>,
765{
766    fn get(&self) -> Option<&FromTail> {
767        use self::Coproduct::*;
768        match *self {
769            Inr(ref rest) => rest.get(),
770            _ => None, // Impossible
771        }
772    }
773}
774
775/// Trait for retrieving a coproduct element by type
776///
777/// This trait is part of the implementation of the inherent method
778/// [`Coproduct::take`]. Please see that method for more information.
779///
780/// You only need to import this trait when working with generic
781/// Coproducts of unknown type. If you have a Coproduct of known type,
782/// then `co.take()` should "just work" even without the trait.
783///
784/// [`Coproduct::take`]: enum.Coproduct.html#method.take
785pub trait CoproductTaker<S, I> {
786    /// Retrieve an element from a coproduct by type, ignoring all others.
787    ///
788    /// Please see the [inherent method] for more information.
789    ///
790    /// The only difference between that inherent method and this
791    /// trait method is the location of the type parameters.
792    /// (here, they are on the trait rather than the method)
793    ///
794    /// [inherent method]: enum.Coproduct.html#method.take
795    fn take(self) -> Option<S>;
796}
797
798impl<Head, Tail> CoproductTaker<Head, Here> for Coproduct<Head, Tail> {
799    fn take(self) -> Option<Head> {
800        use self::Coproduct::*;
801        match self {
802            Inl(thing) => Some(thing),
803            _ => None, // Impossible
804        }
805    }
806}
807
808impl<Head, FromTail, Tail, TailIndex> CoproductTaker<FromTail, There<TailIndex>>
809    for Coproduct<Head, Tail>
810where
811    Tail: CoproductTaker<FromTail, TailIndex>,
812{
813    fn take(self) -> Option<FromTail> {
814        use self::Coproduct::*;
815        match self {
816            Inr(rest) => rest.take(),
817            _ => None, // Impossible
818        }
819    }
820}
821
822/// Trait for folding a coproduct into a single value.
823///
824/// This trait is part of the implementation of the inherent method
825/// [`Coproduct::fold`]. Please see that method for more information.
826///
827/// You only need to import this trait when working with generic
828/// Coproducts or Folders of unknown type. If the type of everything is known,
829/// then `co.fold(folder)` should "just work" even without the trait.
830///
831/// [`Coproduct::fold`]: enum.Coproduct.html#method.fold
832pub trait CoproductFoldable<Folder, Output> {
833    /// Use functions to fold a coproduct into a single value.
834    ///
835    /// Please see the [inherent method] for more information.
836    ///
837    /// The only difference between that inherent method and this
838    /// trait method is the location of the type parameters.
839    /// (here, they are on the trait rather than the method)
840    ///
841    /// [inherent method]: enum.Coproduct.html#method.fold
842    fn fold(self, f: Folder) -> Output;
843}
844
845impl<P, R, CH, CTail> CoproductFoldable<Poly<P>, R> for Coproduct<CH, CTail>
846where
847    P: Func<CH, Output = R>,
848    CTail: CoproductFoldable<Poly<P>, R>,
849{
850    fn fold(self, f: Poly<P>) -> R {
851        use self::Coproduct::*;
852        match self {
853            Inl(r) => P::call(r),
854            Inr(rest) => rest.fold(f),
855        }
856    }
857}
858
859impl<F, R, FTail, CH, CTail> CoproductFoldable<HCons<F, FTail>, R> for Coproduct<CH, CTail>
860where
861    F: FnOnce(CH) -> R,
862    CTail: CoproductFoldable<FTail, R>,
863{
864    fn fold(self, f: HCons<F, FTail>) -> R {
865        use self::Coproduct::*;
866        let f_head = f.head;
867        let f_tail = f.tail;
868        match self {
869            Inl(r) => (f_head)(r),
870            Inr(rest) => rest.fold(f_tail),
871        }
872    }
873}
874
875/// This is literally impossible; CNil is not instantiable
876impl<F, R> CoproductFoldable<F, R> for CNil {
877    fn fold(self, _: F) -> R {
878        unreachable!()
879    }
880}
881
882/// Trait for mapping over a coproduct's variants.
883///
884/// This trait is part of the implementation of the inherent method
885/// [`Coproduct::map`]. Please see that method for more information.
886///
887/// You only need to import this trait when working with generic Coproducts or
888/// mappers of unknown type. If the type of everything is known, then
889/// `co.map(mapper)` should "just work" even without the trait.
890pub trait CoproductMappable<Mapper> {
891    type Output;
892
893    /// Use functions to map each variant of a coproduct.
894    ///
895    /// Please see the [inherent method] for more information.
896    ///
897    /// The only difference between that inherent method and this
898    /// trait method is the location of the type parameters.
899    /// (here, they are on the trait rather than the method)
900    ///
901    /// [inherent method]: Coproduct::map
902    fn map(self, f: Mapper) -> Self::Output;
903}
904
905/// Implementation for mapping a Coproduct using an `hlist!`.
906impl<F, R, MapperTail, CH, CTail> CoproductMappable<HCons<F, MapperTail>> for Coproduct<CH, CTail>
907where
908    F: FnOnce(CH) -> R,
909    CTail: CoproductMappable<MapperTail>,
910{
911    type Output = Coproduct<R, <CTail as CoproductMappable<MapperTail>>::Output>;
912
913    #[inline]
914    fn map(self, mapper: HCons<F, MapperTail>) -> Self::Output {
915        match self {
916            Coproduct::Inl(l) => Coproduct::Inl((mapper.head)(l)),
917            Coproduct::Inr(rest) => Coproduct::Inr(rest.map(mapper.tail)),
918        }
919    }
920}
921
922/// Implementation for mapping a Coproduct using a `&hlist!`.
923impl<'a, F, R, MapperTail, CH, CTail> CoproductMappable<&'a HCons<F, MapperTail>>
924    for Coproduct<CH, CTail>
925where
926    F: Fn(CH) -> R,
927    CTail: CoproductMappable<&'a MapperTail>,
928{
929    type Output = Coproduct<R, <CTail as CoproductMappable<&'a MapperTail>>::Output>;
930
931    #[inline]
932    fn map(self, mapper: &'a HCons<F, MapperTail>) -> Self::Output {
933        match self {
934            Coproduct::Inl(l) => Coproduct::Inl((mapper.head)(l)),
935            Coproduct::Inr(rest) => Coproduct::Inr(rest.map(&mapper.tail)),
936        }
937    }
938}
939
940/// Implementation for mapping a Coproduct using a `&mut hlist!`.
941impl<'a, F, R, MapperTail, CH, CTail> CoproductMappable<&'a mut HCons<F, MapperTail>>
942    for Coproduct<CH, CTail>
943where
944    F: FnMut(CH) -> R,
945    CTail: CoproductMappable<&'a mut MapperTail>,
946{
947    type Output = Coproduct<R, <CTail as CoproductMappable<&'a mut MapperTail>>::Output>;
948
949    #[inline]
950    fn map(self, mapper: &'a mut HCons<F, MapperTail>) -> Self::Output {
951        match self {
952            Coproduct::Inl(l) => Coproduct::Inl((mapper.head)(l)),
953            Coproduct::Inr(rest) => Coproduct::Inr(rest.map(&mut mapper.tail)),
954        }
955    }
956}
957
958/// Implementation for mapping a Coproduct using a `poly_fn!`.
959impl<P, CH, CTail> CoproductMappable<Poly<P>> for Coproduct<CH, CTail>
960where
961    P: Func<CH>,
962    CTail: CoproductMappable<Poly<P>>,
963{
964    type Output = Coproduct<<P as Func<CH>>::Output, <CTail as CoproductMappable<Poly<P>>>::Output>;
965
966    #[inline]
967    fn map(self, poly: Poly<P>) -> Self::Output {
968        match self {
969            Coproduct::Inl(l) => Coproduct::Inl(P::call(l)),
970            Coproduct::Inr(rest) => Coproduct::Inr(rest.map(poly)),
971        }
972    }
973}
974
975/// Implementation for mapping a Coproduct using a `&poly_fn!`.
976impl<'a, P, CH, CTail> CoproductMappable<&'a Poly<P>> for Coproduct<CH, CTail>
977where
978    P: Func<CH>,
979    CTail: CoproductMappable<&'a Poly<P>>,
980{
981    type Output =
982        Coproduct<<P as Func<CH>>::Output, <CTail as CoproductMappable<&'a Poly<P>>>::Output>;
983
984    #[inline]
985    fn map(self, poly: &'a Poly<P>) -> Self::Output {
986        match self {
987            Coproduct::Inl(l) => Coproduct::Inl(P::call(l)),
988            Coproduct::Inr(rest) => Coproduct::Inr(rest.map(poly)),
989        }
990    }
991}
992
993/// Implementation for mapping a Coproduct using a `&mut poly_fn!`.
994impl<'a, P, CH, CTail> CoproductMappable<&'a mut Poly<P>> for Coproduct<CH, CTail>
995where
996    P: Func<CH>,
997    CTail: CoproductMappable<&'a mut Poly<P>>,
998{
999    type Output =
1000        Coproduct<<P as Func<CH>>::Output, <CTail as CoproductMappable<&'a mut Poly<P>>>::Output>;
1001
1002    #[inline]
1003    fn map(self, poly: &'a mut Poly<P>) -> Self::Output {
1004        match self {
1005            Coproduct::Inl(l) => Coproduct::Inl(P::call(l)),
1006            Coproduct::Inr(rest) => Coproduct::Inr(rest.map(poly)),
1007        }
1008    }
1009}
1010
1011/// Implementation for mapping a Coproduct using a single function that can
1012/// handle all variants.
1013impl<F, R, CH, CTail> CoproductMappable<F> for Coproduct<CH, CTail>
1014where
1015    F: FnMut(CH) -> R,
1016    CTail: CoproductMappable<F>,
1017{
1018    type Output = Coproduct<R, <CTail as CoproductMappable<F>>::Output>;
1019
1020    #[inline]
1021    fn map(self, mut f: F) -> Self::Output {
1022        match self {
1023            Coproduct::Inl(l) => Coproduct::Inl(f(l)),
1024            Coproduct::Inr(rest) => Coproduct::Inr(rest.map(f)),
1025        }
1026    }
1027}
1028
1029/// Base case map impl.
1030impl<F> CoproductMappable<F> for CNil {
1031    type Output = CNil;
1032
1033    #[inline(always)]
1034    fn map(self, _: F) -> Self::Output {
1035        match self {}
1036    }
1037}
1038
1039impl<'a, CH: 'a, CTail> ToRef<'a> for Coproduct<CH, CTail>
1040where
1041    CTail: ToRef<'a>,
1042{
1043    type Output = Coproduct<&'a CH, <CTail as ToRef<'a>>::Output>;
1044
1045    #[inline(always)]
1046    fn to_ref(&'a self) -> Self::Output {
1047        match *self {
1048            Coproduct::Inl(ref r) => Coproduct::Inl(r),
1049            Coproduct::Inr(ref rest) => Coproduct::Inr(rest.to_ref()),
1050        }
1051    }
1052}
1053
1054impl<'a> ToRef<'a> for CNil {
1055    type Output = CNil;
1056
1057    fn to_ref(&'a self) -> CNil {
1058        match *self {}
1059    }
1060}
1061
1062impl<'a, CH: 'a, CTail> ToMut<'a> for Coproduct<CH, CTail>
1063where
1064    CTail: ToMut<'a>,
1065{
1066    type Output = Coproduct<&'a mut CH, <CTail as ToMut<'a>>::Output>;
1067
1068    #[inline(always)]
1069    fn to_mut(&'a mut self) -> Self::Output {
1070        match *self {
1071            Coproduct::Inl(ref mut r) => Coproduct::Inl(r),
1072            Coproduct::Inr(ref mut rest) => Coproduct::Inr(rest.to_mut()),
1073        }
1074    }
1075}
1076
1077impl<'a> ToMut<'a> for CNil {
1078    type Output = CNil;
1079
1080    fn to_mut(&'a mut self) -> CNil {
1081        match *self {}
1082    }
1083}
1084
1085/// Trait for extracting a value from a coproduct in an exhaustive way.
1086///
1087/// This trait is part of the implementation of the inherent method
1088/// [`Coproduct::uninject`]. Please see that method for more information.
1089///
1090/// You only need to import this trait when working with generic
1091/// Coproducts of unknown type. If you have a Coproduct of known type,
1092/// then `co.uninject()` should "just work" even without the trait.
1093///
1094/// [`Coproduct::uninject`]: enum.Coproduct.html#method.uninject
1095pub trait CoprodUninjector<T, Idx>: CoprodInjector<T, Idx> {
1096    type Remainder;
1097
1098    /// Attempt to extract a value from a coproduct (or get the remaining possibilities).
1099    ///
1100    /// Please see the [inherent method] for more information.
1101    ///
1102    /// The only difference between that inherent method and this
1103    /// trait method is the location of the type parameters.
1104    /// (here, they are on the trait rather than the method)
1105    ///
1106    /// [inherent method]: enum.Coproduct.html#method.uninject
1107    fn uninject(self) -> Result<T, Self::Remainder>;
1108}
1109
1110impl<Hd, Tl> CoprodUninjector<Hd, Here> for Coproduct<Hd, Tl> {
1111    type Remainder = Tl;
1112
1113    fn uninject(self) -> Result<Hd, Tl> {
1114        match self {
1115            Coproduct::Inl(h) => Ok(h),
1116            Coproduct::Inr(t) => Err(t),
1117        }
1118    }
1119}
1120
1121impl<Hd, Tl, T, N> CoprodUninjector<T, There<N>> for Coproduct<Hd, Tl>
1122where
1123    Tl: CoprodUninjector<T, N>,
1124{
1125    type Remainder = Coproduct<Hd, Tl::Remainder>;
1126
1127    fn uninject(self) -> Result<T, Self::Remainder> {
1128        match self {
1129            Coproduct::Inl(h) => Err(Coproduct::Inl(h)),
1130            Coproduct::Inr(t) => t.uninject().map_err(Coproduct::Inr),
1131        }
1132    }
1133}
1134
1135/// Trait for extracting a subset of the possible types in a coproduct.
1136///
1137/// This trait is part of the implementation of the inherent method
1138/// [`Coproduct::subset`]. Please see that method for more information.
1139///
1140/// You only need to import this trait when working with generic
1141/// Coproducts of unknown type. If you have a Coproduct of known type,
1142/// then `co.subset()` should "just work" even without the trait.
1143///
1144/// [`Coproduct::subset`]: enum.Coproduct.html#method.subset
1145pub trait CoproductSubsetter<Targets, Indices>: Sized {
1146    type Remainder;
1147
1148    /// Extract a subset of the possible types in a coproduct (or get the remaining possibilities)
1149    ///
1150    /// Please see the [inherent method] for more information.
1151    ///
1152    /// The only difference between that inherent method and this
1153    /// trait method is the location of the type parameters.
1154    /// (here, they are on the trait rather than the method)
1155    ///
1156    /// [inherent method]: enum.Coproduct.html#method.subset
1157    fn subset(self) -> Result<Targets, Self::Remainder>;
1158}
1159
1160impl<Choices, THead, TTail, NHead, NTail, Rem>
1161    CoproductSubsetter<Coproduct<THead, TTail>, HCons<NHead, NTail>> for Choices
1162where
1163    Self: CoprodUninjector<THead, NHead, Remainder = Rem>,
1164    Rem: CoproductSubsetter<TTail, NTail>,
1165{
1166    type Remainder = <Rem as CoproductSubsetter<TTail, NTail>>::Remainder;
1167
1168    /// Attempt to extract a value from a subset of the types.
1169    fn subset(self) -> Result<Coproduct<THead, TTail>, Self::Remainder> {
1170        match self.uninject() {
1171            Ok(good) => Ok(Coproduct::Inl(good)),
1172            Err(bads) => match bads.subset() {
1173                Ok(goods) => Ok(Coproduct::Inr(goods)),
1174                Err(bads) => Err(bads),
1175            },
1176        }
1177    }
1178}
1179
1180impl<Choices> CoproductSubsetter<CNil, HNil> for Choices {
1181    type Remainder = Self;
1182
1183    #[inline(always)]
1184    fn subset(self) -> Result<CNil, Self::Remainder> {
1185        Err(self)
1186    }
1187}
1188
1189/// Trait for converting a coproduct into another that can hold its variants.
1190///
1191/// This trait is part of the implementation of the inherent method
1192/// [`Coproduct::embed`]. Please see that method for more information.
1193///
1194/// You only need to import this trait when working with generic
1195/// Coproducts of unknown type. If you have a Coproduct of known type,
1196/// then `co.embed()` should "just work" even without the trait.
1197///
1198/// [`Coproduct::embed`]: enum.Coproduct.html#method.embed
1199pub trait CoproductEmbedder<Out, Indices> {
1200    /// Convert a coproduct into another that can hold its variants.
1201    ///
1202    /// Please see the [inherent method] for more information.
1203    ///
1204    /// The only difference between that inherent method and this
1205    /// trait method is the location of the type parameters.
1206    /// (here, they are on the trait rather than the method)
1207    ///
1208    /// [inherent method]: enum.Coproduct.html#method.embed
1209    fn embed(self) -> Out;
1210}
1211
1212impl CoproductEmbedder<CNil, HNil> for CNil {
1213    fn embed(self) -> CNil {
1214        match self {
1215        // impossible!
1216    }
1217    }
1218}
1219
1220impl<Head, Tail> CoproductEmbedder<Coproduct<Head, Tail>, HNil> for CNil
1221where
1222    CNil: CoproductEmbedder<Tail, HNil>,
1223{
1224    fn embed(self) -> Coproduct<Head, Tail> {
1225        match self {
1226        // impossible!
1227    }
1228    }
1229}
1230
1231impl<Head, Tail, Out, NHead, NTail> CoproductEmbedder<Out, HCons<NHead, NTail>>
1232    for Coproduct<Head, Tail>
1233where
1234    Out: CoprodInjector<Head, NHead>,
1235    Tail: CoproductEmbedder<Out, NTail>,
1236{
1237    fn embed(self) -> Out {
1238        match self {
1239            Coproduct::Inl(this) => Out::inject(this),
1240            Coproduct::Inr(those) => those.embed(),
1241        }
1242    }
1243}
1244
1245#[cfg(test)]
1246mod tests {
1247    use super::Coproduct::*;
1248    use super::*;
1249
1250    use std::format;
1251    use std::string::{String, ToString};
1252
1253    #[test]
1254    fn test_coproduct_inject() {
1255        type I32StrBool = Coprod!(i32, &'static str, bool);
1256
1257        let co1 = I32StrBool::inject(3);
1258        assert_eq!(co1, Inl(3));
1259        let get_from_1a: Option<&i32> = co1.get();
1260        let get_from_1b: Option<&bool> = co1.get();
1261        assert_eq!(get_from_1a, Some(&3));
1262        assert_eq!(get_from_1b, None);
1263
1264        let co2 = I32StrBool::inject(false);
1265        assert_eq!(co2, Inr(Inr(Inl(false))));
1266        let get_from_2a: Option<&i32> = co2.get();
1267        let get_from_2b: Option<&bool> = co2.get();
1268        assert_eq!(get_from_2a, None);
1269        assert_eq!(get_from_2b, Some(&false));
1270    }
1271
1272    #[test]
1273    fn test_coproduct_fold_consuming() {
1274        type I32F32StrBool = Coprod!(i32, f32, bool);
1275
1276        let co1 = I32F32StrBool::inject(3);
1277        let folded = co1.fold(hlist![
1278            |i| format!("int {}", i),
1279            |f| format!("float {}", f),
1280            |b| (if b { "t" } else { "f" }).to_string(),
1281        ]);
1282
1283        assert_eq!(folded, "int 3".to_string());
1284    }
1285
1286    #[test]
1287    fn test_coproduct_poly_fold_consuming() {
1288        type I32F32StrBool = Coprod!(i32, f32, bool);
1289
1290        impl Func<i32> for P {
1291            type Output = bool;
1292            fn call(args: i32) -> Self::Output {
1293                args > 100
1294            }
1295        }
1296        impl Func<bool> for P {
1297            type Output = bool;
1298            fn call(args: bool) -> Self::Output {
1299                args
1300            }
1301        }
1302        impl Func<f32> for P {
1303            type Output = bool;
1304            fn call(args: f32) -> Self::Output {
1305                args > 9000f32
1306            }
1307        }
1308        struct P;
1309
1310        let co1 = I32F32StrBool::inject(3);
1311        let folded = co1.fold(Poly(P));
1312
1313        assert!(!folded);
1314    }
1315
1316    #[test]
1317    fn test_coproduct_fold_non_consuming() {
1318        type I32F32Bool = Coprod!(i32, f32, bool);
1319
1320        let co1 = I32F32Bool::inject(3);
1321        let co2 = I32F32Bool::inject(true);
1322        let co3 = I32F32Bool::inject(42f32);
1323
1324        assert_eq!(
1325            co1.to_ref().fold(hlist![
1326                |&i| format!("int {}", i),
1327                |&f| format!("float {}", f),
1328                |&b| (if b { "t" } else { "f" }).to_string(),
1329            ]),
1330            "int 3".to_string()
1331        );
1332        assert_eq!(
1333            co2.to_ref().fold(hlist![
1334                |&i| format!("int {}", i),
1335                |&f| format!("float {}", f),
1336                |&b| (if b { "t" } else { "f" }).to_string(),
1337            ]),
1338            "t".to_string()
1339        );
1340        assert_eq!(
1341            co3.to_ref().fold(hlist![
1342                |&i| format!("int {}", i),
1343                |&f| format!("float {}", f),
1344                |&b| (if b { "t" } else { "f" }).to_string(),
1345            ]),
1346            "float 42".to_string()
1347        );
1348    }
1349
1350    #[test]
1351    fn test_coproduct_uninject() {
1352        type I32StrBool = Coprod!(i32, &'static str, bool);
1353
1354        let co1 = I32StrBool::inject(3);
1355        let co2 = I32StrBool::inject("hello");
1356        let co3 = I32StrBool::inject(false);
1357
1358        let uninject_i32_co1: Result<i32, _> = co1.uninject();
1359        let uninject_str_co1: Result<&'static str, _> = co1.uninject();
1360        let uninject_bool_co1: Result<bool, _> = co1.uninject();
1361        assert_eq!(uninject_i32_co1, Ok(3));
1362        assert!(uninject_str_co1.is_err());
1363        assert!(uninject_bool_co1.is_err());
1364
1365        let uninject_i32_co2: Result<i32, _> = co2.uninject();
1366        let uninject_str_co2: Result<&'static str, _> = co2.uninject();
1367        let uninject_bool_co2: Result<bool, _> = co2.uninject();
1368        assert!(uninject_i32_co2.is_err());
1369        assert_eq!(uninject_str_co2, Ok("hello"));
1370        assert!(uninject_bool_co2.is_err());
1371
1372        let uninject_i32_co3: Result<i32, _> = co3.uninject();
1373        let uninject_str_co3: Result<&'static str, _> = co3.uninject();
1374        let uninject_bool_co3: Result<bool, _> = co3.uninject();
1375        assert!(uninject_i32_co3.is_err());
1376        assert!(uninject_str_co3.is_err());
1377        assert_eq!(uninject_bool_co3, Ok(false));
1378    }
1379
1380    #[test]
1381    fn test_coproduct_subset() {
1382        type I32StrBool = Coprod!(i32, &'static str, bool);
1383
1384        // CNil can be extracted from anything.
1385        let res: Result<CNil, _> = I32StrBool::inject(3).subset();
1386        assert!(res.is_err());
1387
1388        if false {
1389            #[allow(unreachable_code, clippy::diverging_sub_expression)]
1390            {
1391                // ...including CNil.
1392                #[allow(unused)]
1393                let cnil: CNil = panic!();
1394                let _res: Result<CNil, _> = cnil.subset();
1395                let _ = res;
1396            }
1397        }
1398
1399        {
1400            // Order does not matter.
1401            let co = I32StrBool::inject(3);
1402            let res: Result<Coprod!(bool, i32), _> = co.subset();
1403            assert_eq!(res, Ok(Coproduct::Inr(Coproduct::Inl(3))));
1404
1405            let co = I32StrBool::inject("4");
1406            let res: Result<Coprod!(bool, i32), _> = co.subset();
1407            assert_eq!(res, Err(Coproduct::Inl("4")));
1408        }
1409    }
1410
1411    #[test]
1412    fn test_coproduct_embed() {
1413        // CNil can be embedded into any coproduct.
1414        if false {
1415            #[allow(unreachable_code, clippy::diverging_sub_expression)]
1416            {
1417                #[allow(unused)]
1418                let cnil: CNil = panic!();
1419                let _: CNil = cnil.embed();
1420
1421                #[allow(unused)]
1422                let cnil: CNil = panic!();
1423                let _: Coprod!(i32, bool) = cnil.embed();
1424            }
1425        }
1426
1427        #[derive(Debug, PartialEq)]
1428        struct A;
1429        #[derive(Debug, PartialEq)]
1430        struct B;
1431        #[derive(Debug, PartialEq)]
1432        struct C;
1433
1434        {
1435            // Order does not matter.
1436            let co_a = <Coprod!(C, A, B)>::inject(A);
1437            let co_b = <Coprod!(C, A, B)>::inject(B);
1438            let co_c = <Coprod!(C, A, B)>::inject(C);
1439            let out_a: Coprod!(A, B, C) = co_a.embed();
1440            let out_b: Coprod!(A, B, C) = co_b.embed();
1441            let out_c: Coprod!(A, B, C) = co_c.embed();
1442            assert_eq!(out_a, Coproduct::Inl(A));
1443            assert_eq!(out_b, Coproduct::Inr(Coproduct::Inl(B)));
1444            assert_eq!(out_c, Coproduct::Inr(Coproduct::Inr(Coproduct::Inl(C))));
1445        }
1446
1447        #[allow(clippy::upper_case_acronyms)]
1448        {
1449            // Multiple variants can resolve to the same output w/o type annotations
1450            type ABC = Coprod!(A, B, C);
1451            type BBB = Coprod!(B, B, B);
1452
1453            let b1 = BBB::inject::<_, Here>(B);
1454            let b2 = BBB::inject::<_, There<Here>>(B);
1455            let out1: ABC = b1.embed();
1456            let out2: ABC = b2.embed();
1457            assert_eq!(out1, Coproduct::Inr(Coproduct::Inl(B)));
1458            assert_eq!(out2, Coproduct::Inr(Coproduct::Inl(B)));
1459        }
1460    }
1461
1462    #[test]
1463    fn test_coproduct_map_ref() {
1464        type I32Bool = Coprod!(i32, bool);
1465        type I32BoolRef<'a> = Coprod!(i32, &'a bool);
1466
1467        fn map_it(co: &I32Bool) -> I32BoolRef<'_> {
1468            // For some reason rustc complains about lifetimes if you try to
1469            // inline the closure literal into the hlist ๐Ÿคท.
1470            let map_bool: fn(&bool) -> &bool = |b| b;
1471
1472            let mapper = hlist![|n: &i32| *n + 3, map_bool];
1473
1474            co.to_ref().map(mapper)
1475        }
1476
1477        let co = I32Bool::inject(3);
1478        let new = map_it(&co);
1479        assert_eq!(new, I32BoolRef::inject(6))
1480    }
1481
1482    #[test]
1483    fn test_coproduct_map_with_ref_mapper() {
1484        type I32Bool = Coprod!(i32, bool);
1485
1486        // HList mapper
1487
1488        let mapper = hlist![|n| n + 3, |b: bool| !b];
1489
1490        let co = I32Bool::inject(3);
1491        let co = co.map(&mapper);
1492        let co = co.map(&mapper);
1493
1494        assert_eq!(co, I32Bool::inject(9));
1495
1496        // Poly mapper
1497
1498        let mapper = poly_fn!(|n: i32| -> i32 { n + 3 }, |b: bool| -> bool { !b });
1499
1500        let co = I32Bool::inject(3);
1501        let co = co.map(&mapper);
1502        let co = co.map(&mapper);
1503
1504        assert_eq!(co, I32Bool::inject(9));
1505
1506        // Fn mapper
1507
1508        type StrStr = Coprod!(String, String);
1509
1510        let captured = String::from("!");
1511        let mapper = |s: String| format!("{}{}", s, &captured);
1512
1513        let co = StrStr::Inl(String::from("hi"));
1514        let co = co.map(&mapper);
1515        let co = co.map(&mapper);
1516
1517        assert_eq!(co, StrStr::Inl(String::from("hi!!")));
1518    }
1519
1520    #[test]
1521    fn test_coproduct_map_with_mut_mapper() {
1522        type I32Bool = Coprod!(i32, bool);
1523
1524        // HList mapper
1525
1526        let mut number = None;
1527        let mut boolean = None;
1528
1529        let mut mapper = hlist![
1530            |n: i32| {
1531                number = Some(n);
1532                n
1533            },
1534            |b: bool| {
1535                boolean = Some(b);
1536                b
1537            },
1538        ];
1539
1540        let co = I32Bool::inject(3);
1541        let co = co.map(&mut mapper);
1542        assert_eq!(co, I32Bool::inject(3));
1543        assert_eq!(number, Some(3));
1544        assert_eq!(boolean, None);
1545
1546        // Poly mapper
1547
1548        let mut mapper = poly_fn!(
1549            |n: i32| -> i32 {
1550                // Poly doesn't support capturing values.
1551                /* number = Some(n); */
1552                n
1553            },
1554            |b: bool| -> bool {
1555                // Poly doesn't support capturing values.
1556                /* boolean = Some(b) */
1557                b
1558            },
1559        );
1560
1561        let co = I32Bool::inject(3);
1562        let co = co.map(&mut mapper);
1563        assert_eq!(co, I32Bool::inject(3));
1564
1565        // Fn mapper
1566
1567        type StrStr = Coprod!(String, String);
1568
1569        let mut captured = String::new();
1570        let mut mapper = |s: String| {
1571            let s = format!("{s}!");
1572            captured.push_str(&s);
1573            s
1574        };
1575
1576        let co = StrStr::Inl(String::from("hi"));
1577        let co = co.map(&mut mapper);
1578        let co = co.map(&mut mapper);
1579
1580        assert_eq!(co, StrStr::Inl(String::from("hi!!")));
1581        assert_eq!(captured, String::from("hi!hi!!"));
1582    }
1583}