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}