use crate::config::error::LoadError;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::path::Path;
pub const SUPPORTED: u32 = 1;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(transparent)]
pub struct Version(pub u32);
impl Version {
pub fn parse(raw: &str, origin: &Path) -> Result<Self, LoadError> {
let trimmed = raw.trim();
let parsed: u32 = trimmed.parse().map_err(|_| LoadError::Invalid {
path: origin.to_path_buf(),
key: ".".into(),
message: format!("expected an unsigned integer, got {trimmed:?}"),
})?;
if parsed > SUPPORTED {
return Err(LoadError::Invalid {
path: origin.to_path_buf(),
key: ".".into(),
message: format!(
"schema version {parsed} is newer than this harness understands \
(supported: {SUPPORTED}); upgrade lernie to read this config"
),
});
}
Ok(Version(parsed))
}
}
#[cfg(test)]
mod tests {
use super::*;
fn origin() -> &'static Path {
Path::new("<commit>:version")
}
#[test]
fn parses_a_bare_integer() {
assert_eq!(Version::parse("1\n", origin()).unwrap(), Version(SUPPORTED));
}
#[test]
fn tolerates_trailing_whitespace() {
assert_eq!(Version::parse(" 1 \n\n", origin()).unwrap(), Version(1));
}
#[test]
fn rejects_non_integer_content() {
let err = Version::parse("v1\n", origin()).unwrap_err();
match err {
LoadError::Invalid { message, .. } => {
assert!(message.contains("\"v1\""), "got: {message}");
}
other => panic!("expected Invalid, got {other:?}"),
}
}
#[test]
fn declines_a_version_newer_than_this_harness() {
let err = Version::parse(&format!("{}\n", SUPPORTED + 1), origin()).unwrap_err();
match err {
LoadError::Invalid { path, message, .. } => {
assert_eq!(path, origin());
assert!(message.contains(&(SUPPORTED + 1).to_string()), "{message}");
assert!(message.contains("upgrade lernie"), "{message}");
}
other => panic!("expected Invalid, got {other:?}"),
}
}
}