pipe_chain/pipe/
either.rs

1//! Either combinator
2use crate::{Pipe, Result};
3use either::Either;
4use std::marker::PhantomData;
5use tuplify::Unpack;
6
7/// Either combinator
8///
9/// Combine 2 [`Pipe`]s that can produce different outputs but share the same input
10pub trait EitherExt<I, O, E, R> {
11    /// Combine 2 [Pipe]s with the same input and remaining but differents outputs
12    ///
13    /// ```
14    /// # use pipe_chain::{str::digits, tag, EitherExt, str::TagStrError, Incomplete, MapExt, Pipe};
15    /// # use either::Either;
16    /// # use std::error::Error as StdError;
17    /// # #[derive(Debug, PartialEq, Eq)]
18    /// # enum Error {
19    /// #     Incomplete(Incomplete),
20    /// #     Tag(TagStrError),
21    /// # }
22    /// #
23    /// # impl std::fmt::Display for Error {
24    /// #     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25    /// #        write!(f, "{self:?}")
26    /// #     }
27    /// # }
28    /// # impl StdError for Error {}
29    /// #
30    /// # impl From<Incomplete> for Error {
31    /// #     fn from(value: Incomplete) -> Error { Error::Incomplete(value) }
32    /// # }
33    /// #
34    /// # impl From<TagStrError> for Error {
35    /// #     fn from(value: TagStrError) -> Error { Error::Tag(value) }
36    /// # }
37    /// let mut p =
38    ///     tag::<Error, _, _>("a").either(digits::<Error>(1..).map1(|x: &str| x.parse().unwrap()));
39    ///
40    /// assert_eq!(p.apply("a"), Ok(("", (Either::Left("a"),))));
41    /// assert_eq!(p.apply("12"), Ok(("", (Either::Right(12u32),))));
42    /// ```
43    fn either<O2, P>(self, p: P) -> EitherPipe<O, O2, Self, P>
44    where
45        I: Clone,
46        Self: Pipe<I, O, E, R> + Sized,
47        P: Pipe<I, O2, E, R>,
48    {
49        EitherPipe::new(self, p)
50    }
51
52    /// Combine 2 [Pipe]s with the same input and differents outputs and remaining
53    ///
54    /// ```
55    /// #  use pipe_chain::{
56    ///     str::digits, tag, AndThenExt, EitherExt, str::TagStrError, Incomplete, MapExt, Pipe,
57    /// };
58    /// # use either::Either;
59    /// # use std::error::Error as StdError;
60    /// # #[derive(Debug, PartialEq, Eq)]
61    /// # enum Error {
62    /// #     Incomplete(Incomplete),
63    /// #     Tag(TagStrError),
64    /// # }
65    /// #
66    /// # impl std::fmt::Display for Error {
67    /// #     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
68    /// #        write!(f, "{self:?}")
69    /// #     }
70    /// # }
71    /// # impl StdError for Error {}
72    /// #
73    /// # impl From<Incomplete> for Error {
74    /// #     fn from(value: Incomplete) -> Error { Error::Incomplete(value) }
75    /// # }
76    /// #
77    /// # impl From<TagStrError> for Error {
78    /// #     fn from(value: TagStrError) -> Error { Error::Tag(value) }
79    /// # }
80    /// let mut p = tag::<Error, _, _>("a").either_full(
81    ///     digits::<Error>(1..).map1(|x: &str| x.parse().unwrap()).ok_and_then(|_, x| Ok((0, x))),
82    /// );
83    ///
84    /// assert_eq!(p.apply("a"), Ok((Either::Left(""), (Either::Left("a"),))));
85    /// assert_eq!(p.apply("12"), Ok((Either::Right(0), (Either::Right(12u32),))));
86    /// ```
87    fn either_full<O2, R2, P>(self, p: P) -> EitherPipeFull<O, O2, Self, P>
88    where
89        I: Clone,
90        Self: Pipe<I, O, E, R> + Sized,
91        P: Pipe<I, O2, E, R2>,
92    {
93        EitherPipeFull::new(self, p)
94    }
95}
96
97impl<I, O, E, R, P> EitherExt<I, O, E, R> for P where P: Pipe<I, O, E, R> {}
98
99/// [EitherExt::either] implementation
100pub struct EitherPipe<O, O2, P, P1>(P, P1, PhantomData<O>, PhantomData<O2>);
101
102impl<O, O2, P, P1> EitherPipe<O, O2, P, P1> {
103    fn new(p: P, p1: P1) -> Self { Self(p, p1, PhantomData, PhantomData) }
104}
105
106impl<I, O, O2, E, R, P, P1> Pipe<I, (Either<O::Output, O2::Output>,), E, R>
107    for EitherPipe<O, O2, P, P1>
108where
109    I: Clone,
110    O: Unpack,
111    O2: Unpack,
112    P: Pipe<I, O, E, R> + Sized,
113    P1: Pipe<I, O2, E, R>,
114{
115    fn apply(&mut self, input: I) -> Result<R, (Either<O::Output, O2::Output>,), E> {
116        self.0
117            .apply(input.clone())
118            .map(|(i, o)| (i, (Either::Left(o.unpack()),)))
119            .or_else(|x| {
120                x.fatality()?;
121                self.1.apply(input).map(|(i, x)| (i, (Either::Right(x.unpack()),)))
122            })
123    }
124}
125
126/// [EitherExt::either_full] implementation
127pub struct EitherPipeFull<O, O2, P, P1>(P, P1, PhantomData<O>, PhantomData<O2>);
128
129impl<O, O2, P, P1> EitherPipeFull<O, O2, P, P1> {
130    fn new(p: P, p1: P1) -> Self { Self(p, p1, PhantomData, PhantomData) }
131}
132
133impl<I, O, O2, E, R, R2, P, P1>
134    Pipe<I, (Either<O::Output, O2::Output>,), E, Either<R, R2>>
135    for EitherPipeFull<O, O2, P, P1>
136where
137    I: Clone,
138    O: Unpack,
139    O2: Unpack,
140    P: Pipe<I, O, E, R> + Sized,
141    P1: Pipe<I, O2, E, R2>,
142{
143    fn apply(
144        &mut self, input: I,
145    ) -> Result<Either<R, R2>, (Either<O::Output, O2::Output>,), E> {
146        self.0
147            .apply(input.clone())
148            .map(|(i, o)| (Either::Left(i), (Either::Left(o.unpack()),)))
149            .or_else(|x| {
150                x.fatality()?;
151                self.1
152                    .apply(input)
153                    .map(|(i, x)| (Either::Right(i), (Either::Right(x.unpack()),)))
154            })
155    }
156}