use std::fmt;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct ApiVersion {
pub major: u32,
pub minor: u32,
}
impl ApiVersion {
pub const fn new(major: u32, minor: u32) -> Self {
Self { major, minor }
}
}
impl fmt::Display for ApiVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.minor == 0 {
write!(f, "v{}", self.major)
} else {
write!(f, "v{}.{}", self.major, self.minor)
}
}
}
pub fn parse_api_version(s: &str) -> Option<ApiVersion> {
let s = s.trim().strip_prefix('v').unwrap_or(s.trim());
if s.is_empty() {
return None;
}
let mut parts = s.split('.');
let major: u32 = parts.next()?.parse().ok()?;
let minor: u32 = parts.next().map(|p| p.parse().unwrap_or(0)).unwrap_or(0);
if parts.next().is_some() {
return None; }
Some(ApiVersion { major, minor })
}
pub fn is_compatible(supported: ApiVersion, requested: ApiVersion) -> bool {
requested.major == supported.major
}
pub const MCP_API_VERSION: ApiVersion = ApiVersion::new(1, 0);
pub const MCP_VERSION_META_KEY: &str = "io.kaptein/apiVersion";
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_accepts_forms() {
assert_eq!(parse_api_version("1"), Some(ApiVersion::new(1, 0)));
assert_eq!(parse_api_version("v1"), Some(ApiVersion::new(1, 0)));
assert_eq!(parse_api_version("1.2"), Some(ApiVersion::new(1, 2)));
assert_eq!(parse_api_version("v1.2"), Some(ApiVersion::new(1, 2)));
assert_eq!(parse_api_version(" v2 "), Some(ApiVersion::new(2, 0)));
}
#[test]
fn parse_rejects_malformed() {
assert_eq!(parse_api_version(""), None);
assert_eq!(parse_api_version("v"), None);
assert_eq!(parse_api_version("abc"), None);
assert_eq!(parse_api_version("1.2.3"), None);
assert_eq!(parse_api_version("v1.2.3"), None);
}
#[test]
fn same_major_is_compatible() {
assert!(is_compatible(ApiVersion::new(1, 0), ApiVersion::new(1, 9)));
assert!(is_compatible(ApiVersion::new(1, 9), ApiVersion::new(1, 0)));
}
#[test]
fn different_major_is_incompatible() {
assert!(!is_compatible(ApiVersion::new(1, 0), ApiVersion::new(2, 0)));
assert!(!is_compatible(ApiVersion::new(2, 0), ApiVersion::new(1, 0)));
}
#[test]
fn display_omits_zero_minor() {
assert_eq!(ApiVersion::new(1, 0).to_string(), "v1");
assert_eq!(ApiVersion::new(1, 2).to_string(), "v1.2");
}
}