oni_comb_parser/text/
escaped.rs1use alloc::string::String;
2
3use crate::error::ParseError;
4use crate::fail::{Fail, PResult};
5use crate::input::Input;
6use crate::parser::Parser;
7use crate::str_input::StrInput;
8
9pub struct Escaped<F> {
10 open: char,
11 close: char,
12 escape: char,
13 handler: F,
14}
15
16pub fn escaped<F>(open: char, close: char, escape: char, handler: F) -> Escaped<F>
17where
18 F: FnMut(char) -> Option<char>, {
19 Escaped {
20 open,
21 close,
22 escape,
23 handler,
24 }
25}
26
27impl<'a, F> Parser<StrInput<'a>> for Escaped<F>
28where
29 F: FnMut(char) -> Option<char>,
30{
31 type Error = ParseError;
32 type Output = String;
33
34 #[inline]
35 fn parse_next(&mut self, input: &mut StrInput<'a>) -> PResult<String, ParseError> {
36 let pos = input.offset();
37 let remaining = input.as_str();
38 let mut chars = remaining.chars();
39
40 match chars.next() {
42 Some(c) if c == self.open => {}
43 _ => {
44 return Err(Fail::Backtrack(ParseError::expected_char(pos, self.open)));
45 }
46 }
47
48 let mut result = String::new();
49 let mut consumed = self.open.len_utf8();
50
51 loop {
52 match chars.next() {
53 Some(c) if c == self.close => {
54 consumed += c.len_utf8();
55 input.advance(consumed);
56 return Ok(result);
57 }
58 Some(c) if c == self.escape => {
59 consumed += c.len_utf8();
60 match chars.next() {
61 Some(next) => {
62 consumed += next.len_utf8();
63 match (self.handler)(next) {
64 Some(replacement) => result.push(replacement),
65 None => {
66 return Err(Fail::Cut(ParseError::expected_description(
67 pos + consumed - next.len_utf8(),
68 "valid escape sequence",
69 )));
70 }
71 }
72 }
73 None => {
74 return Err(Fail::Cut(ParseError::expected_description(
75 pos + consumed,
76 "escape character",
77 )));
78 }
79 }
80 }
81 Some(c) => {
82 consumed += c.len_utf8();
83 result.push(c);
84 }
85 None => {
86 return Err(Fail::Cut(ParseError::expected_char(pos + consumed, self.close)));
87 }
88 }
89 }
90 }
91}