Skip to main content

reddb_server/storage/query/parser/
path.rs

1//! Path query parsing (PATH FROM ... TO ...)
2
3use super::super::ast::{CompareOp, NodeSelector, PathQuery, PropertyFilter, QueryExpr};
4use super::super::lexer::Token;
5use super::error::ParseError;
6use super::Parser;
7
8impl<'a> Parser<'a> {
9    /// Parse PATH FROM ... TO ... query
10    pub fn parse_path_query(&mut self) -> Result<QueryExpr, ParseError> {
11        self.expect(Token::Path)?;
12        self.expect(Token::From)?;
13
14        let from = self.parse_node_selector()?;
15
16        self.expect(Token::To)?;
17
18        let to = self.parse_node_selector()?;
19
20        let via = if self.consume(&Token::Via)? {
21            self.parse_edge_type_list()?
22        } else {
23            Vec::new()
24        };
25
26        let mut max_length = 10;
27        loop {
28            if self.consume(&Token::Algorithm)? || self.consume(&Token::Direction)? {
29                let _ = self.expect_ident_or_keyword()?;
30            } else if self.consume(&Token::Limit)? {
31                max_length = self.parse_integer()? as u32;
32            } else {
33                break;
34            }
35        }
36
37        let filter = if self.consume(&Token::Where)? {
38            Some(self.parse_filter()?)
39        } else {
40            None
41        };
42
43        let return_ = if self.consume(&Token::Return)? {
44            self.parse_return_list()?
45        } else {
46            Vec::new()
47        };
48
49        Ok(QueryExpr::Path(PathQuery {
50            alias: None,
51            from,
52            to,
53            via,
54            max_length,
55            filter,
56            return_,
57        }))
58    }
59
60    /// Parse node selector: host('id'), ByType, etc.
61    fn parse_node_selector(&mut self) -> Result<NodeSelector, ParseError> {
62        if let Token::String(id) = self.peek().clone() {
63            self.advance()?;
64            return Ok(NodeSelector::ById(id));
65        }
66
67        let name = self.expect_ident()?;
68
69        if !self.consume(&Token::LParen)? {
70            return Ok(NodeSelector::ById(name));
71        }
72
73        let selector = match name.to_lowercase().as_str() {
74            "host" | "node" | "id" => {
75                let id = self.parse_string()?;
76                NodeSelector::ById(id)
77            }
78            "row" => {
79                let table = self.parse_string()?;
80                self.expect(Token::Comma)?;
81                let row_id = self.parse_integer()? as u64;
82                NodeSelector::ByRow { table, row_id }
83            }
84            type_name => {
85                // ByType with optional filter — label kept as canonical string.
86                let node_label = self.parse_node_label(type_name)?;
87                let filter = if !self.check(&Token::RParen) {
88                    let name = self.expect_ident()?;
89                    self.expect(Token::Eq)?;
90                    let value = self.parse_value()?;
91                    Some(PropertyFilter {
92                        name,
93                        op: CompareOp::Eq,
94                        value,
95                    })
96                } else {
97                    None
98                };
99                NodeSelector::ByType { node_label, filter }
100            }
101        };
102
103        self.expect(Token::RParen)?;
104
105        Ok(selector)
106    }
107
108    /// Parse edge label list: `[:LABEL1, :LABEL2]`
109    fn parse_edge_type_list(&mut self) -> Result<Vec<String>, ParseError> {
110        self.expect(Token::LBracket)?;
111
112        let mut labels = Vec::new();
113        loop {
114            self.expect(Token::Colon)?;
115            let type_name = self.expect_ident_or_keyword()?;
116            labels.push(self.parse_edge_label(&type_name)?);
117
118            if !self.consume(&Token::Comma)? {
119                break;
120            }
121        }
122
123        self.expect(Token::RBracket)?;
124        Ok(labels)
125    }
126}