use serde_json::Value as Json;
use crate::errors::GraphDDBError;
pub const SPEC_VERSION_SUPPORTED: &str = "1.1";
pub fn validate_spec_version(document: &Json, label: &str) -> Result<(), GraphDDBError> {
let version = document.get("version").and_then(Json::as_str);
let version = match version {
Some(v) if is_major_minor(v) => v,
other => {
let shown = other
.map(|s| format!("'{s}'"))
.unwrap_or_else(|| "None".to_string());
return Err(GraphDDBError::new(format!(
"{label}: missing or malformed spec version {shown} — expected \
'<major>.<minor>' (this runtime supports {SPEC_VERSION_SUPPORTED}). \
Regenerate the document with a matching graphddb generator."
)));
}
};
let (major, minor) = split(version);
let (smajor, sminor) = split(SPEC_VERSION_SUPPORTED);
if major != smajor || minor > sminor {
return Err(GraphDDBError::new(format!(
"{label}: document spec version '{version}' is not supported by this runtime \
(supports major {smajor}, minor <= {sminor}). A newer document may carry \
binding forms this runtime would silently mis-execute; upgrade graphddb_runtime \
or regenerate the document with a matching generator version."
)));
}
Ok(())
}
fn is_major_minor(v: &str) -> bool {
let mut parts = v.split('.');
match (parts.next(), parts.next(), parts.next()) {
(Some(a), Some(b), None) => {
!a.is_empty()
&& !b.is_empty()
&& a.bytes().all(|c| c.is_ascii_digit())
&& b.bytes().all(|c| c.is_ascii_digit())
}
_ => false,
}
}
fn split(v: &str) -> (u32, u32) {
let mut parts = v.split('.');
let major = parts.next().unwrap().parse().unwrap();
let minor = parts.next().unwrap().parse().unwrap();
(major, minor)
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn accepts_supported_and_older() {
assert!(validate_spec_version(&json!({"version": "1.1"}), "d").is_ok());
assert!(validate_spec_version(&json!({"version": "1.0"}), "d").is_ok());
}
#[test]
fn rejects_newer_minor_and_major_and_missing() {
assert!(validate_spec_version(&json!({"version": "1.2"}), "d").is_err());
assert!(validate_spec_version(&json!({"version": "2.0"}), "d").is_err());
assert!(validate_spec_version(&json!({"version": "bad"}), "d").is_err());
assert!(validate_spec_version(&json!({}), "d").is_err());
}
}