selene_gql/ast/pattern.rs
1//! Graph-pattern AST nodes.
2
3use std::fmt;
4
5use selene_core::DbString;
6
7use crate::ast::{expr::ValueExpr, span::SourceSpan, util::Vec2OrMore};
8
9/// Path traversal mode for `MATCH`.
10#[derive(
11 Clone, Copy, Debug, Default, Eq, Hash, PartialEq, serde::Deserialize, serde::Serialize,
12)]
13pub enum PathMode {
14 /// Edges and nodes may repeat.
15 #[default]
16 Walk,
17 /// No repeated edges.
18 Trail,
19 /// No repeated nodes.
20 Acyclic,
21 /// No repeated nodes except the first and last node may be equal.
22 Simple,
23}
24
25/// Path selector.
26#[derive(Clone, Copy, Eq, Hash, PartialEq, serde::Deserialize, serde::Serialize)]
27pub enum PathSelector {
28 /// ISO §16.6 G016 any path search: `ANY [N] [PATHS]`.
29 ///
30 /// Retains up to N implementation-dependent path bindings per endpoint
31 /// partition (§22.4 GR13a). Bare `ANY` defaults to `paths = 1`.
32 Any {
33 /// Number of path bindings to retain per endpoint partition.
34 paths: u32,
35 },
36 /// `ALL`.
37 All,
38 /// `ANY SHORTEST`.
39 AnyShortest,
40 /// `ALL SHORTEST`.
41 AllShortest,
42 /// ISO §16.6 G019 counted shortest path search: `SHORTEST N [PATHS]`.
43 /// Retains up to N individually-shortest path bindings per endpoint
44 /// partition (§22.4 GR13b-i). `ANY SHORTEST` == `CountedShortest { paths: 1 }`.
45 CountedShortest {
46 /// Number of shortest path bindings to retain per endpoint partition.
47 paths: u32,
48 },
49 /// ISO §16.6 G020 counted shortest group search: `SHORTEST [N] GROUP[S]`.
50 /// Retains all bindings within the N smallest distinct-length groups per
51 /// endpoint partition (§22.4 GR13b-ii). `ALL SHORTEST` == `CountedShortestGroup { groups: 1 }`.
52 CountedShortestGroup {
53 /// Number of smallest distinct-length groups to retain per endpoint partition.
54 groups: u32,
55 },
56}
57
58impl fmt::Debug for PathSelector {
59 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60 match self {
61 Self::Any { paths: 1 } => f.write_str("Any"),
62 Self::Any { paths } => f.debug_struct("Any").field("paths", paths).finish(),
63 Self::All => f.write_str("All"),
64 Self::AnyShortest => f.write_str("AnyShortest"),
65 Self::AllShortest => f.write_str("AllShortest"),
66 Self::CountedShortest { paths } => f
67 .debug_struct("CountedShortest")
68 .field("paths", paths)
69 .finish(),
70 Self::CountedShortestGroup { groups } => f
71 .debug_struct("CountedShortestGroup")
72 .field("groups", groups)
73 .finish(),
74 }
75 }
76}
77
78/// Match-mode modifier.
79#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, serde::Deserialize, serde::Serialize)]
80pub enum MatchMode {
81 /// `DIFFERENT EDGES`.
82 DifferentEdges,
83 /// `REPEATABLE ELEMENTS`.
84 RepeatableElements,
85}
86
87/// Label expression.
88#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
89#[non_exhaustive]
90pub enum LabelExpr {
91 /// One label name.
92 Single(DbString),
93 /// Conjunction.
94 Conjunction(Vec2OrMore<LabelExpr>),
95 /// Disjunction.
96 Disjunction(Vec2OrMore<LabelExpr>),
97 /// Negation.
98 Negation(Box<LabelExpr>),
99 /// Wildcard label.
100 Wildcard,
101}
102
103/// Direction requested by an edge pattern.
104#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, serde::Deserialize, serde::Serialize)]
105pub enum EdgeDirection {
106 /// `-[]->`.
107 Right,
108 /// `<-[]-`.
109 Left,
110 /// `-[]-`, matching either direction.
111 Undirected,
112}
113
114/// Edge-pattern quantifier.
115#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, serde::Deserialize, serde::Serialize)]
116pub enum Quantifier {
117 /// ISO graph-pattern quantifier (`*`, `+`, `{n}`, `{m,n}`, etc.).
118 GraphPattern {
119 /// Minimum number of repetitions.
120 min: u32,
121 /// Maximum number of repetitions, or `None` for unbounded.
122 max: Option<u32>,
123 },
124 /// ISO questioned path primary (`?`), which preserves singleton exposure.
125 Questioned,
126}
127
128/// Node pattern.
129#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
130pub struct NodePattern {
131 /// Optional binding.
132 pub binding: Option<DbString>,
133 /// Optional label expression.
134 pub label_expr: Option<LabelExpr>,
135 /// Inline property predicates in source order.
136 pub properties: Vec<(DbString, ValueExpr)>,
137 /// Optional inline `WHERE`.
138 pub inline_where: Option<ValueExpr>,
139 /// Source span.
140 pub span: SourceSpan,
141}
142
143/// Edge pattern.
144#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
145pub struct EdgePattern {
146 /// Optional binding.
147 pub binding: Option<DbString>,
148 /// Direction.
149 pub direction: EdgeDirection,
150 /// Optional label expression.
151 pub label_expr: Option<LabelExpr>,
152 /// Inline property predicates in source order.
153 pub properties: Vec<(DbString, ValueExpr)>,
154 /// Optional variable-length quantifier.
155 pub quantifier: Option<Quantifier>,
156 /// Optional inline `WHERE`.
157 pub inline_where: Option<ValueExpr>,
158 /// Source span.
159 pub span: SourceSpan,
160}
161
162/// One graph-pattern element.
163#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
164#[non_exhaustive]
165pub enum PatternElement {
166 /// Node pattern.
167 Node(NodePattern),
168 /// Edge pattern.
169 Edge(EdgePattern),
170}
171
172/// One graph pattern.
173#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
174pub struct GraphPattern {
175 /// Optional path binding.
176 pub path_binding: Option<DbString>,
177 /// Alternating node/edge/node elements.
178 pub elements: Vec<PatternElement>,
179 /// Source span.
180 pub span: SourceSpan,
181}
182
183/// `MATCH` clause.
184#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
185pub struct MatchClause {
186 /// Whether this is `OPTIONAL MATCH`.
187 pub optional: bool,
188 /// Optional path selector.
189 pub selector: Option<PathSelector>,
190 /// Optional match mode.
191 pub match_mode: Option<MatchMode>,
192 /// Path mode; defaults to [`PathMode::Walk`].
193 pub path_mode: PathMode,
194 /// Whether the path mode was written explicitly in source.
195 pub path_mode_explicit: bool,
196 /// Whether an explicit `PATH` / `PATHS` keyword was written in source
197 /// (ISO/IEC 39075:2024 §16.6 `<path or paths>`, Feature G014).
198 ///
199 /// Pure surface sugar per ISO §1.2.4: it has no semantic effect over the
200 /// no-keyword spelling. The flagger stamps `FeatureId::G014` iff this is
201 /// `true`; the runtime treats it as inert. `false` when absent.
202 pub path_or_paths: bool,
203 /// Graph patterns.
204 pub patterns: Vec<GraphPattern>,
205 /// Optional statement-level `WHERE`.
206 pub where_clause: Option<ValueExpr>,
207 /// Source span.
208 pub span: SourceSpan,
209}