Skip to main content

fraiseql_core/schema/dependency_graph/
graph.rs

1//! Core graph structure and direct query methods.
2
3use std::collections::{HashMap, HashSet};
4
5/// Schema dependency graph for analyzing type relationships.
6///
7/// This graph tracks which types depend on which other types, enabling:
8/// - Circular dependency detection
9/// - Unused type detection
10/// - Impact analysis for schema changes
11#[derive(Debug, Clone)]
12pub struct SchemaDependencyGraph {
13    /// Map of type name to types it depends on (outgoing edges).
14    pub(super) outgoing:   HashMap<String, HashSet<String>>,
15    /// Map of type name to types that depend on it (incoming edges).
16    pub(super) incoming:   HashMap<String, HashSet<String>>,
17    /// All type names in the schema.
18    pub(super) all_types:  HashSet<String>,
19    /// Root types that are always considered "used" (Query, Mutation, Subscription).
20    pub(super) root_types: HashSet<String>,
21}
22
23impl SchemaDependencyGraph {
24    /// Get all types that a given type depends on (outgoing edges).
25    #[must_use]
26    pub fn dependencies_of(&self, type_name: &str) -> Vec<String> {
27        self.outgoing
28            .get(type_name)
29            .map(|deps| {
30                let mut v: Vec<_> = deps.iter().cloned().collect();
31                v.sort();
32                v
33            })
34            .unwrap_or_default()
35    }
36
37    /// Get all types that depend on a given type (incoming edges).
38    #[must_use]
39    pub fn dependents_of(&self, type_name: &str) -> Vec<String> {
40        self.incoming
41            .get(type_name)
42            .map(|refs| {
43                let mut v: Vec<_> = refs.iter().cloned().collect();
44                v.sort();
45                v
46            })
47            .unwrap_or_default()
48    }
49
50    /// Get all type names in the graph.
51    #[must_use]
52    pub fn all_types(&self) -> Vec<String> {
53        let mut types: Vec<_> = self.all_types.iter().cloned().collect();
54        types.sort();
55        types
56    }
57
58    /// Get the total number of types in the graph.
59    #[must_use]
60    pub fn type_count(&self) -> usize {
61        self.all_types.len()
62    }
63
64    /// Check if a type exists in the graph.
65    #[must_use]
66    pub fn has_type(&self, type_name: &str) -> bool {
67        self.all_types.contains(type_name)
68    }
69}