postgrest_parser/parser/
order.rs1use super::common::{field, parse_field_fallback};
2use crate::ast::{Direction, Field, Nulls, OrderTerm};
3use crate::error::ParseError;
4
5fn is_direction(s: &str) -> bool {
6 matches!(s, "asc" | "desc")
7}
8
9fn is_nulls_option(s: &str) -> bool {
10 matches!(s, "nullsfirst" | "nullslast")
11}
12
13pub fn parse_order(order_str: &str) -> Result<Vec<OrderTerm>, ParseError> {
56 if order_str.is_empty() || order_str.trim().is_empty() {
57 return Ok(Vec::new());
58 }
59
60 let items: Vec<&str> = order_str.split(',').map(|s| s.trim()).collect();
61
62 items
63 .iter()
64 .map(|item_str| parse_order_term(item_str))
65 .collect()
66}
67
68pub fn parse_order_term(term_str: &str) -> Result<OrderTerm, ParseError> {
80 let parts: Vec<&str> = term_str.split('.').collect();
81
82 if parts.is_empty() || parts[0].is_empty() {
83 return Err(ParseError::InvalidOrderOptions(term_str.to_string()));
84 }
85
86 let (field_parts, option_parts) = split_field_and_options(&parts);
87
88 let field_str = field_parts.join(".");
89 let field = parse_order_field(&field_str)?;
90
91 let (direction, nulls) = parse_options(&option_parts)?;
92
93 let mut term = OrderTerm::new(field).with_direction(direction);
94 if let Some(n) = nulls {
95 term = term.with_nulls(n);
96 }
97
98 Ok(term)
99}
100
101fn split_field_and_options(parts: &[&str]) -> (Vec<String>, Vec<String>) {
102 if parts.is_empty() {
103 return (Vec::new(), Vec::new());
104 }
105
106 let mut field_parts = vec![parts[0].to_string()];
107 let mut option_parts = Vec::new();
108 let mut seen_option = false;
109
110 for part in &parts[1..] {
111 if is_direction(part) || is_nulls_option(part) {
112 option_parts.push(part.to_string());
113 seen_option = true;
114 } else if !seen_option && (part.contains("->") || part.contains("::")) {
115 field_parts.push(part.to_string());
116 } else {
117 option_parts.push(part.to_string());
118 }
119 }
120
121 (field_parts, option_parts)
122}
123
124fn parse_order_field(field_str: &str) -> Result<Field, ParseError> {
125 match field(field_str) {
126 Ok((_, field)) => Ok(field),
127 Err(_) => parse_field_fallback(field_str),
128 }
129}
130
131fn parse_options(option_parts: &[String]) -> Result<(Direction, Option<Nulls>), ParseError> {
132 let mut direction = Direction::Asc;
133 let mut nulls: Option<Nulls> = None;
134
135 for part in option_parts {
136 let lower = part.to_lowercase();
137 match lower.as_str() {
138 "asc" => direction = Direction::Asc,
139 "desc" => direction = Direction::Desc,
140 "nullsfirst" => nulls = Some(Nulls::First),
141 "nullslast" => nulls = Some(Nulls::Last),
142 _ => return Err(ParseError::InvalidOrderOptions(lower.to_string())),
143 }
144 }
145
146 Ok((direction, nulls))
147}
148
149#[cfg(test)]
150mod tests {
151 use super::*;
152
153 #[test]
154 fn test_parse_order_simple() {
155 let result = parse_order("id");
156 assert!(result.is_ok());
157 let terms = result.unwrap();
158 assert_eq!(terms.len(), 1);
159 assert_eq!(terms[0].direction, Direction::Asc);
160 }
161
162 #[test]
163 fn test_parse_order_desc() {
164 let result = parse_order("id.desc");
165 assert!(result.is_ok());
166 let terms = result.unwrap();
167 assert_eq!(terms[0].direction, Direction::Desc);
168 }
169
170 #[test]
171 fn test_parse_order_with_nulls() {
172 let result = parse_order("id.desc.nullslast");
173 assert!(result.is_ok());
174 let terms = result.unwrap();
175 assert_eq!(terms[0].direction, Direction::Desc);
176 assert_eq!(terms[0].nulls, Some(Nulls::Last));
177 }
178
179 #[test]
180 fn test_parse_order_multiple() {
181 let result = parse_order("id.desc,name.asc");
182 assert!(result.is_ok());
183 let terms = result.unwrap();
184 assert_eq!(terms.len(), 2);
185 assert_eq!(terms[0].direction, Direction::Desc);
186 assert_eq!(terms[1].direction, Direction::Asc);
187 }
188
189 #[test]
190 fn test_parse_order_with_json_path() {
191 let result = parse_order("data->key.desc");
192 assert!(result.is_ok());
193 let terms = result.unwrap();
194 assert_eq!(terms[0].field.name, "data");
195 assert_eq!(terms[0].field.json_path.len(), 1);
196 }
197
198 #[test]
199 fn test_parse_order_empty() {
200 let result = parse_order("");
201 assert!(result.is_ok());
202 assert!(result.unwrap().is_empty());
203 }
204
205 #[test]
206 fn test_parse_order_invalid() {
207 let result = parse_order("id.invalid");
208 assert!(matches!(result, Err(ParseError::InvalidOrderOptions(_))));
209 }
210
211 #[test]
212 fn test_parse_order_with_cast() {
213 let result = parse_order("price::numeric.desc");
214 assert!(result.is_ok());
215 let terms = result.unwrap();
216 assert_eq!(terms[0].field.cast, Some("numeric".to_string()));
217 }
218}