use yaml_serde::Value;
pub const SPEC_VERSION_FLOOR: u32 = 2;
pub const SPEC_VERSION_ARRAY_MATCHING: u32 = 3;
pub const SPEC_VERSION_SUPPORTED: u32 = 3;
#[must_use]
pub fn resolve_major(declared: Option<u32>) -> u32 {
declared.unwrap_or(SPEC_VERSION_FLOOR)
}
#[must_use]
pub fn array_matching_enabled(declared: Option<u32>) -> bool {
resolve_major(declared) >= SPEC_VERSION_ARRAY_MATCHING
}
#[must_use]
pub fn is_unsupported(declared: Option<u32>) -> bool {
matches!(declared, Some(major) if major > SPEC_VERSION_SUPPORTED)
}
#[must_use]
pub fn major_from_value(value: &Value) -> Option<u32> {
match value {
Value::Number(n) => {
if let Some(u) = n.as_u64() {
u32::try_from(u).ok()
} else if let Some(i) = n.as_i64() {
u32::try_from(i).ok()
} else if let Some(f) = n.as_f64() {
if f.is_finite() && f >= 0.0 {
Some(f.trunc() as u32)
} else {
None
}
} else {
None
}
}
Value::String(s) => major_from_str(s),
_ => None,
}
}
#[must_use]
pub fn major_from_str(s: &str) -> Option<u32> {
let head = s
.trim()
.trim_start_matches(['v', 'V'])
.split('.')
.next()
.unwrap_or("");
head.parse::<u32>().ok()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn floor_and_gating() {
assert_eq!(resolve_major(None), SPEC_VERSION_FLOOR);
assert_eq!(resolve_major(Some(3)), 3);
assert!(!array_matching_enabled(None));
assert!(!array_matching_enabled(Some(2)));
assert!(array_matching_enabled(Some(3)));
assert!(array_matching_enabled(Some(4)));
}
#[test]
fn unsupported_major() {
assert!(!is_unsupported(None));
assert!(!is_unsupported(Some(SPEC_VERSION_SUPPORTED)));
assert!(is_unsupported(Some(SPEC_VERSION_SUPPORTED + 1)));
}
#[test]
fn major_parsing() {
assert_eq!(major_from_str("3"), Some(3));
assert_eq!(major_from_str("2.1.0"), Some(2));
assert_eq!(major_from_str("v3.1"), Some(3));
assert_eq!(major_from_str(" 2 "), Some(2));
assert_eq!(major_from_str("abc"), None);
assert_eq!(major_from_str(""), None);
}
}