pipe_chain/pipe/or.rs
1use crate::{Pipe, Result};
2use std::marker::PhantomData;
3use tuplify::Unpack;
4
5/// Or combinator
6pub trait OrExt<I, O, E, R> {
7 /// Apply the second [Pipe] if the first fails
8 ///
9 /// Example:
10 /// ```
11 /// # use fatal_error::FatalError;
12 /// # use pipe_chain::{Pipe, OrExt, tag, str::TagStrError, Incomplete};
13 /// # use std::error::Error as StdError;
14 /// # #[derive(Debug, PartialEq, Eq)]
15 /// # enum Error {
16 /// # Incomplete(Incomplete),
17 /// # Tag(TagStrError),
18 /// # }
19 /// #
20 /// # impl std::fmt::Display for Error {
21 /// # fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22 /// # write!(f, "{self:?}")
23 /// # }
24 /// # }
25 /// # impl StdError for Error {}
26 /// #
27 /// # impl From<Incomplete> for Error {
28 /// # fn from(value: Incomplete) -> Error { Error::Incomplete(value) }
29 /// # }
30 /// #
31 /// # impl From<TagStrError> for Error {
32 /// # fn from(value: TagStrError) -> Error { Error::Tag(value) }
33 /// # }
34 /// assert_eq!(tag::<Error, _, _>("foo").or(tag("bar")).apply("foo"), Ok(("", ("foo",))));
35 ///
36 /// assert_eq!(tag::<Error, _, _>("foo").or(tag("boo")).apply("boo"), Ok(("", ("boo",))));
37 ///
38 /// assert_eq!(
39 /// tag::<Error, _, _>("foo").or(tag("boo")).apply("something"),
40 /// Err(FatalError::Error(Error::Tag(TagStrError("boo".into(), "som".into()))))
41 /// );
42 /// ```
43 fn or<P>(self, p: P) -> Or<Self, P>
44 where
45 Self: Sized,
46 I: Clone,
47 P: Pipe<I, O, E, R>,
48 {
49 Or::new(self, p)
50 }
51
52 /// Apply the second [Pipe] if the first fails discarding the output of the second pipe
53 ///
54 /// Example:
55 /// ```
56 /// # use fatal_error::FatalError;
57 /// # use pipe_chain::{Pipe, OrExt, tag, str::TagStrError, Incomplete};
58 /// # use std::error::Error as StdError;
59 /// # #[derive(Debug, PartialEq, Eq)]
60 /// # enum Error {
61 /// # Incomplete(Incomplete),
62 /// # Tag(TagStrError),
63 /// # }
64 /// #
65 /// # impl std::fmt::Display for Error {
66 /// # fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67 /// # write!(f, "{self:?}")
68 /// # }
69 /// # }
70 /// # impl StdError for Error {}
71 /// #
72 /// # impl From<Incomplete> for Error {
73 /// # fn from(value: Incomplete) -> Error { Error::Incomplete(value) }
74 /// # }
75 /// #
76 /// # impl From<TagStrError> for Error {
77 /// # fn from(value: TagStrError) -> Error { Error::Tag(value) }
78 /// # }
79 /// assert_eq!(
80 /// tag::<Error, _, _>("foo").or_self(tag("bar")).apply("foo"),
81 /// Ok(("", (Some("foo"),)))
82 /// );
83 ///
84 /// assert_eq!(
85 /// tag::<Error, _, _>("foo").or_self(tag("boo")).apply("boo"),
86 /// Ok(("", (None,)))
87 /// );
88 ///
89 /// assert_eq!(
90 /// tag::<Error, _, _>("foo").or_self(tag("boo")).apply("something"),
91 /// Err(FatalError::Error(Error::Tag(TagStrError("boo".into(), "som".into()))))
92 /// );
93 /// ```
94 fn or_self<O2, P>(self, p: P) -> OrSelf<O, O2, Self, P>
95 where
96 Self: Sized,
97 O: Unpack,
98 I: Clone,
99 P: Pipe<I, O2, E, R>,
100 {
101 OrSelf::new(self, p)
102 }
103
104 /// Apply the second [Pipe] if the first fails discarding the output of the first pipe
105 ///
106 /// Example:
107 /// ```
108 /// # use fatal_error::FatalError;
109 /// # use pipe_chain::{Pipe, OrExt, tag, str::TagStrError, Incomplete};
110 /// # use std::error::Error as StdError;
111 /// # #[derive(Debug, PartialEq, Eq)]
112 /// # enum Error {
113 /// # Incomplete(Incomplete),
114 /// # Tag(TagStrError),
115 /// # }
116 /// #
117 /// # impl std::fmt::Display for Error {
118 /// # fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119 /// # write!(f, "{self:?}")
120 /// # }
121 /// # }
122 /// # impl StdError for Error {}
123 /// #
124 /// # impl From<Incomplete> for Error {
125 /// # fn from(value: Incomplete) -> Error { Error::Incomplete(value) }
126 /// # }
127 /// #
128 /// # impl From<TagStrError> for Error {
129 /// # fn from(value: TagStrError) -> Error { Error::Tag(value) }
130 /// # }
131 /// assert_eq!(
132 /// tag::<Error, _, _>("foo").or_other(tag("bar")).apply("foo"),
133 /// Ok(("", (None,)))
134 /// );
135 ///
136 /// assert_eq!(
137 /// tag::<Error, _, _>("foo").or_other(tag("boo")).apply("boo"),
138 /// Ok(("", (Some("boo"),)))
139 /// );
140 ///
141 /// assert_eq!(
142 /// tag::<Error, _, _>("foo").or_other(tag("boo")).apply("something"),
143 /// Err(FatalError::Error(Error::Tag(TagStrError("boo".into(), "som".into()))))
144 /// );
145 /// ```
146 fn or_other<O2, P>(self, p: P) -> OrOther<O, O2, Self, P>
147 where
148 Self: Sized,
149 I: Clone,
150 O2: Unpack,
151 P: Pipe<I, O2, E, R>,
152 {
153 OrOther::new(self, p)
154 }
155}
156
157impl<I, O, E, R, P> OrExt<I, O, E, R> for P where P: Pipe<I, O, E, R> {}
158
159/// [OrExt::or] implementation
160pub struct Or<P, P1> {
161 p: P,
162 p1: P1,
163}
164
165impl<P, P1> Or<P, P1> {
166 fn new(p: P, p1: P1) -> Self { Self { p, p1 } }
167}
168
169impl<I, O, E, R, P, P1> Pipe<I, O, E, R> for Or<P, P1>
170where
171 P: Pipe<I, O, E, R>,
172 P1: Pipe<I, O, E, R>,
173 I: Clone,
174{
175 fn apply(&mut self, input: I) -> Result<R, O, E> {
176 match self.p.apply(input.clone()) {
177 x @ Ok(_) => x,
178 Err(x) => {
179 x.fatality()?;
180 self.p1.apply(input)
181 }
182 }
183 }
184}
185
186/// [OrExt::or_self] implementation
187pub struct OrSelf<O, O2, P, P1> {
188 p: P,
189 p1: P1,
190 o: PhantomData<O>,
191 o2: PhantomData<O2>,
192}
193
194impl<O, O2, P, P1> OrSelf<O, O2, P, P1> {
195 fn new(p: P, p1: P1) -> Self { Self { p, p1, o: PhantomData, o2: PhantomData } }
196}
197
198impl<I, O, O2, E, R, P, P1> Pipe<I, (Option<O::Output>,), E, R> for OrSelf<O, O2, P, P1>
199where
200 O: Unpack,
201 P: Pipe<I, O, E, R>,
202 P1: Pipe<I, O2, E, R>,
203 I: Clone,
204{
205 fn apply(&mut self, input: I) -> Result<R, (Option<O::Output>,), E> {
206 match self.p.apply(input.clone()).map(|(x, y)| (x, (Some(y.unpack()),))) {
207 x @ Ok(_) => x,
208 Err(x) => {
209 x.fatality()?;
210 self.p1.apply(input).map(|(x, _)| (x, (None,)))
211 }
212 }
213 }
214}
215
216/// [OrExt::or_other] implementation
217pub struct OrOther<O, O2, P, P1> {
218 p: P,
219 p1: P1,
220 o: PhantomData<O>,
221 o2: PhantomData<O2>,
222}
223
224impl<O, O2, P, P1> OrOther<O, O2, P, P1> {
225 fn new(p: P, p1: P1) -> Self { Self { p, p1, o: PhantomData, o2: PhantomData } }
226}
227
228impl<I, O, O2, E, R, P, P1> Pipe<I, (Option<O2::Output>,), E, R> for OrOther<O, O2, P, P1>
229where
230 O2: Unpack,
231 P: Pipe<I, O, E, R>,
232 P1: Pipe<I, O2, E, R>,
233 I: Clone,
234{
235 fn apply(&mut self, input: I) -> Result<R, (Option<O2::Output>,), E> {
236 match self.p.apply(input.clone()).map(|(x, _)| (x, (None,))) {
237 x @ Ok(_) => x,
238 Err(x) => {
239 x.fatality()?;
240 self.p1.apply(input).map(|(x, y)| (x, (Some(y.unpack()),)))
241 }
242 }
243 }
244}
245
246/// [any_of] implementation detail
247pub trait AnyOf<I, O, E, R> {
248 /// Process the input
249 fn apply_any_of(&mut self, input: I) -> Result<R, O, E>;
250}
251
252/// Similar to [OrExt::or] but with many possibilities at once
253///
254/// Example
255/// ```
256/// # use fatal_error::FatalError;
257/// # use pipe_chain::{Pipe, any_of, tag, str::TagStrError, Incomplete};
258/// # use std::error::Error as StdError;
259/// # #[derive(Debug, PartialEq, Eq)]
260/// # enum Error {
261/// # Incomplete(Incomplete),
262/// # Tag(TagStrError),
263/// # }
264/// #
265/// # impl std::fmt::Display for Error {
266/// # fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
267/// # write!(f, "{self:?}")
268/// # }
269/// # }
270/// # impl StdError for Error {}
271/// #
272/// # impl From<Incomplete> for Error {
273/// # fn from(value: Incomplete) -> Error { Error::Incomplete(value) }
274/// # }
275/// #
276/// # impl From<TagStrError> for Error {
277/// # fn from(value: TagStrError) -> Error { Error::Tag(value) }
278/// # }
279/// let mut p = any_of((tag::<Error, _, _>("foo"), tag("bar"), tag("baz")));
280///
281/// assert_eq!(p.apply("foo"), Ok(("", ("foo",))));
282///
283/// assert_eq!(p.apply("bar"), Ok(("", ("bar",))));
284///
285/// assert_eq!(p.apply("baz"), Ok(("", ("baz",))));
286///
287/// assert_eq!(
288/// p.apply("something"),
289/// Err(FatalError::Error(Error::Tag(TagStrError("baz".into(), "som".into()))))
290/// );
291/// ```
292pub fn any_of<I, O, E, R>(mut p: impl AnyOf<I, O, E, R>) -> impl Pipe<I, O, E, R> {
293 move |x| p.apply_any_of(x)
294}
295
296macro_rules! any_of_impl {
297 ($_head:ident) => {};
298 ($head:ident $($tail:ident) *) => {
299 any_of_impl!($($tail) *);
300
301 impl<I: Clone, O, E, R, $head: Pipe<I, O, E, R>, $($tail: Pipe<I, O, E, R>), *> AnyOf<I, O, E, R> for ($head, $($tail), *) {
302 #[allow(non_snake_case)]
303 fn apply_any_of(&mut self, input: I) -> Result<R, O, E> {
304 let ($head, $($tail), *) = self;
305 let e = $head.apply(input.clone());
306 if e.as_ref().map_or_else(|x|x.is_fatal(), |_| true) { return e; }
307 $(
308 let e = $tail.apply(input.clone());
309 if e.as_ref().map_or_else(|x|x.is_fatal(), |_| true) { return e; }
310 ) *
311 e
312 }
313 }
314 };
315}
316
317any_of_impl!(T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15 T16 T17 T18 T19 T20 T21 T22 T23 T24 T25 T26 T27 T28 T29 T30 T31 T32);