1use crate::ast::{Field, JsonOp};
2use crate::error::ParseError;
3use nom::{
4 branch::alt,
5 bytes::complete::{tag, take_while, take_while1},
6 character::complete::{char, one_of},
7 combinator::{opt, recognize},
8 multi::{many0, many1, separated_list1},
9 sequence::{delimited, preceded},
10 IResult,
11};
12
13pub fn identifier(i: &str) -> IResult<&str, String> {
14 let (i, s) = recognize(many1(alt((one_of(
15 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_",
16 ),))))(i)?;
17 Ok((i, s.to_string()))
18}
19
20pub fn json_double_arrow(i: &str) -> IResult<&str, JsonOp> {
21 let (i, _) = tag("->>")(i)?;
22 Ok((i, JsonOp::DoubleArrow(String::new())))
23}
24
25pub fn json_single_arrow(i: &str) -> IResult<&str, JsonOp> {
26 let (i, _) = tag("->")(i)?;
27 Ok((i, JsonOp::Arrow(String::new())))
28}
29
30pub fn json_operator(i: &str) -> IResult<&str, JsonOp> {
31 alt((json_double_arrow, json_single_arrow))(i)
32}
33
34pub fn json_path_segment(i: &str) -> IResult<&str, JsonOp> {
35 let (i, op) = json_operator(i)?;
36 let (i, key) = identifier(i)?;
37 let json_op = match op {
38 JsonOp::Arrow(_) => JsonOp::Arrow(key),
39 JsonOp::DoubleArrow(_) => JsonOp::DoubleArrow(key),
40 JsonOp::ArrayIndex(idx) => JsonOp::ArrayIndex(idx),
41 };
42 Ok((i, json_op))
43}
44
45pub fn json_path(i: &str) -> IResult<&str, Vec<JsonOp>> {
46 many0(json_path_segment)(i)
47}
48
49pub fn type_cast(i: &str) -> IResult<&str, String> {
50 preceded(tag("::"), identifier)(i)
51}
52
53pub fn field(i: &str) -> IResult<&str, Field> {
54 let (i, name) = identifier(i)?;
55 let (i, json_path) = json_path(i)?;
56 let (i, cast) = opt(type_cast)(i)?;
57 Ok((
58 i,
59 Field {
60 name,
61 json_path,
62 cast,
63 },
64 ))
65}
66
67pub fn quoted_string(i: &str) -> IResult<&str, &str> {
68 delimited(char('"'), take_while(|c| c != '"'), char('"'))(i)
69}
70
71pub fn paren_list(i: &str) -> IResult<&str, Vec<String>> {
72 delimited(char('('), list_items, char(')'))(i)
73}
74
75pub fn brace_list(i: &str) -> IResult<&str, Vec<String>> {
76 delimited(char('{'), list_items, char('}'))(i)
77}
78
79fn list_items(i: &str) -> IResult<&str, Vec<String>> {
80 let (i, items) = separated_list1(
81 preceded(opt(whitespace), char(',')),
82 delimited(opt(whitespace), list_item, opt(whitespace)),
83 )(i)?;
84 let strings: Vec<String> = items.iter().map(|s| s.to_string()).collect();
85 Ok((i, strings))
86}
87
88pub fn list_item(i: &str) -> IResult<&str, &str> {
89 alt((unquoted_list_item, quoted_list_item))(i)
90}
91
92pub fn unquoted_list_item(i: &str) -> IResult<&str, &str> {
93 take_while1(|c| c != ',' && c != ')' && c != '}')(i)
94}
95
96pub fn quoted_list_item(i: &str) -> IResult<&str, &str> {
97 quoted_string(i)
98}
99
100pub fn whitespace(i: &str) -> IResult<&str, &str> {
101 take_while(char::is_whitespace)(i)
102}
103
104pub fn parse_json_path(field_str: &str) -> Result<(String, Vec<JsonOp>), ParseError> {
105 if !field_str.contains("->") && !field_str.contains("->>") {
106 return Ok((field_str.to_string(), Vec::new()));
107 }
108
109 let mut name = String::new();
110 let mut json_path = Vec::new();
111 let mut chars = field_str.chars().peekable();
112
113 while let Some(c) = chars.next() {
114 match c {
115 '-' => {
116 if let Some(&'>') = chars.peek() {
117 chars.next();
118 if let Some(&'>') = chars.peek() {
119 chars.next();
120 let key = take_json_identifier(&mut chars);
121 json_path.push(JsonOp::DoubleArrow(key));
122 } else {
123 let key = take_json_identifier(&mut chars);
124 json_path.push(JsonOp::Arrow(key));
125 }
126 } else {
127 name.push(c);
128 }
129 }
130 _ => name.push(c),
131 }
132 }
133
134 if name.is_empty() {
135 return Err(ParseError::EmptyFieldName);
136 }
137
138 Ok((name, json_path))
139}
140
141fn take_json_identifier(chars: &mut std::iter::Peekable<std::str::Chars>) -> String {
142 let mut result = String::new();
143 while let Some(&c) = chars.peek() {
144 if c.is_alphanumeric() || c == '_' {
145 result.push(c);
146 chars.next();
147 } else {
148 break;
149 }
150 }
151 result
152}
153
154pub fn parse_field_fallback(field_str: &str) -> Result<Field, ParseError> {
155 let parts: Vec<&str> = field_str.split("::").collect();
156
157 match parts.as_slice() {
158 [field_part, cast] => {
159 let (name, json_path) = parse_json_path(field_part)?;
160 Ok(Field {
161 name,
162 json_path,
163 cast: Some(cast.to_string()),
164 })
165 }
166 [field_part] => {
167 let (name, json_path) = parse_json_path(field_part)?;
168 Ok(Field {
169 name,
170 json_path,
171 cast: None,
172 })
173 }
174 _ => Err(ParseError::InvalidFieldName(field_str.to_string())),
175 }
176}
177
178#[cfg(test)]
179mod tests {
180 use super::*;
181
182 #[test]
183 fn test_identifier_simple() {
184 let result = identifier("id");
185 assert!(result.is_ok());
186 assert_eq!(result.unwrap().1, "id");
187 }
188
189 #[test]
190 fn test_identifier_with_underscore() {
191 let result = identifier("user_id");
192 assert!(result.is_ok());
193 assert_eq!(result.unwrap().1, "user_id");
194 }
195
196 #[test]
197 fn test_json_path_segment_arrow() {
198 let result = json_path_segment("->key");
199 assert!(result.is_ok());
200 let (_, op) = result.unwrap();
201 assert_eq!(op, JsonOp::Arrow("key".to_string()));
202 }
203
204 #[test]
205 fn test_json_path_segment_double_arrow() {
206 let result = json_path_segment("->>key");
207 assert!(result.is_ok());
208 let (_, op) = result.unwrap();
209 assert_eq!(op, JsonOp::DoubleArrow("key".to_string()));
210 }
211
212 #[test]
213 fn test_json_path_multiple() {
214 let result = json_path("->outer->inner->>final");
215 assert!(result.is_ok());
216 let (_, path) = result.unwrap();
217 assert_eq!(path.len(), 3);
218 }
219
220 #[test]
221 fn test_type_cast() {
222 let result = type_cast("::text");
223 assert!(result.is_ok());
224 assert_eq!(result.unwrap().1, "text");
225 }
226
227 #[test]
228 fn test_field_simple() {
229 let result = field("id");
230 assert!(result.is_ok());
231 let field = result.unwrap().1;
232 assert_eq!(field.name, "id");
233 assert!(field.json_path.is_empty());
234 assert!(field.cast.is_none());
235 }
236
237 #[test]
238 fn test_field_with_json_path() {
239 let result = field("data->key");
240 assert!(result.is_ok());
241 let field = result.unwrap().1;
242 assert_eq!(field.name, "data");
243 assert_eq!(field.json_path.len(), 1);
244 }
245
246 #[test]
247 fn test_field_with_type_cast() {
248 let result = field("price::numeric");
249 assert!(result.is_ok());
250 let field = result.unwrap().1;
251 assert_eq!(field.cast, Some("numeric".to_string()));
252 }
253
254 #[test]
255 fn test_field_with_json_path_and_cast() {
256 let result = field("data->price::numeric");
257 assert!(result.is_ok());
258 let field = result.unwrap().1;
259 assert_eq!(field.name, "data");
260 assert_eq!(field.json_path.len(), 1);
261 assert_eq!(field.cast, Some("numeric".to_string()));
262 }
263
264 #[test]
265 fn test_paren_list() {
266 let result = paren_list("(item1,item2,item3)");
267 assert!(result.is_ok());
268 let items = result.unwrap().1;
269 assert_eq!(items.len(), 3);
270 }
271
272 #[test]
273 fn test_brace_list() {
274 let result = brace_list("{item1,item2,item3}");
275 assert!(result.is_ok());
276 let items = result.unwrap().1;
277 assert_eq!(items.len(), 3);
278 }
279
280 #[test]
281 fn test_quoted_string() {
282 let result = quoted_string("\"test string\"");
283 assert!(result.is_ok());
284 assert_eq!(result.unwrap().1, "test string");
285 }
286}