pipe_chain/pipe/opt.rs
1use crate::{Pipe, Result};
2use std::marker::PhantomData;
3use tuplify::Unpack;
4
5/// Makes a [Pipe] optional
6pub trait OptExt<I, O, E> {
7 /// Makes a [Pipe] optional if it returns a non fatal error
8 ///
9 /// example:
10 /// ```
11 /// # use fatal_error::FatalError;
12 /// # use pipe_chain::{tag, Incomplete, str::TagStrError, OptExt, Pipe, Tag};
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!(
35 /// tag::<Error, _, _>("foo").opt().apply("foobar"),
36 /// Ok(("bar", (Some("foo"),)))
37 /// );
38 ///
39 /// assert_eq!(tag::<Error, _, _>("boo").opt().apply("foobar"), Ok(("foobar", (None,))));
40 /// ```
41 fn opt(self) -> Opt<Self, O>
42 where
43 Self: Sized,
44 I: Clone,
45 O: Unpack,
46 {
47 Opt::new(self)
48 }
49}
50
51impl<I, O, E, P> OptExt<I, O, E> for P where P: Pipe<I, O, E> {}
52
53/// [OptExt::opt] implementation
54pub struct Opt<P, O> {
55 p: P,
56 o: PhantomData<O>,
57}
58
59impl<P, O> Opt<P, O> {
60 fn new(p: P) -> Self { Self { p, o: PhantomData } }
61}
62
63impl<I, O, E, P> Pipe<I, (Option<<O as Unpack>::Output>,), E, I> for Opt<P, O>
64where
65 O: Unpack,
66 I: Clone,
67 P: Pipe<I, O, E>,
68{
69 fn apply(&mut self, input: I) -> Result<I, (Option<<O as Unpack>::Output>,), E> {
70 self.p.apply(input.clone()).map_or_else(
71 |x| x.fatality().map(|_| (input, (None,))),
72 |(i, o)| Ok((i, (Some(o.unpack()),))),
73 )
74 }
75}