aptu_core/config/
graph.rs1use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Deserialize, Serialize, Clone)]
20#[serde(default)]
21pub struct GraphConfig {
22 #[serde(default = "default_enabled")]
24 pub enabled: bool,
25 #[serde(default = "default_cache_ttl_hours")]
27 pub cache_ttl_hours: u64,
28 #[serde(default = "default_max_nodes")]
30 pub max_nodes: usize,
31 #[serde(default = "default_max_depth")]
33 pub max_depth: usize,
34}
35
36fn default_enabled() -> bool {
37 false
38}
39
40fn default_cache_ttl_hours() -> u64 {
41 24
42}
43
44fn default_max_nodes() -> usize {
45 50_000
46}
47
48fn default_max_depth() -> usize {
49 4
50}
51
52impl Default for GraphConfig {
53 fn default() -> Self {
54 Self {
55 enabled: default_enabled(),
56 cache_ttl_hours: default_cache_ttl_hours(),
57 max_nodes: default_max_nodes(),
58 max_depth: default_max_depth(),
59 }
60 }
61}
62
63impl GraphConfig {
64 #[must_use]
69 pub fn validate_consistency(&self) -> Vec<String> {
70 let mut warnings = Vec::new();
71
72 if self.enabled && self.max_nodes == 0 {
73 warnings.push(
74 "max_nodes is 0 while graph is enabled: blast-radius subgraph will always be empty"
75 .to_string(),
76 );
77 }
78
79 if self.enabled && self.max_depth == 0 {
80 warnings.push(
81 "max_depth is 0 while graph is enabled: blast-radius subgraph will always be empty"
82 .to_string(),
83 );
84 }
85
86 if self.enabled && self.cache_ttl_hours == 0 {
87 warnings.push(
88 "cache_ttl_hours is 0 while graph is enabled: cache is rebuilt on every review"
89 .to_string(),
90 );
91 }
92
93 warnings
94 }
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100
101 #[test]
102 fn test_default_disabled() {
103 let config = GraphConfig::default();
104 assert!(!config.enabled, "graph should be disabled by default");
105 assert_eq!(config.cache_ttl_hours, 24);
106 assert_eq!(config.max_nodes, 50_000);
107 }
108
109 #[test]
110 fn test_validate_consistency_ok() {
111 let config = GraphConfig::default();
112 let warnings = config.validate_consistency();
113 assert!(
114 warnings.is_empty(),
115 "default config should produce no warnings: {:?}",
116 warnings
117 );
118 }
119
120 #[test]
121 fn test_validate_consistency_zero_max_nodes_enabled() {
122 let config = GraphConfig {
123 enabled: true,
124 max_nodes: 0,
125 ..GraphConfig::default()
126 };
127 let warnings = config.validate_consistency();
128 assert_eq!(warnings.len(), 1, "should produce exactly 1 warning");
129 assert!(warnings[0].contains("max_nodes is 0"));
130 }
131
132 #[test]
133 fn test_deserializes_from_toml_with_missing_fields() {
134 let toml_str = "";
135 let config: GraphConfig = toml::from_str(toml_str).unwrap();
136 assert!(!config.enabled);
137 assert_eq!(config.cache_ttl_hours, 24);
138 assert_eq!(config.max_nodes, 50_000);
139 }
140}