1use crate::ast::QueryOptions;
2use crate::error::{ODataError, Result};
3use crate::lexer::{Lexer, Token};
4
5pub struct Parser {
7 lexer: Lexer,
8 current_token: Token,
9}
10
11impl Parser {
12 pub fn new(input: &str) -> Result<Self> {
13 let mut lexer = Lexer::new(input);
14 let current_token = lexer.next_token()?;
15 Ok(Self {
16 lexer,
17 current_token,
18 })
19 }
20
21 fn advance(&mut self) -> Result<()> {
22 self.current_token = self.lexer.next_token()?;
23 Ok(())
24 }
25
26 fn expect(&mut self, expected: Token) -> Result<()> {
27 if self.current_token == expected {
28 self.advance()?;
29 Ok(())
30 } else {
31 Err(ODataError::UnexpectedToken {
32 position: self.lexer.position(),
33 expected: format!("{:?}", expected),
34 actual: format!("{:?}", self.current_token),
35 })
36 }
37 }
38
39 fn parse_select(&mut self) -> Result<Vec<String>> {
40 self.expect(Token::Equals)?;
41
42 let mut fields = Vec::new();
43
44 loop {
45 let mut current_field = String::new();
46
47 loop {
48 match &self.current_token {
50 Token::Identifier(name) => {
51 current_field.push_str(name);
52 self.advance()?;
53
54 if self.current_token == Token::Mul {
56 current_field.push('*');
57 self.advance()?;
58 }
59 }
60 Token::Mul => {
61 current_field.push('*');
62 self.advance()?;
63 }
64 _ => {
65 return Err(ODataError::ParseError {
66 position: self.lexer.position(),
67 message: "Expected field name or * in $select".to_string(),
68 });
69 }
70 }
71
72 if self.current_token == Token::Slash {
74 current_field.push('/');
75 self.advance()?;
76 } else {
77 break;
78 }
79 }
80
81 fields.push(current_field);
82
83 if self.current_token == Token::Comma {
84 self.advance()?;
85 } else {
86 break;
87 }
88 }
89
90 Ok(fields)
91 }
92
93 fn parse_number(&mut self) -> Result<u32> {
94 self.expect(Token::Equals)?;
95
96 match &self.current_token {
97 Token::Number(num_str) => {
98 let value = num_str.parse::<u32>().map_err(|_| {
99 ODataError::InvalidNumber(num_str.clone())
100 })?;
101 self.advance()?;
102 Ok(value)
103 }
104 _ => Err(ODataError::ParseError {
105 position: self.lexer.position(),
106 message: "Expected number".to_string(),
107 }),
108 }
109 }
110
111 fn parse_expand(&mut self) -> Result<Vec<crate::ast::ExpandItem>> {
112 self.expect(Token::Equals)?;
113
114 let mut items = Vec::new();
115
116 loop {
117 let mut field_path = String::new();
119
120 loop {
121 match &self.current_token {
123 Token::Identifier(name) => {
124 field_path.push_str(name);
125 self.advance()?;
126
127 if self.current_token == Token::Mul {
129 field_path.push('*');
130 self.advance()?;
131 }
132 }
133 Token::Mul => {
134 field_path.push('*');
135 self.advance()?;
136 }
137 _ => {
138 return Err(ODataError::ParseError {
139 position: self.lexer.position(),
140 message: "Expected field name or * in $expand".to_string(),
141 });
142 }
143 }
144
145 if self.current_token == Token::Slash {
147 field_path.push('/');
148 self.advance()?;
149 } else {
150 break;
151 }
152 }
153
154 let mut item = crate::ast::ExpandItem::new(field_path);
155
156 if self.current_token == Token::LParen {
158 self.advance()?;
159
160 let options = self.parse_options_loop(Token::Semicolon, Token::RParen)?;
162
163 item.options = Some(Box::new(options));
168
169 self.expect(Token::RParen)?;
170 }
171
172 items.push(item);
173
174 if self.current_token == Token::Comma {
175 self.advance()?;
176 } else {
177 break;
178 }
179 }
180
181 Ok(items)
182 }
183
184 fn parse_options_loop(&mut self, separator: Token, terminator: Token) -> Result<QueryOptions> {
186 let mut options = QueryOptions::new();
187
188 loop {
189 if self.current_token == terminator {
191 break;
192 }
193
194 if self.current_token == Token::Eof && terminator != Token::Eof {
196 return Err(ODataError::ParseError {
197 position: self.lexer.position(),
198 message: format!("Expected terminator {:?}, found Eof", terminator),
199 });
200 }
201
202 if self.current_token == Token::RParen && terminator == Token::Eof {
204 return Err(ODataError::ParseError {
205 position: self.lexer.position(),
206 message: "Unexpected closing parenthesis".to_string(),
207 });
208 }
209
210 match &self.current_token {
211 Token::QueryOption(opt) => {
212 match opt.as_str() {
213 "$select" => {
214 self.advance()?;
215 options.select = Some(self.parse_select()?);
216 }
217 "$top" => {
218 self.advance()?;
219 options.top = Some(self.parse_number()?);
220 }
221 "$skip" => {
222 self.advance()?;
223 options.skip = Some(self.parse_number()?);
224 }
225 "$expand" => {
226 self.advance()?;
227 options.expand = Some(self.parse_expand()?);
228 }
229 "$filter" => {
230 self.advance()?;
231 options.filter = Some(self.parse_filter()?);
232 }
233 "$orderby" => {
234 self.advance()?;
235 options.orderby = Some(self.parse_orderby()?);
236 }
237 "$groupby" => {
238 self.advance()?;
239 options.groupby = Some(self.parse_groupby()?);
240 }
241 "$count" => {
242 self.advance()?;
243 options.count = Some(self.parse_count()?);
244 }
245 "$format" => {
246 self.advance()?;
247 options.format = Some(self.parse_format()?);
248 }
249 "$id" => {
250 self.advance()?;
251 options.id = Some(self.parse_id()?);
252 }
253 "$skiptoken" => {
254 self.advance()?;
255 options.skiptoken = Some(self.parse_skiptoken()?);
256 }
257 "$search" => {
258 self.advance()?;
259 options.search = Some(self.parse_search()?);
260 }
261 "$levels" => {
262 self.advance()?;
264 let _ = self.parse_number()?;
269 }
271 _ => {
272 return Err(ODataError::UnsupportedOption(opt.clone()));
273 }
274 }
275
276 if self.current_token == separator {
277 self.advance()?;
278 }
279 }
280 _ => {
281 return Err(ODataError::ParseError {
282 position: self.lexer.position(),
283 message: format!("Unexpected token: {:?}", self.current_token),
284 });
285 }
286 }
287 }
288
289 Ok(options)
290 }
291
292 pub fn parse(&mut self) -> Result<QueryOptions> {
293 self.parse_options_loop(Token::Ampersand, Token::Eof)
294 }
295
296 fn parse_orderby(&mut self) -> Result<Vec<crate::ast::OrderByItem>> {
297 self.expect(Token::Equals)?;
298
299 let mut items = Vec::new();
300
301 loop {
302 match &self.current_token {
303 Token::Identifier(field) => {
304 let field_name = field.clone();
305 self.advance()?;
306
307 let direction = if let Token::Identifier(dir) = &self.current_token {
309 match dir.as_str() {
310 "asc" => {
311 self.advance()?;
312 crate::ast::SortDirection::Asc
313 }
314 "desc" => {
315 self.advance()?;
316 crate::ast::SortDirection::Desc
317 }
318 _ => crate::ast::SortDirection::Asc, }
320 } else {
321 crate::ast::SortDirection::Asc };
323
324 items.push(crate::ast::OrderByItem::new(field_name, direction));
325
326 if self.current_token == Token::Comma {
327 self.advance()?;
328 } else {
329 break;
330 }
331 }
332 _ => {
333 return Err(ODataError::ParseError {
334 position: self.lexer.position(),
335 message: "Expected field name in $orderby".to_string(),
336 });
337 }
338 }
339 }
340
341 Ok(items)
342 }
343
344 fn parse_groupby(&mut self) -> Result<Vec<String>> {
345 self.expect(Token::Equals)?;
346
347 let mut fields = Vec::new();
348
349 loop {
350 match &self.current_token {
351 Token::Identifier(name) => {
352 fields.push(name.clone());
353 self.advance()?;
354
355 if self.current_token == Token::Comma {
356 self.advance()?;
357 } else {
358 break;
359 }
360 }
361 _ => {
362 return Err(ODataError::ParseError {
363 position: self.lexer.position(),
364 message: "Expected field name in $groupby".to_string(),
365 });
366 }
367 }
368 }
369
370 Ok(fields)
371 }
372
373 fn parse_count(&mut self) -> Result<bool> {
374 self.expect(Token::Equals)?;
375
376 match &self.current_token {
377 Token::Identifier(value) => {
378 let result = match value.as_str() {
379 "true" => true,
380 "false" => false,
381 _ => {
382 return Err(ODataError::ParseError {
383 position: self.lexer.position(),
384 message: "Expected 'true' or 'false' for $count".to_string(),
385 });
386 }
387 };
388 self.advance()?;
389 Ok(result)
390 }
391 _ => Err(ODataError::ParseError {
392 position: self.lexer.position(),
393 message: "Expected boolean value for $count".to_string(),
394 }),
395 }
396 }
397
398 fn parse_format(&mut self) -> Result<String> {
399 self.expect(Token::Equals)?;
400
401 match &self.current_token {
402 Token::Identifier(format) => {
403 let value = format.clone();
404 self.advance()?;
405 Ok(value)
406 }
407 _ => Err(ODataError::ParseError {
408 position: self.lexer.position(),
409 message: "Expected format value".to_string(),
410 }),
411 }
412 }
413
414 fn parse_id(&mut self) -> Result<String> {
415 self.expect(Token::Equals)?;
416
417 match &self.current_token {
418 Token::Identifier(id) | Token::Number(id) => {
419 let value = id.clone();
420 self.advance()?;
421 Ok(value)
422 }
423 Token::StringLiteral(id) => {
424 let value = id.clone();
425 self.advance()?;
426 Ok(value)
427 }
428 _ => Err(ODataError::ParseError {
429 position: self.lexer.position(),
430 message: "Expected ID value".to_string(),
431 }),
432 }
433 }
434
435 fn parse_skiptoken(&mut self) -> Result<String> {
436 self.expect(Token::Equals)?;
437
438 match &self.current_token {
439 Token::Identifier(token) | Token::Number(token) => {
440 let value = token.clone();
441 self.advance()?;
442 Ok(value)
443 }
444 Token::StringLiteral(token) => {
445 let value = token.clone();
446 self.advance()?;
447 Ok(value)
448 }
449 _ => Err(ODataError::ParseError {
450 position: self.lexer.position(),
451 message: "Expected skiptoken value".to_string(),
452 }),
453 }
454 }
455
456 fn parse_search(&mut self) -> Result<String> {
457 self.expect(Token::Equals)?;
458
459 match &self.current_token {
460 Token::Identifier(value) | Token::Number(value) => {
461 let search = value.clone();
462 self.advance()?;
463 Ok(search)
464 }
465 Token::StringLiteral(value) => {
466 let search = value.clone();
467 self.advance()?;
468 Ok(search)
469 }
470 _ => Err(ODataError::ParseError {
471 position: self.lexer.position(),
472 message: "Expected search value".to_string(),
473 }),
474 }
475 }
476
477 fn parse_filter(&mut self) -> Result<crate::ast::FilterExpression> {
479 self.expect(Token::Equals)?;
480 self.parse_or_expression()
481 }
482
483 fn parse_or_expression(&mut self) -> Result<crate::ast::FilterExpression> {
484 let mut left = self.parse_and_expression()?;
485
486 while let Token::Identifier(ref id) = self.current_token {
487 if id == "or" {
488 self.advance()?;
489 let right = self.parse_and_expression()?;
490 left = crate::ast::FilterExpression::Logical {
491 left: Box::new(left),
492 op: crate::ast::LogicalOp::Or,
493 right: Box::new(right),
494 };
495 } else {
496 break;
497 }
498 }
499
500 Ok(left)
501 }
502
503 fn parse_and_expression(&mut self) -> Result<crate::ast::FilterExpression> {
504 let mut left = self.parse_comparison_expression()?;
505
506 while let Token::Identifier(ref id) = self.current_token {
507 if id == "and" {
508 self.advance()?;
509 let right = self.parse_comparison_expression()?;
510 left = crate::ast::FilterExpression::Logical {
511 left: Box::new(left),
512 op: crate::ast::LogicalOp::And,
513 right: Box::new(right),
514 };
515 } else {
516 break;
517 }
518 }
519
520 Ok(left)
521 }
522
523 fn parse_comparison_expression(&mut self) -> Result<crate::ast::FilterExpression> {
524 let left = self.parse_additive_expression()?;
525
526 if let Token::Identifier(ref id) = self.current_token {
528 match id.as_str() {
529 "eq" | "ne" | "gt" | "ge" | "lt" | "le" | "has" => {
530 let op = match id.as_str() {
531 "eq" => crate::ast::ComparisonOp::Eq,
532 "ne" => crate::ast::ComparisonOp::Ne,
533 "gt" => crate::ast::ComparisonOp::Gt,
534 "ge" => crate::ast::ComparisonOp::Ge,
535 "lt" => crate::ast::ComparisonOp::Lt,
536 "le" => crate::ast::ComparisonOp::Le,
537 "has" => crate::ast::ComparisonOp::Has,
538 _ => unreachable!(),
539 };
540 self.advance()?;
541 let right = self.parse_additive_expression()?;
542 Ok(crate::ast::FilterExpression::Comparison {
543 left: Box::new(left),
544 op,
545 right: Box::new(right),
546 })
547 }
548 "in" => {
549 self.advance()?;
550 self.expect(Token::LParen)?;
551 let mut values = Vec::new();
552 loop {
553 values.push(self.parse_primary_expression()?);
554 if self.current_token == Token::Comma {
555 self.advance()?;
556 } else {
557 break;
558 }
559 }
560 self.expect(Token::RParen)?;
561 Ok(crate::ast::FilterExpression::In {
562 field: Box::new(left),
563 values,
564 })
565 }
566 _ => Ok(left),
567 }
568 } else {
569 Ok(left)
570 }
571 }
572
573 fn parse_additive_expression(&mut self) -> Result<crate::ast::FilterExpression> {
574 let mut left = self.parse_multiplicative_expression()?;
575
576 while let Token::Identifier(ref id) = self.current_token {
577 let op = match id.as_str() {
578 "add" => crate::ast::ArithmeticOp::Add,
579 "sub" => crate::ast::ArithmeticOp::Sub,
580 _ => break,
581 };
582 self.advance()?;
583 let right = self.parse_multiplicative_expression()?;
584 left = crate::ast::FilterExpression::Arithmetic {
585 left: Box::new(left),
586 op,
587 right: Box::new(right),
588 };
589 }
590 Ok(left)
591 }
592
593 fn parse_multiplicative_expression(&mut self) -> Result<crate::ast::FilterExpression> {
594 let mut left = self.parse_unary_expression()?;
595
596 while let Token::Identifier(ref id) = self.current_token {
597 let op = match id.as_str() {
598 "mul" => crate::ast::ArithmeticOp::Mul,
599 "div" => crate::ast::ArithmeticOp::Div,
600 "mod" => crate::ast::ArithmeticOp::Mod,
601 _ => break,
602 };
603 self.advance()?;
604 let right = self.parse_unary_expression()?;
605 left = crate::ast::FilterExpression::Arithmetic {
606 left: Box::new(left),
607 op,
608 right: Box::new(right),
609 };
610 }
611 Ok(left)
612 }
613
614 fn parse_unary_expression(&mut self) -> Result<crate::ast::FilterExpression> {
615 if self.current_token == Token::Minus {
617 self.advance()?;
618 let expr = self.parse_unary_expression()?;
619 return Ok(crate::ast::FilterExpression::UnaryMinus(Box::new(expr)));
620 }
621 self.parse_primary_expression()
622 }
623
624 fn parse_identifier_with_hyphens(&mut self) -> Result<String> {
627 let mut result = String::new();
628
629 loop {
631 if self.current_token != Token::Minus {
632 if let Token::Identifier(id) = &self.current_token {
635 result.push_str(id);
638 self.advance()?;
639 }
640 break;
641 }
642 result.push('-');
643 self.advance()?;
644
645 match &self.current_token {
646 Token::Identifier(id) | Token::Number(id) => {
647 result.push_str(id);
648 self.advance()?;
649 }
652 _ => break,
653 }
654 }
655
656 Ok(result)
657 }
658
659 fn parse_primary_expression(&mut self) -> Result<crate::ast::FilterExpression> {
660 match &self.current_token.clone() {
661 Token::Identifier(id) => {
662 match id.as_str() {
663 "not" => {
664 self.advance()?;
665 let expr = self.parse_unary_expression()?;
666 Ok(crate::ast::FilterExpression::Not(Box::new(expr)))
667 }
668 "true" => {
669 self.advance()?;
670 Ok(crate::ast::FilterExpression::BooleanLiteral(true))
671 }
672 "false" => {
673 self.advance()?;
674 Ok(crate::ast::FilterExpression::BooleanLiteral(false))
675 }
676 "null" => {
677 self.advance()?;
678 Ok(crate::ast::FilterExpression::Null)
679 }
680 _ => {
681 let mut name = id.clone();
683 self.advance()?;
684
685 if self.current_token == Token::Minus {
687 let full_id = format!("{}{}", name, self.parse_identifier_with_hyphens()?);
689 if full_id.len() == 36 && full_id.chars().filter(|c| *c == '-').count() == 4 {
691 return Ok(crate::ast::FilterExpression::GuidLiteral(full_id));
692 } else if full_id.len() == 10 && full_id.chars().filter(|c| *c == '-').count() == 2 {
693 return Ok(crate::ast::FilterExpression::DateLiteral(full_id));
694 }
695 name = full_id;
696 }
697
698 let collection_name = if self.current_token == Token::Slash {
700 let _saved_pos = self.lexer.position();
702 self.advance()?;
703
704 let is_lambda = if let Token::Identifier(method) = &self.current_token {
705 method == "any" || method == "all"
706 } else {
707 false
708 };
709
710 if is_lambda {
711 if let Token::Identifier(method) = &self.current_token {
712 let method_name = method.clone();
713 self.advance()?;
714 if self.current_token == Token::LParen {
715 self.advance()?;
716 if let Token::Identifier(var) = &self.current_token {
718 let variable = var.clone();
719 self.advance()?;
720 self.expect(Token::Colon)?;
721 let predicate = self.parse_or_expression()?;
722 self.expect(Token::RParen)?;
723 let operator = match method_name.as_str() {
724 "any" => crate::ast::LambdaOp::Any,
725 "all" => crate::ast::LambdaOp::All,
726 _ => unreachable!(),
727 };
728 return Ok(crate::ast::FilterExpression::Lambda {
729 collection: name,
730 operator,
731 variable,
732 predicate: Box::new(predicate),
733 });
734 }
735 }
736 }
737 return Err(ODataError::ParseError {
738 position: self.lexer.position(),
739 message: "Expected lambda operator after /".to_string(),
740 });
741 } else {
742 let mut field_path = format!("{}/", name);
745 if let Token::Identifier(next_part) = &self.current_token {
746 field_path.push_str(next_part);
747 self.advance()?;
748 }
749 field_path
750 }
751 } else {
752 name.clone()
753 };
754
755 if self.current_token == Token::LParen {
757 let allowed_functions = [
760 "concat", "contains", "endswith", "indexof", "length", "startswith", "substring",
761 "tolower", "toupper", "trim",
762 "date", "day", "fractionalsecond", "hour", "minute", "month", "now", "second",
763 "time", "totaloffsetminutes", "totalseconds", "year", "maxdatetime", "mindatetime",
764 "ceiling", "floor", "round",
765 "cast", "isof",
766 "geo.distance", "geo.intersects", "geo.length"
767 ];
768
769 if !allowed_functions.contains(&collection_name.as_str()) {
770 return Err(ODataError::ParseError {
771 position: self.lexer.position(),
772 message: format!("Unknown or unauthorized function: '{}'. Only standard OData functions are allowed.", collection_name),
773 });
774 }
775
776 self.advance()?;
777 let mut args = Vec::new();
778
779 if self.current_token != Token::RParen {
780 loop {
781 args.push(self.parse_or_expression()?);
782 if self.current_token == Token::Comma {
783 self.advance()?;
784 } else {
785 break;
786 }
787 }
788 }
789 self.expect(Token::RParen)?;
790 Ok(crate::ast::FilterExpression::FunctionCall { name: collection_name, args })
791 } else if collection_name.len() == 36 && collection_name.chars().filter(|c| *c == '-').count() == 4 {
792 Ok(crate::ast::FilterExpression::GuidLiteral(collection_name))
794 } else if collection_name.len() == 10 && collection_name.chars().filter(|c| *c == '-').count() == 2 {
795 Ok(crate::ast::FilterExpression::DateLiteral(collection_name))
797 } else {
798 Ok(crate::ast::FilterExpression::Field(collection_name))
800 }
801 }
802 }
803 }
804 Token::StringLiteral(s) => {
805 let value = s.clone();
806 self.advance()?;
807 Ok(crate::ast::FilterExpression::StringLiteral(value))
808 }
809 Token::Number(n) => {
810 let mut value_str = n.clone();
811 self.advance()?;
812
813 if self.current_token == Token::Minus {
815 let rest = self.parse_identifier_with_hyphens()?;
816 let full_id = format!("{}{}", value_str, rest);
817 if full_id.len() == 36 && full_id.chars().filter(|c| *c == '-').count() == 4 {
819 return Ok(crate::ast::FilterExpression::GuidLiteral(full_id));
820 } else if full_id.len() == 10 && full_id.chars().filter(|c| *c == '-').count() == 2 {
821 return Ok(crate::ast::FilterExpression::DateLiteral(full_id));
822 }
823 value_str = full_id;
824 }
825
826 let value = value_str.parse::<f64>().map_err(|_| {
827 ODataError::InvalidNumber(value_str)
828 })?;
829 Ok(crate::ast::FilterExpression::NumberLiteral(value))
830 }
831 Token::LParen => {
832 self.advance()?;
833 let expr = self.parse_or_expression()?;
834 self.expect(Token::RParen)?;
835 Ok(expr)
836 }
837 Token::Minus => {
838 Err(ODataError::ParseError {
841 position: self.lexer.position(),
842 message: "Unexpected minus token - unary minus should use parse_unary_expression".to_string(),
843 })
844 }
845 _ => Err(ODataError::ParseError {
846 position: self.lexer.position(),
847 message: format!("Unexpected token in filter expression: {:?}", self.current_token),
848 }),
849 }
850 }
851}
852
853pub fn parse(query: &str) -> Result<QueryOptions> {
855 let mut parser = Parser::new(query)?;
856 parser.parse()
857}
858
859#[cfg(test)]
860mod tests {
861 use super::*;
862
863 #[test]
864 fn test_parse_select() {
865 let result = parse("$select=id,name,email").unwrap();
866 assert_eq!(
867 result.select,
868 Some(vec!["id".to_string(), "name".to_string(), "email".to_string()])
869 );
870 }
871
872 #[test]
873 fn test_parse_top() {
874 let result = parse("$top=10").unwrap();
875 assert_eq!(result.top, Some(10));
876 }
877
878 #[test]
879 fn test_parse_skip() {
880 let result = parse("$skip=20").unwrap();
881 assert_eq!(result.skip, Some(20));
882 }
883
884 #[test]
885 fn test_parse_combined() {
886 let result = parse("$select=id,name&$top=10&$skip=20").unwrap();
887 assert_eq!(result.select, Some(vec!["id".to_string(), "name".to_string()]));
888 assert_eq!(result.top, Some(10));
889 assert_eq!(result.skip, Some(20));
890 }
891
892 #[test]
893 fn test_parse_expand() {
894 let result = parse("$expand=orders,profile").unwrap();
895 let expand = result.expand.unwrap();
896 assert_eq!(expand.len(), 2);
897 assert_eq!(expand[0].field, "orders");
898 assert_eq!(expand[1].field, "profile");
899 }
900
901 #[test]
902 fn test_parse_filter_simple() {
903 use crate::ast::*;
904 let result = parse("$filter=age gt 18").unwrap();
905
906 match result.filter {
907 Some(FilterExpression::Comparison { left, op, right }) => {
908 assert!(matches!(*left, FilterExpression::Field(ref f) if f == "age"));
909 assert_eq!(op, ComparisonOp::Gt);
910 assert!(matches!(*right, FilterExpression::NumberLiteral(n) if n == 18.0));
911 }
912 _ => panic!("Expected comparison expression"),
913 }
914 }
915
916 #[test]
917 fn test_parse_filter_string() {
918 use crate::ast::*;
919 let result = parse("$filter=name eq 'John'").unwrap();
920
921 match result.filter {
922 Some(FilterExpression::Comparison { left, op, right }) => {
923 assert!(matches!(*left, FilterExpression::Field(ref f) if f == "name"));
924 assert_eq!(op, ComparisonOp::Eq);
925 assert!(matches!(*right, FilterExpression::StringLiteral(ref s) if s == "John"));
926 }
927 _ => panic!("Expected comparison expression"),
928 }
929 }
930
931 #[test]
932 fn test_parse_filter_logical() {
933 use crate::ast::*;
934 let result = parse("$filter=age gt 18 and active eq true").unwrap();
935
936 match result.filter {
937 Some(FilterExpression::Logical { left: _, op, right: _ }) => {
938 assert_eq!(op, LogicalOp::And);
939 }
940 _ => panic!("Expected logical expression"),
941 }
942 }
943
944 #[test]
945 fn test_parse_filter_not() {
946 use crate::ast::*;
947 let result = parse("$filter=not active").unwrap();
948
949 match result.filter {
950 Some(FilterExpression::Not(inner)) => {
951 assert!(matches!(*inner, FilterExpression::Field(ref f) if f == "active"));
952 }
953 _ => panic!("Expected not expression"),
954 }
955 }
956
957 #[test]
958 fn test_parse_combined_all() {
959 let result = parse("$select=id,name&$filter=age gt 18&$expand=orders&$top=10").unwrap();
960 assert_eq!(result.select, Some(vec!["id".to_string(), "name".to_string()]));
961 assert!(result.filter.is_some());
962 assert_eq!(result.expand.unwrap()[0].field, "orders");
963 assert_eq!(result.top, Some(10));
964 }
965
966 #[test]
967 fn test_parse_unsupported_option() {
968 let result = parse("$unsupported=value");
969 assert!(result.is_err());
970 match result {
971 Err(ODataError::UnsupportedOption(opt)) => {
972 assert_eq!(opt, "$unsupported");
973 }
974 _ => panic!("Expected UnsupportedOption error"),
975 }
976 }
977}