Skip to main content

khive_query/
ast.rs

1//! GQL abstract syntax tree.
2
3use std::collections::HashMap;
4
5/// A SQL parameter value emitted by the query compiler.
6#[derive(Clone, Debug)]
7pub enum QueryValue {
8    Null,
9    Integer(i64),
10    Float(f64),
11    Text(String),
12    Blob(Vec<u8>),
13}
14
15/// Top-level GQL query node produced by the parser.
16#[derive(Debug, Clone)]
17pub struct GqlQuery {
18    pub pattern: MatchPattern,
19    pub where_clause: WhereExpr,
20    pub return_items: Vec<ReturnItem>,
21    pub limit: Option<usize>,
22}
23
24/// A WHERE expression tree supporting AND, OR, and leaf conditions.
25#[derive(Debug, Clone)]
26pub enum WhereExpr {
27    /// AND of two sub-expressions.
28    And(Box<WhereExpr>, Box<WhereExpr>),
29    /// OR of two sub-expressions.
30    Or(Box<WhereExpr>, Box<WhereExpr>),
31    /// A single scalar condition.
32    Condition(Condition),
33    /// Always-true — used when there is no WHERE clause.
34    True,
35}
36
37impl WhereExpr {
38    /// Iterate all leaf conditions in the expression tree (depth-first).
39    pub fn conditions(&self) -> impl Iterator<Item = &Condition> {
40        let mut stack = vec![self];
41        let mut out: Vec<&Condition> = Vec::new();
42        while let Some(expr) = stack.pop() {
43            match expr {
44                WhereExpr::Condition(c) => out.push(c),
45                WhereExpr::And(l, r) | WhereExpr::Or(l, r) => {
46                    stack.push(r);
47                    stack.push(l);
48                }
49                WhereExpr::True => {}
50            }
51        }
52        out.into_iter()
53    }
54
55    /// Mutable walk — applies `f` to every leaf condition.
56    pub fn for_each_condition_mut(&mut self, f: &mut impl FnMut(&mut Condition)) {
57        match self {
58            WhereExpr::Condition(c) => f(c),
59            WhereExpr::And(l, r) | WhereExpr::Or(l, r) => {
60                l.for_each_condition_mut(f);
61                r.for_each_condition_mut(f);
62            }
63            WhereExpr::True => {}
64        }
65    }
66
67    /// Return `true` when the expression has no conditions (is always-true).
68    pub fn is_true(&self) -> bool {
69        matches!(self, WhereExpr::True)
70    }
71}
72
73/// A single item in the RETURN clause — either a bound variable or a property projection.
74#[derive(Debug, Clone, PartialEq, Eq)]
75pub enum ReturnItem {
76    Variable(String),
77    Property(String, String),
78}
79
80impl ReturnItem {
81    /// Returns the variable name bound to this return item.
82    pub fn variable(&self) -> &str {
83        match self {
84            Self::Variable(v) | Self::Property(v, _) => v,
85        }
86    }
87}
88
89/// The MATCH pattern of a GQL query, as an alternating sequence of node and edge elements.
90#[derive(Debug, Clone)]
91pub struct MatchPattern {
92    pub elements: Vec<PatternElement>,
93}
94
95impl MatchPattern {
96    pub fn nodes(&self) -> impl Iterator<Item = &NodePattern> {
97        self.elements.iter().filter_map(|e| match e {
98            PatternElement::Node(n) => Some(n),
99            _ => None,
100        })
101    }
102
103    pub fn edges(&self) -> impl Iterator<Item = &EdgePattern> {
104        self.elements.iter().filter_map(|e| match e {
105            PatternElement::Edge(e) => Some(e),
106            _ => None,
107        })
108    }
109
110    pub fn has_variable_length(&self) -> bool {
111        self.edges().any(|e| e.max_hops > 1)
112    }
113}
114
115/// A single element in the MATCH pattern -- either a node or an edge.
116#[derive(Debug, Clone)]
117pub enum PatternElement {
118    Node(NodePattern),
119    Edge(EdgePattern),
120}
121
122/// A node binding in the MATCH pattern with optional kind, entity_type, and property filters.
123#[derive(Debug, Clone)]
124pub struct NodePattern {
125    pub variable: Option<String>,
126    pub kind: Option<String>,
127    /// Governed subtype within the kind (e.g. "researcher" within "person").
128    /// Compiled to `entity_type = ?` — a direct column, not a property extraction.
129    pub entity_type: Option<String>,
130    pub properties: HashMap<String, String>,
131}
132
133#[derive(Debug, Clone)]
134pub struct EdgePattern {
135    pub variable: Option<String>,
136    pub relations: Vec<String>,
137    pub direction: EdgeDirection,
138    pub min_hops: usize,
139    pub max_hops: usize,
140}
141
142#[derive(Debug, Clone, Copy, PartialEq, Eq)]
143pub enum EdgeDirection {
144    Out,
145    In,
146    Both,
147}
148
149#[derive(Debug, Clone)]
150pub struct Condition {
151    pub variable: String,
152    pub property: String,
153    pub op: CompareOp,
154    pub value: ConditionValue,
155}
156
157#[derive(Debug, Clone, Copy, PartialEq, Eq)]
158pub enum CompareOp {
159    Eq,
160    Neq,
161    Gt,
162    Lt,
163    Gte,
164    Lte,
165    Like,
166}
167
168#[derive(Debug, Clone)]
169pub enum ConditionValue {
170    String(String),
171    Number(f64),
172    Bool(bool),
173}