pipe_chain/pipe/
skip.rs

1use crate::{Pipe, Result};
2use std::marker::PhantomData;
3
4/// Combinator that discards output
5pub trait SkipExt<I, O, E, R> {
6    /// Discards the output of the pipe
7    ///
8    /// Example:
9    /// ```
10    /// # use fatal_error::FatalError;
11    /// # use pipe_chain::{Pipe, SkipExt, tag, str::TagStrError, Incomplete};
12    /// # use std::error::Error as StdError;
13    /// # #[derive(Debug, PartialEq, Eq)]
14    /// # enum Error {
15    /// #     Incomplete(Incomplete),
16    /// #     Tag(TagStrError),
17    /// # }
18    /// #
19    /// # impl std::fmt::Display for Error {
20    /// #     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21    /// #        write!(f, "{self:?}")
22    /// #     }
23    /// # }
24    /// # impl StdError for Error {}
25    /// #
26    /// # impl From<Incomplete> for Error {
27    /// #     fn from(value: Incomplete) -> Error { Error::Incomplete(value) }
28    /// # }
29    /// #
30    /// # impl From<TagStrError> for Error {
31    /// #     fn from(value: TagStrError) -> Error { Error::Tag(value) }
32    /// # }
33    /// assert_eq!(tag::<Error, _, _>("foo").skip().apply("foobar"), Ok(("bar", ())));
34    ///
35    /// assert_eq!(
36    ///     tag::<Error, _, _>("foo").skip().apply("something"),
37    ///     Err(FatalError::Error(Error::Tag(TagStrError("foo".into(), "som".into()))))
38    /// );
39    /// ```
40    fn skip(self) -> Skip<Self, O>
41    where
42        Self: Sized,
43    {
44        Skip::new(self)
45    }
46
47    /// Chains 2 [`Pipe`]s one after another both output are discarded
48    ///
49    /// Example:
50    /// ```
51    /// # use fatal_error::FatalError;
52    /// # use pipe_chain::{Pipe, SkipExt, tag, str::TagStrError, Incomplete};
53    /// # use std::error::Error as StdError;
54    /// # #[derive(Debug, PartialEq, Eq)]
55    /// # enum Error {
56    /// #     Incomplete(Incomplete),
57    /// #     Tag(TagStrError),
58    /// # }
59    /// #
60    /// # impl std::fmt::Display for Error {
61    /// #     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62    /// #        write!(f, "{self:?}")
63    /// #     }
64    /// # }
65    /// # impl StdError for Error {}
66    /// #
67    /// # impl From<Incomplete> for Error {
68    /// #     fn from(value: Incomplete) -> Error { Error::Incomplete(value) }
69    /// # }
70    /// #
71    /// # impl From<TagStrError> for Error {
72    /// #     fn from(value: TagStrError) -> Error { Error::Tag(value) }
73    /// # }
74    /// assert_eq!(
75    ///     tag::<Error, _, _>("foo").and_skip(tag("bar")).apply("foobar"),
76    ///     Ok(("", ()))
77    /// );
78    ///
79    /// assert_eq!(
80    ///     tag::<Error, _, _>("foo").and_skip(tag("boo")).apply("something"),
81    ///     Err(FatalError::Error(Error::Tag(TagStrError("foo".into(), "som".into()))))
82    /// );
83    /// ```
84    fn and_skip<O2, R2, P>(self, other: P) -> AndSkip<Self, P, O, O2, R>
85    where
86        Self: Sized,
87        P: Pipe<R, O2, E, R2>,
88    {
89        AndSkip::new(self, other)
90    }
91
92    /// Apply the second [Pipe] if the first fails discarding both output
93    ///
94    /// Example:
95    /// ```
96    /// # use fatal_error::FatalError;
97    /// # use pipe_chain::{Pipe, SkipExt, tag, str::TagStrError, Incomplete};
98    /// # use std::error::Error as StdError;
99    /// # #[derive(Debug, PartialEq, Eq)]
100    /// # enum Error {
101    /// #     Incomplete(Incomplete),
102    /// #     Tag(TagStrError),
103    /// # }
104    /// #
105    /// # impl std::fmt::Display for Error {
106    /// #     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107    /// #        write!(f, "{self:?}")
108    /// #     }
109    /// # }
110    /// # impl StdError for Error {}
111    /// #
112    /// # impl From<Incomplete> for Error {
113    /// #     fn from(value: Incomplete) -> Error { Error::Incomplete(value) }
114    /// # }
115    /// #
116    /// # impl From<TagStrError> for Error {
117    /// #     fn from(value: TagStrError) -> Error { Error::Tag(value) }
118    /// # }
119    /// assert_eq!(tag::<Error, _, _>("foo").or_skip(tag("bar")).apply("foo"), Ok(("", ())));
120    ///
121    /// assert_eq!(tag::<Error, _, _>("foo").or_skip(tag("boo")).apply("boo"), Ok(("", ())));
122    ///
123    /// assert_eq!(
124    ///     tag::<Error, _, _>("foo").or_skip(tag("boo")).apply("something"),
125    ///     Err(FatalError::Error(Error::Tag(TagStrError("boo".into(), "som".into()))))
126    /// );
127    /// ```
128    fn or_skip<O2, P>(self, p: P) -> OrSkip<O, O2, Self, P>
129    where
130        Self: Sized,
131        I: Clone,
132        P: Pipe<I, O2, E, R>,
133    {
134        OrSkip::new(self, p)
135    }
136}
137
138impl<I, O, E, R, P> SkipExt<I, O, E, R> for P where P: Pipe<I, O, E, R> {}
139
140/// [SkipExt::skip] implementation
141pub struct Skip<P, O> {
142    p: P,
143    o: PhantomData<O>,
144}
145
146impl<P, O> Skip<P, O> {
147    fn new(p: P) -> Self { Self { p, o: PhantomData } }
148}
149
150impl<I, I2, O, E, P> Pipe<I, (), E, I2> for Skip<P, O>
151where
152    P: Pipe<I, O, E, I2>,
153{
154    fn apply(&mut self, input: I) -> Result<I2, (), E> {
155        self.p.apply(input).map(|x| (x.0, ()))
156    }
157}
158
159/// [SkipExt::and_skip] implementation
160pub struct AndSkip<P1, P2, O, O2, R>(
161    P1,
162    P2,
163    PhantomData<O>,
164    PhantomData<O2>,
165    PhantomData<R>,
166);
167
168impl<P1, P2, O, O2, R> AndSkip<P1, P2, O, O2, R> {
169    fn new(p: P1, p1: P2) -> AndSkip<P1, P2, O, O2, R> {
170        AndSkip(p, p1, PhantomData, PhantomData, PhantomData)
171    }
172}
173
174impl<I, O, E, R, O2, R2, P1, P2> Pipe<I, (), E, R2> for AndSkip<P1, P2, O, O2, R>
175where
176    P1: Pipe<I, O, E, R>,
177    P2: Pipe<R, O2, E, R2>,
178{
179    fn apply(&mut self, input: I) -> Result<R2, (), E> {
180        self.0.apply(input).and_then(|(i, _)| self.1.apply(i).map(|(i, _)| (i, ())))
181    }
182}
183
184/// [SkipExt::or_skip] implementation
185pub struct OrSkip<O, O2, P, P1> {
186    p:  P,
187    p1: P1,
188    o:  PhantomData<O>,
189    o2: PhantomData<O2>,
190}
191
192impl<O, O2, P, P1> OrSkip<O, O2, P, P1> {
193    fn new(p: P, p1: P1) -> Self { Self { p, p1, o: PhantomData, o2: PhantomData } }
194}
195
196impl<I, O, O2, E, R, P, P1> Pipe<I, (), E, R> for OrSkip<O, O2, P, P1>
197where
198    P: Pipe<I, O, E, R>,
199    P1: Pipe<I, O2, E, R>,
200    I: Clone,
201{
202    fn apply(&mut self, input: I) -> Result<R, (), E> {
203        match self.p.apply(input.clone()).map(|(x, _)| (x, ())) {
204            x @ Ok(_) => x,
205            Err(x) => {
206                x.fatality()?;
207                self.p1.apply(input).map(|(x, _)| (x, ()))
208            }
209        }
210    }
211}