Skip to main content

xsd_schema/xpath/ast/
paths.rs

1// ============================================================================
2// Path Expressions
3// ============================================================================
4
5use super::{AstNodeId, SourceSpan};
6
7/// XPath axis specifier.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum Axis {
10    /// `child::` (default for element names)
11    Child,
12    /// `descendant::`
13    Descendant,
14    /// `attribute::` (abbreviated `@`)
15    Attribute,
16    /// `self::`
17    SelfAxis,
18    /// `descendant-or-self::`
19    DescendantOrSelf,
20    /// `following-sibling::`
21    FollowingSibling,
22    /// `following::`
23    Following,
24    /// `parent::` (abbreviated `..`)
25    Parent,
26    /// `ancestor::`
27    Ancestor,
28    /// `preceding-sibling::`
29    PrecedingSibling,
30    /// `preceding::`
31    Preceding,
32    /// `ancestor-or-self::`
33    AncestorOrSelf,
34    /// `namespace::`
35    Namespace,
36}
37
38impl Axis {
39    /// Check if this is a reverse axis (traverses in reverse document order).
40    pub fn is_reverse(&self) -> bool {
41        matches!(
42            self,
43            Axis::Parent
44                | Axis::Ancestor
45                | Axis::PrecedingSibling
46                | Axis::Preceding
47                | Axis::AncestorOrSelf
48        )
49    }
50
51    /// Check if this is a forward axis.
52    pub fn is_forward(&self) -> bool {
53        !self.is_reverse()
54    }
55}
56
57/// Node test in a path step.
58#[derive(Debug, Clone)]
59pub enum NodeTest {
60    /// Name test (QName with optional wildcards).
61    Name(NameTest),
62    /// Kind test (`node()`, `element()`, etc.).
63    Kind(KindTest),
64}
65
66/// Name test with optional wildcards.
67#[derive(Debug, Clone)]
68pub struct NameTest {
69    /// Namespace prefix (None = wildcard `*:local`, Some("") = no prefix).
70    pub prefix: Option<String>,
71    /// Local name (None = wildcard `prefix:*` or `*`).
72    pub local_name: Option<String>,
73}
74
75impl NameTest {
76    /// Match any node: `*`.
77    pub fn any() -> Self {
78        Self {
79            prefix: None,
80            local_name: None,
81        }
82    }
83
84    /// Match any local name in a namespace: `prefix:*`.
85    pub fn any_in_ns(prefix: String) -> Self {
86        Self {
87            prefix: Some(prefix),
88            local_name: None,
89        }
90    }
91
92    /// Match any namespace with a specific local name: `*:local`.
93    pub fn any_ns(local_name: String) -> Self {
94        Self {
95            prefix: None,
96            local_name: Some(local_name),
97        }
98    }
99
100    /// Match a specific QName.
101    pub fn qname(prefix: String, local_name: String) -> Self {
102        Self {
103            prefix: Some(prefix),
104            local_name: Some(local_name),
105        }
106    }
107}
108
109/// Kind test (`node()`, `text()`, `element()`, etc.).
110#[derive(Debug, Clone)]
111pub enum KindTest {
112    /// `node()` - matches any node.
113    AnyKind,
114    /// `text()` - matches text nodes.
115    Text,
116    /// `comment()` - matches comment nodes.
117    Comment,
118    /// `processing-instruction()` or `processing-instruction('name')`.
119    ProcessingInstruction(Option<String>),
120    /// `document-node()` or `document-node(element(...))`.
121    Document(Option<Box<KindTest>>),
122    /// `element()` or `element(name)` or `element(name, type)`.
123    Element(ElementTest),
124    /// `attribute()` or `attribute(name)` or `attribute(name, type)`.
125    Attribute(AttributeTest),
126    /// `schema-element(name)`.
127    SchemaElement(String),
128    /// `schema-attribute(name)`.
129    SchemaAttribute(String),
130}
131
132/// Element test: `element()`, `element(name)`, or `element(name, type)`.
133#[derive(Debug, Clone, Default)]
134pub struct ElementTest {
135    /// Element name (None = wildcard).
136    pub name: Option<QName>,
137    /// Type annotation (None = any type).
138    pub type_name: Option<QName>,
139    /// Whether the type allows nilled elements.
140    pub nillable: bool,
141}
142
143/// Attribute test: `attribute()`, `attribute(name)`, or `attribute(name, type)`.
144#[derive(Debug, Clone, Default)]
145pub struct AttributeTest {
146    /// Attribute name (None = wildcard).
147    pub name: Option<QName>,
148    /// Type annotation (None = any type).
149    pub type_name: Option<QName>,
150}
151
152/// Qualified name (prefix:local or just local).
153#[derive(Debug, Clone)]
154pub struct QName {
155    /// Namespace prefix (empty string if none).
156    pub prefix: String,
157    /// Local part.
158    pub local: String,
159}
160
161impl QName {
162    pub fn new(prefix: String, local: String) -> Self {
163        Self { prefix, local }
164    }
165
166    pub fn local_only(local: String) -> Self {
167        Self {
168            prefix: String::new(),
169            local,
170        }
171    }
172}
173
174/// Single step in a path expression.
175#[derive(Debug, Clone)]
176pub struct PathStepNode {
177    /// Axis specifier.
178    pub axis: Axis,
179    /// Node test (AST form with raw strings).
180    pub test: NodeTest,
181    /// Predicates (expression IDs).
182    pub predicates: Vec<AstNodeId>,
183    /// Source location.
184    pub span: SourceSpan,
185    /// Resolved name test (populated during binding).
186    /// Uses interned NameIds and resolved namespace URIs.
187    pub resolved_test: Option<crate::types::NameTest>,
188}
189
190impl PathStepNode {
191    pub fn new(axis: Axis, test: NodeTest, span: SourceSpan) -> Self {
192        Self {
193            axis,
194            test,
195            predicates: Vec::new(),
196            span,
197            resolved_test: None,
198        }
199    }
200
201    pub fn with_predicates(
202        axis: Axis,
203        test: NodeTest,
204        predicates: Vec<AstNodeId>,
205        span: SourceSpan,
206    ) -> Self {
207        Self {
208            axis,
209            test,
210            predicates,
211            span,
212            resolved_test: None,
213        }
214    }
215
216    /// Abbreviated parent step (`..`).
217    pub fn abbrev_parent(span: SourceSpan) -> Self {
218        Self {
219            axis: Axis::Parent,
220            test: NodeTest::Kind(KindTest::AnyKind),
221            predicates: Vec::new(),
222            span,
223            resolved_test: None,
224        }
225    }
226}
227
228/// Path expression (sequence of steps).
229#[derive(Debug, Clone)]
230pub struct PathExprNode {
231    /// Whether the path starts from root (`/`).
232    pub is_absolute: bool,
233    /// Steps in the path (IDs of PathStep nodes or filter expressions).
234    pub steps: Vec<AstNodeId>,
235    /// Source location.
236    pub span: SourceSpan,
237    /// Hint that result order doesn't matter (optimization).
238    pub unordered_hint: bool,
239}
240
241impl PathExprNode {
242    /// Root-only path (`/`).
243    pub fn root_only(span: SourceSpan) -> Self {
244        Self {
245            is_absolute: true,
246            steps: Vec::new(),
247            span,
248            unordered_hint: false,
249        }
250    }
251
252    /// Absolute path (`/a/b`).
253    pub fn absolute(steps: Vec<AstNodeId>, span: SourceSpan) -> Self {
254        Self {
255            is_absolute: true,
256            steps,
257            span,
258            unordered_hint: false,
259        }
260    }
261
262    /// Relative path (`a/b`).
263    pub fn relative(steps: Vec<AstNodeId>, span: SourceSpan) -> Self {
264        Self {
265            is_absolute: false,
266            steps,
267            span,
268            unordered_hint: false,
269        }
270    }
271}
272
273/// Filter expression (`primary[predicate][predicate]...`).
274#[derive(Debug, Clone)]
275pub struct FilterExprNode {
276    /// Base/primary expression.
277    pub base: AstNodeId,
278    /// Predicate expressions.
279    pub predicates: Vec<AstNodeId>,
280    /// Source location.
281    pub span: SourceSpan,
282}
283
284impl FilterExprNode {
285    pub fn new(base: AstNodeId, predicates: Vec<AstNodeId>, span: SourceSpan) -> Self {
286        Self {
287            base,
288            predicates,
289            span,
290        }
291    }
292}