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//!             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}