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}