Skip to main content

xrust/parser/combinators/
tag.rs

1use crate::item::Node;
2use crate::parser::{ParseError, ParseInput, StaticState};
3use qualname::{NamespacePrefix, NamespaceUri};
4
5pub fn tag<'a, N: Node, L>(
6    expected: &str,
7) -> impl Fn(ParseInput<'a, N>, &mut StaticState<L>) -> Result<(ParseInput<'a, N>, ()), ParseError> + '_
8where
9    L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError>,
10{
11    move |(input, state), _ss| match input.get(0..expected.len()) {
12        None => Err(ParseError::Combinator(format!(
13            "expected \"{}\" but didn't find it",
14            expected
15        ))),
16        Some(chars) => {
17            if chars == expected {
18                Ok(((&input[expected.len()..], state), ()))
19            } else {
20                Err(ParseError::Combinator(format!(
21                    "expected \"{}\", found \"{}\"",
22                    expected, chars
23                )))
24            }
25        }
26    }
27}
28
29/// Return the longest possible of one of the given tags.
30/// If there are multiple tags of the same length, the first one that matches will be returned.
31pub(crate) fn anytag<'a, N: Node, L>(
32    s: Vec<&str>,
33) -> impl Fn(ParseInput<'a, N>, &mut StaticState<L>) -> Result<(ParseInput<'a, N>, String), ParseError>
34+ '_
35where
36    L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError>,
37{
38    move |(input, state), _ss| {
39        // NB. this algorithm could probably be optimised
40        let u = s.iter().fold("", |result, t| {
41            if t.len() > result.len() {
42                // Since this tag is longer, it is a candidate
43                match input.get(0..t.len()) {
44                    None => result,
45                    Some(chars) => {
46                        if chars == *t {
47                            t
48                        } else {
49                            result
50                        }
51                    }
52                }
53            } else {
54                result
55            }
56        });
57        if u.is_empty() {
58            Err(ParseError::Combinator(String::from("anytag: no input")))
59        } else {
60            Ok(((&input[u.len()..], state), u.to_string()))
61        }
62    }
63}
64
65pub(crate) fn anychar<'a, N: Node, L>(
66    expected: char,
67) -> impl Fn(ParseInput<'a, N>, &mut StaticState<L>) -> Result<(ParseInput<'a, N>, ()), ParseError>
68where
69    L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError>,
70{
71    move |(input, state), _ss| {
72        if input.starts_with(expected) {
73            Ok(((&input[1..], state), ()))
74        } else {
75            Err(ParseError::Combinator(String::from(
76                "anychar: unexpected characters",
77            )))
78        }
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use crate::parser::combinators::tag::{anychar, anytag, tag};
85    use crate::parser::{ParseError, ParserState, StaticStateBuilder};
86    use crate::trees::nullo::Nullo;
87    use qualname::NamespaceUri;
88
89    #[test]
90    fn parser_tag_test1() {
91        let testdoc = "<doc>";
92        let teststate: ParserState<Nullo> = ParserState::new();
93        let mut static_state = StaticStateBuilder::new()
94            .namespace(|_| {
95                NamespaceUri::try_from("urn:xrust").map_err(|_| ParseError::MissingNameSpace)
96            })
97            .build();
98        let parse_doc = tag("<");
99        assert_eq!(
100            Ok((("doc>", ParserState::new()), ())),
101            parse_doc((testdoc, teststate), &mut static_state)
102        );
103    }
104
105    #[test]
106    fn parser_tag_test2() {
107        let testdoc = "<doc>";
108        let teststate: ParserState<Nullo> = ParserState::new();
109        let mut static_state = StaticStateBuilder::new()
110            .namespace(|_| {
111                NamespaceUri::try_from("urn:xrust").map_err(|_| ParseError::MissingNameSpace)
112            })
113            .build();
114        let parse_doc = tag(">");
115        assert_eq!(
116            Err(ParseError::Combinator(String::from(
117                "expected \">\", found \"<\""
118            ))),
119            parse_doc((testdoc, teststate), &mut static_state)
120        );
121    }
122
123    #[test]
124    fn parser_tag_test3() {
125        let testdoc = "<?ProcessingInstruction?>";
126        let teststate: ParserState<Nullo> = ParserState::new();
127        let mut static_state = StaticStateBuilder::new()
128            .namespace(|_| {
129                NamespaceUri::try_from("urn:xrust").map_err(|_| ParseError::MissingNameSpace)
130            })
131            .build();
132        let parse_doc = tag("<?");
133        assert_eq!(
134            Ok((("ProcessingInstruction?>", ParserState::new()), ())),
135            parse_doc((testdoc, teststate), &mut static_state)
136        );
137    }
138
139    #[test]
140    fn parser_char_test1() {
141        let testdoc = "<doc>";
142        let teststate: ParserState<Nullo> = ParserState::new();
143        let mut static_state = StaticStateBuilder::new()
144            .namespace(|_| {
145                NamespaceUri::try_from("urn:xrust").map_err(|_| ParseError::MissingNameSpace)
146            })
147            .build();
148        let parse_doc = anychar('<');
149        assert_eq!(
150            Ok((("doc>", ParserState::new()), ())),
151            parse_doc((testdoc, teststate), &mut static_state)
152        )
153    }
154    #[test]
155    fn parser_char_test2() {
156        let testdoc = "<doc>";
157        let teststate: ParserState<Nullo> = ParserState::new();
158        let mut static_state = StaticStateBuilder::new()
159            .namespace(|_| {
160                NamespaceUri::try_from("urn:xrust").map_err(|_| ParseError::MissingNameSpace)
161            })
162            .build();
163        let parse_doc = anychar('>');
164        assert_eq!(
165            Err(ParseError::Combinator(String::from(
166                "anychar: unexpected characters"
167            ))),
168            parse_doc((testdoc, teststate), &mut static_state)
169        )
170    }
171    #[test]
172    fn parser_anytag_test1() {
173        let testdoc = "<doc>";
174        let teststate: ParserState<Nullo> = ParserState::new();
175        let mut static_state = StaticStateBuilder::new()
176            .namespace(|_| {
177                NamespaceUri::try_from("urn:xrust").map_err(|_| ParseError::MissingNameSpace)
178            })
179            .build();
180        let parse_doc = anytag(vec![">", ">=", "<=", "<"]);
181        assert_eq!(
182            Ok((("doc>", ParserState::new()), "<".to_string())),
183            parse_doc((testdoc, teststate), &mut static_state)
184        )
185    }
186    #[test]
187    fn parser_anytag_test2() {
188        let testdoc = "<=>";
189        let teststate: ParserState<Nullo> = ParserState::new();
190        let mut static_state = StaticStateBuilder::new()
191            .namespace(|_| {
192                NamespaceUri::try_from("urn:xrust").map_err(|_| ParseError::MissingNameSpace)
193            })
194            .build();
195        let parse_doc = anytag(vec![">", ">=", "<=", "<"]);
196        assert_eq!(
197            Ok(((">", ParserState::new()), "<=".to_string())),
198            parse_doc((testdoc, teststate), &mut static_state)
199        )
200    }
201}