1use std::collections::HashMap;
43
44use serde::{Deserialize, Serialize};
45
46use crate::validation::ValidationRule;
47
48#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
53pub struct AuthoringIR {
54 pub types: Vec<IRType>,
56
57 #[serde(default)]
59 pub enums: Vec<IREnum>,
60
61 #[serde(default)]
63 pub interfaces: Vec<IRInterface>,
64
65 #[serde(default)]
67 pub unions: Vec<IRUnion>,
68
69 #[serde(default)]
71 pub input_types: Vec<IRInputType>,
72
73 #[serde(default)]
75 pub scalars: Vec<IRScalar>,
76
77 pub queries: Vec<IRQuery>,
79
80 pub mutations: Vec<IRMutation>,
82
83 pub subscriptions: Vec<IRSubscription>,
85
86 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
90 pub fact_tables: HashMap<String, serde_json::Value>,
91}
92
93impl AuthoringIR {
94 #[must_use]
96 pub fn new() -> Self {
97 Self {
98 types: Vec::new(),
99 enums: Vec::new(),
100 interfaces: Vec::new(),
101 unions: Vec::new(),
102 input_types: Vec::new(),
103 scalars: Vec::new(),
104 queries: Vec::new(),
105 mutations: Vec::new(),
106 subscriptions: Vec::new(),
107 fact_tables: HashMap::new(),
108 }
109 }
110}
111
112impl Default for AuthoringIR {
113 fn default() -> Self {
114 Self::new()
115 }
116}
117
118#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
120pub struct IRType {
121 pub name: String,
123
124 pub fields: Vec<IRField>,
126
127 pub sql_source: Option<String>,
129
130 pub description: Option<String>,
132}
133
134#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
136pub struct IRField {
137 pub name: String,
139
140 pub field_type: String,
142
143 pub nullable: bool,
145
146 pub description: Option<String>,
148
149 pub sql_column: Option<String>,
151}
152
153#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
155pub struct IRQuery {
156 pub name: String,
158
159 pub return_type: String,
161
162 pub returns_list: bool,
164
165 pub nullable: bool,
167
168 pub arguments: Vec<IRArgument>,
170
171 pub sql_source: Option<String>,
173
174 pub description: Option<String>,
176
177 pub auto_params: AutoParams,
179}
180
181#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
183pub struct IRMutation {
184 pub name: String,
186
187 pub return_type: String,
189
190 pub nullable: bool,
192
193 pub arguments: Vec<IRArgument>,
195
196 pub description: Option<String>,
198
199 pub operation: MutationOperation,
201}
202
203#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
205pub struct IRSubscription {
206 pub name: String,
208
209 pub return_type: String,
211
212 pub arguments: Vec<IRArgument>,
214
215 pub description: Option<String>,
217}
218
219#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
221pub struct IRArgument {
222 pub name: String,
224
225 pub arg_type: String,
227
228 pub nullable: bool,
230
231 pub default_value: Option<serde_json::Value>,
233
234 pub description: Option<String>,
236}
237
238#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
240pub struct AutoParams {
241 #[serde(default)]
243 pub has_where: bool,
244
245 #[serde(default)]
247 pub has_order_by: bool,
248
249 #[serde(default)]
251 pub has_limit: bool,
252
253 #[serde(default)]
255 pub has_offset: bool,
256}
257
258#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
260pub enum MutationOperation {
261 Create,
263
264 Update,
266
267 Delete,
269
270 Custom,
272}
273
274#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
276pub struct IREnum {
277 pub name: String,
279
280 pub values: Vec<IREnumValue>,
282
283 pub description: Option<String>,
285}
286
287#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
289pub struct IREnumValue {
290 pub name: String,
292
293 pub description: Option<String>,
295
296 pub deprecation_reason: Option<String>,
298}
299
300#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
302pub struct IRInterface {
303 pub name: String,
305
306 pub fields: Vec<IRField>,
308
309 pub description: Option<String>,
311}
312
313#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
315pub struct IRUnion {
316 pub name: String,
318
319 pub types: Vec<String>,
321
322 pub description: Option<String>,
324}
325
326#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
328pub struct IRInputType {
329 pub name: String,
331
332 pub fields: Vec<IRInputField>,
334
335 pub description: Option<String>,
337}
338
339#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
341pub struct IRInputField {
342 pub name: String,
344
345 pub field_type: String,
347
348 pub nullable: bool,
350
351 pub default_value: Option<serde_json::Value>,
353
354 pub description: Option<String>,
356}
357
358#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
364pub struct IRScalar {
365 pub name: String,
367
368 pub description: Option<String>,
370
371 pub specified_by_url: Option<String>,
374
375 #[serde(default)]
377 pub validation_rules: Vec<ValidationRule>,
378
379 pub base_type: Option<String>,
381}
382
383impl IRScalar {
384 #[must_use]
386 pub 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}
396
397#[cfg(test)]
398mod tests {
399 use super::*;
400
401 #[test]
402 fn test_authoring_ir_new() {
403 let ir = AuthoringIR::new();
404 assert!(ir.types.is_empty());
405 assert!(ir.queries.is_empty());
406 assert!(ir.mutations.is_empty());
407 assert!(ir.subscriptions.is_empty());
408 }
409
410 #[test]
411 fn test_authoring_ir_with_scalars() {
412 let mut ir = AuthoringIR::new();
413
414 ir.scalars.push(IRScalar::new("Email".to_string()));
416 ir.scalars.push(IRScalar::new("ISBN".to_string()));
417
418 assert_eq!(ir.scalars.len(), 2);
419 assert_eq!(ir.scalars[0].name, "Email");
420 assert_eq!(ir.scalars[1].name, "ISBN");
421 }
422
423 #[test]
424 fn test_ir_type() {
425 let ir_type = IRType {
426 name: "User".to_string(),
427 fields: vec![IRField {
428 name: "id".to_string(),
429 field_type: "Int!".to_string(),
430 nullable: false,
431 description: None,
432 sql_column: Some("id".to_string()),
433 }],
434 sql_source: Some("v_user".to_string()),
435 description: Some("User type".to_string()),
436 };
437
438 assert_eq!(ir_type.name, "User");
439 assert_eq!(ir_type.fields.len(), 1);
440 assert_eq!(ir_type.sql_source, Some("v_user".to_string()));
441 }
442
443 #[test]
444 fn test_ir_query() {
445 let query = IRQuery {
446 name: "users".to_string(),
447 return_type: "User".to_string(),
448 returns_list: true,
449 nullable: false,
450 arguments: vec![],
451 sql_source: Some("v_user".to_string()),
452 description: None,
453 auto_params: AutoParams {
454 has_where: true,
455 has_limit: true,
456 ..Default::default()
457 },
458 };
459
460 assert_eq!(query.name, "users");
461 assert!(query.returns_list);
462 assert!(query.auto_params.has_where);
463 assert!(query.auto_params.has_limit);
464 }
465
466 #[test]
467 fn test_ir_mutation() {
468 let mutation = IRMutation {
469 name: "createUser".to_string(),
470 return_type: "User".to_string(),
471 nullable: false,
472 arguments: vec![IRArgument {
473 name: "input".to_string(),
474 arg_type: "CreateUserInput!".to_string(),
475 nullable: false,
476 default_value: None,
477 description: None,
478 }],
479 description: None,
480 operation: MutationOperation::Create,
481 };
482
483 assert_eq!(mutation.name, "createUser");
484 assert_eq!(mutation.operation, MutationOperation::Create);
485 assert_eq!(mutation.arguments.len(), 1);
486 }
487
488 #[test]
489 fn test_auto_params_default() {
490 let params = AutoParams::default();
491 assert!(!params.has_where);
492 assert!(!params.has_order_by);
493 assert!(!params.has_limit);
494 assert!(!params.has_offset);
495 }
496
497 #[test]
498 fn test_mutation_operations() {
499 assert_eq!(MutationOperation::Create, MutationOperation::Create);
500 assert_ne!(MutationOperation::Create, MutationOperation::Update);
501 }
502
503 #[test]
504 fn test_ir_scalar_new() {
505 let scalar = IRScalar::new("Email".to_string());
506
507 assert_eq!(scalar.name, "Email");
508 assert_eq!(scalar.description, None);
509 assert_eq!(scalar.specified_by_url, None);
510 assert_eq!(scalar.validation_rules.len(), 0);
511 assert_eq!(scalar.base_type, None);
512 }
513
514 #[test]
515 fn test_ir_scalar_with_all_fields() {
516 let scalar = IRScalar {
517 name: "Email".to_string(),
518 description: Some("Valid email address".to_string()),
519 specified_by_url: Some("https://html.spec.whatwg.org/".to_string()),
520 validation_rules: vec![ValidationRule::Pattern {
521 pattern: r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$".to_string(),
522 message: Some("Invalid email format".to_string()),
523 }],
524 base_type: Some("String".to_string()),
525 };
526
527 assert_eq!(scalar.name, "Email");
528 assert_eq!(scalar.description, Some("Valid email address".to_string()));
529 assert_eq!(scalar.specified_by_url, Some("https://html.spec.whatwg.org/".to_string()));
530 assert_eq!(scalar.validation_rules.len(), 1);
531 assert_eq!(scalar.base_type, Some("String".to_string()));
532 }
533
534 #[test]
535 fn test_ir_scalar_serialization() {
536 let scalar = IRScalar {
537 name: "ISBN".to_string(),
538 description: Some("International Standard Book Number".to_string()),
539 specified_by_url: Some("https://www.isbn-international.org/".to_string()),
540 validation_rules: vec![],
541 base_type: None,
542 };
543
544 let json = serde_json::to_value(&scalar).expect("Should serialize");
546
547 assert_eq!(json["name"], "ISBN");
549 assert_eq!(json["description"], "International Standard Book Number");
550 assert_eq!(json["specified_by_url"], "https://www.isbn-international.org/");
551 assert_eq!(json["validation_rules"], serde_json::json!([]));
552 }
553
554 #[test]
555 fn test_ir_scalar_deserialization() {
556 let json = serde_json::json!({
557 "name": "PhoneNumber",
558 "description": "Valid phone number",
559 "specified_by_url": null,
560 "validation_rules": [],
561 "base_type": "String"
562 });
563
564 let scalar: IRScalar = serde_json::from_value(json).expect("Should deserialize");
565
566 assert_eq!(scalar.name, "PhoneNumber");
567 assert_eq!(scalar.description, Some("Valid phone number".to_string()));
568 assert_eq!(scalar.specified_by_url, None);
569 assert_eq!(scalar.validation_rules.len(), 0);
570 assert_eq!(scalar.base_type, Some("String".to_string()));
571 }
572
573 #[test]
574 fn test_ir_scalar_equality() {
575 let scalar1 = IRScalar {
576 name: "UUID".to_string(),
577 description: Some("Universal Unique Identifier".to_string()),
578 specified_by_url: None,
579 validation_rules: vec![],
580 base_type: None,
581 };
582
583 let scalar2 = IRScalar {
584 name: "UUID".to_string(),
585 description: Some("Universal Unique Identifier".to_string()),
586 specified_by_url: None,
587 validation_rules: vec![],
588 base_type: None,
589 };
590
591 assert_eq!(scalar1, scalar2);
592 }
593}