Skip to main content

xsd_schema/schema/
dependencies.rs

1//! Dependency graph for type compilation order
2//!
3//! This module builds a dependency graph from type derivations and provides
4//! topological sorting for determining compilation order. It also detects
5//! circular dependencies which are invalid in XSD.
6//!
7//! # Dependency Types
8//!
9//! Types depend on their base types:
10//! - Simple type restriction: depends on base type
11//! - Simple type list: depends on item type
12//! - Simple type union: depends on all member types
13//! - Complex type extension: depends on base type
14//! - Complex type restriction: depends on base type
15//!
16//! # Compilation Order
17//!
18//! Types must be compiled in topological order so that base types are
19//! available when compiling derived types. Built-in types are always
20//! available and don't need to be in the graph.
21
22use crate::error::{SchemaError, SchemaResult};
23use crate::ids::{ComplexTypeKey, SimpleTypeKey, TypeKey};
24use crate::schema::SchemaSet;
25use std::collections::{HashMap, HashSet, VecDeque};
26
27/// Dependency graph for type compilation order
28///
29/// Tracks dependencies between types and provides topological sorting
30/// to determine the correct compilation order.
31#[derive(Debug)]
32pub struct DependencyGraph {
33    /// Type → types it depends on (base types, item types, member types)
34    dependencies: HashMap<TypeKey, Vec<TypeKey>>,
35
36    /// Type → types that depend on it (reverse edges for in-degree calculation)
37    dependents: HashMap<TypeKey, Vec<TypeKey>>,
38
39    /// All types in the graph
40    all_types: HashSet<TypeKey>,
41
42    /// Topologically sorted type keys (dependencies first)
43    /// Populated after calling sort()
44    sorted_types: Vec<TypeKey>,
45
46    /// Whether the graph has been sorted
47    is_sorted: bool,
48}
49
50impl Default for DependencyGraph {
51    fn default() -> Self {
52        Self::new()
53    }
54}
55
56impl DependencyGraph {
57    /// Create a new empty dependency graph
58    pub fn new() -> Self {
59        Self {
60            dependencies: HashMap::new(),
61            dependents: HashMap::new(),
62            all_types: HashSet::new(),
63            sorted_types: Vec::new(),
64            is_sorted: false,
65        }
66    }
67
68    /// Add a type to the graph (without dependencies)
69    pub fn add_type(&mut self, type_key: TypeKey) {
70        self.all_types.insert(type_key);
71        self.is_sorted = false;
72    }
73
74    /// Add a type dependency (derived_type depends on base_type)
75    ///
76    /// This means base_type must be compiled before derived_type.
77    pub fn add_dependency(&mut self, derived: TypeKey, base: TypeKey) {
78        // Add both types to the graph
79        self.all_types.insert(derived);
80        self.all_types.insert(base);
81
82        // Add forward edge: derived -> base (derived depends on base)
83        self.dependencies.entry(derived).or_default().push(base);
84
85        // Add reverse edge: base -> derived (derived is a dependent of base)
86        self.dependents.entry(base).or_default().push(derived);
87
88        self.is_sorted = false;
89    }
90
91    /// Get the number of types in the graph
92    pub fn type_count(&self) -> usize {
93        self.all_types.len()
94    }
95
96    /// Get the number of dependency edges in the graph
97    pub fn edge_count(&self) -> usize {
98        self.dependencies.values().map(|v| v.len()).sum()
99    }
100
101    /// Check if a type has any dependencies
102    pub fn has_dependencies(&self, type_key: TypeKey) -> bool {
103        self.dependencies
104            .get(&type_key)
105            .map(|deps| !deps.is_empty())
106            .unwrap_or(false)
107    }
108
109    /// Get the dependencies of a type
110    pub fn get_dependencies(&self, type_key: TypeKey) -> &[TypeKey] {
111        self.dependencies
112            .get(&type_key)
113            .map(|v| v.as_slice())
114            .unwrap_or(&[])
115    }
116
117    /// Perform topological sort and cycle detection using Kahn's algorithm
118    ///
119    /// After successful sorting, use `compilation_order()` to get types
120    /// in the order they should be compiled (dependencies first).
121    ///
122    /// # Errors
123    ///
124    /// Returns an error if a circular dependency is detected.
125    pub fn sort(&mut self) -> SchemaResult<()> {
126        if self.is_sorted {
127            return Ok(());
128        }
129
130        // Calculate in-degrees (number of dependencies for each type)
131        let mut in_degree: HashMap<TypeKey, usize> = HashMap::new();
132        for type_key in &self.all_types {
133            let deps = self
134                .dependencies
135                .get(type_key)
136                .map(|v| v.len())
137                .unwrap_or(0);
138            in_degree.insert(*type_key, deps);
139        }
140
141        // Queue of types with no dependencies (in-degree 0)
142        let mut queue: VecDeque<TypeKey> = in_degree
143            .iter()
144            .filter(|(_, &deg)| deg == 0)
145            .map(|(&key, _)| key)
146            .collect();
147
148        let mut sorted = Vec::with_capacity(self.all_types.len());
149
150        while let Some(type_key) = queue.pop_front() {
151            sorted.push(type_key);
152
153            // For each type that depends on this one, decrement its in-degree
154            if let Some(deps) = self.dependents.get(&type_key) {
155                for dependent in deps {
156                    if let Some(deg) = in_degree.get_mut(dependent) {
157                        *deg -= 1;
158                        if *deg == 0 {
159                            queue.push_back(*dependent);
160                        }
161                    }
162                }
163            }
164        }
165
166        // If we couldn't process all types, there's a cycle
167        if sorted.len() != self.all_types.len() {
168            // Find the cycle for error reporting
169            let cycle = self.find_cycle()?;
170            return Err(SchemaError::structural(
171                "ct-props-correct.3",
172                format!("Circular type dependency detected: {}", cycle),
173                None,
174            ));
175        }
176
177        self.sorted_types = sorted;
178        self.is_sorted = true;
179        Ok(())
180    }
181
182    /// Get types in compilation order (dependencies first)
183    ///
184    /// Must call `sort()` first or this will return an empty slice.
185    pub fn compilation_order(&self) -> &[TypeKey] {
186        &self.sorted_types
187    }
188
189    /// Find a cycle in the graph for error reporting
190    ///
191    /// Uses DFS with path tracking to find a cycle.
192    fn find_cycle(&self) -> SchemaResult<String> {
193        let mut visited = HashSet::new();
194        let mut in_stack = HashSet::new();
195        let mut path = Vec::new();
196
197        for &start in &self.all_types {
198            if !visited.contains(&start) {
199                if let Some(cycle) =
200                    self.dfs_find_cycle(start, &mut visited, &mut in_stack, &mut path)
201                {
202                    return Ok(cycle);
203                }
204            }
205        }
206
207        // Fallback if we couldn't find the cycle (shouldn't happen)
208        Ok("unknown cycle".to_string())
209    }
210
211    /// DFS helper to find a cycle
212    fn dfs_find_cycle(
213        &self,
214        node: TypeKey,
215        visited: &mut HashSet<TypeKey>,
216        in_stack: &mut HashSet<TypeKey>,
217        path: &mut Vec<TypeKey>,
218    ) -> Option<String> {
219        visited.insert(node);
220        in_stack.insert(node);
221        path.push(node);
222
223        if let Some(deps) = self.dependencies.get(&node) {
224            for &dep in deps {
225                if !visited.contains(&dep) {
226                    if let Some(cycle) = self.dfs_find_cycle(dep, visited, in_stack, path) {
227                        return Some(cycle);
228                    }
229                } else if in_stack.contains(&dep) {
230                    // Found a cycle! Build the cycle description
231                    let cycle_start = path.iter().position(|&t| t == dep).unwrap();
232                    let cycle_path: Vec<String> = path[cycle_start..]
233                        .iter()
234                        .map(|t| format!("{:?}", t))
235                        .collect();
236                    return Some(format!("{} -> {:?}", cycle_path.join(" -> "), dep));
237                }
238            }
239        }
240
241        path.pop();
242        in_stack.remove(&node);
243        None
244    }
245
246    /// Check if the graph contains a cycle
247    pub fn has_cycle(&self) -> bool {
248        let mut visited = HashSet::new();
249        let mut in_stack = HashSet::new();
250        let mut path = Vec::new();
251
252        for &start in &self.all_types {
253            if !visited.contains(&start)
254                && self
255                    .dfs_find_cycle(start, &mut visited, &mut in_stack, &mut path)
256                    .is_some()
257            {
258                return true;
259            }
260        }
261
262        false
263    }
264}
265
266/// Statistics from building the dependency graph
267#[derive(Debug, Default)]
268pub struct DependencyStats {
269    /// Number of simple types in the graph
270    pub simple_types: usize,
271    /// Number of complex types in the graph
272    pub complex_types: usize,
273    /// Number of dependency edges
274    pub dependencies: usize,
275    /// Number of types with no dependencies (root types)
276    pub root_types: usize,
277    /// Maximum depth of the dependency tree
278    pub max_depth: usize,
279}
280
281/// Build a dependency graph from a schema set
282///
283/// Uses the resolved references from Task 3.1 to build the graph.
284/// Built-in types are not added to the graph since they're always available.
285///
286/// # Arguments
287///
288/// * `schema_set` - The schema set with resolved references
289///
290/// # Returns
291///
292/// A tuple of (DependencyGraph, DependencyStats)
293pub fn build_dependency_graph(
294    schema_set: &SchemaSet,
295) -> SchemaResult<(DependencyGraph, DependencyStats)> {
296    let mut graph = DependencyGraph::new();
297    let mut stats = DependencyStats::default();
298
299    // Collect all user-defined type keys
300    let simple_type_keys: Vec<SimpleTypeKey> = schema_set.arenas.simple_types.keys().collect();
301    let complex_type_keys: Vec<ComplexTypeKey> = schema_set.arenas.complex_types.keys().collect();
302
303    // Track which types are built-in (we don't add them to the graph)
304    let builtin_types = schema_set.builtin_types();
305
306    // Add simple types and their dependencies
307    for key in simple_type_keys {
308        let type_key = TypeKey::Simple(key);
309
310        // Skip built-in types
311        if is_builtin_simple_type(key, builtin_types) {
312            continue;
313        }
314
315        // Add type to graph
316        graph.add_type(type_key);
317        stats.simple_types += 1;
318
319        if let Some(type_def) = schema_set.arenas.simple_types.get(key) {
320            // Add dependency on base type (for restriction)
321            if let Some(base_key) = type_def.resolved_base_type {
322                if !is_builtin_type(base_key, builtin_types) {
323                    graph.add_dependency(type_key, base_key);
324                    stats.dependencies += 1;
325                }
326            }
327
328            // Add dependency on item type (for list)
329            if let Some(item_key) = type_def.resolved_item_type {
330                if !is_builtin_type(item_key, builtin_types) {
331                    graph.add_dependency(type_key, item_key);
332                    stats.dependencies += 1;
333                }
334            }
335
336            // Add dependencies on member types (for union)
337            for member_key in &type_def.resolved_member_types {
338                if !is_builtin_type(*member_key, builtin_types) {
339                    graph.add_dependency(type_key, *member_key);
340                    stats.dependencies += 1;
341                }
342            }
343        }
344    }
345
346    // Add complex types and their dependencies
347    for key in complex_type_keys {
348        let type_key = TypeKey::Complex(key);
349
350        if is_builtin_type(type_key, builtin_types) {
351            continue;
352        }
353
354        // Add type to graph
355        graph.add_type(type_key);
356        stats.complex_types += 1;
357
358        if let Some(type_def) = schema_set.arenas.complex_types.get(key) {
359            // Add dependency on base type (for extension/restriction)
360            if let Some(base_key) = type_def.resolved_base_type {
361                if !is_builtin_type(base_key, builtin_types) {
362                    graph.add_dependency(type_key, base_key);
363                    stats.dependencies += 1;
364                }
365            }
366        }
367    }
368
369    // Calculate root types (types with no dependencies)
370    for type_key in graph.all_types.iter() {
371        if !graph.has_dependencies(*type_key) {
372            stats.root_types += 1;
373        }
374    }
375
376    // Perform topological sort (this also validates there are no cycles)
377    graph.sort()?;
378
379    // Calculate max depth
380    stats.max_depth = calculate_max_depth(&graph);
381
382    Ok((graph, stats))
383}
384
385/// Check if a simple type key refers to a built-in type
386fn is_builtin_simple_type(
387    key: SimpleTypeKey,
388    builtin: &crate::types::builtin::BuiltinTypes,
389) -> bool {
390    // Check if this key matches any of the well-known built-in type keys
391    // We only need to check a few common ones since built-in types are
392    // created with specific keys during initialization
393    key == builtin.any_simple_type
394        || key == builtin.string
395        || key == builtin.boolean
396        || key == builtin.decimal
397        || key == builtin.float
398        || key == builtin.double
399        || key == builtin.duration
400        || key == builtin.datetime
401        || key == builtin.time
402        || key == builtin.date
403        || key == builtin.g_year_month
404        || key == builtin.g_year
405        || key == builtin.g_month_day
406        || key == builtin.g_day
407        || key == builtin.g_month
408        || key == builtin.hex_binary
409        || key == builtin.base64_binary
410        || key == builtin.any_uri
411        || key == builtin.xsi_schema_location_type
412        || key == builtin.qname
413        || key == builtin.notation
414        || key == builtin.normalized_string
415        || key == builtin.token
416        || key == builtin.language
417        || key == builtin.nmtoken
418        || key == builtin.nmtokens
419        || key == builtin.name
420        || key == builtin.ncname
421        || key == builtin.id
422        || key == builtin.idref
423        || key == builtin.idrefs
424        || key == builtin.entity
425        || key == builtin.entities
426        || key == builtin.integer
427        || key == builtin.non_positive_integer
428        || key == builtin.negative_integer
429        || key == builtin.long
430        || key == builtin.int
431        || key == builtin.short
432        || key == builtin.byte
433        || key == builtin.non_negative_integer
434        || key == builtin.unsigned_long
435        || key == builtin.unsigned_int
436        || key == builtin.unsigned_short
437        || key == builtin.unsigned_byte
438        || key == builtin.positive_integer
439        // Check optional XSD 1.1 types
440        || (builtin.any_atomic_type == Some(key))
441        || (builtin.year_month_duration == Some(key))
442        || (builtin.day_time_duration == Some(key))
443        || (builtin.datetime_stamp == Some(key))
444        || (builtin.untyped_atomic == Some(key))
445}
446
447/// Check if a type key refers to a built-in type
448fn is_builtin_type(key: TypeKey, builtin: &crate::types::builtin::BuiltinTypes) -> bool {
449    match key {
450        TypeKey::Simple(simple_key) => is_builtin_simple_type(simple_key, builtin),
451        TypeKey::Complex(complex_key) => complex_key == builtin.any_type,
452    }
453}
454
455/// Calculate the maximum depth of the dependency tree
456fn calculate_max_depth(graph: &DependencyGraph) -> usize {
457    let mut depth_cache: HashMap<TypeKey, usize> = HashMap::new();
458
459    fn get_depth(
460        key: TypeKey,
461        graph: &DependencyGraph,
462        cache: &mut HashMap<TypeKey, usize>,
463    ) -> usize {
464        if let Some(&cached) = cache.get(&key) {
465            return cached;
466        }
467
468        let deps = graph.get_dependencies(key);
469        let depth = if deps.is_empty() {
470            0
471        } else {
472            1 + deps
473                .iter()
474                .map(|&d| get_depth(d, graph, cache))
475                .max()
476                .unwrap_or(0)
477        };
478
479        cache.insert(key, depth);
480        depth
481    }
482
483    graph
484        .all_types
485        .iter()
486        .map(|&key| get_depth(key, graph, &mut depth_cache))
487        .max()
488        .unwrap_or(0)
489}
490
491#[cfg(test)]
492mod tests {
493    use super::*;
494    use slotmap::SlotMap;
495
496    // Helper to create type keys for testing
497    fn make_simple_key(_id: u32) -> TypeKey {
498        // Create a temporary SlotMap just to generate keys
499        // In tests we use these keys symbolically
500        thread_local! {
501            static SIMPLE_MAP: std::cell::RefCell<SlotMap<SimpleTypeKey, ()>> =
502                std::cell::RefCell::new(SlotMap::with_key());
503        }
504        SIMPLE_MAP.with(|m| TypeKey::Simple(m.borrow_mut().insert(())))
505    }
506
507    fn make_complex_key(_id: u32) -> TypeKey {
508        thread_local! {
509            static COMPLEX_MAP: std::cell::RefCell<SlotMap<ComplexTypeKey, ()>> =
510                std::cell::RefCell::new(SlotMap::with_key());
511        }
512        COMPLEX_MAP.with(|m| TypeKey::Complex(m.borrow_mut().insert(())))
513    }
514
515    #[test]
516    fn test_empty_graph() {
517        let mut graph = DependencyGraph::new();
518        assert_eq!(graph.type_count(), 0);
519        assert_eq!(graph.edge_count(), 0);
520        assert!(graph.sort().is_ok());
521        assert!(graph.compilation_order().is_empty());
522    }
523
524    #[test]
525    fn test_single_type_no_deps() {
526        let mut graph = DependencyGraph::new();
527        let t1 = make_simple_key(1);
528        graph.add_type(t1);
529
530        assert_eq!(graph.type_count(), 1);
531        assert_eq!(graph.edge_count(), 0);
532        assert!(!graph.has_dependencies(t1));
533
534        assert!(graph.sort().is_ok());
535        assert_eq!(graph.compilation_order().len(), 1);
536        assert_eq!(graph.compilation_order()[0], t1);
537    }
538
539    #[test]
540    fn test_simple_chain() {
541        // t3 -> t2 -> t1 (t3 depends on t2, t2 depends on t1)
542        let mut graph = DependencyGraph::new();
543        let t1 = make_simple_key(1);
544        let t2 = make_simple_key(2);
545        let t3 = make_simple_key(3);
546
547        graph.add_dependency(t2, t1); // t2 depends on t1
548        graph.add_dependency(t3, t2); // t3 depends on t2
549
550        assert_eq!(graph.type_count(), 3);
551        assert_eq!(graph.edge_count(), 2);
552
553        assert!(graph.sort().is_ok());
554        let order = graph.compilation_order();
555
556        // t1 must come before t2, t2 must come before t3
557        let pos_t1 = order.iter().position(|&t| t == t1).unwrap();
558        let pos_t2 = order.iter().position(|&t| t == t2).unwrap();
559        let pos_t3 = order.iter().position(|&t| t == t3).unwrap();
560
561        assert!(pos_t1 < pos_t2);
562        assert!(pos_t2 < pos_t3);
563    }
564
565    #[test]
566    fn test_diamond_dependency() {
567        //     t1
568        //    /  \
569        //   t2   t3
570        //    \  /
571        //     t4
572        // t4 depends on both t2 and t3, which both depend on t1
573        let mut graph = DependencyGraph::new();
574        let t1 = make_simple_key(1);
575        let t2 = make_simple_key(2);
576        let t3 = make_simple_key(3);
577        let t4 = make_simple_key(4);
578
579        graph.add_dependency(t2, t1);
580        graph.add_dependency(t3, t1);
581        graph.add_dependency(t4, t2);
582        graph.add_dependency(t4, t3);
583
584        assert_eq!(graph.type_count(), 4);
585        assert_eq!(graph.edge_count(), 4);
586
587        assert!(graph.sort().is_ok());
588        let order = graph.compilation_order();
589
590        // t1 must come before t2 and t3, both must come before t4
591        let pos_t1 = order.iter().position(|&t| t == t1).unwrap();
592        let pos_t2 = order.iter().position(|&t| t == t2).unwrap();
593        let pos_t3 = order.iter().position(|&t| t == t3).unwrap();
594        let pos_t4 = order.iter().position(|&t| t == t4).unwrap();
595
596        assert!(pos_t1 < pos_t2);
597        assert!(pos_t1 < pos_t3);
598        assert!(pos_t2 < pos_t4);
599        assert!(pos_t3 < pos_t4);
600    }
601
602    #[test]
603    fn test_cycle_detection_simple() {
604        // t1 -> t2 -> t1 (simple cycle)
605        let mut graph = DependencyGraph::new();
606        let t1 = make_simple_key(1);
607        let t2 = make_simple_key(2);
608
609        graph.add_dependency(t1, t2);
610        graph.add_dependency(t2, t1);
611
612        assert!(graph.has_cycle());
613        let result = graph.sort();
614        assert!(result.is_err());
615    }
616
617    #[test]
618    fn test_cycle_detection_triangle() {
619        // t1 -> t2 -> t3 -> t1 (triangle cycle)
620        let mut graph = DependencyGraph::new();
621        let t1 = make_simple_key(1);
622        let t2 = make_simple_key(2);
623        let t3 = make_simple_key(3);
624
625        graph.add_dependency(t1, t2);
626        graph.add_dependency(t2, t3);
627        graph.add_dependency(t3, t1);
628
629        assert!(graph.has_cycle());
630        let result = graph.sort();
631        assert!(result.is_err());
632    }
633
634    #[test]
635    fn test_self_dependency() {
636        // t1 -> t1 (self-cycle)
637        let mut graph = DependencyGraph::new();
638        let t1 = make_simple_key(1);
639
640        graph.add_dependency(t1, t1);
641
642        assert!(graph.has_cycle());
643        let result = graph.sort();
644        assert!(result.is_err());
645    }
646
647    #[test]
648    fn test_multiple_independent_chains() {
649        // Chain 1: t1 -> t2
650        // Chain 2: t3 -> t4
651        // (independent chains)
652        let mut graph = DependencyGraph::new();
653        let t1 = make_simple_key(1);
654        let t2 = make_simple_key(2);
655        let t3 = make_simple_key(3);
656        let t4 = make_simple_key(4);
657
658        graph.add_dependency(t2, t1);
659        graph.add_dependency(t4, t3);
660
661        assert_eq!(graph.type_count(), 4);
662        assert!(!graph.has_cycle());
663        assert!(graph.sort().is_ok());
664
665        let order = graph.compilation_order();
666
667        // t1 before t2, t3 before t4
668        let pos_t1 = order.iter().position(|&t| t == t1).unwrap();
669        let pos_t2 = order.iter().position(|&t| t == t2).unwrap();
670        let pos_t3 = order.iter().position(|&t| t == t3).unwrap();
671        let pos_t4 = order.iter().position(|&t| t == t4).unwrap();
672
673        assert!(pos_t1 < pos_t2);
674        assert!(pos_t3 < pos_t4);
675    }
676
677    #[test]
678    fn test_mixed_simple_and_complex_types() {
679        let mut graph = DependencyGraph::new();
680        let s1 = make_simple_key(1);
681        let c1 = make_complex_key(1);
682
683        // Complex type c1 depends on simple type s1
684        graph.add_dependency(c1, s1);
685
686        assert_eq!(graph.type_count(), 2);
687        assert!(graph.sort().is_ok());
688
689        let order = graph.compilation_order();
690        let pos_s1 = order.iter().position(|&t| t == s1).unwrap();
691        let pos_c1 = order.iter().position(|&t| t == c1).unwrap();
692
693        assert!(pos_s1 < pos_c1);
694    }
695
696    #[test]
697    fn test_repeated_sort() {
698        let mut graph = DependencyGraph::new();
699        let t1 = make_simple_key(1);
700        let t2 = make_simple_key(2);
701
702        graph.add_dependency(t2, t1);
703
704        // Sort multiple times
705        assert!(graph.sort().is_ok());
706        let first_order = graph.compilation_order().to_vec();
707
708        assert!(graph.sort().is_ok());
709        let second_order = graph.compilation_order().to_vec();
710
711        // Should be the same
712        assert_eq!(first_order, second_order);
713    }
714
715    #[test]
716    fn test_dependency_stats_default() {
717        let stats = DependencyStats::default();
718        assert_eq!(stats.simple_types, 0);
719        assert_eq!(stats.complex_types, 0);
720        assert_eq!(stats.dependencies, 0);
721        assert_eq!(stats.root_types, 0);
722        assert_eq!(stats.max_depth, 0);
723    }
724
725    #[test]
726    fn test_get_dependencies() {
727        let mut graph = DependencyGraph::new();
728        let t1 = make_simple_key(1);
729        let t2 = make_simple_key(2);
730        let t3 = make_simple_key(3);
731
732        graph.add_type(t1);
733        graph.add_dependency(t2, t1);
734        graph.add_dependency(t3, t1);
735        graph.add_dependency(t3, t2);
736
737        assert!(graph.get_dependencies(t1).is_empty());
738        assert_eq!(graph.get_dependencies(t2).len(), 1);
739        assert_eq!(graph.get_dependencies(t3).len(), 2);
740    }
741
742    #[test]
743    fn test_build_dependency_graph_empty_schema() {
744        let schema_set = SchemaSet::new();
745        let result = build_dependency_graph(&schema_set);
746        assert!(result.is_ok());
747
748        let (graph, stats) = result.unwrap();
749        assert_eq!(stats.simple_types, 0);
750        assert_eq!(stats.complex_types, 0);
751        assert_eq!(graph.type_count(), 0);
752    }
753
754    #[test]
755    fn test_build_dependency_graph_with_user_simple_type() {
756        use crate::arenas::SimpleTypeDefData;
757        use crate::parser::frames::SimpleTypeVariety;
758        use crate::schema::model::DerivationSet;
759        use crate::types::facets::FacetSet;
760
761        let mut schema_set = SchemaSet::new();
762
763        // Create a user-defined simple type that derives from xs:string
764        let type_name = schema_set.name_table.add("myStringType");
765        let string_key = schema_set.builtin_types().string;
766
767        let simple_data = SimpleTypeDefData {
768            name: Some(type_name),
769            target_namespace: None,
770            variety: SimpleTypeVariety::Atomic,
771            base_type: None,
772            item_type: None,
773            member_types: Vec::new(),
774            facets: FacetSet::new(),
775            final_derivation: DerivationSet::empty(),
776            id: None,
777            derivation_id: None,
778            annotation: None,
779            source: None,
780            // Already resolved base type (as if from resolution phase)
781            resolved_base_type: Some(TypeKey::Simple(string_key)),
782            resolved_item_type: None,
783            resolved_member_types: Vec::new(),
784            redefine_original: None,
785            deferred_item_type_error: None,
786        };
787
788        let _user_type_key = schema_set.arenas.alloc_simple_type(simple_data);
789
790        // Build the dependency graph
791        let result = build_dependency_graph(&schema_set);
792        assert!(result.is_ok());
793
794        let (graph, stats) = result.unwrap();
795        // One user-defined simple type
796        assert_eq!(stats.simple_types, 1);
797        // The user type depends on the built-in string, but built-in is excluded
798        assert_eq!(graph.type_count(), 1);
799    }
800
801    #[test]
802    fn test_build_dependency_graph_chain_of_user_types() {
803        use crate::arenas::SimpleTypeDefData;
804        use crate::parser::frames::SimpleTypeVariety;
805        use crate::schema::model::DerivationSet;
806        use crate::types::facets::FacetSet;
807
808        let mut schema_set = SchemaSet::new();
809
810        // Create type chain: myType2 -> myType1 -> xs:string
811        let type1_name = schema_set.name_table.add("myType1");
812        let type2_name = schema_set.name_table.add("myType2");
813        let string_key = schema_set.builtin_types().string;
814
815        // First type derives from xs:string
816        let type1_data = SimpleTypeDefData {
817            name: Some(type1_name),
818            target_namespace: None,
819            variety: SimpleTypeVariety::Atomic,
820            base_type: None,
821            item_type: None,
822            member_types: Vec::new(),
823            facets: FacetSet::new(),
824            final_derivation: DerivationSet::empty(),
825            id: None,
826            derivation_id: None,
827            annotation: None,
828            source: None,
829            resolved_base_type: Some(TypeKey::Simple(string_key)),
830            resolved_item_type: None,
831            resolved_member_types: Vec::new(),
832            redefine_original: None,
833            deferred_item_type_error: None,
834        };
835        let type1_key = schema_set.arenas.alloc_simple_type(type1_data);
836
837        // Second type derives from first type
838        let type2_data = SimpleTypeDefData {
839            name: Some(type2_name),
840            target_namespace: None,
841            variety: SimpleTypeVariety::Atomic,
842            base_type: None,
843            item_type: None,
844            member_types: Vec::new(),
845            facets: FacetSet::new(),
846            final_derivation: DerivationSet::empty(),
847            id: None,
848            derivation_id: None,
849            annotation: None,
850            source: None,
851            resolved_base_type: Some(TypeKey::Simple(type1_key)),
852            resolved_item_type: None,
853            resolved_member_types: Vec::new(),
854            redefine_original: None,
855            deferred_item_type_error: None,
856        };
857        let type2_key = schema_set.arenas.alloc_simple_type(type2_data);
858
859        // Build the dependency graph
860        let result = build_dependency_graph(&schema_set);
861        assert!(result.is_ok());
862
863        let (graph, stats) = result.unwrap();
864        // Two user-defined simple types
865        assert_eq!(stats.simple_types, 2);
866        // Type2 depends on type1
867        assert_eq!(stats.dependencies, 1);
868
869        // Sort should succeed (no cycles)
870        assert!(!graph.has_cycle());
871
872        // Verify order: type1 must come before type2
873        let order = graph.compilation_order();
874        let pos_type1 = order.iter().position(|&t| t == TypeKey::Simple(type1_key));
875        let pos_type2 = order.iter().position(|&t| t == TypeKey::Simple(type2_key));
876
877        assert!(pos_type1.is_some() && pos_type2.is_some());
878        assert!(pos_type1.unwrap() < pos_type2.unwrap());
879    }
880
881    #[test]
882    fn test_is_builtin_simple_type_xsd11() {
883        use crate::schema::model::XsdVersion;
884
885        let schema_set = SchemaSet::with_version(XsdVersion::V1_1);
886        let builtin = schema_set.builtin_types();
887
888        // XSD 1.0 types should be recognized
889        assert!(is_builtin_simple_type(builtin.string, builtin));
890        assert!(is_builtin_simple_type(builtin.integer, builtin));
891        assert!(is_builtin_simple_type(builtin.any_simple_type, builtin));
892
893        // XSD 1.1 types should also be recognized
894        if let Some(year_month_duration) = builtin.year_month_duration {
895            assert!(is_builtin_simple_type(year_month_duration, builtin));
896        }
897        if let Some(day_time_duration) = builtin.day_time_duration {
898            assert!(is_builtin_simple_type(day_time_duration, builtin));
899        }
900        if let Some(datetime_stamp) = builtin.datetime_stamp {
901            assert!(is_builtin_simple_type(datetime_stamp, builtin));
902        }
903        if let Some(untyped_atomic) = builtin.untyped_atomic {
904            assert!(is_builtin_simple_type(untyped_atomic, builtin));
905        }
906        if let Some(any_atomic_type) = builtin.any_atomic_type {
907            assert!(is_builtin_simple_type(any_atomic_type, builtin));
908        }
909    }
910}