Skip to main content

khive_query/
ast.rs

1//! GQL abstract syntax tree.
2
3use std::collections::HashMap;
4
5/// A SQL parameter value local to the query layer.
6///
7/// Deliberately mirrors the subset of `khive_storage::types::SqlValue` that the
8/// query compiler needs to emit.  The runtime converts these to the storage-layer
9/// `SqlValue` at the query–storage boundary (ADR-008 §"Query crate compiles
10/// against khive-types only").
11#[derive(Clone, Debug)]
12pub enum QueryValue {
13    Null,
14    Integer(i64),
15    Float(f64),
16    Text(String),
17    Blob(Vec<u8>),
18}
19
20#[derive(Debug, Clone)]
21pub struct GqlQuery {
22    pub pattern: MatchPattern,
23    pub where_clause: WhereExpr,
24    pub return_items: Vec<ReturnItem>,
25    pub limit: Option<usize>,
26}
27
28/// A WHERE expression tree supporting AND, OR, and leaf conditions (ADR-008
29/// §"GQL WHERE expression").
30#[derive(Debug, Clone)]
31pub enum WhereExpr {
32    /// AND of two sub-expressions.
33    And(Box<WhereExpr>, Box<WhereExpr>),
34    /// OR of two sub-expressions.
35    Or(Box<WhereExpr>, Box<WhereExpr>),
36    /// A single scalar condition.
37    Condition(Condition),
38    /// Always-true — used when there is no WHERE clause.
39    True,
40}
41
42impl WhereExpr {
43    /// Iterate all leaf conditions in the expression tree (depth-first).
44    pub fn conditions(&self) -> impl Iterator<Item = &Condition> {
45        let mut stack = vec![self];
46        let mut out: Vec<&Condition> = Vec::new();
47        while let Some(expr) = stack.pop() {
48            match expr {
49                WhereExpr::Condition(c) => out.push(c),
50                WhereExpr::And(l, r) | WhereExpr::Or(l, r) => {
51                    stack.push(r);
52                    stack.push(l);
53                }
54                WhereExpr::True => {}
55            }
56        }
57        out.into_iter()
58    }
59
60    /// Mutable walk — applies `f` to every leaf condition.
61    pub fn for_each_condition_mut(&mut self, f: &mut impl FnMut(&mut Condition)) {
62        match self {
63            WhereExpr::Condition(c) => f(c),
64            WhereExpr::And(l, r) | WhereExpr::Or(l, r) => {
65                l.for_each_condition_mut(f);
66                r.for_each_condition_mut(f);
67            }
68            WhereExpr::True => {}
69        }
70    }
71
72    /// Return `true` when the expression has no conditions (is always-true).
73    pub fn is_true(&self) -> bool {
74        matches!(self, WhereExpr::True)
75    }
76}
77
78#[derive(Debug, Clone, PartialEq, Eq)]
79pub enum ReturnItem {
80    Variable(String),
81    Property(String, String),
82}
83
84impl ReturnItem {
85    pub fn variable(&self) -> &str {
86        match self {
87            Self::Variable(v) | Self::Property(v, _) => v,
88        }
89    }
90}
91
92#[derive(Debug, Clone)]
93pub struct MatchPattern {
94    pub elements: Vec<PatternElement>,
95}
96
97impl MatchPattern {
98    pub fn nodes(&self) -> impl Iterator<Item = &NodePattern> {
99        self.elements.iter().filter_map(|e| match e {
100            PatternElement::Node(n) => Some(n),
101            _ => None,
102        })
103    }
104
105    pub fn edges(&self) -> impl Iterator<Item = &EdgePattern> {
106        self.elements.iter().filter_map(|e| match e {
107            PatternElement::Edge(e) => Some(e),
108            _ => None,
109        })
110    }
111
112    pub fn has_variable_length(&self) -> bool {
113        self.edges().any(|e| e.max_hops > 1)
114    }
115}
116
117#[derive(Debug, Clone)]
118pub enum PatternElement {
119    Node(NodePattern),
120    Edge(EdgePattern),
121}
122
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}