#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SchemaVersion {
V01,
V02,
V03,
V04,
V05,
V06,
V07,
V08,
V09,
V10,
V11,
V12,
}
impl SchemaVersion {
pub fn parse(s: &str) -> Option<Self> {
match s {
"nika/workflow@0.1" => Some(Self::V01),
"nika/workflow@0.2" => Some(Self::V02),
"nika/workflow@0.3" => Some(Self::V03),
"nika/workflow@0.4" => Some(Self::V04),
"nika/workflow@0.5" => Some(Self::V05),
"nika/workflow@0.6" => Some(Self::V06),
"nika/workflow@0.7" => Some(Self::V07),
"nika/workflow@0.8" => Some(Self::V08),
"nika/workflow@0.9" => Some(Self::V09),
"nika/workflow@0.10" => Some(Self::V10),
"nika/workflow@0.11" => Some(Self::V11),
"nika/workflow@0.12" => Some(Self::V12),
_ => None,
}
}
pub fn as_str(&self) -> &'static str {
match self {
Self::V01 => "nika/workflow@0.1",
Self::V02 => "nika/workflow@0.2",
Self::V03 => "nika/workflow@0.3",
Self::V04 => "nika/workflow@0.4",
Self::V05 => "nika/workflow@0.5",
Self::V06 => "nika/workflow@0.6",
Self::V07 => "nika/workflow@0.7",
Self::V08 => "nika/workflow@0.8",
Self::V09 => "nika/workflow@0.9",
Self::V10 => "nika/workflow@0.10",
Self::V11 => "nika/workflow@0.11",
Self::V12 => "nika/workflow@0.12",
}
}
pub fn all() -> &'static [Self] {
&[
Self::V01,
Self::V02,
Self::V03,
Self::V04,
Self::V05,
Self::V06,
Self::V07,
Self::V08,
Self::V09,
Self::V10,
Self::V11,
Self::V12,
]
}
pub fn latest() -> Self {
Self::V12
}
pub fn version_number(&self) -> u32 {
match self {
Self::V01 => 1,
Self::V02 => 2,
Self::V03 => 3,
Self::V04 => 4,
Self::V05 => 5,
Self::V06 => 6,
Self::V07 => 7,
Self::V08 => 8,
Self::V09 => 9,
Self::V10 => 10,
Self::V11 => 11,
Self::V12 => 12,
}
}
pub fn supports(&self, min_version: Self) -> bool {
self.version_number() >= min_version.version_number()
}
pub fn migration_hint(&self) -> Option<&'static str> {
match self {
Self::V01 => Some("@0.2 adds MCP servers, invoke: and agent: verbs"),
Self::V02 => Some("@0.3 adds for_each iteration and retry config"),
Self::V03 => Some("@0.4 adds decompose and structured output"),
Self::V04 => Some("@0.5 adds extended thinking and output format"),
Self::V05 => Some("@0.6 adds agents: definitions and skills:"),
Self::V06 => Some("@0.7 adds log: config and catch: error handling"),
Self::V07 => Some("@0.8 adds fetch: verb improvements"),
Self::V08 => Some("@0.9 adds context: files and imports:"),
Self::V09 => Some("@0.10 adds inputs: with defaults and artifacts:"),
Self::V10 => Some("@0.11 adds with: bindings replacing include:"),
Self::V11 => Some("@0.12 adds depends_on:, imports:, and ?? fallback operator"),
Self::V12 => None,
}
}
pub fn supports_mcp(&self) -> bool {
self.supports(Self::V02)
}
pub fn supports_invoke_agent(&self) -> bool {
self.supports(Self::V02)
}
pub fn supports_for_each(&self) -> bool {
self.supports(Self::V03)
}
pub fn supports_skills(&self) -> bool {
self.supports(Self::V06)
}
pub fn supports_agent_defs(&self) -> bool {
self.supports(Self::V06)
}
pub fn supports_context(&self) -> bool {
self.supports(Self::V09)
}
pub fn supports_include(&self) -> bool {
self.supports(Self::V09)
}
pub fn supports_inputs(&self) -> bool {
self.supports(Self::V10)
}
pub fn supports_artifacts(&self) -> bool {
self.supports(Self::V10)
}
pub fn supports_retry(&self) -> bool {
self.supports(Self::V03)
}
pub fn supports_with(&self) -> bool {
self.supports(Self::V12)
}
pub fn supports_imports(&self) -> bool {
self.supports(Self::V12)
}
pub fn supports_depends_on(&self) -> bool {
self.supports(Self::V12)
}
}
impl std::fmt::Display for SchemaVersion {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_schema_version_parse() {
assert_eq!(
SchemaVersion::parse("nika/workflow@0.1"),
Some(SchemaVersion::V01)
);
assert_eq!(
SchemaVersion::parse("nika/workflow@0.12"),
Some(SchemaVersion::V12)
);
assert_eq!(SchemaVersion::parse("invalid"), None);
assert_eq!(SchemaVersion::parse("nika/workflow@0.99"), None);
}
#[test]
fn test_schema_version_latest() {
assert_eq!(SchemaVersion::latest(), SchemaVersion::V12);
assert_eq!(SchemaVersion::latest().as_str(), "nika/workflow@0.12");
}
#[test]
fn test_schema_version_number() {
assert_eq!(SchemaVersion::V01.version_number(), 1);
assert_eq!(SchemaVersion::V05.version_number(), 5);
assert_eq!(SchemaVersion::V12.version_number(), 12);
}
#[test]
fn test_schema_version_supports() {
assert!(SchemaVersion::V01.supports(SchemaVersion::V01));
assert!(!SchemaVersion::V01.supports(SchemaVersion::V02));
assert!(SchemaVersion::V12.supports(SchemaVersion::V01));
assert!(SchemaVersion::V12.supports(SchemaVersion::V12));
}
#[test]
fn test_schema_version_feature_gates() {
assert!(!SchemaVersion::V01.supports_mcp());
assert!(SchemaVersion::V02.supports_mcp());
assert!(SchemaVersion::V12.supports_mcp());
assert!(!SchemaVersion::V01.supports_for_each());
assert!(!SchemaVersion::V02.supports_for_each());
assert!(SchemaVersion::V03.supports_for_each());
assert!(SchemaVersion::V12.supports_for_each());
assert!(!SchemaVersion::V05.supports_skills());
assert!(SchemaVersion::V06.supports_skills());
assert!(SchemaVersion::V12.supports_skills());
assert!(!SchemaVersion::V08.supports_context());
assert!(SchemaVersion::V09.supports_context());
assert!(SchemaVersion::V12.supports_context());
assert!(!SchemaVersion::V09.supports_inputs());
assert!(SchemaVersion::V10.supports_inputs());
assert!(!SchemaVersion::V11.supports_with());
assert!(SchemaVersion::V12.supports_with());
assert!(!SchemaVersion::V11.supports_imports());
assert!(SchemaVersion::V12.supports_imports());
assert!(!SchemaVersion::V11.supports_depends_on());
assert!(SchemaVersion::V12.supports_depends_on());
}
}