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