1use crate::ast::values::IntervalUnit;
2use crate::ast::*;
3use nom::{
4 IResult, Parser,
5 branch::alt,
6 bytes::complete::{tag, tag_no_case, take_while1},
7 character::complete::{char, digit1, multispace1},
8 combinator::{map, opt, recognize, value},
9 sequence::{delimited, preceded},
10};
11
12pub fn parse_identifier(input: &str) -> IResult<&str, &str> {
14 take_while1(|c: char| c.is_alphanumeric() || c == '_' || c == '.').parse(input)
15}
16
17pub fn parse_interval(input: &str) -> IResult<&str, Value> {
19 let (input, num_str) = digit1(input)?;
20 let amount: i64 = num_str.parse().unwrap_or(0);
21
22 let (input, unit) = alt((
23 value(IntervalUnit::Second, tag_no_case("s")),
24 value(IntervalUnit::Minute, tag_no_case("m")),
25 value(IntervalUnit::Hour, tag_no_case("h")),
26 value(IntervalUnit::Day, tag_no_case("d")),
27 value(IntervalUnit::Week, tag_no_case("w")),
28 value(IntervalUnit::Month, tag_no_case("mo")),
29 value(IntervalUnit::Year, tag_no_case("y")),
30 ))
31 .parse(input)?;
32
33 Ok((input, Value::Interval { amount, unit }))
34}
35
36pub fn parse_value(input: &str) -> IResult<&str, Value> {
38 alt((
39 map(preceded(char('$'), digit1), |d: &str| {
41 Value::Param(d.parse().unwrap_or(0))
42 }),
43 map(
45 preceded(
46 char(':'),
47 take_while1(|c: char| c.is_alphanumeric() || c == '_'),
48 ),
49 |name: &str| Value::NamedParam(name.to_string()),
50 ),
51 value(Value::Bool(true), tag_no_case("true")),
53 value(Value::Bool(false), tag_no_case("false")),
54 value(Value::Null, tag_no_case("null")),
56 parse_triple_quoted_string,
58 parse_json_literal,
60 map(
62 delimited(
63 char('"'),
64 nom::bytes::complete::take_while(|c| c != '"'),
65 char('"'),
66 ),
67 |s: &str| Value::String(s.to_string()),
68 ),
69 map(
71 delimited(
72 char('\''),
73 nom::bytes::complete::take_while(|c| c != '\''),
74 char('\''),
75 ),
76 |s: &str| Value::String(s.to_string()),
77 ),
78 map(
80 recognize((opt(char('-')), digit1, char('.'), digit1)),
81 |s: &str| Value::Float(s.parse().unwrap_or(0.0)),
82 ),
83 parse_interval,
85 map(recognize((opt(char('-')), digit1)), |s: &str| {
87 Value::Int(s.parse().unwrap_or(0))
88 }),
89 ))
90 .parse(input)
91}
92
93fn parse_triple_quoted_string(input: &str) -> IResult<&str, Value> {
95 alt((
96 map(
98 delimited(
99 tag("'''"),
100 nom::bytes::complete::take_until("'''"),
101 tag("'''"),
102 ),
103 |s: &str| Value::String(s.to_string()),
104 ),
105 map(
107 delimited(
108 tag("\"\"\""),
109 nom::bytes::complete::take_until("\"\"\""),
110 tag("\"\"\""),
111 ),
112 |s: &str| Value::String(s.to_string()),
113 ),
114 ))
115 .parse(input)
116}
117
118fn parse_json_literal(input: &str) -> IResult<&str, Value> {
121 let trimmed = input.trim_start();
123 if trimmed.is_empty() {
124 return Err(nom::Err::Error(nom::error::Error::new(input, nom::error::ErrorKind::Tag)));
125 }
126
127 let (open_char, close_char) = match trimmed.chars().next() {
128 Some('{') => ('{', '}'),
129 Some('[') => ('[', ']'),
130 _ => return Err(nom::Err::Error(nom::error::Error::new(input, nom::error::ErrorKind::Tag))),
131 };
132
133 let mut depth = 0;
135 let mut in_string = false;
136 let mut escape_next = false;
137 let mut end_pos = 0;
138
139 for (i, c) in trimmed.char_indices() {
140 if escape_next {
141 escape_next = false;
142 continue;
143 }
144
145 if c == '\\' && in_string {
146 escape_next = true;
147 continue;
148 }
149
150 if c == '"' {
151 in_string = !in_string;
152 continue;
153 }
154
155 if !in_string {
156 if c == open_char {
157 depth += 1;
158 } else if c == close_char {
159 depth -= 1;
160 if depth == 0 {
161 end_pos = i + 1;
162 break;
163 }
164 }
165 }
166 }
167
168 if depth != 0 || end_pos == 0 {
169 return Err(nom::Err::Error(nom::error::Error::new(input, nom::error::ErrorKind::Eof)));
170 }
171
172 let json_str = &trimmed[..end_pos];
173 let _remaining = &trimmed[end_pos..];
174
175 let consumed = input.len() - trimmed.len() + end_pos;
177 let remaining_original = &input[consumed..];
178
179 Ok((remaining_original, Value::Json(json_str.to_string())))
180}
181
182pub fn parse_operator(input: &str) -> IResult<&str, Operator> {
184 alt((
185 value(Operator::NotBetween, tag_no_case("not between")),
187 value(Operator::Between, tag_no_case("between")),
188 value(Operator::IsNotNull, tag_no_case("is not null")),
189 value(Operator::IsNull, tag_no_case("is null")),
190 value(Operator::NotIn, tag_no_case("not in")),
191 value(Operator::NotILike, tag_no_case("not ilike")),
192 value(Operator::NotLike, tag_no_case("not like")),
193 value(Operator::ILike, tag_no_case("ilike")),
194 value(Operator::Like, tag_no_case("like")),
195 value(Operator::In, tag_no_case("in")),
196 value(Operator::Gte, tag(">=")),
197 value(Operator::Lte, tag("<=")),
198 value(Operator::Ne, tag("!=")),
199 value(Operator::Ne, tag("<>")),
200 value(Operator::Eq, tag("=")),
202 value(Operator::Gt, tag(">")),
203 value(Operator::Lt, tag("<")),
204 value(Operator::Fuzzy, tag("~")),
205 ))
206 .parse(input)
207}
208
209pub fn parse_action(input: &str) -> IResult<&str, (Action, bool)> {
211 alt((
212 map(
214 (tag_no_case("get"), multispace1, tag_no_case("distinct")),
215 |_| (Action::Get, true),
216 ),
217 value((Action::Get, false), tag_no_case("get")),
219 alt((
221 value((Action::Cnt, false), tag_no_case("count")),
222 value((Action::Cnt, false), tag_no_case("cnt")),
223 )),
224 value((Action::Set, false), tag_no_case("set")),
226 alt((
228 value((Action::Del, false), tag_no_case("delete")),
229 value((Action::Del, false), tag_no_case("del")),
230 )),
231 alt((
233 value((Action::Add, false), tag_no_case("insert")),
234 value((Action::Add, false), tag_no_case("add")),
235 )),
236 alt((
238 value((Action::Make, false), tag_no_case("create")),
239 value((Action::Make, false), tag_no_case("make")),
240 )),
241 ))
242 .parse(input)
243}
244
245pub fn parse_txn_command(input: &str) -> IResult<&str, Qail> {
247 let (input, action) = alt((
248 value(Action::TxnStart, tag_no_case("begin")),
249 value(Action::TxnCommit, tag_no_case("commit")),
250 value(Action::TxnRollback, tag_no_case("rollback")),
251 ))
252 .parse(input)?;
253
254 Ok((
255 input,
256 Qail {
257 action,
258 table: String::new(),
259 columns: vec![],
260 joins: vec![],
261 cages: vec![],
262 distinct: false,
263 distinct_on: vec![],
264 index_def: None,
265 table_constraints: vec![],
266 set_ops: vec![],
267 having: vec![],
268 group_by_mode: GroupByMode::default(),
269 ctes: vec![],
270 returning: None,
271 on_conflict: None,
272 source_query: None,
273 channel: None,
274 payload: None,
275 savepoint_name: None,
276 from_tables: vec![],
277 using_tables: vec![],
278 lock_mode: None,
279 fetch: None,
280 default_values: false,
281 overriding: None,
282 sample: None,
283 only_table: false,
284 vector: None,
285 score_threshold: None,
286 vector_name: None,
287 with_vector: false,
288 vector_size: None,
289 distance: None,
290 on_disk: None,
291 function_def: None,
292 trigger_def: None,
293 raw_value: None,
294 redis_ttl: None,
295 redis_set_condition: None,
296 },
297 ))
298}