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}