From 4c9c7afb871d337172985be974239a20bd128c31 Mon Sep 17 00:00:00 2001 From: Tomaz Vieira Date: Fri, 18 Oct 2024 15:01:38 +0200 Subject: [PATCH] bug: Fix version parsing expecting only major.minor.patch --- Cargo.lock | 65 ++++++ bioimg_spec/Cargo.toml | 2 + bioimg_spec/src/rdf/model/unsupported/mod.rs | 2 +- bioimg_spec/src/rdf/version.rs | 198 ++++++------------- 4 files changed, 131 insertions(+), 136 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9b08f56..517b3ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -247,6 +247,7 @@ dependencies = [ name = "bioimg_spec" version = "0.1.14" dependencies = [ + "derive_more", "image", "iso8601-timestamp", "ndarray", @@ -259,6 +260,7 @@ dependencies = [ "thiserror", "url", "uuid", + "versions", ] [[package]] @@ -770,6 +772,27 @@ dependencies = [ "syn", ] +[[package]] +name = "derive_more" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + [[package]] name = "digest" version = "0.10.7" @@ -1585,6 +1608,15 @@ dependencies = [ "time", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -1801,6 +1833,12 @@ dependencies = [ "unicase", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.7.4" @@ -1884,6 +1922,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -3172,6 +3220,12 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "unsafe-libyaml" version = "0.2.11" @@ -3279,6 +3333,17 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "versions" +version = "6.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f25d498b63d1fdb376b4250f39ab3a5ee8d103957346abacd911e2d8b612c139" +dependencies = [ + "itertools", + "nom", + "serde", +] + [[package]] name = "walkdir" version = "2.5.0" diff --git a/bioimg_spec/Cargo.toml b/bioimg_spec/Cargo.toml index cdf3f94..8879fa5 100644 --- a/bioimg_spec/Cargo.toml +++ b/bioimg_spec/Cargo.toml @@ -24,6 +24,8 @@ paste = "1.0.14" iso8601-timestamp = { workspace = true } serde_yaml = { workspace = true } uuid = { workspace = true, features = ["v4"] } +versions = { version = "6.3.2", features = ["serde"] } +derive_more = { version = "1.0.0", features = ["display", "deref", "from_str"] } [target.'cfg(target_arch = "wasm32")'.dependencies] uuid = { workspace = true, features = ["v4", "js"] } diff --git a/bioimg_spec/src/rdf/model/unsupported/mod.rs b/bioimg_spec/src/rdf/model/unsupported/mod.rs index 10da0b0..63bc7e7 100644 --- a/bioimg_spec/src/rdf/model/unsupported/mod.rs +++ b/bioimg_spec/src/rdf/model/unsupported/mod.rs @@ -22,7 +22,7 @@ impl Display for Version_0_4_X_OrEarlier{ impl TryFrom for Version_0_4_X_OrEarlier{ type Error = LegacyVersionParsingError; fn try_from(value: Version) -> Result { - if value.major == 0 && value.minor <=4{ + if value < Version::version_0_5_0() { return Ok(Self(value)) } return Err(LegacyVersionParsingError::VersionTooHigh { found: value }) diff --git a/bioimg_spec/src/rdf/version.rs b/bioimg_spec/src/rdf/version.rs index ae351e0..8849228 100644 --- a/bioimg_spec/src/rdf/version.rs +++ b/bioimg_spec/src/rdf/version.rs @@ -1,93 +1,40 @@ -use std::{fmt::Display, num::ParseIntError, str::FromStr}; - -use serde::{Deserialize, Serialize}; -use thiserror::Error; - -#[derive(Error, Debug, PartialEq, Eq, Clone)] -pub enum VersionParsingError { - #[error("Version must have 3 fields, found {found}")] - WrongNumberOfComponents { found: usize }, - #[error("Could not parse version field: {0}")] - ParseIntError(ParseIntError), - #[error("Expected version '{expected}', found '{found}'")] - UnexpectedVersion { expected: Version, found: Version }, - #[error("Unexpected version number {found}; expecting {expecting}")] - UnexpectedVersionNumber{found: Version, expecting: String}, -} -impl From for VersionParsingError { - fn from(value: ParseIntError) -> Self { - return Self::ParseIntError(value); - } -} +use std::str::FromStr; -#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)] -#[serde(try_from = "VersionMsg")] -#[serde(into = "String")] -pub struct Version { - pub major: usize, - pub minor: usize, - pub patch: usize, -} -impl Ord for Version{ - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - match self.major.cmp(&other.major){ - std::cmp::Ordering::Equal => (), - out => return out - } - match self.minor.cmp(&other.minor){ - std::cmp::Ordering::Equal => (), - out => return out - } - return self.patch.cmp(&other.patch) - } -} -impl PartialOrd for Version{ - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} -impl Display for Version { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let self_string: String = self.clone().into(); - write!(f, "{self_string}",) - } -} -impl FromStr for Version{ - type Err = VersionParsingError; - fn from_str(s: &str) -> Result { - Self::try_from(s) + +#[derive(thiserror::Error, Debug)] +#[error("Error parsing version: {reason}")] +pub struct VersionParsingError { + reason: String, +} + +#[derive( + PartialOrd, Ord, Clone, Debug, PartialEq, Eq, + serde::Deserialize, serde::Serialize, + derive_more::Display, derive_more::Deref, derive_more::FromStr, +)] +#[serde(try_from="VersionMsg")] +#[serde(into="String")] +pub struct Version(versions::Version); + +impl Version{ + pub fn major_minor_patch(major: u32, minor: u32, patch: u32) -> Self{ + Version(versions::Version{ + chunks: versions::Chunks(vec![ + versions::Chunk::Numeric(major), + versions::Chunk::Numeric(minor), + versions::Chunk::Numeric(patch), + ]), + ..Default::default() + }) } -} -impl TryFrom<&str> for Version { - type Error = VersionParsingError; - fn try_from(value: &str) -> Result { - let mut parts = value - .split(".") - .map(|comp| comp.parse::()) - .collect::, _>>()?; - match parts.len(){ - 0 => return Err(VersionParsingError::WrongNumberOfComponents { found: 0 }), - 1..=3 => while parts.len() < 3{ - parts.push(0) - }, - num_comps => return Err(VersionParsingError::WrongNumberOfComponents { found: num_comps }) - } - let three_parts: [usize; 3] = parts - .try_into() - .map_err(|parts: Vec| VersionParsingError::WrongNumberOfComponents { found: parts.len() })?; - return Ok(Version { major: three_parts[0], minor: three_parts[1], patch: three_parts[2] }); + pub fn version_0_5_3() -> Version{ + Self::major_minor_patch(0, 5, 3) } -} -impl TryFrom for Version { - type Error = VersionParsingError; - fn try_from(value: String) -> Result { - return >::try_from(&value); + pub fn version_0_5_0() -> Version{ + Self::major_minor_patch(0, 5, 0) } -} - -impl Into for Version { - fn into(self) -> String { - format!("{}.{}.{}", self.major, self.minor, self.patch) + pub fn version_0_6_0() -> Version{ + Self::major_minor_patch(0, 6, 0) } } @@ -96,18 +43,36 @@ impl Into for Version { pub enum VersionMsg{ Text(String), Float(f32), + Int(u32), +} + +impl TryFrom for Version{ + type Error = VersionParsingError; + fn try_from(value: String) -> Result { + match versions::Version::from_str(&value){ + Err(e) => Err(VersionParsingError{reason: e.to_string()}), + Ok(v) => Ok(Version(v)) + } + } } impl TryFrom for Version{ type Error = VersionParsingError; fn try_from(value: VersionMsg) -> Result { match value{ - VersionMsg::Text(s) => Self::try_from(s), + VersionMsg::Text(s) => Self::try_from(s.to_owned()), VersionMsg::Float(f) => Self::try_from(f.to_string()), + VersionMsg::Int(i) => Self::try_from(i.to_string()), } } } +impl From for String{ + fn from(value: Version) -> Self { + value.0.to_string() + } +} + #[allow(non_camel_case_types)] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)] #[serde(try_from="Version")] @@ -115,32 +80,27 @@ pub struct Version_0_5_x(Version); impl Version_0_5_x{ pub fn new() -> Self{ - Self(Version{major: 0, minor: 5, patch: 3}) + Self(Version::version_0_5_3()) } } impl TryFrom for Version_0_5_x { type Error = VersionParsingError; fn try_from(version: Version) -> Result { - if version.major == 0 && version.minor == 5 { - Ok(Self(version)) - } else { - Err(VersionParsingError::UnexpectedVersionNumber{found: version, expecting: format!("0.5.*")}) + if version < Version::version_0_5_0() { + return Err(VersionParsingError { reason: format!("Version is too low: {version}") }) + } + if version >= Version::version_0_6_0() { + return Err(VersionParsingError { reason: format!("Version is too high: {version}") }) } + Ok(Self(version)) } } -#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)] -#[allow(non_camel_case_types)] +#[derive(serde::Serialize, serde::Deserialize, Clone, Debug, derive_more::Display)] #[serde(try_from = "Version")] pub struct FutureRdfVersion(Version); -impl Display for FutureRdfVersion{ - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.fmt(f) - } -} - #[derive(thiserror::Error, Debug)] pub enum FutureRdfVersionParsingError{ #[error("Version '{found}' is too low")] @@ -150,7 +110,7 @@ pub enum FutureRdfVersionParsingError{ impl TryFrom for FutureRdfVersion{ type Error = FutureRdfVersionParsingError; fn try_from(value: Version) -> Result { - match value.cmp(&Version{major: 0, minor: 5, patch: 3}){ + match value.cmp(&Version::version_0_5_3()){ std::cmp::Ordering::Greater => Ok(Self(value)), std::cmp::Ordering::Equal | std::cmp::Ordering::Less => Err(Self::Error::VersionTooLow { found: value }) } @@ -165,39 +125,7 @@ fn test_version_parsing() { assert_eq!( serde_json::from_value::(raw_version).unwrap(), - Version { major: 1, minor: 2, patch: 3 } - ); - assert_eq!( - Version::try_from("1.2"), - Err(VersionParsingError::WrongNumberOfComponents { found: 2 }) - ); - assert_eq!( - Version::try_from("1.2.bla"), - Err(VersionParsingError::ParseIntError( - "bla".parse::().expect_err("should fail parsing") - )) + Version::major_minor_patch(1, 2, 3) ); -} - -#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone, Copy)] -#[serde(try_from = "Version")] -#[serde(into = "Version")] -pub struct LiteralVersion; - -impl Into for LiteralVersion { - fn into(self) -> Version { - return Version { major: MAJOR, minor: MINOR, patch: PATCH }; - } -} - -impl TryFrom for LiteralVersion { - type Error = VersionParsingError; - - fn try_from(value: Version) -> Result { - if value.major == MAJOR && value.minor == MINOR && value.patch == PATCH { - Ok(Self) - } else { - Err(VersionParsingError::UnexpectedVersion { expected: Self.into(), found: value }) - } - } + Version::try_from("1.2.bla".to_owned()).expect_err("Should have failed to parse this verison"); }