git_bug/query/parse/parser/
strict.rs1use crate::query::{
14 Matcher,
15 parse::{
16 parser::{format_unexpected_token, format_unknown_key},
17 tokenizer::{Token, TokenKind, TokenSpan},
18 },
19 queryable::{QueryKeyValue, Queryable},
20};
21
22#[derive(Debug, thiserror::Error)]
23#[allow(missing_docs)]
24pub enum Error<E: Queryable>
25where
26 <E::KeyValue as QueryKeyValue>::Err: std::fmt::Debug + std::fmt::Display,
27{
28 #[error(fmt = format_unexpected_token)]
29 UnexpectedToken { expected: TokenKind, found: Token },
30
31 #[error("The input stream contained {found} but expected one of: {expected:?}")]
34 UnexpectedTokens {
35 expected: Vec<TokenKind>,
37
38 found: Token,
40 },
41
42 #[error(fmt = format_unknown_key::<E>)]
43 UnknownKey {
44 err: <E::KeyValue as QueryKeyValue>::Err,
46
47 key: String,
49
50 at: TokenSpan,
52 },
53
54 #[error("The Query was not completely parsed. Parsing stopped at: {0}")]
55 LeftoverTokens(Token),
56}
57
58impl<E: Queryable> From<super::parsing::Error<E>> for Error<E>
59where
60 <E::KeyValue as QueryKeyValue>::Err: std::fmt::Debug + std::fmt::Display,
61{
62 fn from(value: super::parsing::Error<E>) -> Self {
63 match value {
64 super::parsing::Error::UnexpectedToken { expected, found } => {
65 Self::UnexpectedToken { expected, found }
66 }
67 super::parsing::Error::UnknownKey { err, key, at } => Self::UnknownKey { err, key, at },
68 }
69 }
70}
71
72pub(super) struct Parser<'a, E: Queryable> {
73 parent: &'a mut super::Parser<'a, E>,
74}
75
76impl<'a, E: Queryable> Parser<'a, E>
77where
78 <E::KeyValue as QueryKeyValue>::Err: std::fmt::Debug + std::fmt::Display,
79{
80 pub(super) fn parse(parent: &'a mut super::Parser<'a, E>) -> Result<Matcher<E>, Error<E>> {
81 let mut me = Parser { parent };
82 let output = me.parse_matcher()?;
83
84 let next = me.parent.tokenizer.next_token();
85 if next.kind() == TokenKind::Eof {
86 Ok(output)
87 } else {
88 Err(Error::LeftoverTokens(next))
89 }
90 }
91
92 fn parse_matcher(&mut self) -> Result<Matcher<E>, Error<E>> {
93 if self.parent.tokenizer.peek().kind() == TokenKind::ParenOpen {
94 self.parent.expect(TokenKind::ParenOpen)?;
96
97 let lhs = self.parse_matcher()?;
98 self.parent.expect(TokenKind::Break)?;
99
100 let mode = match self.parent.tokenizer.next_token() {
101 token if token.kind == TokenKind::And => TokenKind::And,
102 token if token.kind == TokenKind::Or => TokenKind::Or,
103 other => {
104 return Err(Error::UnexpectedTokens {
105 expected: vec![TokenKind::And, TokenKind::Or],
106 found: other,
107 });
108 }
109 };
110
111 self.parent.expect(TokenKind::Break)?;
112 let rhs = self.parse_matcher()?;
113 self.parent.expect(TokenKind::ParenClose)?;
114
115 match mode {
116 TokenKind::And => Ok(Matcher::And {
117 lhs: Box::new(lhs),
118 rhs: Box::new(rhs),
119 }),
120 TokenKind::Or => Ok(Matcher::Or {
121 lhs: Box::new(lhs),
122 rhs: Box::new(rhs),
123 }),
124 _ => unreachable!("Checked above"),
125 }
126 } else {
127 let key_tokens = self
129 .parent
130 .take_while(|t| matches!(t, TokenKind::Char(_)))?;
131 self.parent.expect(TokenKind::Colon)?;
132
133 let value_tokens = self
134 .parent
135 .take_while(|t| matches!(t, TokenKind::Char(_)))?;
136 let value = super::Parser::<'a, E>::parse_value_from(&value_tokens);
137
138 let key_value = self.parent.parse_key_from(
139 &key_tokens,
140 value,
141 value_tokens.last().map_or(0, |t| t.span().stop()),
142 )?;
143
144 Ok(Matcher::Match { key_value })
145 }
146 }
147}
148
149#[cfg(test)]
150mod test {
151 use pretty_assertions::assert_eq;
152
153 use crate::query::{
154 Matcher::{And, Match, Or},
155 ParseMode, Query,
156 parse::parser::test::{
157 QueryTestKey1::{Value1, Value2},
158 QueryTestKeyValue::{Key1, Key2, Key3},
159 QueryTestObj,
160 },
161 };
162
163 #[test]
164 fn test_simple_query() {
165 let input = "(key1:value1 AND 'key2:state p2 ')";
166 let query = Query::<QueryTestObj>::from_continuous_str(&(), input, ParseMode::Strict)
167 .map_err(|err| panic!("{err}"))
168 .unwrap();
169
170 assert_eq!(
171 query,
172 Query {
173 root: Some(And {
174 lhs: Box::new(Match {
175 key_value: Key1(Value1),
176 }),
177 rhs: Box::new(Match {
178 key_value: Key2("state p2 \u{f0d58}".to_owned()),
179 }),
180 },)
181 }
182 );
183 }
184
185 #[test]
186 fn test_complexer_query() {
187 let input = "((key1:value1 AND 'key2:state p2 ') OR ((key1:value2 AND 'key2:state ') \
188 AND key3:20))";
189 let query = Query::<QueryTestObj>::from_continuous_str(&(), input, ParseMode::Strict)
190 .map_err(|err| panic!("{err}"))
191 .unwrap();
192
193 assert_eq!(
194 query,
195 Query {
196 root: Some(Or {
197 lhs: Box::new(And {
198 lhs: Box::new(Match {
199 key_value: Key1(Value1,),
200 }),
201 rhs: Box::new(Match {
202 key_value: Key2("state p2 \u{f0d58}".to_owned()),
203 }),
204 }),
205 rhs: Box::new(And {
206 lhs: Box::new(And {
207 lhs: Box::new(Match {
208 key_value: Key1(Value2),
209 }),
210 rhs: Box::new(Match {
211 key_value: Key2("state \u{f0d58}".to_owned()),
212 }),
213 }),
214 rhs: Box::new(Match {
215 key_value: Key3 {
216 _value: 20,
217 original: "20".to_owned(),
218 },
219 }),
220 }),
221 },),
222 }
223 );
224 }
225}