fraiseql_core/compiler/ir.rs
1//! Intermediate Representation (IR) for schema compilation.
2//!
3//! The IR is the internal representation of a GraphQL schema during compilation.
4//! It's created from authoring-time JSON and transformed into runtime-optimized
5//! `CompiledSchema`.
6//!
7//! # IR Structure
8//!
9//! ```text
10//! AuthoringIR
11//! ├─ types: Vec<IRType>
12//! ├─ queries: Vec<IRQuery>
13//! ├─ mutations: Vec<IRMutation>
14//! └─ subscriptions: Vec<IRSubscription>
15//! ```
16//!
17//! # Example
18//!
19//! ```rust
20//! use fraiseql_core::compiler::ir::{AuthoringIR, IRType, IRField};
21//!
22//! let mut ir = AuthoringIR::new();
23//! ir.types.push(IRType {
24//! name: "User".to_string(),
25//! fields: vec![
26//! IRField {
27//! name: "id".to_string(),
28//! field_type: "Int!".to_string(),
29//! nullable: false,
30//! description: None,
31//! sql_column: None,
32//! }
33//! ],
34//! sql_source: Some("v_user".to_string()),
35//! description: None,
36//! });
37//! assert_eq!(ir.types.len(), 1);
38//! ```
39
40use std::collections::HashMap;
41
42use serde::{Deserialize, Serialize};
43
44use crate::{
45 compiler::fact_table::FactTableMetadata, schema::GraphQLValue, validation::ValidationRule,
46};
47
48/// Authoring Intermediate Representation.
49///
50/// This is the parsed representation of a GraphQL schema before
51/// SQL template generation and optimization.
52#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
53pub struct AuthoringIR {
54 /// Type definitions.
55 pub types: Vec<IRType>,
56
57 /// Enum definitions.
58 #[serde(default)]
59 pub enums: Vec<IREnum>,
60
61 /// Interface definitions.
62 #[serde(default)]
63 pub interfaces: Vec<IRInterface>,
64
65 /// Union definitions.
66 #[serde(default)]
67 pub unions: Vec<IRUnion>,
68
69 /// Input type definitions.
70 #[serde(default)]
71 pub input_types: Vec<IRInputType>,
72
73 /// Custom scalar type definitions.
74 #[serde(default)]
75 pub scalars: Vec<IRScalar>,
76
77 /// Query definitions.
78 pub queries: Vec<IRQuery>,
79
80 /// Mutation definitions.
81 pub mutations: Vec<IRMutation>,
82
83 /// Subscription definitions.
84 pub subscriptions: Vec<IRSubscription>,
85
86 /// Fact table metadata (from authoring-language decorators).
87 /// Key: table name (e.g., "`tf_sales`")
88 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
89 pub fact_tables: HashMap<String, FactTableMetadata>,
90}
91
92impl AuthoringIR {
93 /// Create empty IR.
94 #[must_use]
95 pub fn new() -> Self {
96 Self {
97 types: Vec::new(),
98 enums: Vec::new(),
99 interfaces: Vec::new(),
100 unions: Vec::new(),
101 input_types: Vec::new(),
102 scalars: Vec::new(),
103 queries: Vec::new(),
104 mutations: Vec::new(),
105 subscriptions: Vec::new(),
106 fact_tables: HashMap::new(),
107 }
108 }
109}
110
111impl Default for AuthoringIR {
112 fn default() -> Self {
113 Self::new()
114 }
115}
116
117/// IR Type definition.
118#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
119pub struct IRType {
120 /// Type name (e.g., "User").
121 pub name: String,
122
123 /// Field definitions.
124 pub fields: Vec<IRField>,
125
126 /// SQL source (table/view name).
127 pub sql_source: Option<String>,
128
129 /// Type description.
130 pub description: Option<String>,
131}
132
133/// IR Field definition.
134#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
135pub struct IRField {
136 /// Field name.
137 pub name: String,
138
139 /// Field type (e.g., `"String!"`, `"Int"`, `"[User]"`).
140 pub field_type: String,
141
142 /// Is field nullable?
143 pub nullable: bool,
144
145 /// Field description.
146 pub description: Option<String>,
147
148 /// SQL column mapping.
149 pub sql_column: Option<String>,
150}
151
152/// IR Query definition.
153#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
154pub struct IRQuery {
155 /// Query name (e.g., "users", "user").
156 pub name: String,
157
158 /// Return type name.
159 pub return_type: String,
160
161 /// Does this return a list?
162 pub returns_list: bool,
163
164 /// Is return value nullable?
165 pub nullable: bool,
166
167 /// Query arguments.
168 pub arguments: Vec<IRArgument>,
169
170 /// SQL source (table/view).
171 pub sql_source: Option<String>,
172
173 /// Query description.
174 pub description: Option<String>,
175
176 /// Auto-wired parameters (where, orderBy, limit, offset).
177 pub auto_params: AutoParams,
178}
179
180/// IR Mutation definition.
181#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
182pub struct IRMutation {
183 /// Mutation name (e.g., "createUser", "updatePost").
184 pub name: String,
185
186 /// Return type name.
187 pub return_type: String,
188
189 /// Is return value nullable?
190 pub nullable: bool,
191
192 /// Mutation arguments.
193 pub arguments: Vec<IRArgument>,
194
195 /// Mutation description.
196 pub description: Option<String>,
197
198 /// SQL operation type.
199 pub operation: MutationOperation,
200}
201
202/// IR Subscription definition.
203#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
204pub struct IRSubscription {
205 /// Subscription name.
206 pub name: String,
207
208 /// Return type name.
209 pub return_type: String,
210
211 /// Subscription arguments.
212 pub arguments: Vec<IRArgument>,
213
214 /// Subscription description.
215 pub description: Option<String>,
216}
217
218/// IR Argument definition.
219#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
220pub struct IRArgument {
221 /// Argument name.
222 pub name: String,
223
224 /// Argument type.
225 pub arg_type: String,
226
227 /// Is argument nullable?
228 pub nullable: bool,
229
230 /// Default value.
231 pub default_value: Option<GraphQLValue>,
232
233 /// Argument description.
234 pub description: Option<String>,
235}
236
237/// Auto-wired parameters configuration.
238#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
239pub struct AutoParams {
240 /// Enable WHERE parameter?
241 #[serde(default)]
242 pub has_where: bool,
243
244 /// Enable orderBy parameter?
245 #[serde(default)]
246 pub has_order_by: bool,
247
248 /// Enable limit parameter?
249 #[serde(default)]
250 pub has_limit: bool,
251
252 /// Enable offset parameter?
253 #[serde(default)]
254 pub has_offset: bool,
255}
256
257/// Mutation operation type.
258#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
259#[non_exhaustive]
260pub enum MutationOperation {
261 /// INSERT operation.
262 Create,
263
264 /// UPDATE operation.
265 Update,
266
267 /// DELETE operation.
268 Delete,
269
270 /// Custom SQL operation.
271 Custom,
272}
273
274/// IR Enum definition.
275#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
276pub struct IREnum {
277 /// Enum name (e.g., "Status").
278 pub name: String,
279
280 /// Enum values.
281 pub values: Vec<IREnumValue>,
282
283 /// Enum description.
284 pub description: Option<String>,
285}
286
287/// IR Enum value definition.
288#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
289pub struct IREnumValue {
290 /// Value name (e.g., "ACTIVE").
291 pub name: String,
292
293 /// Value description.
294 pub description: Option<String>,
295
296 /// Deprecation reason (if deprecated).
297 pub deprecation_reason: Option<String>,
298}
299
300/// IR Interface definition.
301#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
302pub struct IRInterface {
303 /// Interface name (e.g., "Node").
304 pub name: String,
305
306 /// Interface fields.
307 pub fields: Vec<IRField>,
308
309 /// Interface description.
310 pub description: Option<String>,
311}
312
313/// IR Union definition.
314#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
315pub struct IRUnion {
316 /// Union name (e.g., "`SearchResult`").
317 pub name: String,
318
319 /// Types that are part of this union.
320 pub types: Vec<String>,
321
322 /// Union description.
323 pub description: Option<String>,
324}
325
326/// IR Input type definition.
327#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
328pub struct IRInputType {
329 /// Input type name (e.g., "`CreateUserInput`").
330 pub name: String,
331
332 /// Input fields.
333 pub fields: Vec<IRInputField>,
334
335 /// Input type description.
336 pub description: Option<String>,
337}
338
339/// IR Input field definition.
340#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
341pub struct IRInputField {
342 /// Field name.
343 pub name: String,
344
345 /// Field type (e.g., "String!", "Int").
346 pub field_type: String,
347
348 /// Is field nullable?
349 pub nullable: bool,
350
351 /// Default value.
352 pub default_value: Option<GraphQLValue>,
353
354 /// Field description.
355 pub description: Option<String>,
356}
357
358/// IR Scalar type definition.
359///
360/// Represents a custom scalar type with optional validation rules.
361/// Custom scalars allow developers to define domain-specific scalar types
362/// with validation rules beyond the builtin GraphQL scalars.
363#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
364pub struct IRScalar {
365 /// Scalar name (e.g., "Email", "ISBN", "IBAN").
366 pub name: String,
367
368 /// Scalar description.
369 pub description: Option<String>,
370
371 /// URL specification (RFC or standard that defines this scalar type).
372 /// Per GraphQL spec §3.5.1 (`specified_by_url`).
373 pub specified_by_url: Option<String>,
374
375 /// Validation rules for this scalar.
376 #[serde(default)]
377 pub validation_rules: Vec<ValidationRule>,
378
379 /// Base type for type aliases (e.g., "String" for Email alias).
380 pub base_type: Option<String>,
381}
382
383impl IRScalar {
384 /// Create a new scalar definition with minimal required fields.
385 #[must_use]
386 pub const fn new(name: String) -> Self {
387 Self {
388 name,
389 description: None,
390 specified_by_url: None,
391 validation_rules: Vec::new(),
392 base_type: None,
393 }
394 }
395}