Skip to main content

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}