1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum SchemaVersion {
13 V01,
15 V02,
17 V03,
19 V04,
21 V05,
23 V06,
25 V07,
27 V08,
29 V09,
31 V10,
33 V11,
35 V12,
37}
38
39impl SchemaVersion {
40 pub fn parse(s: &str) -> Option<Self> {
42 match s {
43 "nika/workflow@0.1" => Some(Self::V01),
44 "nika/workflow@0.2" => Some(Self::V02),
45 "nika/workflow@0.3" => Some(Self::V03),
46 "nika/workflow@0.4" => Some(Self::V04),
47 "nika/workflow@0.5" => Some(Self::V05),
48 "nika/workflow@0.6" => Some(Self::V06),
49 "nika/workflow@0.7" => Some(Self::V07),
50 "nika/workflow@0.8" => Some(Self::V08),
51 "nika/workflow@0.9" => Some(Self::V09),
52 "nika/workflow@0.10" => Some(Self::V10),
53 "nika/workflow@0.11" => Some(Self::V11),
54 "nika/workflow@0.12" => Some(Self::V12),
55 _ => None,
56 }
57 }
58
59 pub fn as_str(&self) -> &'static str {
61 match self {
62 Self::V01 => "nika/workflow@0.1",
63 Self::V02 => "nika/workflow@0.2",
64 Self::V03 => "nika/workflow@0.3",
65 Self::V04 => "nika/workflow@0.4",
66 Self::V05 => "nika/workflow@0.5",
67 Self::V06 => "nika/workflow@0.6",
68 Self::V07 => "nika/workflow@0.7",
69 Self::V08 => "nika/workflow@0.8",
70 Self::V09 => "nika/workflow@0.9",
71 Self::V10 => "nika/workflow@0.10",
72 Self::V11 => "nika/workflow@0.11",
73 Self::V12 => "nika/workflow@0.12",
74 }
75 }
76
77 pub fn all() -> &'static [Self] {
79 &[
80 Self::V01,
81 Self::V02,
82 Self::V03,
83 Self::V04,
84 Self::V05,
85 Self::V06,
86 Self::V07,
87 Self::V08,
88 Self::V09,
89 Self::V10,
90 Self::V11,
91 Self::V12,
92 ]
93 }
94
95 pub fn latest() -> Self {
97 Self::V12
98 }
99
100 pub fn version_number(&self) -> u32 {
102 match self {
103 Self::V01 => 1,
104 Self::V02 => 2,
105 Self::V03 => 3,
106 Self::V04 => 4,
107 Self::V05 => 5,
108 Self::V06 => 6,
109 Self::V07 => 7,
110 Self::V08 => 8,
111 Self::V09 => 9,
112 Self::V10 => 10,
113 Self::V11 => 11,
114 Self::V12 => 12,
115 }
116 }
117
118 pub fn supports(&self, min_version: Self) -> bool {
120 self.version_number() >= min_version.version_number()
121 }
122
123 pub fn migration_hint(&self) -> Option<&'static str> {
127 match self {
128 Self::V01 => Some("@0.2 adds MCP servers, invoke: and agent: verbs"),
129 Self::V02 => Some("@0.3 adds for_each iteration and retry config"),
130 Self::V03 => Some("@0.4 adds decompose and structured output"),
131 Self::V04 => Some("@0.5 adds extended thinking and output format"),
132 Self::V05 => Some("@0.6 adds agents: definitions and skills:"),
133 Self::V06 => Some("@0.7 adds log: config and catch: error handling"),
134 Self::V07 => Some("@0.8 adds fetch: verb improvements"),
135 Self::V08 => Some("@0.9 adds context: files and imports:"),
136 Self::V09 => Some("@0.10 adds inputs: with defaults and artifacts:"),
137 Self::V10 => Some("@0.11 adds with: bindings replacing include:"),
138 Self::V11 => Some("@0.12 adds depends_on:, imports:, and ?? fallback operator"),
139 Self::V12 => None,
140 }
141 }
142
143 pub fn supports_mcp(&self) -> bool {
145 self.supports(Self::V02)
146 }
147
148 pub fn supports_invoke_agent(&self) -> bool {
150 self.supports(Self::V02)
151 }
152
153 pub fn supports_for_each(&self) -> bool {
155 self.supports(Self::V03)
156 }
157
158 pub fn supports_skills(&self) -> bool {
160 self.supports(Self::V06)
161 }
162
163 pub fn supports_agent_defs(&self) -> bool {
165 self.supports(Self::V06)
166 }
167
168 pub fn supports_context(&self) -> bool {
170 self.supports(Self::V09)
171 }
172
173 pub fn supports_include(&self) -> bool {
175 self.supports(Self::V09)
176 }
177
178 pub fn supports_inputs(&self) -> bool {
180 self.supports(Self::V10)
181 }
182
183 pub fn supports_artifacts(&self) -> bool {
185 self.supports(Self::V10)
186 }
187
188 pub fn supports_retry(&self) -> bool {
190 self.supports(Self::V03)
191 }
192
193 pub fn supports_with(&self) -> bool {
195 self.supports(Self::V12)
196 }
197
198 pub fn supports_imports(&self) -> bool {
200 self.supports(Self::V12)
201 }
202
203 pub fn supports_depends_on(&self) -> bool {
205 self.supports(Self::V12)
206 }
207}
208
209impl std::fmt::Display for SchemaVersion {
210 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
211 write!(f, "{}", self.as_str())
212 }
213}
214
215#[cfg(test)]
216mod tests {
217 use super::*;
218
219 #[test]
220 fn test_schema_version_parse() {
221 assert_eq!(
222 SchemaVersion::parse("nika/workflow@0.1"),
223 Some(SchemaVersion::V01)
224 );
225 assert_eq!(
226 SchemaVersion::parse("nika/workflow@0.12"),
227 Some(SchemaVersion::V12)
228 );
229 assert_eq!(SchemaVersion::parse("invalid"), None);
230 assert_eq!(SchemaVersion::parse("nika/workflow@0.99"), None);
231 }
232
233 #[test]
234 fn test_schema_version_latest() {
235 assert_eq!(SchemaVersion::latest(), SchemaVersion::V12);
236 assert_eq!(SchemaVersion::latest().as_str(), "nika/workflow@0.12");
237 }
238
239 #[test]
240 fn test_schema_version_number() {
241 assert_eq!(SchemaVersion::V01.version_number(), 1);
242 assert_eq!(SchemaVersion::V05.version_number(), 5);
243 assert_eq!(SchemaVersion::V12.version_number(), 12);
244 }
245
246 #[test]
247 fn test_schema_version_supports() {
248 assert!(SchemaVersion::V01.supports(SchemaVersion::V01));
250 assert!(!SchemaVersion::V01.supports(SchemaVersion::V02));
251
252 assert!(SchemaVersion::V12.supports(SchemaVersion::V01));
254 assert!(SchemaVersion::V12.supports(SchemaVersion::V12));
255 }
256
257 #[test]
258 fn test_schema_version_feature_gates() {
259 assert!(!SchemaVersion::V01.supports_mcp());
261 assert!(SchemaVersion::V02.supports_mcp());
262 assert!(SchemaVersion::V12.supports_mcp());
263
264 assert!(!SchemaVersion::V01.supports_for_each());
266 assert!(!SchemaVersion::V02.supports_for_each());
267 assert!(SchemaVersion::V03.supports_for_each());
268 assert!(SchemaVersion::V12.supports_for_each());
269
270 assert!(!SchemaVersion::V05.supports_skills());
272 assert!(SchemaVersion::V06.supports_skills());
273 assert!(SchemaVersion::V12.supports_skills());
274
275 assert!(!SchemaVersion::V08.supports_context());
277 assert!(SchemaVersion::V09.supports_context());
278 assert!(SchemaVersion::V12.supports_context());
279
280 assert!(!SchemaVersion::V09.supports_inputs());
282 assert!(SchemaVersion::V10.supports_inputs());
283
284 assert!(!SchemaVersion::V11.supports_with());
286 assert!(SchemaVersion::V12.supports_with());
287 assert!(!SchemaVersion::V11.supports_imports());
288 assert!(SchemaVersion::V12.supports_imports());
289 assert!(!SchemaVersion::V11.supports_depends_on());
290 assert!(SchemaVersion::V12.supports_depends_on());
291 }
292}