Skip to main content

postgrest_parser/parser/
common.rs

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}