1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
use super::{Expr, Projection};
use crate::schema::app::{FieldId, ModelId, VariantId};
/// The root of a path traversal.
///
/// A path can originate from a top-level model or from a specific variant of
/// an embedded enum field.
#[derive(Debug, Clone, PartialEq)]
pub enum PathRoot {
/// The path originates from a top-level model.
Model(ModelId),
/// The path originates from a specific variant of an embedded enum.
///
/// `parent` navigates to the enum field; subsequent projection steps index
/// into that variant's fields using 0-based local indices.
Variant {
/// Path that navigates to the enum field containing this variant.
parent: Box<Path>,
/// Identifies which variant of the enum this path targets.
variant_id: VariantId,
},
}
impl PathRoot {
/// Returns `true` if this root is an embedded enum variant.
pub fn is_variant(&self) -> bool {
matches!(self, Self::Variant { .. })
}
/// Returns the `ModelId`, panicking if this root is a `Variant` root.
pub fn as_model_unwrap(&self) -> ModelId {
match self {
PathRoot::Model(id) => *id,
PathRoot::Variant { .. } => panic!("expected Model root, got Variant root"),
}
}
/// Returns the `ModelId` if this is a `Model` root, or `None` for a
/// `Variant` root.
pub fn as_model(&self) -> Option<ModelId> {
match self {
PathRoot::Model(id) => Some(*id),
PathRoot::Variant { .. } => None,
}
}
}
/// A rooted field traversal path through the application schema.
///
/// A `Path` starts at a [`PathRoot`] (a model or an enum variant) and
/// navigates through fields via a [`Projection`]. It is used by the query
/// engine to identify which field or nested field is being referenced.
///
/// # Examples
///
/// ```ignore
/// use toasty_core::stmt::Path;
/// use toasty_core::schema::app::ModelId;
///
/// // Path pointing to the root of model 0
/// let p = Path::model(ModelId::from_index(0));
/// assert!(p.projection.is_empty()); // no field steps
/// ```
#[derive(Debug, Clone, PartialEq)]
pub struct Path {
/// Where the path originates from.
pub root: PathRoot,
/// Traversal through the fields.
pub projection: Projection,
}
impl Path {
/// Creates a path rooted at a model with an identity projection (no field steps).
pub fn model(root: impl Into<ModelId>) -> Self {
Self {
root: PathRoot::Model(root.into()),
projection: Projection::identity(),
}
}
/// Creates a path rooted at a model that navigates to a single field by index.
pub fn field(root: impl Into<ModelId>, field: usize) -> Self {
Self {
root: PathRoot::Model(root.into()),
projection: Projection::single(field),
}
}
/// Creates a path rooted at a model with a single field step (const-compatible).
pub const fn from_index(root: ModelId, index: usize) -> Self {
Self {
root: PathRoot::Model(root),
projection: Projection::from_index(index),
}
}
/// Creates a path rooted at a specific enum variant.
///
/// `parent` is the path that navigates to the enum field. Subsequent
/// projection steps (appended via [`chain`][Path::chain]) index into the
/// variant's fields using 0-based local indices. [`into_stmt`] renders
/// the root as a variant selection ([`Expr::variant`]) over the parent's
/// expression.
///
/// [`into_stmt`]: Path::into_stmt
pub fn from_variant(parent: Path, variant_id: VariantId) -> Self {
Self {
root: PathRoot::Variant {
parent: Box::new(parent),
variant_id,
},
projection: Projection::identity(),
}
}
/// Appends `other`'s traversal onto this path.
///
/// `other`'s root model is dropped: its steps continue from wherever
/// `self` ends. A variant selection in `other` is preserved — the result
/// selects the same variant at the same point of the traversal — so a
/// variant-rooted path can be chained onto a path reaching its enum from
/// another model.
pub fn chain(&mut self, other: &Self) {
if let PathRoot::Variant { parent, variant_id } = &other.root {
self.chain(parent);
*self = Self::from_variant(self.clone(), *variant_id);
}
for field in &other.projection[..] {
self.projection.push(*field);
}
}
/// Converts this path into an [`Expr`] that references the path's field.
///
/// A variant root becomes an [`ExprVariant`](super::ExprVariant) over the
/// parent path's expression, projected by the variant-local steps. The
/// expression carries no variant check; the engine's statement
/// normalization adds one per selection to the predicate built over it.
pub fn into_stmt(self) -> Expr {
self.into_stmt_with_nesting(0)
}
/// Converts this path into an [`Expr`] at the specified query nesting level.
///
/// A nesting level of `0` references the current query; `1` references its
/// parent. Projections and variant selections preserve this nesting level.
pub fn into_stmt_with_nesting(self, nesting: usize) -> Expr {
match self.root {
PathRoot::Model(model_id) => match self.projection.as_slice() {
[] => Expr::ref_ancestor_model(nesting),
[field, project @ ..] => {
let mut ret = Expr::ref_field(
nesting,
FieldId {
model: model_id,
index: *field,
},
);
if !project.is_empty() {
ret = Expr::project(ret, project);
}
ret
}
},
PathRoot::Variant { parent, variant_id } => {
// The selection stands for the variant's payload, so the
// steps index the variant's fields by their local positions.
let selection = Expr::variant(parent.into_stmt_with_nesting(nesting), variant_id);
match self.projection.as_slice() {
[] => selection,
steps => Expr::project(selection, steps),
}
}
}
}
}