use crate::e173_types_no_serde;
e173_types_no_serde! {
#[derive(PartialEq)]
pub struct Version {
major: u64,
minor: u64,
patch: u64,
}
}
mod serde {
use super::Version;
use serde::{
de::{Unexpected, Visitor},
Deserialize, Deserializer, Serialize,
};
struct VersionVisitor;
impl<'a> VersionVisitor {
fn validate_version_part<E>(
&self,
value: &str,
parts: &mut impl Iterator<Item = &'a str>,
) -> Result<u64, E>
where
E: serde::de::Error,
{
let part = parts
.next()
.ok_or(E::invalid_value(Unexpected::Str(value), self))?;
if part.starts_with('0') && part.len() > 1 {
return Err(E::invalid_value(Unexpected::Str(value), self));
}
part.parse()
.map_err(|_| E::invalid_value(Unexpected::Str(value), self))
}
}
impl<'de> Visitor<'de> for VersionVisitor {
type Value = Version;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a string formatted as a 3-digit version number, e.g. 1.2.3")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
let mut parts = v.split('.');
let major = self.validate_version_part(v, &mut parts)?;
let minor = self.validate_version_part(v, &mut parts)?;
let patch = self.validate_version_part(v, &mut parts)?;
if parts.next().is_none() {
Ok(Version {
major,
minor,
patch,
})
} else {
Err(E::invalid_value(Unexpected::Str(v), &self))
}
}
}
impl<'de> Deserialize<'de> for Version {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(VersionVisitor)
}
}
impl Serialize for Version {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let vers = format!("{}.{}.{}", self.major, self.minor, self.patch);
serializer.serialize_str(&vers)
}
}
}
#[cfg(test)]
mod test {
use super::*;
macro_rules! test_correct_vers {
($str:literal, $maj:literal, $min:literal, $pat:literal) => {
assert_eq!(
serde_json::from_str::<Version>($str).unwrap(),
Version {
major: $maj,
minor: $min,
patch: $pat
}
)
};
}
#[test]
fn test_deserialize_correct_version() {
test_correct_vers!("\"1.0.0\"", 1, 0, 0);
test_correct_vers!("\"0.1.0\"", 0, 1, 0);
test_correct_vers!("\"1.2.3\"", 1, 2, 3);
test_correct_vers!("\"10000.20000.30000\"", 10000, 20000, 30000);
}
#[test]
fn test_deserialize_incorrect_version() {
assert!(serde_json::from_str::<Version>("\"01.0.0\"").is_err());
assert!(serde_json::from_str::<Version>("\"1.01.0\"").is_err());
assert!(serde_json::from_str::<Version>("\"1.0.01\"").is_err());
assert!(serde_json::from_str::<Version>("\"1.0\"").is_err());
assert!(serde_json::from_str::<Version>("\"1.0.0.3\"").is_err());
assert!(serde_json::from_str::<Version>("\"1.a.b\"").is_err());
assert!(serde_json::from_str::<Version>("\"1.0.0-beta.3\"").is_err());
assert!(serde_json::from_str::<Version>("\"1.0.0e\"").is_err());
assert!(serde_json::from_str::<Version>("\"foo\"").is_err());
assert!(serde_json::from_str::<Version>("\"\"").is_err());
assert!(serde_json::from_str::<Version>("40").is_err());
}
}