ruprizzle_parser/ast.rs
1//! The loose syntax tree, one step away from the grammar.
2//!
3//! This mirrors what was *written*, not what it *means*: no types are resolved,
4//! no names are mapped, no relation has two sides yet. Lowering turns it into the
5//! strict IR.
6//!
7//! The separation is not ceremony. Relation resolution needs the complete set of
8//! models, which does not exist part-way through a parse, so an IR built directly
9//! in the parse walk would be wrong for any schema that references a model before
10//! declaring it — which is most of them.
11
12use ruprizzle_core::span::Span;
13
14/// A parsed schema file.
15#[derive(Debug, Clone, Default, PartialEq)]
16pub struct Ast {
17 /// Declarations in source order.
18 pub decls: Vec<Decl>,
19}
20
21impl Ast {
22 /// The `datasource` blocks, in source order.
23 pub fn datasources(&self) -> impl Iterator<Item = &Block> {
24 self.decls.iter().filter_map(|d| match d {
25 Decl::Datasource(b) => Some(b),
26 _ => None,
27 })
28 }
29
30 /// The `generator` blocks, in source order.
31 pub fn generators(&self) -> impl Iterator<Item = &Block> {
32 self.decls.iter().filter_map(|d| match d {
33 Decl::Generator(b) => Some(b),
34 _ => None,
35 })
36 }
37
38 /// The `enum` declarations, in source order.
39 pub fn enums(&self) -> impl Iterator<Item = &EnumDecl> {
40 self.decls.iter().filter_map(|d| match d {
41 Decl::Enum(e) => Some(e),
42 _ => None,
43 })
44 }
45
46 /// The `model` declarations, in source order.
47 pub fn models(&self) -> impl Iterator<Item = &ModelDecl> {
48 self.decls.iter().filter_map(|d| match d {
49 Decl::Model(m) => Some(m),
50 _ => None,
51 })
52 }
53}
54
55/// One top-level declaration.
56#[derive(Debug, Clone, PartialEq)]
57pub enum Decl {
58 /// `datasource db { ... }`
59 Datasource(Block),
60 /// `generator client { ... }`
61 Generator(Block),
62 /// `enum Role { ... }`
63 Enum(EnumDecl),
64 /// `model User { ... }`
65 Model(ModelDecl),
66}
67
68/// A `datasource` or `generator` block.
69#[derive(Debug, Clone, PartialEq)]
70pub struct Block {
71 /// Block name as written, e.g. `db`.
72 pub name: String,
73 /// `key = value` entries, in source order.
74 pub entries: Vec<ConfigEntry>,
75 /// Source location of the whole block.
76 pub span: Span,
77}
78
79impl Block {
80 /// The entry with the given key, if present.
81 #[must_use]
82 pub fn get(&self, key: &str) -> Option<&ConfigEntry> {
83 self.entries.iter().find(|e| e.key == key)
84 }
85}
86
87/// One `key = value` line inside a block.
88#[derive(Debug, Clone, PartialEq)]
89pub struct ConfigEntry {
90 /// Left-hand side.
91 pub key: String,
92 /// Right-hand side.
93 pub value: Value,
94 /// Source location of the entry.
95 pub span: Span,
96}
97
98/// An `enum` declaration.
99#[derive(Debug, Clone, PartialEq)]
100pub struct EnumDecl {
101 /// Name as written.
102 pub name: String,
103 /// Source location of the name alone, for diagnostics that point at it.
104 pub name_span: Span,
105 /// Variants in source order.
106 pub variants: Vec<VariantDecl>,
107 /// Joined `///` lines.
108 pub docs: Option<String>,
109 /// Source location of the whole declaration.
110 pub span: Span,
111}
112
113/// One variant of an [`EnumDecl`].
114#[derive(Debug, Clone, PartialEq)]
115pub struct VariantDecl {
116 /// Name as written.
117 pub name: String,
118 /// `@map("...")`, if given.
119 pub map: Option<String>,
120 /// Joined `///` lines.
121 pub docs: Option<String>,
122 /// Source location of the variant.
123 pub span: Span,
124}
125
126/// A `model` declaration.
127#[derive(Debug, Clone, PartialEq)]
128pub struct ModelDecl {
129 /// Name as written.
130 pub name: String,
131 /// Source location of the name alone.
132 pub name_span: Span,
133 /// Fields in source order.
134 pub fields: Vec<FieldDecl>,
135 /// `@@`-attributes in source order.
136 pub block_attrs: Vec<Attr>,
137 /// Joined `///` lines.
138 pub docs: Option<String>,
139 /// Source location of the whole declaration.
140 pub span: Span,
141}
142
143/// A field within a [`ModelDecl`].
144#[derive(Debug, Clone, PartialEq)]
145pub struct FieldDecl {
146 /// Name as written.
147 pub name: String,
148 /// Source location of the name alone.
149 pub name_span: Span,
150 /// Type name as written, before resolution.
151 pub type_name: String,
152 /// Source location of the type.
153 pub type_span: Span,
154 /// Whether the type carried `[]` or `?`.
155 pub arity: Arity,
156 /// `@`-attributes in source order.
157 pub attrs: Vec<Attr>,
158 /// Joined `///` lines.
159 pub docs: Option<String>,
160 /// Source location of the whole field.
161 pub span: Span,
162}
163
164impl FieldDecl {
165 /// The first attribute whose path matches, if any.
166 #[must_use]
167 pub fn attr(&self, path: &str) -> Option<&Attr> {
168 self.attrs.iter().find(|a| a.path == path)
169 }
170
171 /// Whether an attribute with the given path is present.
172 #[must_use]
173 pub fn has_attr(&self, path: &str) -> bool {
174 self.attr(path).is_some()
175 }
176}
177
178/// How many values a field holds.
179#[derive(Debug, Clone, Copy, PartialEq, Eq)]
180pub enum Arity {
181 /// `T` — exactly one.
182 Required,
183 /// `T?` — zero or one.
184 Optional,
185 /// `T[]` — many.
186 List,
187}
188
189/// An `@attr` or `@@attr`, with its arguments.
190#[derive(Debug, Clone, PartialEq)]
191pub struct Attr {
192 /// Dotted path as written, e.g. `id`, `db.VarChar`, `relation`.
193 pub path: String,
194 /// Arguments, in source order.
195 pub args: Vec<Arg>,
196 /// Source location of the attribute.
197 pub span: Span,
198}
199
200impl Attr {
201 /// The named argument with this name, if given.
202 #[must_use]
203 pub fn named(&self, name: &str) -> Option<&Value> {
204 self.args.iter().find_map(|a| match a {
205 Arg::Named { name: n, value, .. } if n == name => Some(value),
206 _ => None,
207 })
208 }
209
210 /// Positional arguments, in source order.
211 pub fn positional(&self) -> impl Iterator<Item = &Value> {
212 self.args.iter().filter_map(|a| match a {
213 Arg::Positional(v) => Some(v),
214 Arg::Named { .. } => None,
215 })
216 }
217
218 /// The first positional argument, if any.
219 #[must_use]
220 pub fn first_positional(&self) -> Option<&Value> {
221 self.positional().next()
222 }
223}
224
225/// One argument of an [`Attr`].
226#[derive(Debug, Clone, PartialEq)]
227pub enum Arg {
228 /// `name: value`
229 Named {
230 /// Argument name.
231 name: String,
232 /// Argument value.
233 value: Value,
234 /// Source location of the whole argument.
235 span: Span,
236 },
237 /// A bare value.
238 Positional(Value),
239}
240
241/// A value written in an attribute argument or a config entry.
242#[derive(Debug, Clone, PartialEq)]
243pub enum Value {
244 /// `"text"`
245 Str(String, Span),
246 /// A number, kept as written so integer and float stay distinguishable.
247 Num(String, Span),
248 /// `true` / `false`
249 Bool(bool, Span),
250 /// A bare identifier, e.g. `USER` or `Cascade`.
251 Ident(String, Span),
252 /// `env("DATABASE_URL")`
253 Env(String, Span),
254 /// `uuid7()`, `dbgenerated("...")`
255 Func {
256 /// Function name.
257 name: String,
258 /// Arguments, in source order.
259 args: Vec<Value>,
260 /// Source location of the call.
261 span: Span,
262 },
263 /// `[a, b]`
264 Array(Vec<Value>, Span),
265}
266
267impl Value {
268 /// Source location of the value.
269 #[must_use]
270 pub fn span(&self) -> Span {
271 match self {
272 Value::Str(_, s)
273 | Value::Num(_, s)
274 | Value::Bool(_, s)
275 | Value::Ident(_, s)
276 | Value::Env(_, s)
277 | Value::Func { span: s, .. }
278 | Value::Array(_, s) => *s,
279 }
280 }
281
282 /// The string, if this is a string literal.
283 #[must_use]
284 pub fn as_str(&self) -> Option<&str> {
285 match self {
286 Value::Str(s, _) => Some(s),
287 _ => None,
288 }
289 }
290
291 /// The identifier, if this is a bare identifier.
292 #[must_use]
293 pub fn as_ident(&self) -> Option<&str> {
294 match self {
295 Value::Ident(s, _) => Some(s),
296 _ => None,
297 }
298 }
299
300 /// The elements, if this is an array.
301 #[must_use]
302 pub fn as_array(&self) -> Option<&[Value]> {
303 match self {
304 Value::Array(v, _) => Some(v),
305 _ => None,
306 }
307 }
308
309 /// How this value reads in a diagnostic.
310 #[must_use]
311 pub fn describe(&self) -> String {
312 match self {
313 Value::Str(s, _) => format!("\"{s}\""),
314 Value::Num(n, _) => n.clone(),
315 Value::Bool(b, _) => b.to_string(),
316 Value::Ident(i, _) => i.clone(),
317 Value::Env(v, _) => format!("env(\"{v}\")"),
318 Value::Func { name, .. } => format!("{name}(…)"),
319 Value::Array(..) => "[…]".to_owned(),
320 }
321 }
322}