chant/domain/
spec_validation.rs1use crate::spec::{Spec, SpecStatus};
4
5pub fn is_spec_ready(spec: &Spec, all_specs: &[Spec]) -> bool {
21 if spec.frontmatter.status != SpecStatus::Pending {
23 return false;
24 }
25
26 if let Some(deps) = &spec.frontmatter.depends_on {
28 for dep_id in deps {
29 let dep = all_specs.iter().find(|s| s.id == *dep_id);
30 match dep {
31 Some(d) if d.frontmatter.status == SpecStatus::Completed => continue,
32 _ => return false, }
34 }
35 }
36
37 true
38}
39
40pub fn get_blockers(spec: &Spec, all_specs: &[Spec]) -> Vec<String> {
53 let mut blockers = Vec::new();
54
55 if let Some(deps) = &spec.frontmatter.depends_on {
56 for dep_id in deps {
57 let dep = all_specs.iter().find(|s| s.id == *dep_id);
58 match dep {
59 Some(d) if d.frontmatter.status == SpecStatus::Completed => continue,
60 _ => blockers.push(dep_id.clone()),
61 }
62 }
63 }
64
65 blockers
66}
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71
72 #[test]
73 fn test_is_spec_ready_no_deps() {
74 let spec = Spec::parse(
75 "001",
76 r#"---
77status: pending
78---
79# Test
80"#,
81 )
82 .unwrap();
83
84 assert!(is_spec_ready(&spec, &[]));
85 }
86
87 #[test]
88 fn test_is_spec_ready_with_completed_dep() {
89 let dep_spec = Spec::parse(
90 "001",
91 r#"---
92status: completed
93---
94# Dependency
95"#,
96 )
97 .unwrap();
98
99 let spec = Spec::parse(
100 "002",
101 r#"---
102status: pending
103depends_on:
104 - "001"
105---
106# Test
107"#,
108 )
109 .unwrap();
110
111 assert!(is_spec_ready(&spec, &[dep_spec]));
112 }
113
114 #[test]
115 fn test_is_spec_ready_with_pending_dep() {
116 let dep_spec = Spec::parse(
117 "001",
118 r#"---
119status: pending
120---
121# Dependency
122"#,
123 )
124 .unwrap();
125
126 let spec = Spec::parse(
127 "002",
128 r#"---
129status: pending
130depends_on:
131 - "001"
132---
133# Test
134"#,
135 )
136 .unwrap();
137
138 assert!(!is_spec_ready(&spec, &[dep_spec]));
139 }
140
141 #[test]
142 fn test_get_blockers_returns_pending_deps() {
143 let dep_spec = Spec::parse(
144 "001",
145 r#"---
146status: pending
147---
148# Dependency
149"#,
150 )
151 .unwrap();
152
153 let spec = Spec::parse(
154 "002",
155 r#"---
156status: pending
157depends_on:
158 - "001"
159---
160# Test
161"#,
162 )
163 .unwrap();
164
165 let blockers = get_blockers(&spec, &[dep_spec]);
166 assert_eq!(blockers, vec!["001"]);
167 }
168
169 #[test]
170 fn test_get_blockers_empty_when_all_complete() {
171 let dep_spec = Spec::parse(
172 "001",
173 r#"---
174status: completed
175---
176# Dependency
177"#,
178 )
179 .unwrap();
180
181 let spec = Spec::parse(
182 "002",
183 r#"---
184status: pending
185depends_on:
186 - "001"
187---
188# Test
189"#,
190 )
191 .unwrap();
192
193 let blockers = get_blockers(&spec, &[dep_spec]);
194 assert!(blockers.is_empty());
195 }
196}