Skip to main content

postgrest_parser/parser/
prefer.rs

1use crate::ast::{Count, Missing, Plurality, PreferOptions, Resolution, ReturnRepresentation};
2use crate::error::Error;
3use nom::{branch::alt, bytes::complete::tag, combinator::map, sequence::preceded, IResult};
4
5/// Parses "return=representation|minimal|headers-only"
6fn parse_return(input: &str) -> IResult<&str, ReturnRepresentation> {
7    preceded(
8        tag("return="),
9        alt((
10            map(tag("representation"), |_| ReturnRepresentation::Full),
11            map(tag("minimal"), |_| ReturnRepresentation::Minimal),
12            map(tag("headers-only"), |_| ReturnRepresentation::HeadersOnly),
13        )),
14    )(input)
15}
16
17/// Parses "resolution=merge-duplicates|ignore-duplicates"
18fn parse_resolution(input: &str) -> IResult<&str, Resolution> {
19    preceded(
20        tag("resolution="),
21        alt((
22            map(tag("merge-duplicates"), |_| Resolution::MergeDuplicates),
23            map(tag("ignore-duplicates"), |_| Resolution::IgnoreDuplicates),
24        )),
25    )(input)
26}
27
28/// Parses "count=exact|planned|estimated"
29fn parse_count(input: &str) -> IResult<&str, Count> {
30    preceded(
31        tag("count="),
32        alt((
33            map(tag("exact"), |_| Count::Exact),
34            map(tag("planned"), |_| Count::Planned),
35            map(tag("estimated"), |_| Count::Estimated),
36        )),
37    )(input)
38}
39
40/// Parses "plurality=singular"
41fn parse_plurality(input: &str) -> IResult<&str, Plurality> {
42    preceded(
43        tag("plurality="),
44        alt((
45            map(tag("singular"), |_| Plurality::Singular),
46            map(tag("multiple"), |_| Plurality::Multiple),
47        )),
48    )(input)
49}
50
51/// Parses "missing=default"
52fn parse_missing(input: &str) -> IResult<&str, Missing> {
53    preceded(
54        tag("missing="),
55        alt((
56            map(tag("default"), |_| Missing::Default),
57            map(tag("null"), |_| Missing::Null),
58        )),
59    )(input)
60}
61
62/// Parses a full Prefer header value
63///
64/// Format: "option1=value1, option2=value2, ..."
65///
66/// # Examples
67///
68/// ```
69/// use postgrest_parser::parser::parse_prefer_header;
70///
71/// let opts = parse_prefer_header("return=representation, count=exact").unwrap();
72/// ```
73pub fn parse_prefer_header(input: &str) -> Result<PreferOptions, Error> {
74    let parts: Vec<&str> = input.split(',').map(|s| s.trim()).collect();
75
76    let mut options = PreferOptions::new();
77
78    for part in parts {
79        if part.is_empty() {
80            continue;
81        }
82
83        // Try each parser
84        if let Ok((_, ret)) = parse_return(part) {
85            options.return_representation = Some(ret);
86        } else if let Ok((_, res)) = parse_resolution(part) {
87            options.resolution = Some(res);
88        } else if let Ok((_, cnt)) = parse_count(part) {
89            options.count = Some(cnt);
90        } else if let Ok((_, plur)) = parse_plurality(part) {
91            options.plurality = Some(plur);
92        } else if let Ok((_, miss)) = parse_missing(part) {
93            options.missing = Some(miss);
94        } else {
95            // Unknown preference - skip it (PostgREST behavior)
96            continue;
97        }
98    }
99
100    Ok(options)
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    #[test]
108    fn test_parse_return_representation() {
109        assert_eq!(
110            parse_return("return=representation").unwrap().1,
111            ReturnRepresentation::Full
112        );
113        assert_eq!(
114            parse_return("return=minimal").unwrap().1,
115            ReturnRepresentation::Minimal
116        );
117        assert_eq!(
118            parse_return("return=headers-only").unwrap().1,
119            ReturnRepresentation::HeadersOnly
120        );
121    }
122
123    #[test]
124    fn test_parse_resolution() {
125        assert_eq!(
126            parse_resolution("resolution=merge-duplicates").unwrap().1,
127            Resolution::MergeDuplicates
128        );
129        assert_eq!(
130            parse_resolution("resolution=ignore-duplicates").unwrap().1,
131            Resolution::IgnoreDuplicates
132        );
133    }
134
135    #[test]
136    fn test_parse_count() {
137        assert_eq!(parse_count("count=exact").unwrap().1, Count::Exact);
138        assert_eq!(parse_count("count=planned").unwrap().1, Count::Planned);
139        assert_eq!(parse_count("count=estimated").unwrap().1, Count::Estimated);
140    }
141
142    #[test]
143    fn test_parse_plurality() {
144        assert_eq!(
145            parse_plurality("plurality=singular").unwrap().1,
146            Plurality::Singular
147        );
148        assert_eq!(
149            parse_plurality("plurality=multiple").unwrap().1,
150            Plurality::Multiple
151        );
152    }
153
154    #[test]
155    fn test_parse_missing() {
156        assert_eq!(
157            parse_missing("missing=default").unwrap().1,
158            Missing::Default
159        );
160        assert_eq!(parse_missing("missing=null").unwrap().1, Missing::Null);
161    }
162
163    #[test]
164    fn test_parse_prefer_header_single() {
165        let opts = parse_prefer_header("return=representation").unwrap();
166        assert_eq!(opts.return_representation, Some(ReturnRepresentation::Full));
167    }
168
169    #[test]
170    fn test_parse_prefer_header_multiple() {
171        let opts = parse_prefer_header("return=representation, count=exact").unwrap();
172        assert_eq!(opts.return_representation, Some(ReturnRepresentation::Full));
173        assert_eq!(opts.count, Some(Count::Exact));
174    }
175
176    #[test]
177    fn test_parse_prefer_header_all_options() {
178        let input = "return=minimal, resolution=merge-duplicates, count=planned, plurality=singular, missing=default";
179        let opts = parse_prefer_header(input).unwrap();
180
181        assert_eq!(
182            opts.return_representation,
183            Some(ReturnRepresentation::Minimal)
184        );
185        assert_eq!(opts.resolution, Some(Resolution::MergeDuplicates));
186        assert_eq!(opts.count, Some(Count::Planned));
187        assert_eq!(opts.plurality, Some(Plurality::Singular));
188        assert_eq!(opts.missing, Some(Missing::Default));
189    }
190
191    #[test]
192    fn test_parse_prefer_header_with_spaces() {
193        let opts = parse_prefer_header("  return=representation  ,  count=exact  ").unwrap();
194        assert_eq!(opts.return_representation, Some(ReturnRepresentation::Full));
195        assert_eq!(opts.count, Some(Count::Exact));
196    }
197
198    #[test]
199    fn test_parse_prefer_header_unknown_option() {
200        // Should skip unknown options
201        let opts =
202            parse_prefer_header("return=representation, unknown=value, count=exact").unwrap();
203        assert_eq!(opts.return_representation, Some(ReturnRepresentation::Full));
204        assert_eq!(opts.count, Some(Count::Exact));
205    }
206
207    #[test]
208    fn test_parse_prefer_header_empty() {
209        let opts = parse_prefer_header("").unwrap();
210        assert!(opts.is_empty());
211    }
212
213    #[test]
214    fn test_parse_prefer_header_empty_parts() {
215        let opts = parse_prefer_header("return=representation,  , count=exact").unwrap();
216        assert_eq!(opts.return_representation, Some(ReturnRepresentation::Full));
217        assert_eq!(opts.count, Some(Count::Exact));
218    }
219}