1use crate::not_containing;
2use chumsky::prelude::*;
3
4#[allow(clippy::type_complexity)]
6pub struct Branch<'a, T, E> {
7 branches: Vec<(&'a str, Box<dyn Parser<char, T, Error = E> + 'a>)>
8}
9
10pub fn branch<'a, P, T>(begin: &'a str, parser: P) -> Branch<'a, T, P::Error>
12where
13 P: Parser<char, T> + 'a
14{
15 Branch {
16 branches: vec![(begin, Box::new(parser))]
17 }
18}
19
20impl<'a, T, E> Branch<'a, T, E>
21where
22 E: chumsky::Error<char> + 'a
23{
24 pub fn or_branch<P>(mut self, begin: &'a str, parser: P) -> Self
26 where
27 P: Parser<char, T, Error = E> + 'a
28 {
29 self.branches.push((begin, Box::new(parser)));
30 self
31 }
32
33 pub fn or_else<B>(self, branch: B) -> Box<dyn Parser<char, T, Error = E> + 'a>
35 where
36 B: Fn(String) -> T + 'a,
37 T: 'a
38 {
39 let mut parser: Box<dyn Parser<char, T, Error = E> + 'a> = Box::new(
40 not_containing(
41 self.branches
42 .iter()
43 .map(|(pattern, _)| *pattern)
44 .collect::<Vec<_>>()
45 )
46 .map(branch)
47 );
48
49 for (pattern, branch) in self.branches {
50 parser = Box::new(choice((parser, just(pattern).ignore_then(branch))));
51 }
52
53 parser
54 }
55}
56
57#[cfg(test)]
58mod tests {
59 use super::*;
60
61 #[derive(Debug, Eq, PartialEq)]
62 enum Token {
63 Foo,
64 Bar,
65 Comment,
66 Verbatim(String)
67 }
68
69 fn test_lexer() -> impl Parser<char, Token, Error = Simple<char>> {
70 branch(
71 "{{",
72 just("foo").then_ignore(just("}}")).map(|_| Token::Foo)
73 )
74 .or_branch(
75 "{%",
76 just("bar").then_ignore(just("%}")).map(|_| Token::Bar)
77 )
78 .or_branch(
79 "/*",
80 just("TODO").then_ignore(just("*/")).map(|_| Token::Comment)
81 )
82 .or_else(Token::Verbatim)
83 .then_ignore(end())
84 }
85
86 #[test]
87 fn parse_foo() {
88 let token = test_lexer().parse("{{foo}}");
89 assert_eq!(token, Ok(Token::Foo));
90 }
91
92 #[test]
93 fn parse_bar() {
94 let token = test_lexer().parse("{%bar%}");
95 assert_eq!(token, Ok(Token::Bar));
96 }
97
98 #[test]
99 fn parse_comment() {
100 let token = test_lexer().parse("/*TODO*/");
101 assert_eq!(token, Ok(Token::Comment));
102 }
103
104 #[test]
105 fn parse_verbatim() {
106 let token = test_lexer().parse("just some random text");
107 assert_eq!(
108 token,
109 Ok(Token::Verbatim("just some random text".to_owned()))
110 );
111 }
112
113 #[test]
114 fn parse_foo_unclosed() {
115 let token = test_lexer().parse("{{foo}");
116 assert_matches!(token, Err(_));
117 }
118
119 #[test]
120 fn parse_bar_unclosed() {
121 let token = test_lexer().parse("{%foo%");
122 assert_matches!(token, Err(_));
123 }
124
125 #[test]
126 fn parse_comment_unclosed() {
127 let token = test_lexer().parse("/*TODO//");
128 assert_matches!(token, Err(_));
129 }
130
131 #[test]
132 fn parse_invalid() {
133 let token = test_lexer().parse("foo{{bar");
134 assert_matches!(token, Err(_));
135 }
136}