binator/
parsed.rs

1use core::{
2  convert::Infallible,
3  fmt::{
4    self,
5    Debug,
6    Display,
7    Formatter,
8  },
9  ops::{
10    ControlFlow,
11    FromResidual,
12    Try,
13  },
14};
15
16use crate::{
17  Contexting,
18  ParsedAux,
19  ProvideElement,
20  Split,
21  Streaming,
22  Success,
23};
24
25/// Parsed represent the result of a `parse()`.
26#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
27#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
28pub enum Parsed<Token, Stream, Context> {
29  /// When parser successfully parse the stream
30  Success {
31    /// token produced by the parser.
32    token: Token,
33    /// the stream used by the parser, should have less item than before.
34    stream: Stream,
35  },
36  /// When parser fail to parse the stream.
37  /// This is not fatal and is a normal behavior for a parser.
38  Failure(Context),
39  // this could be added as "fast fail" path
40  // Cut(Context),
41  /// When parser encouter an error, this should be fatal. Could be a
42  /// programming error or something wrong will the stream.
43  Error(Context),
44}
45
46impl<Token, Stream, Context> Parsed<Token, Stream, Context> {
47  /// Shortcut for `Parsed::Success { token, stream }`
48  pub const fn new_success(token: Token, stream: Stream) -> Self {
49    Self::Success { token, stream }
50  }
51
52  /// Shortcut for `Parsed::Failure(context_handle)`
53  pub const fn new_failure(content: Context) -> Self {
54    Self::Failure(content)
55  }
56
57  /// Shortcut for `Parsed::Error(context_handle)`
58  pub const fn new_error(content: Context) -> Self {
59    Self::Error(content)
60  }
61
62  /// Borrow `Parsed` to make temporary Parsed of reference
63  pub const fn as_ref(&self) -> Parsed<&Token, &Stream, &Context> {
64    match self {
65      Self::Success { token, stream } => Parsed::Success { token, stream },
66      Self::Failure(context) => Parsed::Failure(context),
67      Self::Error(context) => Parsed::Error(context),
68    }
69  }
70
71  /// Allow to quickly access success to map it.
72  pub fn map_success<MappedToken, Map>(self, map: Map) -> Parsed<MappedToken, Stream, Context>
73  where
74    Map: FnOnce(Success<Token, Stream>) -> Success<MappedToken, Stream>,
75  {
76    match self {
77      Parsed::Success { token, stream } => map(Success { token, stream }).into(),
78      Parsed::Failure(context) => Parsed::Failure(context),
79      Parsed::Error(context) => Parsed::Error(context),
80    }
81  }
82
83  /// Allow to quickly access token to map it.
84  pub fn map_token<MappedToken, Map>(self, map: Map) -> Parsed<MappedToken, Stream, Context>
85  where
86    Map: FnOnce(Token) -> MappedToken,
87  {
88    match self {
89      Parsed::Success { token, stream } => Parsed::Success {
90        token: map(token),
91        stream,
92      },
93      Parsed::Failure(context) => Parsed::Failure(context),
94      Parsed::Error(context) => Parsed::Error(context),
95    }
96  }
97
98  /// Allow to quickly access stream to map it.
99  pub fn map_stream<Map>(self, map: Map) -> Parsed<Token, Stream, Context>
100  where
101    Map: FnOnce(Stream) -> Stream,
102  {
103    match self {
104      Parsed::Success { token, stream } => Parsed::Success {
105        token,
106        stream: map(stream),
107      },
108      Parsed::Failure(context) => Parsed::Failure(context),
109      Parsed::Error(context) => Parsed::Error(context),
110    }
111  }
112
113  /// Allow to quickly access context to map it.
114  pub fn map_context<MappedAtom, Map>(self, map: Map) -> Parsed<Token, Stream, MappedAtom>
115  where
116    Map: FnOnce(Context) -> MappedAtom,
117  {
118    match self {
119      Parsed::Success { token, stream } => Parsed::Success { token, stream },
120      Parsed::Failure(context) => Parsed::Failure(map(context)),
121      Parsed::Error(context) => Parsed::Error(map(context)),
122    }
123  }
124
125  /// Shortcut to add a Atom to the Context
126  pub fn add_context<C, Map>(self, map: Map) -> Parsed<Token, Stream, Context>
127  where
128    Map: FnOnce() -> C,
129    Context: Contexting<C>,
130  {
131    match self {
132      Parsed::Success { token, stream } => Parsed::Success { token, stream },
133      Parsed::Failure(content) => Parsed::Failure(content.add(map())),
134      Parsed::Error(content) => Parsed::Error(content.add(map())),
135    }
136  }
137
138  /// Return Success if Parsed is Success otherwise panic.
139  /// Use only for testing purpose.
140  pub fn unwrap(self) -> Success<Token, Stream>
141  where
142    Context: Debug,
143  {
144    match self {
145      Parsed::Success { token, stream } => Success { token, stream },
146      Parsed::Failure(context) => panic!("Call unwrap on Parsed::Failure: {:?}", context),
147      Parsed::Error(context) => panic!("Call unwrap on Parsed::Error: {:?}", context),
148    }
149  }
150
151  /// Return Context if Parsed is Failure or Error otherwise panic.
152  /// Use only for testing purpose.
153  pub fn unwrap_context(self) -> Context
154  where
155    Token: Debug,
156    Stream: Debug,
157  {
158    match self {
159      Parsed::Success { token, stream } => {
160        panic!("Call unwrap on Parsed::Success: {:?} {:?}", token, stream)
161      }
162      Parsed::Failure(context) => context,
163      Parsed::Error(context) => context,
164    }
165  }
166
167  /// Return true if Parsed is Success.
168  pub const fn is_success(&self) -> bool {
169    match self {
170      Parsed::Success { .. } => true,
171      _ => false,
172    }
173  }
174}
175
176impl<Token, Stream, Context> From<Success<Token, Stream>> for Parsed<Token, Stream, Context> {
177  fn from(Success { token, stream }: Success<Token, Stream>) -> Self {
178    Parsed::Success { token, stream }
179  }
180}
181
182impl<Token, Stream, Context> FromResidual for Parsed<Token, Stream, Context> {
183  fn from_residual(residual: Parsed<Infallible, Infallible, Context>) -> Self {
184    match residual {
185      Parsed::Success { .. } => unreachable!(),
186      Parsed::Failure(context) => Parsed::Failure(context),
187      Parsed::Error(context) => Parsed::Error(context),
188    }
189  }
190}
191
192impl<Token, Stream, Context> FromResidual<ParsedAux<Infallible, Context>>
193  for Parsed<Token, Stream, Context>
194{
195  fn from_residual(residual: ParsedAux<Infallible, Context>) -> Self {
196    match residual {
197      ParsedAux::Success(_success) => unreachable!(),
198      ParsedAux::Failure(context) => Parsed::Failure(context),
199      ParsedAux::Error(context) => Parsed::Error(context),
200    }
201  }
202}
203
204impl<Token, Stream, Context> FromResidual<Result<Infallible, Context>>
205  for Parsed<Token, Stream, Context>
206{
207  fn from_residual(residual: Result<Infallible, Context>) -> Self {
208    match residual {
209      Ok(_success) => unreachable!(),
210      Err(context) => Parsed::Failure(context),
211    }
212  }
213}
214
215// impl<Token, Stream, Context> FromResidual<Infallible>
216//   for Parsed<Token, Stream, Context>
217// {
218//   fn from_residual(_: Infallible) -> Self {
219//     unreachable!()
220//   }
221// }
222
223impl<Token, Stream, Context> Try for Parsed<Token, Stream, Context> {
224  type Output = Success<Token, Stream>;
225  type Residual = Parsed<Infallible, Infallible, Context>;
226
227  fn from_output(output: Self::Output) -> Self {
228    output.into()
229  }
230
231  fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
232    match self {
233      Parsed::Success { token, stream } => ControlFlow::Continue(Success { token, stream }),
234      Parsed::Failure(context) => ControlFlow::Break(Parsed::Failure(context)),
235      Parsed::Error(context) => ControlFlow::Break(Parsed::Error(context)),
236    }
237  }
238}
239
240use owo_colors::OwoColorize;
241
242impl<Token, Stream, Context> Display for Parsed<Token, Stream, Context>
243where
244  Token: Debug,
245  Stream: Streaming,
246  Context: ProvideElement,
247{
248  fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
249    match self {
250      Parsed::Success { token, stream } => match stream.clone().split_at(8) {
251        Split::Success {
252          item: stream,
253          stream: _,
254        } => {
255          write!(
256            f,
257            "{}: {:02X?}, stream: {:02X?} ..",
258            "Success".green(),
259            token,
260            stream
261          )
262        }
263        Split::NotEnoughItem(stream) => {
264          write!(
265            f,
266            "{}: {:02X?}, stream: {:02X?}",
267            "Success".green(),
268            token,
269            stream
270          )
271        }
272        Split::Error(error) => {
273          write!(
274            f,
275            "{}: {:02X?}, stream: {:?}",
276            "Success".green(),
277            token,
278            error
279          )
280        }
281      },
282      Parsed::Failure(context) => {
283        write!(f, "{}: {}", "Failure".yellow(), context.last())
284      }
285      Parsed::Error(context) => {
286        write!(f, "{}: {}", "Error".red(), context.last())
287      }
288    }
289  }
290}
291
292#[cfg(test)]
293mod tests {
294  use crate::{
295    Parsed,
296    Success,
297  };
298
299  fn multiply_by_42(parsed: Parsed<u8, (), ()>) -> Parsed<u8, (), ()> {
300    let Success { token, .. } = parsed?;
301    Parsed::Success {
302      token: token * 42,
303      stream: (),
304    }
305  }
306
307  #[test]
308  fn parsed() {
309    assert_eq!(
310      multiply_by_42(Parsed::new_success(1, ())),
311      Parsed::new_success(42, ())
312    );
313    assert_eq!(multiply_by_42(Parsed::Failure(())), Parsed::Failure(()));
314    assert_eq!(multiply_by_42(Parsed::Error(())), Parsed::Error(()));
315  }
316}