1use crate::symbol::{STDLIB_TYPES, STDLIB_TYPE_PREFIXES};
7use crate::{
8 Enum, Field, FieldStyle, Fields, Function, Primitive, ResolvedTypeReference, Schema,
9 SemanticEnum, SemanticField, SemanticFunction, SemanticOutputType, SemanticPrimitive,
10 SemanticSchema, SemanticStruct, SemanticType, SemanticTypeParameter, SemanticVariant, Struct,
11 SymbolId, SymbolInfo, SymbolKind, SymbolTable, Type, TypeReference, Variant,
12};
13use std::collections::{BTreeMap, BTreeSet, HashMap};
14
15pub trait NormalizationStage {
17 fn name(&self) -> &'static str;
18 fn transform(&self, schema: &mut Schema) -> Result<(), Vec<NormalizationError>>;
19}
20
21#[derive(Default)]
23pub struct NormalizationPipeline {
24 stages: Vec<Box<dyn NormalizationStage>>,
25}
26
27impl NormalizationPipeline {
28 pub fn new() -> Self {
29 Self { stages: Vec::new() }
30 }
31
32 pub fn add_stage<S: NormalizationStage + 'static>(mut self, stage: S) -> Self {
33 self.stages.push(Box::new(stage));
34 self
35 }
36
37 pub fn run(&self, schema: &mut Schema) -> Result<(), Vec<NormalizationError>> {
38 for stage in &self.stages {
39 stage.transform(schema)?;
40 }
41 Ok(())
42 }
43
44 pub fn standard() -> Self {
48 PipelineBuilder::new().build()
49 }
50
51 pub fn for_codegen() -> Self {
60 PipelineBuilder::new()
61 .consolidation(Consolidation::Skip)
62 .naming(Naming::Skip)
63 .build()
64 }
65}
66
67#[derive(Debug, Clone, Default)]
73pub enum Consolidation {
74 #[default]
76 Standard,
77 Skip,
79}
80
81#[derive(Default)]
83pub enum Naming {
84 #[default]
86 Standard,
87 Skip,
89 Custom(Box<dyn NormalizationStage>),
91}
92
93impl std::fmt::Debug for Naming {
94 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95 match self {
96 Naming::Standard => write!(f, "Naming::Standard"),
97 Naming::Skip => write!(f, "Naming::Skip"),
98 Naming::Custom(_) => write!(f, "Naming::Custom(...)"),
99 }
100 }
101}
102
103pub struct PipelineBuilder {
127 consolidation: Consolidation,
128 naming: Naming,
129 circular_dependency_strategy: ResolutionStrategy,
130 extra_stages: Vec<Box<dyn NormalizationStage>>,
131}
132
133impl Default for PipelineBuilder {
134 fn default() -> Self {
135 Self::new()
136 }
137}
138
139impl PipelineBuilder {
140 pub fn new() -> Self {
142 Self {
143 consolidation: Consolidation::default(),
144 naming: Naming::default(),
145 circular_dependency_strategy: ResolutionStrategy::default(),
146 extra_stages: Vec::new(),
147 }
148 }
149
150 pub fn consolidation(mut self, consolidation: Consolidation) -> Self {
152 self.consolidation = consolidation;
153 self
154 }
155
156 pub fn naming(mut self, naming: Naming) -> Self {
158 self.naming = naming;
159 self
160 }
161
162 pub fn circular_dependency_strategy(mut self, strategy: ResolutionStrategy) -> Self {
164 self.circular_dependency_strategy = strategy;
165 self
166 }
167
168 pub fn add_stage<S: NormalizationStage + 'static>(mut self, stage: S) -> Self {
170 self.extra_stages.push(Box::new(stage));
171 self
172 }
173
174 pub fn build(self) -> NormalizationPipeline {
182 let mut pipeline = NormalizationPipeline::new();
183
184 match self.consolidation {
185 Consolidation::Standard => {
186 pipeline = pipeline.add_stage(TypeConsolidationStage);
187 }
188 Consolidation::Skip => {}
189 }
190
191 match self.naming {
192 Naming::Standard => {
193 pipeline = pipeline.add_stage(NamingResolutionStage);
194 }
195 Naming::Skip => {}
196 Naming::Custom(stage) => {
197 pipeline.stages.push(stage);
198 }
199 }
200
201 pipeline = pipeline.add_stage(CircularDependencyResolutionStage::with_strategy(
202 self.circular_dependency_strategy,
203 ));
204
205 for stage in self.extra_stages {
206 pipeline.stages.push(stage);
207 }
208
209 pipeline
210 }
211}
212
213pub struct TypeConsolidationStage;
220
221impl NormalizationStage for TypeConsolidationStage {
222 fn name(&self) -> &'static str {
223 "TypeConsolidation"
224 }
225
226 fn transform(&self, schema: &mut Schema) -> Result<(), Vec<NormalizationError>> {
227 use crate::Typespace;
228
229 let mut consolidated = Typespace::new();
230 let mut name_conflicts = HashMap::new();
231 let mut rename_map: HashMap<String, String> = HashMap::new();
233
234 let mut input_type_names = HashMap::new();
235 let mut output_type_names = HashMap::new();
236
237 for ty in schema.input_types.types() {
238 let simple_name = extract_simple_name(ty.name());
239 input_type_names.insert(simple_name.clone(), ty.clone());
240
241 if output_type_names.contains_key(&simple_name) {
242 name_conflicts.insert(simple_name, true);
243 }
244 }
245
246 for ty in schema.output_types.types() {
247 let simple_name = extract_simple_name(ty.name());
248 output_type_names.insert(simple_name.clone(), ty.clone());
249
250 if input_type_names.contains_key(&simple_name) {
251 name_conflicts.insert(simple_name, true);
252 }
253 }
254
255 for ty in schema.input_types.types() {
256 let simple_name = extract_simple_name(ty.name());
257 let mut new_type = ty.clone();
258
259 if name_conflicts.contains_key(&simple_name) {
260 let old_name = ty.name().to_string();
261 let new_name = format!("input.{}", ty.name().replace("::", "."));
262 rename_type(&mut new_type, &new_name);
263 rename_map.insert(old_name, new_name);
264 }
265
266 consolidated.insert_type(new_type);
267 }
268
269 for ty in schema.output_types.types() {
270 let simple_name = extract_simple_name(ty.name());
271 let mut new_type = ty.clone();
272
273 if name_conflicts.contains_key(&simple_name) {
274 let old_name = ty.name().to_string();
275 let new_name = format!("output.{}", ty.name().replace("::", "."));
276 rename_type(&mut new_type, &new_name);
277 rename_map.insert(old_name, new_name);
278 consolidated.insert_type(new_type);
279 } else if !input_type_names.contains_key(&simple_name) {
280 consolidated.insert_type(new_type);
281 }
282 }
283
284 schema.input_types = consolidated;
285 schema.output_types = Typespace::new();
286
287 if !rename_map.is_empty() {
289 for function in &mut schema.functions {
290 update_type_reference_in_option(&mut function.input_type, &rename_map);
291 update_type_reference_in_option(&mut function.input_headers, &rename_map);
292 update_type_references_in_output_type(&mut function.output_type, &rename_map);
293 update_type_reference_in_option(&mut function.error_type, &rename_map);
294 }
295
296 let types_to_update: Vec<_> = schema.input_types.types().cloned().collect();
297 schema.input_types = Typespace::new();
298 for mut ty in types_to_update {
299 update_type_references_in_type(&mut ty, &rename_map);
300 schema.input_types.insert_type(ty);
301 }
302 }
303
304 Ok(())
305 }
306}
307
308fn extract_simple_name(qualified_name: &str) -> String {
309 qualified_name
310 .split("::")
311 .last()
312 .unwrap_or(qualified_name)
313 .to_string()
314}
315
316fn rename_type(ty: &mut Type, new_name: &str) {
317 let new_path: Vec<String> = new_name.split("::").map(|s| s.to_string()).collect();
318 match ty {
319 Type::Struct(s) => {
320 s.name = new_name.to_string();
321 s.id.path = new_path;
322 }
323 Type::Enum(e) => {
324 e.name = new_name.to_string();
325 e.id.path = new_path;
326 }
327 Type::Primitive(p) => {
328 p.name = new_name.to_string();
329 p.id.path = new_path;
330 }
331 }
332}
333
334pub struct NamingResolutionStage;
340
341impl NormalizationStage for NamingResolutionStage {
342 fn name(&self) -> &'static str {
343 "NamingResolution"
344 }
345
346 fn transform(&self, schema: &mut Schema) -> Result<(), Vec<NormalizationError>> {
347 let mut name_usage: HashMap<String, Vec<String>> = HashMap::new();
348 let mut name_conflicts = HashMap::new();
349
350 for ty in schema.input_types.types() {
351 let qualified_name = ty.name().to_string();
352 let simple_name = extract_simple_name(&qualified_name);
353
354 let entries = name_usage.entry(simple_name.clone()).or_default();
355 if !entries.contains(&qualified_name) {
356 if !entries.is_empty() {
357 name_conflicts.insert(simple_name.clone(), true);
358 }
359 entries.push(qualified_name);
360 }
361 }
362
363 let types_to_update: Vec<_> = schema.input_types.types().cloned().collect();
364 schema.input_types = crate::Typespace::new();
365
366 for mut ty in types_to_update {
367 let qualified_name = ty.name().to_string();
368 let simple_name = extract_simple_name(&qualified_name);
369
370 let resolved_name = if name_conflicts.contains_key(&simple_name) {
371 generate_unique_name(&qualified_name)
372 } else {
373 simple_name
374 };
375
376 rename_type(&mut ty, &resolved_name);
377 schema.input_types.insert_type(ty);
378 }
379
380 update_type_references_in_schema(schema, &name_usage, &name_conflicts);
381
382 Ok(())
383 }
384}
385
386fn generate_unique_name(qualified_name: &str) -> String {
387 let parts: Vec<&str> = qualified_name.split("::").collect();
388 if parts.len() < 2 {
389 return qualified_name.to_string();
390 }
391
392 let type_name = parts.last().unwrap();
393 let module_parts: Vec<&str> = parts[..parts.len() - 1].to_vec();
394
395 let non_excluded: Vec<&str> = module_parts
396 .iter()
397 .filter(|&&part| part != "model" && part != "proto" && !part.is_empty())
398 .copied()
399 .collect();
400
401 let prefix = if non_excluded.is_empty() {
402 module_parts.join("_")
403 } else {
404 non_excluded
405 .iter()
406 .map(|s| capitalize_first_letter(s))
407 .collect::<Vec<_>>()
408 .join("")
409 };
410 format!("{prefix}{type_name}")
411}
412
413fn capitalize_first_letter(s: &str) -> String {
414 let mut chars = s.chars();
415 match chars.next() {
416 None => String::new(),
417 Some(first) => first.to_uppercase().collect::<String>() + chars.as_str(),
418 }
419}
420
421fn update_type_references_in_schema(
422 schema: &mut Schema,
423 name_usage: &HashMap<String, Vec<String>>,
424 name_conflicts: &HashMap<String, bool>,
425) {
426 let mut name_mapping = HashMap::new();
427
428 for (simple_name, qualified_names) in name_usage {
429 if name_conflicts.contains_key(simple_name) {
430 for qualified_name in qualified_names {
431 let resolved_name = generate_unique_name(qualified_name);
432 name_mapping.insert(qualified_name.clone(), resolved_name);
433 }
434 } else {
435 for qualified_name in qualified_names {
436 name_mapping.insert(qualified_name.clone(), simple_name.clone());
437 }
438 }
439 }
440
441 for function in &mut schema.functions {
442 update_type_reference_in_option(&mut function.input_type, &name_mapping);
443 update_type_reference_in_option(&mut function.input_headers, &name_mapping);
444 update_type_references_in_output_type(&mut function.output_type, &name_mapping);
445 update_type_reference_in_option(&mut function.error_type, &name_mapping);
446 }
447
448 let types_to_update: Vec<_> = schema.input_types.types().cloned().collect();
449 schema.input_types = crate::Typespace::new();
450
451 for mut ty in types_to_update {
452 update_type_references_in_type(&mut ty, &name_mapping);
453 schema.input_types.insert_type(ty);
454 }
455}
456
457fn update_type_reference(
458 type_ref: &mut crate::TypeReference,
459 name_mapping: &HashMap<String, String>,
460) {
461 if let Some(new_name) = name_mapping.get(&type_ref.name) {
462 type_ref.name.clone_from(new_name);
463 }
464
465 for arg in &mut type_ref.arguments {
466 update_type_reference(arg, name_mapping);
467 }
468}
469
470fn update_type_reference_in_option(
471 type_ref_opt: &mut Option<crate::TypeReference>,
472 name_mapping: &HashMap<String, String>,
473) {
474 if let Some(type_ref) = type_ref_opt {
475 update_type_reference(type_ref, name_mapping);
476 }
477}
478
479fn update_type_references_in_output_type(
480 output_type: &mut crate::OutputType,
481 name_mapping: &HashMap<String, String>,
482) {
483 match output_type {
484 crate::OutputType::Complete { output_type } => {
485 update_type_reference_in_option(output_type, name_mapping);
486 }
487 crate::OutputType::Stream { item_type } => {
488 update_type_reference(item_type, name_mapping);
489 }
490 }
491}
492
493fn update_type_references_in_type(ty: &mut crate::Type, name_mapping: &HashMap<String, String>) {
494 match ty {
495 crate::Type::Struct(s) => match &mut s.fields {
496 crate::Fields::Named(fields) | crate::Fields::Unnamed(fields) => {
497 for field in fields {
498 update_type_reference(&mut field.type_ref, name_mapping);
499 }
500 }
501 crate::Fields::None => {}
502 },
503 crate::Type::Enum(e) => {
504 for variant in &mut e.variants {
505 match &mut variant.fields {
506 crate::Fields::Named(fields) | crate::Fields::Unnamed(fields) => {
507 for field in fields {
508 update_type_reference(&mut field.type_ref, name_mapping);
509 }
510 }
511 crate::Fields::None => {}
512 }
513 }
514 }
515 crate::Type::Primitive(p) => {
516 if let Some(fallback) = &mut p.fallback {
517 update_type_reference(fallback, name_mapping);
518 }
519 }
520 }
521}
522
523pub struct CircularDependencyResolutionStage {
530 strategy: ResolutionStrategy,
531}
532
533#[derive(Debug, Clone, Default)]
534pub enum ResolutionStrategy {
535 #[default]
537 Intelligent,
538 Boxing,
540 ForwardDeclarations,
542 OptionalBreaking,
544 ReferenceCounted,
546}
547
548impl CircularDependencyResolutionStage {
549 pub fn new() -> Self {
550 Self {
551 strategy: ResolutionStrategy::default(),
552 }
553 }
554
555 pub fn with_strategy(strategy: ResolutionStrategy) -> Self {
556 Self { strategy }
557 }
558}
559
560impl Default for CircularDependencyResolutionStage {
561 fn default() -> Self {
562 Self::new()
563 }
564}
565
566impl NormalizationStage for CircularDependencyResolutionStage {
567 fn name(&self) -> &'static str {
568 "CircularDependencyResolution"
569 }
570
571 fn transform(&self, schema: &mut Schema) -> Result<(), Vec<NormalizationError>> {
572 let cycles = self.detect_circular_dependencies(schema)?;
573
574 if cycles.is_empty() {
575 return Ok(());
576 }
577
578 for cycle in cycles {
579 self.resolve_cycle(schema, &cycle)?;
580 }
581
582 Ok(())
583 }
584}
585
586impl CircularDependencyResolutionStage {
587 fn detect_circular_dependencies(
588 &self,
589 schema: &Schema,
590 ) -> Result<Vec<Vec<String>>, Vec<NormalizationError>> {
591 let mut dependencies: HashMap<String, BTreeSet<String>> = HashMap::new();
592
593 for ty in schema
594 .input_types
595 .types()
596 .chain(schema.output_types.types())
597 {
598 let type_name = ty.name().to_string();
599 let mut deps = BTreeSet::new();
600 self.collect_type_dependencies(ty, &mut deps);
601 dependencies.insert(type_name, deps);
602 }
603
604 let scc_cycles = self.find_strongly_connected_components(&dependencies);
605
606 let mut cycles = Vec::new();
607 for component in scc_cycles {
608 if component.len() > 1
609 || (component.len() == 1
610 && dependencies
611 .get(&component[0])
612 .is_some_and(|deps| deps.contains(&component[0])))
613 {
614 cycles.push(component);
615 }
616 }
617
618 Ok(cycles)
619 }
620
621 fn collect_type_dependencies(&self, ty: &Type, deps: &mut BTreeSet<String>) {
622 match ty {
623 Type::Struct(s) => {
624 for field in s.fields() {
625 self.collect_type_ref_dependencies(&field.type_ref, deps);
626 }
627 }
628 Type::Enum(e) => {
629 for variant in e.variants() {
630 for field in variant.fields() {
631 self.collect_type_ref_dependencies(&field.type_ref, deps);
632 }
633 }
634 }
635 Type::Primitive(p) => {
636 if let Some(fallback) = &p.fallback {
637 self.collect_type_ref_dependencies(fallback, deps);
638 }
639 }
640 }
641 }
642
643 fn collect_type_ref_dependencies(&self, type_ref: &TypeReference, deps: &mut BTreeSet<String>) {
644 if !self.is_stdlib_type(&type_ref.name) && !self.is_generic_parameter(&type_ref.name) {
645 deps.insert(type_ref.name.clone());
646 }
647
648 for arg in &type_ref.arguments {
649 self.collect_type_ref_dependencies(arg, deps);
650 }
651 }
652
653 fn is_stdlib_type(&self, name: &str) -> bool {
654 if STDLIB_TYPES.iter().any(|&(n, _)| n == name) {
656 return true;
657 }
658 STDLIB_TYPE_PREFIXES
660 .iter()
661 .any(|prefix| name.starts_with(prefix))
662 }
663
664 fn is_generic_parameter(&self, name: &str) -> bool {
665 name.len() <= 2 && name.chars().all(|c| c.is_ascii_uppercase())
666 }
667
668 fn find_strongly_connected_components(
669 &self,
670 dependencies: &HashMap<String, BTreeSet<String>>,
671 ) -> Vec<Vec<String>> {
672 let mut index = 0;
673 let mut stack = Vec::new();
674 let mut indices: HashMap<String, usize> = HashMap::new();
675 let mut lowlinks: HashMap<String, usize> = HashMap::new();
676 let mut on_stack: HashMap<String, bool> = HashMap::new();
677 let mut components = Vec::new();
678
679 for node in dependencies.keys() {
680 if !indices.contains_key(node) {
681 self.strongconnect(
682 node,
683 dependencies,
684 &mut index,
685 &mut stack,
686 &mut indices,
687 &mut lowlinks,
688 &mut on_stack,
689 &mut components,
690 );
691 }
692 }
693
694 components
695 }
696
697 #[allow(clippy::too_many_arguments, clippy::only_used_in_recursion)]
698 fn strongconnect(
699 &self,
700 node: &str,
701 dependencies: &HashMap<String, BTreeSet<String>>,
702 index: &mut usize,
703 stack: &mut Vec<String>,
704 indices: &mut HashMap<String, usize>,
705 lowlinks: &mut HashMap<String, usize>,
706 on_stack: &mut HashMap<String, bool>,
707 components: &mut Vec<Vec<String>>,
708 ) {
709 indices.insert(node.to_string(), *index);
710 lowlinks.insert(node.to_string(), *index);
711 *index += 1;
712 stack.push(node.to_string());
713 on_stack.insert(node.to_string(), true);
714
715 if let Some(deps) = dependencies.get(node) {
716 for neighbor in deps {
717 if !indices.contains_key(neighbor) {
718 self.strongconnect(
719 neighbor,
720 dependencies,
721 index,
722 stack,
723 indices,
724 lowlinks,
725 on_stack,
726 components,
727 );
728 lowlinks.insert(node.to_string(), lowlinks[node].min(lowlinks[neighbor]));
729 } else if *on_stack.get(neighbor).unwrap_or(&false) {
730 lowlinks.insert(node.to_string(), lowlinks[node].min(indices[neighbor]));
731 }
732 }
733 }
734
735 if lowlinks[node] == indices[node] {
736 let mut component = Vec::new();
737 loop {
738 let w = stack.pop().unwrap();
739 on_stack.insert(w.clone(), false);
740 component.push(w.clone());
741 if w == node {
742 break;
743 }
744 }
745 if !component.is_empty() {
746 components.push(component);
747 }
748 }
749 }
750
751 fn resolve_cycle(
752 &self,
753 schema: &mut Schema,
754 cycle: &[String],
755 ) -> Result<(), Vec<NormalizationError>> {
756 match self.strategy {
757 ResolutionStrategy::Intelligent => {
758 if cycle.len() == 1 {
759 self.apply_boxing_strategy(schema, cycle)
760 } else {
761 self.apply_forward_declaration_strategy(schema, cycle)
762 }
763 }
764 ResolutionStrategy::Boxing => self.apply_boxing_strategy(schema, cycle),
765 ResolutionStrategy::ForwardDeclarations => {
766 self.apply_forward_declaration_strategy(schema, cycle)
767 }
768 ResolutionStrategy::OptionalBreaking => {
769 self.apply_optional_breaking_strategy(schema, cycle)
770 }
771 ResolutionStrategy::ReferenceCounted => {
772 self.apply_reference_counting_strategy(schema, cycle)
773 }
774 }
775 }
776
777 fn apply_boxing_strategy(
784 &self,
785 _schema: &mut Schema,
786 _cycle: &[String],
787 ) -> Result<(), Vec<NormalizationError>> {
788 Ok(())
789 }
790
791 fn apply_forward_declaration_strategy(
792 &self,
793 _schema: &mut Schema,
794 _cycle: &[String],
795 ) -> Result<(), Vec<NormalizationError>> {
796 Ok(())
798 }
799
800 fn apply_optional_breaking_strategy(
801 &self,
802 _schema: &mut Schema,
803 _cycle: &[String],
804 ) -> Result<(), Vec<NormalizationError>> {
805 Ok(())
807 }
808
809 fn apply_reference_counting_strategy(
810 &self,
811 _schema: &mut Schema,
812 _cycle: &[String],
813 ) -> Result<(), Vec<NormalizationError>> {
814 Ok(())
816 }
817}
818
819#[derive(Debug, Clone, PartialEq, Eq)]
824pub enum NormalizationError {
825 UnresolvedReference {
826 name: String,
827 referrer: SymbolId,
828 },
829 CircularDependency {
830 cycle: Vec<SymbolId>,
831 },
832 ConflictingDefinition {
833 symbol: SymbolId,
834 existing: String,
835 new: String,
836 },
837 InvalidGenericParameter {
838 type_name: String,
839 parameter: String,
840 reason: String,
841 },
842 ValidationError {
843 symbol: SymbolId,
844 message: String,
845 },
846}
847
848impl std::fmt::Display for NormalizationError {
849 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
850 match self {
851 NormalizationError::UnresolvedReference { name, referrer } => {
852 write!(
853 f,
854 "Unresolved type reference '{name}' in symbol {referrer:?}"
855 )
856 }
857 NormalizationError::CircularDependency { cycle } => {
858 write!(f, "Circular dependency detected: {cycle:?}")
859 }
860 NormalizationError::ConflictingDefinition {
861 symbol,
862 existing,
863 new,
864 } => {
865 write!(
866 f,
867 "Conflicting definition for symbol {symbol:?}: existing '{existing}', new '{new}'"
868 )
869 }
870 NormalizationError::InvalidGenericParameter {
871 type_name,
872 parameter,
873 reason,
874 } => {
875 write!(
876 f,
877 "Invalid generic parameter '{parameter}' in type '{type_name}': {reason}"
878 )
879 }
880 NormalizationError::ValidationError { symbol, message } => {
881 write!(f, "Validation error for symbol {symbol:?}: {message}")
882 }
883 }
884 }
885}
886
887impl std::error::Error for NormalizationError {}
888
889#[derive(Debug)]
894struct NormalizationContext {
895 symbol_table: SymbolTable,
896 raw_types: HashMap<SymbolId, Type>,
897 raw_functions: HashMap<SymbolId, Function>,
898 resolution_cache: HashMap<String, SymbolId>,
899 generic_scope: BTreeSet<String>,
900 errors: Vec<NormalizationError>,
901}
902
903impl Default for NormalizationContext {
904 fn default() -> Self {
905 Self::new()
906 }
907}
908
909impl NormalizationContext {
910 fn new() -> Self {
911 Self {
912 symbol_table: SymbolTable::new(),
913 raw_types: HashMap::new(),
914 raw_functions: HashMap::new(),
915 resolution_cache: HashMap::new(),
916 generic_scope: BTreeSet::new(),
917 errors: Vec::new(),
918 }
919 }
920
921 fn has_errors(&self) -> bool {
922 !self.errors.is_empty()
923 }
924
925 fn take_errors(&mut self) -> Vec<NormalizationError> {
926 std::mem::take(&mut self.errors)
927 }
928}
929
930pub struct Normalizer {
932 context: NormalizationContext,
933}
934
935impl Normalizer {
936 pub fn new() -> Self {
937 Self {
938 context: NormalizationContext::new(),
939 }
940 }
941
942 pub fn normalize(self, schema: &Schema) -> Result<SemanticSchema, Vec<NormalizationError>> {
944 self.normalize_with_pipeline(schema, NormalizationPipeline::standard())
945 }
946
947 pub fn normalize_with_pipeline(
952 mut self,
953 schema: &Schema,
954 pipeline: NormalizationPipeline,
955 ) -> Result<SemanticSchema, Vec<NormalizationError>> {
956 let mut schema = schema.clone();
958
959 crate::ids::ensure_symbol_ids(&mut schema);
961
962 let pre_norm_names: Vec<String> = schema
966 .input_types
967 .types()
968 .chain(schema.output_types.types())
969 .map(|t| t.name().to_string())
970 .collect();
971
972 pipeline.run(&mut schema)?;
974
975 let mut original_names: HashMap<String, String> = HashMap::new();
982 for pre_name in &pre_norm_names {
983 let short = pre_name.split("::").last().unwrap_or(pre_name);
984 original_names
985 .entry(short.to_string())
986 .or_insert_with(|| pre_name.clone());
987 }
988
989 self.discover_symbols(&schema)?;
991
992 self.resolve_types()?;
994
995 self.analyze_dependencies()?;
997
998 self.validate_semantics()?;
1000
1001 self.build_semantic_ir(&schema, &original_names)
1003 }
1004
1005 fn discover_symbols(&mut self, schema: &Schema) -> Result<(), Vec<NormalizationError>> {
1006 let schema_info = SymbolInfo {
1007 id: schema.id.clone(),
1008 name: schema.name.clone(),
1009 path: schema.id.path.clone(),
1010 kind: SymbolKind::Struct,
1011 resolved: false,
1012 dependencies: BTreeSet::new(),
1013 };
1014 self.context.symbol_table.register(schema_info);
1015
1016 for function in &schema.functions {
1017 let function_info = SymbolInfo {
1018 id: function.id.clone(),
1019 name: function.name.clone(),
1020 path: function.id.path.clone(),
1021 kind: SymbolKind::Endpoint,
1022 resolved: false,
1023 dependencies: BTreeSet::new(),
1024 };
1025 self.context.symbol_table.register(function_info);
1026 self.context
1027 .raw_functions
1028 .insert(function.id.clone(), function.clone());
1029 }
1030
1031 self.discover_types_from_typespace(&schema.input_types);
1032 self.discover_types_from_typespace(&schema.output_types);
1033
1034 if self.context.has_errors() {
1035 return Err(self.context.take_errors());
1036 }
1037
1038 Ok(())
1039 }
1040
1041 fn discover_types_from_typespace(&mut self, typespace: &crate::Typespace) {
1042 for ty in typespace.types() {
1043 self.discover_type_symbols(ty);
1044 }
1045 }
1046
1047 fn discover_type_symbols(&mut self, ty: &Type) {
1048 let (id, name, kind) = match ty {
1049 Type::Primitive(p) => (p.id.clone(), p.name.clone(), SymbolKind::Primitive),
1050 Type::Struct(s) => (s.id.clone(), s.name.clone(), SymbolKind::Struct),
1051 Type::Enum(e) => (e.id.clone(), e.name.clone(), SymbolKind::Enum),
1052 };
1053
1054 let path = id.path.clone();
1055
1056 let symbol_info = SymbolInfo {
1057 id: id.clone(),
1058 name,
1059 path,
1060 kind,
1061 resolved: false,
1062 dependencies: BTreeSet::new(),
1063 };
1064
1065 self.context.symbol_table.register(symbol_info);
1066 self.context.raw_types.insert(id, ty.clone());
1067
1068 match ty {
1069 Type::Struct(s) => self.discover_struct_symbols(s),
1070 Type::Enum(e) => self.discover_enum_symbols(e),
1071 Type::Primitive(_) => {}
1072 }
1073 }
1074
1075 fn discover_struct_symbols(&mut self, strukt: &Struct) {
1076 for field in strukt.fields() {
1077 let field_info = SymbolInfo {
1078 id: field.id.clone(),
1079 name: field.name.clone(),
1080 path: field.id.path.clone(),
1081 kind: SymbolKind::Field,
1082 resolved: false,
1083 dependencies: BTreeSet::new(),
1084 };
1085 self.context.symbol_table.register(field_info);
1086 }
1087 }
1088
1089 fn discover_enum_symbols(&mut self, enm: &Enum) {
1090 for variant in enm.variants() {
1091 let variant_info = SymbolInfo {
1092 id: variant.id.clone(),
1093 name: variant.name.clone(),
1094 path: variant.id.path.clone(),
1095 kind: SymbolKind::Variant,
1096 resolved: false,
1097 dependencies: BTreeSet::new(),
1098 };
1099 self.context.symbol_table.register(variant_info);
1100
1101 for field in variant.fields() {
1102 let field_info = SymbolInfo {
1103 id: field.id.clone(),
1104 name: field.name.clone(),
1105 path: field.id.path.clone(),
1106 kind: SymbolKind::Field,
1107 resolved: false,
1108 dependencies: BTreeSet::new(),
1109 };
1110 self.context.symbol_table.register(field_info);
1111 }
1112 }
1113 }
1114
1115 fn resolve_types(&mut self) -> Result<(), Vec<NormalizationError>> {
1116 for symbol_info in self.context.symbol_table.symbols.values() {
1117 if !matches!(
1118 symbol_info.kind,
1119 SymbolKind::Struct
1120 | SymbolKind::Enum
1121 | SymbolKind::Primitive
1122 | SymbolKind::TypeAlias
1123 ) {
1124 continue;
1125 }
1126 self.context
1127 .resolution_cache
1128 .insert(symbol_info.name.clone(), symbol_info.id.clone());
1129
1130 let qualified_name = symbol_info.id.qualified_name();
1131 if qualified_name != symbol_info.name {
1132 self.context
1133 .resolution_cache
1134 .insert(qualified_name, symbol_info.id.clone());
1135 }
1136 }
1137
1138 self.add_stdlib_types_to_cache();
1139
1140 for (function_id, function) in &self.context.raw_functions.clone() {
1141 self.resolve_function_references(function_id, function);
1142 }
1143
1144 for (type_id, ty) in &self.context.raw_types.clone() {
1145 self.resolve_type_references(type_id, ty);
1146 }
1147
1148 if self.context.has_errors() {
1149 return Err(self.context.take_errors());
1150 }
1151
1152 Ok(())
1153 }
1154
1155 fn resolve_function_references(&mut self, function_id: &SymbolId, function: &Function) {
1156 if let Some(input_type) = &function.input_type {
1157 self.resolve_single_reference(function_id, input_type);
1158 }
1159 if let Some(input_headers) = &function.input_headers {
1160 self.resolve_single_reference(function_id, input_headers);
1161 }
1162 match &function.output_type {
1163 crate::OutputType::Complete {
1164 output_type: Some(output_type),
1165 } => {
1166 self.resolve_single_reference(function_id, output_type);
1167 }
1168 crate::OutputType::Stream { item_type } => {
1169 self.resolve_single_reference(function_id, item_type);
1170 }
1171 crate::OutputType::Complete { output_type: None } => {}
1172 }
1173 if let Some(error_type) = &function.error_type {
1174 self.resolve_single_reference(function_id, error_type);
1175 }
1176 }
1177
1178 fn resolve_type_references(&mut self, type_id: &SymbolId, ty: &Type) {
1179 let generic_params: BTreeSet<String> = ty.parameters().map(|p| p.name.clone()).collect();
1180 self.context.generic_scope.extend(generic_params.clone());
1181
1182 match ty {
1183 Type::Struct(s) => {
1184 for field in s.fields() {
1185 self.resolve_field_references(type_id, field);
1186 }
1187 }
1188 Type::Enum(e) => {
1189 for variant in e.variants() {
1190 for field in variant.fields() {
1191 self.resolve_field_references(type_id, field);
1192 }
1193 }
1194 }
1195 Type::Primitive(p) => {
1196 if let Some(fallback) = &p.fallback {
1197 self.resolve_single_reference(type_id, fallback);
1198 }
1199 }
1200 }
1201
1202 for param in generic_params {
1203 self.context.generic_scope.remove(¶m);
1204 }
1205 }
1206
1207 fn resolve_field_references(&mut self, owner_id: &SymbolId, field: &Field) {
1208 self.resolve_single_reference(owner_id, &field.type_ref);
1209 }
1210
1211 fn add_stdlib_types_to_cache(&mut self) {
1212 for &(name, kind) in STDLIB_TYPES {
1213 let path = name.split("::").map(|s| s.to_string()).collect();
1214 let symbol_id = SymbolId::new(kind, path);
1215 self.context
1216 .resolution_cache
1217 .insert(name.to_string(), symbol_id);
1218 }
1219 }
1220
1221 fn resolve_single_reference(&mut self, referrer: &SymbolId, type_ref: &TypeReference) {
1222 if self.context.generic_scope.contains(&type_ref.name) {
1223 for arg in &type_ref.arguments {
1224 self.resolve_single_reference(referrer, arg);
1225 }
1226 return;
1227 }
1228
1229 if let Some(target_id) = self.resolve_global_type_reference(&type_ref.name) {
1230 self.context
1231 .symbol_table
1232 .add_dependency(referrer.clone(), target_id);
1233 }
1234
1235 for arg in &type_ref.arguments {
1236 self.resolve_single_reference(referrer, arg);
1237 }
1238 }
1239
1240 fn resolve_global_type_reference(&self, name: &str) -> Option<SymbolId> {
1241 self.context.resolution_cache.get(name).cloned()
1242 }
1243
1244 fn analyze_dependencies(&mut self) -> Result<(), Vec<NormalizationError>> {
1245 match self.context.symbol_table.topological_sort() {
1246 Ok(_) => Ok(()),
1247 Err(_cycle) => {
1248 Ok(())
1250 }
1251 }
1252 }
1253
1254 fn validate_semantics(&mut self) -> Result<(), Vec<NormalizationError>> {
1255 if self.context.has_errors() {
1257 return Err(self.context.take_errors());
1258 }
1259 Ok(())
1260 }
1261
1262 fn build_semantic_ir(
1263 self,
1264 schema: &Schema,
1265 original_names: &HashMap<String, String>,
1266 ) -> Result<SemanticSchema, Vec<NormalizationError>> {
1267 let mut semantic_types = BTreeMap::new();
1268 let mut semantic_functions = BTreeMap::new();
1269
1270 let sorted_symbols = match self.context.symbol_table.topological_sort() {
1271 Ok(sorted) => sorted,
1272 Err(_cycle) => self.context.symbol_table.symbols.keys().cloned().collect(),
1273 };
1274
1275 for symbol_id in sorted_symbols {
1276 if let Some(raw_type) = self.context.raw_types.get(&symbol_id) {
1277 let semantic_type = self.build_semantic_type(raw_type, original_names)?;
1278 semantic_types.insert(symbol_id, semantic_type);
1279 }
1280 }
1281
1282 for (function_id, raw_function) in &self.context.raw_functions {
1283 let semantic_function = self.build_semantic_function(raw_function)?;
1284 semantic_functions.insert(function_id.clone(), semantic_function);
1285 }
1286
1287 Ok(SemanticSchema {
1288 id: schema.id.clone(),
1289 name: schema.name.clone(),
1290 description: schema.description.clone(),
1291 functions: semantic_functions,
1292 types: semantic_types,
1293 symbol_table: self.context.symbol_table,
1294 })
1295 }
1296
1297 fn build_semantic_type(
1298 &self,
1299 raw_type: &Type,
1300 original_names: &HashMap<String, String>,
1301 ) -> Result<SemanticType, Vec<NormalizationError>> {
1302 match raw_type {
1303 Type::Primitive(p) => Ok(SemanticType::Primitive(
1304 self.build_semantic_primitive(p, original_names)?,
1305 )),
1306 Type::Struct(s) => Ok(SemanticType::Struct(
1307 self.build_semantic_struct(s, original_names)?,
1308 )),
1309 Type::Enum(e) => Ok(SemanticType::Enum(
1310 self.build_semantic_enum(e, original_names)?,
1311 )),
1312 }
1313 }
1314
1315 fn build_semantic_primitive(
1316 &self,
1317 primitive: &Primitive,
1318 original_names: &HashMap<String, String>,
1319 ) -> Result<SemanticPrimitive, Vec<NormalizationError>> {
1320 let fallback = primitive
1321 .fallback
1322 .as_ref()
1323 .and_then(|tr| self.resolve_global_type_reference(&tr.name));
1324
1325 let original_name = original_names
1326 .get(&primitive.name)
1327 .cloned()
1328 .unwrap_or_else(|| primitive.name.clone());
1329
1330 Ok(SemanticPrimitive {
1331 id: primitive.id.clone(),
1332 name: primitive.name.clone(),
1333 original_name,
1334 description: primitive.description.clone(),
1335 parameters: primitive
1336 .parameters
1337 .iter()
1338 .map(|p| SemanticTypeParameter {
1339 name: p.name.clone(),
1340 description: p.description.clone(),
1341 bounds: vec![],
1342 default: None,
1343 })
1344 .collect(),
1345 fallback,
1346 })
1347 }
1348
1349 fn build_semantic_struct(
1350 &self,
1351 strukt: &Struct,
1352 original_names: &HashMap<String, String>,
1353 ) -> Result<SemanticStruct, Vec<NormalizationError>> {
1354 let mut fields = BTreeMap::new();
1355
1356 for field in strukt.fields() {
1357 let semantic_field = self.build_semantic_field(field)?;
1358 fields.insert(field.id.clone(), semantic_field);
1359 }
1360
1361 let original_name = original_names
1362 .get(&strukt.name)
1363 .cloned()
1364 .unwrap_or_else(|| strukt.name.clone());
1365
1366 Ok(SemanticStruct {
1367 id: strukt.id.clone(),
1368 name: strukt.name.clone(),
1369 original_name,
1370 serde_name: strukt.serde_name.clone(),
1371 description: strukt.description.clone(),
1372 parameters: strukt
1373 .parameters
1374 .iter()
1375 .map(|p| SemanticTypeParameter {
1376 name: p.name.clone(),
1377 description: p.description.clone(),
1378 bounds: vec![],
1379 default: None,
1380 })
1381 .collect(),
1382 fields,
1383 transparent: strukt.transparent,
1384 is_tuple: strukt.is_tuple(),
1385 is_unit: strukt.is_unit(),
1386 codegen_config: strukt.codegen_config.clone(),
1387 })
1388 }
1389
1390 fn build_semantic_enum(
1391 &self,
1392 enm: &Enum,
1393 original_names: &HashMap<String, String>,
1394 ) -> Result<SemanticEnum, Vec<NormalizationError>> {
1395 let mut variants = BTreeMap::new();
1396
1397 for variant in enm.variants() {
1398 let semantic_variant = self.build_semantic_variant(variant)?;
1399 variants.insert(variant.id.clone(), semantic_variant);
1400 }
1401
1402 let original_name = original_names
1403 .get(&enm.name)
1404 .cloned()
1405 .unwrap_or_else(|| enm.name.clone());
1406
1407 Ok(SemanticEnum {
1408 id: enm.id.clone(),
1409 name: enm.name.clone(),
1410 original_name,
1411 serde_name: enm.serde_name.clone(),
1412 description: enm.description.clone(),
1413 parameters: enm
1414 .parameters
1415 .iter()
1416 .map(|p| SemanticTypeParameter {
1417 name: p.name.clone(),
1418 description: p.description.clone(),
1419 bounds: vec![],
1420 default: None,
1421 })
1422 .collect(),
1423 variants,
1424 representation: enm.representation.clone(),
1425 codegen_config: enm.codegen_config.clone(),
1426 })
1427 }
1428
1429 fn build_semantic_field(
1430 &self,
1431 field: &Field,
1432 ) -> Result<SemanticField, Vec<NormalizationError>> {
1433 let resolved_type_ref = self.build_resolved_type_reference(&field.type_ref)?;
1434
1435 Ok(SemanticField {
1436 id: field.id.clone(),
1437 name: field.name.clone(),
1438 serde_name: field.serde_name.clone(),
1439 description: field.description.clone(),
1440 deprecation_note: field.deprecation_note.clone(),
1441 type_ref: resolved_type_ref,
1442 required: field.required,
1443 flattened: field.flattened,
1444 transform_callback: field.transform_callback.clone(),
1445 })
1446 }
1447
1448 fn build_semantic_variant(
1449 &self,
1450 variant: &Variant,
1451 ) -> Result<SemanticVariant, Vec<NormalizationError>> {
1452 let mut fields = BTreeMap::new();
1453
1454 for field in variant.fields() {
1455 let semantic_field = self.build_semantic_field(field)?;
1456 fields.insert(field.id.clone(), semantic_field);
1457 }
1458
1459 let field_style = match &variant.fields {
1460 Fields::Named(_) => FieldStyle::Named,
1461 Fields::Unnamed(_) => FieldStyle::Unnamed,
1462 Fields::None => FieldStyle::Unit,
1463 };
1464
1465 Ok(SemanticVariant {
1466 id: variant.id.clone(),
1467 name: variant.name.clone(),
1468 serde_name: variant.serde_name.clone(),
1469 description: variant.description.clone(),
1470 fields,
1471 discriminant: variant.discriminant,
1472 untagged: variant.untagged,
1473 field_style,
1474 })
1475 }
1476
1477 fn build_semantic_function(
1478 &self,
1479 function: &Function,
1480 ) -> Result<SemanticFunction, Vec<NormalizationError>> {
1481 let input_type = function
1482 .input_type
1483 .as_ref()
1484 .and_then(|tr| self.resolve_global_type_reference(&tr.name));
1485 let input_headers = function
1486 .input_headers
1487 .as_ref()
1488 .and_then(|tr| self.resolve_global_type_reference(&tr.name));
1489 let output_type = match &function.output_type {
1490 crate::OutputType::Complete { output_type } => SemanticOutputType::Complete(
1491 output_type
1492 .as_ref()
1493 .and_then(|tr| self.resolve_global_type_reference(&tr.name)),
1494 ),
1495 crate::OutputType::Stream { item_type } => SemanticOutputType::Stream {
1496 item_type: self
1497 .resolve_global_type_reference(&item_type.name)
1498 .ok_or_else(|| {
1499 vec![NormalizationError::UnresolvedReference {
1500 name: item_type.name.clone(),
1501 referrer: function.id.clone(),
1502 }]
1503 })?,
1504 },
1505 };
1506 let error_type = function
1507 .error_type
1508 .as_ref()
1509 .and_then(|tr| self.resolve_global_type_reference(&tr.name));
1510
1511 Ok(SemanticFunction {
1512 id: function.id.clone(),
1513 name: function.name.clone(),
1514 path: function.path.clone(),
1515 description: function.description.clone(),
1516 deprecation_note: function.deprecation_note.clone(),
1517 input_type,
1518 input_headers,
1519 output_type,
1520 error_type,
1521 serialization: function.serialization.clone(),
1522 readonly: function.readonly,
1523 tags: function.tags.clone(),
1524 })
1525 }
1526
1527 fn build_resolved_type_reference(
1528 &self,
1529 type_ref: &TypeReference,
1530 ) -> Result<ResolvedTypeReference, Vec<NormalizationError>> {
1531 let is_likely_generic = !type_ref.name.contains("::");
1532
1533 let target =
1534 if let Some(target) = self.context.resolution_cache.get(&type_ref.name).cloned() {
1535 target
1536 } else if is_likely_generic {
1537 SymbolId::new(SymbolKind::TypeAlias, vec![type_ref.name.clone()])
1538 } else {
1539 SymbolId::new(SymbolKind::Struct, vec![type_ref.name.replace("::", "_")])
1540 };
1541
1542 let mut resolved_args = Vec::new();
1543 for arg in &type_ref.arguments {
1544 resolved_args.push(self.build_resolved_type_reference(arg)?);
1545 }
1546
1547 Ok(ResolvedTypeReference::new(
1548 target,
1549 resolved_args,
1550 type_ref.name.clone(),
1551 ))
1552 }
1553}
1554
1555impl Default for Normalizer {
1556 fn default() -> Self {
1557 Self::new()
1558 }
1559}
1560
1561#[cfg(test)]
1566mod tests {
1567 use super::*;
1568 use crate::{Fields, Function, Representation, Schema, Struct, TypeReference, Typespace};
1569
1570 #[test]
1571 fn test_basic_normalization() {
1572 let mut schema = Schema::new();
1573 schema.name = "TestSchema".to_string();
1574
1575 let user_struct = Struct::new("User");
1576 let user_type = Type::Struct(user_struct);
1577
1578 let mut input_types = Typespace::new();
1579 input_types.insert_type(user_type);
1580 schema.input_types = input_types;
1581
1582 let normalizer = Normalizer::new();
1583 let result = normalizer.normalize(&schema);
1584
1585 assert!(
1586 result.is_ok(),
1587 "Normalization should succeed for simple schema"
1588 );
1589
1590 let semantic_schema = result.unwrap();
1591 assert_eq!(semantic_schema.name, "TestSchema");
1592 assert_eq!(semantic_schema.types.len(), 1);
1593 }
1594
1595 #[test]
1596 fn test_unresolved_reference_handled_gracefully() {
1597 let mut schema = Schema::new();
1598 schema.name = "TestSchema".to_string();
1599
1600 let mut function = Function::new("test_function".to_string());
1601 function.input_type = Some(TypeReference::new("NonExistentType", vec![]));
1602 schema.functions.push(function);
1603
1604 let normalizer = Normalizer::new();
1605 let result = normalizer.normalize(&schema);
1606
1607 assert!(
1608 result.is_ok(),
1609 "Normalization should handle unresolved references gracefully"
1610 );
1611
1612 let semantic_schema = result.unwrap();
1613 assert!(!semantic_schema.functions.is_empty());
1614 }
1615
1616 #[test]
1617 fn test_normalize_with_functions_and_types() {
1618 let mut schema = Schema::new();
1619 schema.name = "API".to_string();
1620
1621 let mut user_struct = Struct::new("api::User");
1623 user_struct.fields = Fields::Named(vec![
1624 Field::new("name".into(), "std::string::String".into()),
1625 Field::new("age".into(), "u32".into()),
1626 ]);
1627 schema.input_types.insert_type(user_struct.into());
1628
1629 let mut error_enum = Enum::new("api::Error".into());
1630 error_enum.representation = Representation::Internal { tag: "type".into() };
1631 error_enum.variants = vec![
1632 Variant::new("NotFound".into()),
1633 Variant::new("Forbidden".into()),
1634 ];
1635 schema.output_types.insert_type(error_enum.into());
1636
1637 let mut function = Function::new("get_user".into());
1639 function.input_type = Some(TypeReference::new("api::User", vec![]));
1640 function.error_type = Some(TypeReference::new("api::Error", vec![]));
1641 schema.functions.push(function);
1642
1643 let normalizer = Normalizer::new();
1644 let result = normalizer.normalize(&schema);
1645 assert!(result.is_ok(), "Normalization failed: {:?}", result.err());
1646
1647 let semantic = result.unwrap();
1648 assert_eq!(semantic.types.len(), 2);
1649 assert_eq!(semantic.functions.len(), 1);
1650
1651 let func = semantic.functions.values().next().unwrap();
1653 assert!(func.input_type.is_some());
1654 assert!(func.error_type.is_some());
1655 }
1656
1657 #[test]
1658 fn test_normalize_function_with_input_headers() {
1659 let mut schema = Schema::new();
1660 schema.name = "API".to_string();
1661
1662 let headers_struct = Struct::new("Headers");
1663 schema.input_types.insert_type(headers_struct.into());
1664
1665 let body_struct = Struct::new("Body");
1666 schema.input_types.insert_type(body_struct.into());
1667
1668 let mut function = Function::new("do_thing".into());
1669 function.input_type = Some(TypeReference::new("Body", vec![]));
1670 function.input_headers = Some(TypeReference::new("Headers", vec![]));
1671 schema.functions.push(function);
1672
1673 let normalizer = Normalizer::new();
1674 let semantic = normalizer.normalize(&schema).unwrap();
1675
1676 let func = semantic.functions.values().next().unwrap();
1677 assert!(func.input_type.is_some());
1678 assert!(func.input_headers.is_some());
1679 }
1680
1681 #[test]
1682 fn test_type_consolidation_shared_name() {
1683 let mut schema = Schema::new();
1684 schema.name = "Test".to_string();
1685
1686 let input_struct = Struct::new("Shared");
1688 let output_struct = Struct::new("Shared");
1689 schema.input_types.insert_type(input_struct.into());
1690 schema.output_types.insert_type(output_struct.into());
1691
1692 let stage = TypeConsolidationStage;
1693 stage.transform(&mut schema).unwrap();
1694
1695 let type_names: Vec<_> = schema
1697 .input_types
1698 .types()
1699 .map(|t| t.name().to_string())
1700 .collect();
1701 assert!(
1702 type_names.contains(&"input.Shared".to_string()),
1703 "Expected input.Shared, got: {type_names:?}"
1704 );
1705 assert!(
1706 type_names.contains(&"output.Shared".to_string()),
1707 "Expected output.Shared, got: {type_names:?}"
1708 );
1709 assert!(schema.output_types.is_empty());
1710 }
1711
1712 #[test]
1713 fn test_type_consolidation_conflict_renaming() {
1714 let mut schema = Schema::new();
1715 schema.name = "Test".to_string();
1716
1717 let mut input_struct = Struct::new("Foo");
1719 input_struct.description = "input version".into();
1720 let mut output_struct = Struct::new("Foo");
1721 output_struct.description = "output version".into();
1722 output_struct.fields = Fields::Named(vec![Field::new("x".into(), "u32".into())]);
1724
1725 schema.input_types.insert_type(input_struct.into());
1726 schema.output_types.insert_type(output_struct.into());
1727
1728 let stage = TypeConsolidationStage;
1729 stage.transform(&mut schema).unwrap();
1730
1731 let type_names: Vec<_> = schema
1732 .input_types
1733 .types()
1734 .map(|t| t.name().to_string())
1735 .collect();
1736 assert!(
1737 type_names.contains(&"input.Foo".to_string())
1738 || type_names.contains(&"output.Foo".to_string()),
1739 "Expected conflict renaming, got: {type_names:?}"
1740 );
1741 }
1742
1743 #[test]
1744 fn test_ensure_symbol_ids_idempotent() {
1745 let mut schema = Schema::new();
1746 schema.name = "Test".to_string();
1747
1748 let mut user_struct = Struct::new("User");
1749 user_struct.fields = Fields::Named(vec![Field::new("id".into(), "u64".into())]);
1750 schema.input_types.insert_type(user_struct.into());
1751
1752 crate::ensure_symbol_ids(&mut schema);
1754 let ids_first: Vec<_> = schema
1755 .input_types
1756 .types()
1757 .map(|t| match t {
1758 Type::Struct(s) => s.id.clone(),
1759 _ => unreachable!(),
1760 })
1761 .collect();
1762
1763 crate::ensure_symbol_ids(&mut schema);
1764 let ids_second: Vec<_> = schema
1765 .input_types
1766 .types()
1767 .map(|t| match t {
1768 Type::Struct(s) => s.id.clone(),
1769 _ => unreachable!(),
1770 })
1771 .collect();
1772
1773 assert_eq!(
1774 ids_first, ids_second,
1775 "ensure_symbol_ids should be idempotent"
1776 );
1777 }
1778
1779 #[test]
1780 fn test_ensure_symbol_ids_enum_variants_and_fields() {
1781 let mut schema = Schema::new();
1782 schema.name = "Test".to_string();
1783
1784 let mut enm = Enum::new("Status".into());
1785 let mut variant = Variant::new("Active".into());
1786 variant.fields = Fields::Named(vec![Field::new(
1787 "since".into(),
1788 "std::string::String".into(),
1789 )]);
1790 enm.variants = vec![variant, Variant::new("Inactive".into())];
1791 schema.input_types.insert_type(enm.into());
1792
1793 crate::ensure_symbol_ids(&mut schema);
1794
1795 let enm = schema
1796 .input_types
1797 .get_type("Status")
1798 .unwrap()
1799 .as_enum()
1800 .unwrap();
1801 assert!(!enm.id.is_unknown(), "Enum should have a non-unknown id");
1802
1803 for variant in &enm.variants {
1804 assert!(
1805 !variant.id.is_unknown(),
1806 "Variant '{}' should have a non-unknown id",
1807 variant.name
1808 );
1809 for field in variant.fields() {
1810 assert!(
1811 !field.id.is_unknown(),
1812 "Field '{}' in variant '{}' should have a non-unknown id",
1813 field.name,
1814 variant.name
1815 );
1816 }
1817 }
1818
1819 let active = &enm.variants[0];
1821 assert_eq!(active.id.path.last().unwrap(), "Active");
1822 let since_field = active.fields().next().unwrap();
1823 assert!(
1824 since_field.id.path.contains(&"Active".to_string()),
1825 "Field path should include parent variant: {:?}",
1826 since_field.id.path
1827 );
1828 }
1829
1830 #[test]
1831 fn test_circular_dependency_detection() {
1832 let mut schema = Schema::new();
1833 schema.name = "Test".to_string();
1834
1835 let mut node_struct = Struct::new("Node");
1837 node_struct.fields = Fields::Named(vec![Field::new(
1838 "children".into(),
1839 TypeReference::new("std::vec::Vec", vec![TypeReference::new("Node", vec![])]),
1840 )]);
1841 schema.input_types.insert_type(node_struct.into());
1842
1843 let stage = CircularDependencyResolutionStage::new();
1844 let result = stage.transform(&mut schema);
1846 assert!(result.is_ok());
1847 }
1848
1849 #[test]
1850 fn test_empty_schema_normalization() {
1851 let schema = Schema::new();
1852 let normalizer = Normalizer::new();
1853 let result = normalizer.normalize(&schema);
1854 assert!(result.is_ok());
1855
1856 let semantic = result.unwrap();
1857 assert!(semantic.types.is_empty());
1858 assert!(semantic.functions.is_empty());
1859 }
1860
1861 #[test]
1862 fn test_naming_resolution_all_conflicting_types_have_references_rewritten() {
1863 let mut schema = Schema::new();
1867 schema.name = "Test".to_string();
1868
1869 let a_foo = Struct::new("a::Foo");
1871 let b_foo = Struct::new("b::Foo");
1872 schema.input_types.insert_type(a_foo.into());
1873 schema.input_types.insert_type(b_foo.into());
1874
1875 let mut func1 = Function::new("use_a_foo".into());
1877 func1.input_type = Some(TypeReference::new("a::Foo", vec![]));
1878 schema.functions.push(func1);
1879
1880 let mut func2 = Function::new("use_b_foo".into());
1881 func2.input_type = Some(TypeReference::new("b::Foo", vec![]));
1882 schema.functions.push(func2);
1883
1884 let stage = NamingResolutionStage;
1885 stage.transform(&mut schema).unwrap();
1886
1887 let type_names: std::collections::HashSet<String> = schema
1889 .input_types
1890 .types()
1891 .map(|t| t.name().to_string())
1892 .collect();
1893
1894 for func in &schema.functions {
1896 if let Some(ref input_type) = func.input_type {
1897 assert!(
1898 type_names.contains(&input_type.name),
1899 "Function '{}' references type '{}' which doesn't exist in schema. Available: {:?}",
1900 func.name, input_type.name, type_names
1901 );
1902 }
1903 }
1904 }
1905
1906 #[test]
1907 fn test_generate_unique_name_excluded_modules_no_collision() {
1908 let name1 = generate_unique_name("model::Foo");
1912 let name2 = generate_unique_name("model::proto::Foo");
1913
1914 assert_ne!(
1915 name1, name2,
1916 "model::Foo and model::proto::Foo must produce different names, got '{name1}' and '{name2}'"
1917 );
1918 }
1919
1920 #[test]
1921 fn test_generate_unique_name_with_non_excluded_module() {
1922 let name = generate_unique_name("billing::Invoice");
1924 assert_eq!(name, "BillingInvoice");
1925 }
1926
1927 #[test]
1928 fn test_self_referential_type_normalizes_successfully() {
1929 let mut schema = Schema::new();
1934 schema.name = "TreeSchema".to_string();
1935
1936 let mut tree_node = Struct::new("TreeNode");
1941 tree_node.fields = Fields::Named(vec![
1942 Field::new("label".into(), "std::string::String".into()),
1943 Field::new(
1944 "children".into(),
1945 TypeReference::new(
1946 "std::vec::Vec",
1947 vec![TypeReference::new("TreeNode", vec![])],
1948 ),
1949 ),
1950 Field::new(
1951 "parent".into(),
1952 TypeReference::new(
1953 "std::boxed::Box",
1954 vec![TypeReference::new("TreeNode", vec![])],
1955 ),
1956 ),
1957 ]);
1958 schema.input_types.insert_type(tree_node.into());
1959
1960 let normalizer = Normalizer::new();
1961 let result = normalizer.normalize(&schema);
1962
1963 assert!(
1964 result.is_ok(),
1965 "Self-referential type should not prevent normalization: {:?}",
1966 result.err()
1967 );
1968
1969 let semantic = result.unwrap();
1970 assert_eq!(semantic.types.len(), 1, "TreeNode type should be present");
1971
1972 let tree_node_type = semantic.types.values().next().unwrap();
1974 match tree_node_type {
1975 SemanticType::Struct(s) => {
1976 assert_eq!(s.name, "TreeNode");
1977 assert_eq!(s.fields.len(), 3, "All three fields should survive");
1978 }
1979 other => panic!("Expected Struct, got {:?}", std::mem::discriminant(other)),
1980 }
1981 }
1982
1983 #[test]
1984 fn test_multi_type_cycle_normalizes_successfully() {
1985 let mut schema = Schema::new();
1989 schema.name = "CycleSchema".to_string();
1990
1991 let mut department = Struct::new("Department");
1993 department.fields = Fields::Named(vec![
1994 Field::new("name".into(), "std::string::String".into()),
1995 Field::new("manager".into(), TypeReference::new("Employee", vec![])),
1996 ]);
1997
1998 let mut employee = Struct::new("Employee");
1999 employee.fields = Fields::Named(vec![
2000 Field::new("name".into(), "std::string::String".into()),
2001 Field::new(
2002 "department".into(),
2003 TypeReference::new("Department", vec![]),
2004 ),
2005 ]);
2006
2007 schema.input_types.insert_type(department.into());
2008 schema.input_types.insert_type(employee.into());
2009
2010 let normalizer = Normalizer::new();
2011 let result = normalizer.normalize(&schema);
2012
2013 assert!(
2014 result.is_ok(),
2015 "Multi-type cycle should not prevent normalization: {:?}",
2016 result.err()
2017 );
2018
2019 let semantic = result.unwrap();
2020 assert_eq!(
2021 semantic.types.len(),
2022 2,
2023 "Both Department and Employee types should be present"
2024 );
2025 }
2026
2027 #[test]
2028 fn test_type_consolidation_qualified_name_uniqueness() {
2029 let mut schema = Schema::new();
2033 schema.name = "Test".to_string();
2034
2035 let a_foo = Struct::new("a::Foo");
2036 let b_foo = Struct::new("b::Foo");
2037 let c_foo = Struct::new("c::Foo");
2038
2039 schema.input_types.insert_type(a_foo.into());
2040 schema.input_types.insert_type(b_foo.into());
2041 schema.output_types.insert_type(c_foo.into());
2042
2043 let stage = TypeConsolidationStage;
2044 stage.transform(&mut schema).unwrap();
2045
2046 let type_names: Vec<String> = schema
2047 .input_types
2048 .types()
2049 .map(|t| t.name().to_string())
2050 .collect();
2051
2052 assert_eq!(
2054 type_names.len(),
2055 3,
2056 "All three Foo types should survive consolidation, got: {type_names:?}"
2057 );
2058
2059 let unique_names: std::collections::HashSet<&String> = type_names.iter().collect();
2061 assert_eq!(
2062 unique_names.len(),
2063 3,
2064 "All three names should be distinct, got: {type_names:?}"
2065 );
2066
2067 let has_input_a = type_names
2070 .iter()
2071 .any(|n| n.contains("input") && n.contains("a"));
2072 let has_input_b = type_names
2073 .iter()
2074 .any(|n| n.contains("input") && n.contains("b"));
2075 let has_output_c = type_names
2076 .iter()
2077 .any(|n| n.contains("output") && n.contains("c"));
2078 assert!(
2079 has_input_a,
2080 "Expected an input.a.Foo variant, got: {type_names:?}"
2081 );
2082 assert!(
2083 has_input_b,
2084 "Expected an input.b.Foo variant, got: {type_names:?}"
2085 );
2086 assert!(
2087 has_output_c,
2088 "Expected an output.c.Foo variant, got: {type_names:?}"
2089 );
2090 }
2091
2092 #[test]
2093 fn test_resolve_types_does_not_confuse_variant_with_type() {
2094 let mut schema = Schema::new();
2098 schema.name = "Test".to_string();
2099
2100 let status_struct = Struct::new("Status");
2102 schema.input_types.insert_type(status_struct.into());
2103
2104 let mut state_enum = Enum::new("State".into());
2106 state_enum.variants = vec![Variant::new("Status".into()), Variant::new("Error".into())];
2107 schema.input_types.insert_type(state_enum.into());
2108
2109 let mut function = Function::new("get_status".into());
2111 function.input_type = Some(TypeReference::new("Status", vec![]));
2112 schema.functions.push(function);
2113
2114 let normalizer = Normalizer::new();
2115 let result = normalizer.normalize(&schema);
2116 assert!(
2117 result.is_ok(),
2118 "Normalization should succeed: {:?}",
2119 result.err()
2120 );
2121
2122 let semantic = result.unwrap();
2123 let func = semantic.functions.values().next().unwrap();
2124
2125 let resolved_id = func
2127 .input_type
2128 .as_ref()
2129 .expect("input_type should be resolved");
2130
2131 assert_eq!(
2133 resolved_id.kind,
2134 crate::SymbolKind::Struct,
2135 "Function's input_type should resolve to a Struct, not a Variant. Got: {resolved_id:?}"
2136 );
2137 }
2138
2139 #[test]
2140 fn test_generate_unique_name_same_inner_module() {
2141 let name_a = generate_unique_name("services::user::Profile");
2144 let name_b = generate_unique_name("auth::user::Profile");
2145
2146 assert_ne!(
2147 name_a, name_b,
2148 "services::user::Profile and auth::user::Profile must produce different names, \
2149 got '{name_a}' and '{name_b}'"
2150 );
2151
2152 assert!(
2154 name_a.contains("Services") || name_a.contains("services"),
2155 "Expected 'services' component in name, got '{name_a}'"
2156 );
2157 assert!(
2158 name_b.contains("Auth") || name_b.contains("auth"),
2159 "Expected 'auth' component in name, got '{name_b}'"
2160 );
2161 }
2162
2163 #[test]
2164 fn test_function_symbol_path_matches_id() {
2165 let mut schema = Schema::new();
2168 schema.name = "API".to_string();
2169
2170 let mut function = Function::new("get_user".into());
2171 function.input_type = None;
2172 function.output_type = crate::OutputType::Complete { output_type: None };
2173 schema.functions.push(function);
2174
2175 let normalizer = Normalizer::new();
2176 let semantic = normalizer
2177 .normalize(&schema)
2178 .expect("Normalization should succeed");
2179
2180 let (function_id, _) = semantic.functions.iter().next().unwrap();
2182
2183 let found = semantic.symbol_table.get_by_path(&function_id.path);
2185 assert!(
2186 found.is_some(),
2187 "symbol_table.get_by_path({:?}) should return Some, but got None. \
2188 Function ID: {function_id:?}",
2189 function_id.path
2190 );
2191
2192 let symbol_info = found.unwrap();
2193 assert_eq!(
2194 symbol_info.kind,
2195 crate::SymbolKind::Endpoint,
2196 "Symbol should be an Endpoint, got {:?}",
2197 symbol_info.kind
2198 );
2199 }
2200}