odatav4_parser/
parser.rs

1use crate::ast::QueryOptions;
2use crate::error::{ODataError, Result};
3use crate::lexer::{Lexer, Token};
4
5/// Parser for OData V4 query strings
6pub 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                // Parse one segment
49                match &self.current_token {
50                    Token::Identifier(name) => {
51                        current_field.push_str(name);
52                        self.advance()?;
53                        
54                        // Check for wildcard after identifier (e.g., Namespace.*)
55                        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                // Check for path separator
73                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            // 1. Parse Field Path (e.g. "Orders/Items")
118            let mut field_path = String::new();
119            
120            loop {
121                // Parse one segment
122                match &self.current_token {
123                    Token::Identifier(name) => {
124                        field_path.push_str(name);
125                        self.advance()?;
126                        
127                        // Check for wildcard after identifier (e.g., Namespace.*)
128                        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                // Check for path separator
146                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            // 2. Check for nested options (e.g. "(...)")
157            if self.current_token == Token::LParen {
158                self.advance()?;
159                
160                // Parse nested options separated by semicolon
161                let options = self.parse_options_loop(Token::Semicolon, Token::RParen)?;
162                
163                // Check for $levels inside options and extract strict levels if present
164                // Note: strict OData spec says $levels is a query option. 
165                // We'll store it separately if found, otherwise keep in options.
166                // For simplicity here, we just store the full options.
167                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    /// Generic loop for parsing query options with a specific separator (Ampersand for top-level, Semicolon for embedded)
185    fn parse_options_loop(&mut self, separator: Token, terminator: Token) -> Result<QueryOptions> {
186        let mut options = QueryOptions::new();
187
188        loop {
189            // Check for end of input or terminator
190            if self.current_token == terminator {
191                break;
192            }
193            
194            // If we hit Eof but expected something else (e.g. RParen), that's an error
195            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 we hit RParen but expected Eof, that's an error (e.g. top level query with stray paren)
203            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                            // Special handling for levels (often used in expand)
263                            self.advance()?;
264                            // For now just parse as number and ignore storing specialized field unless we add it to QueryOptions
265                            // Or we could parse it but `QueryOptions` doesn't have `levels` field yet. 
266                            // Only `ExpandItem` has it.
267                            // Let's assume user might put strictly valid options.
268                            let _ = self.parse_number()?; 
269                            // TODO: Store this if we add levels to QueryOptions or handle it in caller
270                        }
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                    // Check for optional asc/desc
308                    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,  // Default to asc
319                        }
320                    } else {
321                        crate::ast::SortDirection::Asc  // Default to asc
322                    };
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    // Filter parsing with operator precedence: or < and < comparison
478    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        // Check for comparison operator or 'in'
527        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        // Check for unary minus
616        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    /// Helper to parse identifier that may contain hyphens (for GUID/Date)
625    /// Expects current token to be Minus at start
626    fn parse_identifier_with_hyphens(&mut self) -> Result<String> {
627        let mut result = String::new();
628        
629        // Continue reading Minus + Identifier/Number sequences
630        loop {
631            if self.current_token != Token::Minus {
632                // Special case: lexer may have already included hyphens in identifier
633                // E.g., after "89", we might have Identifier("ab-cdef-...") 
634                if let Token::Identifier(id) = &self.current_token {
635                    // Identifier comes immediately after the number, lexer split at letter boundary
636                    // E.g., "89ab" became Number("89") + Identifier("ab")
637                    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                    // Check if there's another segment (for GUID: 8-4-4-4-12 format)
650                    // Continue if we see another Minus
651                }
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                        // Try to parse identifier with potential hyphens (for GUID/Date)
682                        let mut name = id.clone();
683                        self.advance()?;
684                        
685                        // Check if followed by hyphens (could be GUID or Date)
686                        if self.current_token == Token::Minus {
687                            // Back up and use helper to parse full identifier
688                            let full_id = format!("{}{}", name, self.parse_identifier_with_hyphens()?);
689                            // Check if it's a GUID or Date
690                            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                        // Check for path navigation (e.g., Comments/any) - only for lambda operators
699                        let collection_name = if self.current_token == Token::Slash {
700                            // Peek ahead to see if this is followed by any/all
701                            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                                        // Parse lambda: variable:predicate
717                                        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                                // Not a lambda, treat slash as field path separator
743                                // Build the field path
744                                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                        // Check for function call
756                        if self.current_token == Token::LParen {
757                            // Validate function name against whitelist
758                            // Standard OData functions
759                            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                            // GUID format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
793                            Ok(crate::ast::FilterExpression::GuidLiteral(collection_name))
794                        } else if collection_name.len() == 10 && collection_name.chars().filter(|c| *c == '-').count() == 2 {
795                            // Date format: YYYY-MM-DD
796                            Ok(crate::ast::FilterExpression::DateLiteral(collection_name))
797                        } else {
798                            // Field reference
799                            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                // Check if this might be a GUID or Date (starts with number but has hyphens)
814                if self.current_token == Token::Minus {
815                    let rest = self.parse_identifier_with_hyphens()?;
816                    let full_id = format!("{}{}", value_str, rest);
817                    // Check if it's a GUID or Date
818                    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                // Unary minus was supposed to be caught in parse_unary_expression
839                // But if we get here, treat it as error
840                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
853/// Parse an OData V4 query string into a QueryOptions AST
854pub 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}