Skip to main content

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}