### Serde Serialization Setup Source: https://docs.rs/versions/7.0.0/src/versions/lib.rs.html Boilerplate for setting up a struct to be serialized with custom Requirement serialization. ```rust #[cfg(feature = "serde")] #[test] fn test_serialize() { use serde::Serialize; #[derive(Serialize)] struct SerializableRequirement { #[serde(serialize_with = "Requirement::serialize")] ``` -------------------------------- ### Match Version Against Requirement Source: https://docs.rs/versions/7.0.0/src/versions/require.rs.html Example usage of the matches method to validate a version against an exact requirement. ```rust let constraint_eq = Requirement::from_str("=1.0.0").unwrap(); assert!(constraint_eq.matches(&Versioning::new("1.0.0").unwrap())); assert!(!constraint_eq.matches(&Versioning::new("1.1.0").unwrap())); assert!(!constraint_eq.matches(&Versioning::new("0.9.0").unwrap())); ``` -------------------------------- ### Parse SemVer metadata Source: https://docs.rs/versions/7.0.0/src/versions/parsers.rs.html Parses SemVer metadata starting with a '+' character. ```rust pub fn meta(i: &str) -> IResult<&str, String> { let (i, _) = char('+')(i)?; map( take_while1(|c: char| c.is_ascii_alphanumeric() || c == '-' || c == '.'), |s: &str| s.to_owned(), ) .parse(i) } ``` -------------------------------- ### Create and Inspect Versioning Source: https://docs.rs/versions/7.0.0/src/versions/versioning.rs.html Demonstrates creating a Versioning instance from strings and checking its type using helper methods. ```rust use versions::Versioning; let a = Versioning::new("1.2.3-1").unwrap(); // SemVer. let b = Versioning::new("1.2.3r1").unwrap(); // Not SemVer but good enough. let c = Versioning::new("000.007-1").unwrap(); // Garbage. assert!(a.is_ideal()); assert!(b.is_general()); assert!(c.is_complex()); ``` -------------------------------- ### Matching Versions with Requirement Source: https://docs.rs/versions/7.0.0/versions/struct.Requirement.html Demonstrates how to parse requirements from strings and verify if specific versions satisfy those constraints. ```rust use versions::{Requirement, Versioning}; use std::str::FromStr; let gt = Requirement::from_str(">=1.0.0").unwrap(); assert!(gt.matches(&Versioning::new("1.0.0").unwrap())); assert!(gt.matches(&Versioning::new("1.1.0").unwrap())); assert!(!gt.matches(&Versioning::new("0.9.0").unwrap())); let wild = Requirement::from_str("*").unwrap(); assert!(wild.matches(&Versioning::new("1.0.0").unwrap())); assert!(wild.matches(&Versioning::new("1.1.0").unwrap())); assert!(wild.matches(&Versioning::new("0.9.0").unwrap())); let constraint_eq = Requirement::from_str("=1.0.0").unwrap(); assert!(constraint_eq.matches(&Versioning::new("1.0.0").unwrap())); assert!(!constraint_eq.matches(&Versioning::new("1.1.0").unwrap())); assert!(!constraint_eq.matches(&Versioning::new("0.9.0").unwrap())); ``` -------------------------------- ### Versioning::new Source: https://docs.rs/versions/7.0.0/versions/enum.Versioning.html Attempts to create a Versioning instance by parsing the input string as SemVer, then Version, and finally Mess. ```APIDOC ## pub fn new(s: S) -> Option where S: AsRef ### Description Creates a `Versioning` instance by attempting to parse the input string as `SemVer`, then as a `Version`, and finally as a `Mess`. ### Parameters - **s** (AsRef) - Required - The version string to parse. ### Returns - **Option** - Returns `Some(Versioning)` if parsing succeeds, otherwise `None`. ``` -------------------------------- ### Parsing and Comparing Versions Source: https://docs.rs/versions/7.0.0/src/versions/version.rs.html Demonstrates how to parse non-SemVer strings into a Version struct and verify their validity. ```rust use versions::{SemVer, Version}; // None of these are SemVer, but can still be parsed and compared. let vers = vec!["0.25-2", "8.u51-1", "20150826-1", "1:2.3.4"]; for v in vers { assert!(SemVer::new(v).is_none()); assert!(Version::new(v).is_some()); } ``` -------------------------------- ### Implement Display for Version Source: https://docs.rs/versions/7.0.0/src/versions/version.rs.html Formats the Version struct into a string representation including epoch, chunks, release, and metadata. ```rust impl std::fmt::Display for Version { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { if let Some(e) = self.epoch { write!(f, "{}:", e)?; } write!(f, "{}", self.chunks)?; if let Some(r) = &self.release { write!(f, "-{}", r)?; } if let Some(m) = &self.meta { write!(f, "+{}", m)?; } Ok(()) } } ``` -------------------------------- ### TryFrom String Conversion for Versioning Source: https://docs.rs/versions/7.0.0/src/versions/versioning.rs.html Demonstrates converting a string slice into a Versioning instance using the TryFrom trait. ```rust use versions::Versioning; let orig = "1.2.3"; let prsd: Versioning = orig.try_into().unwrap(); assert_eq!(orig, prsd.to_string()); ``` -------------------------------- ### Serde Deserialization for Versioning Source: https://docs.rs/versions/7.0.0/src/versions/lib.rs.html Demonstrates deserializing Versioning and Requirement types from JSON strings using custom Serde deserializers. ```rust #[cfg(feature = "serde")] #[test] fn test_deserialize() { use serde::Deserialize; #[derive(Deserialize)] struct DeserializableVersioning { #[serde(deserialize_with = "Versioning::deserialize_pretty")] version: Versioning, } let deserializable: DeserializableVersioning = serde_json::from_str(r#"{"version": "1.2.3"}"#).unwrap(); assert_eq!(deserializable.version, Versioning::new("1.2.3").unwrap()); #[derive(Deserialize)] struct DeserializableRequirement { #[serde(deserialize_with = "Requirement::deserialize")] version: Requirement, } let deserializable: DeserializableRequirement = serde_json::from_str(r#"{"version": ">=1.2.3"}"#).unwrap(); assert_eq!( deserializable.version, Requirement { op: Op::GreaterEq, version: Some(Versioning::new("1.2.3").unwrap()) } ); } ``` -------------------------------- ### Parsing and Validating Mess Versions Source: https://docs.rs/versions/7.0.0/src/versions/mess.rs.html Demonstrates how to initialize a Mess version and verify that it is not a standard SemVer or Version type. ```rust use versions::{Mess, SemVer, Version}; let mess = "20.0026.1_0-2+0.93"; let s = SemVer::new(mess); let v = Version::new(mess); let m = Mess::new(mess); assert!(s.is_none()); assert!(v.is_none()); assert_eq!(Some(mess.to_string()), m.map(|v| format!("{}", v))); ``` -------------------------------- ### Parse and Compare Versions Source: https://docs.rs/versions/7.0.0/index.html Use Versioning::new to parse arbitrary version strings. The resulting objects support comparison even when they follow different versioning schemes. ```rust use versions::Versioning; let good = Versioning::new("1.6.0").unwrap(); let evil = Versioning::new("1.6.0a+2014+m872b87e73dfb-1").unwrap(); assert!(good.is_ideal()); // It parsed as a `SemVer`. assert!(evil.is_complex()); // It parsed as a `Mess`. assert!(good > evil); // We can compare them anyway! ``` -------------------------------- ### Parse a SemVer string Source: https://docs.rs/versions/7.0.0/src/versions/semver.rs.html Demonstrates how to create a new SemVer instance from a string slice. ```rust use versions::SemVer; let orig = "1.2.3-r1+git"; let attempt = SemVer::new(orig).unwrap(); assert_eq!(orig, format!("{}", attempt)); ``` -------------------------------- ### Compare Mixed Versioning Formats Source: https://docs.rs/versions/7.0.0/src/versions/lib.rs.html Validates comparison logic across a wide range of versioning formats and edge cases. ```rust #[test] fn mixed_comparisons() { cmp_versioning("1.2.3", "1.2.3.0"); cmp_versioning("1.2.3.git", "1.2.3"); cmp_versioning("1.2.2r1-1", "1.2.3-1"); cmp_versioning("1.2.3-1", "1.2.4r1-1"); cmp_versioning("1.2.3-1", "2+0007-1"); cmp_versioning("1.2.3r1-1", "2+0007-1"); cmp_versioning("1.2-5", "1.2.3-1"); cmp_versioning("1.6.0a+2014+m872b87e73dfb-1", "1.6.0-1"); cmp_versioning("1.11.0.git.20200404-1", "1.11.0+20200830-1"); cmp_versioning("0.17.0+r8+gc41db5f1-1", "0.17.0+r157+g584760cf-1"); cmp_versioning("2.2.3", "10e"); cmp_versioning("e.2.3", "1.2.3"); cmp_versioning("0.4.8-1", "0.4.9-1"); cmp_versioning("2.1.16102-2", "2.1.17627-1"); cmp_versioning("8.64.0.81-1", "8.65.0.78-1"); cmp_versioning("1.3.00.16851-1", "1.3.00.25560-1"); cmp_versioning("1:3.20-1", "1:3.20.1-1"); cmp_versioning("5.2.458699.0906-1", "5.3.472687.1012-1"); cmp_versioning("1.2.3", "1:1.2.0"); } fn cmp_versioning(a: &str, b: &str) { let x = Versioning::new(a).unwrap(); let y = Versioning::new(b).unwrap(); assert!( x < y, "\nAttempted: {} < {}\nLesser: {:?}\nGreater: {:?}\nThinks: {:?}", x, y, x, y, x.cmp(&y) ); } ``` -------------------------------- ### Parse string to SemVer using TryFrom Source: https://docs.rs/versions/7.0.0/versions/struct.SemVer.html Demonstrates converting a string slice into a SemVer instance using the try_into method. ```rust use versions::SemVer; let orig = "1.2.3"; let prsd: SemVer = orig.try_into().unwrap(); assert_eq!(orig, prsd.to_string()); ``` -------------------------------- ### Requirement::new Source: https://docs.rs/versions/7.0.0/versions/struct.Requirement.html Parses a new Requirement instance from a string slice. ```APIDOC ## pub fn new(s: &str) -> Option ### Description Parses a new `Requirement` from a string. ### Parameters - **s** (&str) - The version requirement string to parse. ### Returns - **Option** - Returns `Some(Requirement)` if parsing is successful, otherwise `None`. ``` -------------------------------- ### Release Structure Implementation Source: https://docs.rs/versions/7.0.0/src/versions/lib.rs.html Defines the Release struct with parsing, ordering, and display capabilities. ```rust pub struct Release(pub Vec); impl Release { fn parse(i: &str) -> IResult<&str, Release> { let (i, _) = char('-')(i)?; map(separated_list1(char('.'), Chunk::parse), Release).parse(i) } } impl PartialOrd for Release { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Ord for Release { fn cmp(&self, other: &Self) -> Ordering { self.0 .iter() .zip_longest(&other.0) .find_map(|eob| match eob { Both(a, b) => match a.cmp_semver(b) { Less => Some(Less), Greater => Some(Greater), Equal => None, }, // From the Semver spec: A larger set of pre-release fields has // a higher precedence than a smaller set, if all the preceding // identifiers are equal. Left(_) => Some(Greater), Right(_) => Some(Less), }) .unwrap_or(Equal) } } impl std::fmt::Display for Release { // FIXME Fri Jan 7 11:44:50 2022 // // Use `itersperse` here once it stabilises. fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self.0.as_slice() { [] => Ok(()), [c] => write!(f, "{}", c), [c, rest @ ..] => { write!(f, "{}", c)?; for r in rest { write!(f, ".{}", r)?; } Ok(()) } } } } ``` -------------------------------- ### SemVer::to_version Source: https://docs.rs/versions/7.0.0/src/versions/semver.rs.html Performs a lossless conversion from a SemVer instance to a Version object. ```APIDOC ## SemVer::to_version ### Description Performs a lossless conversion from a SemVer instance to a Version object. ### Signature `pub fn to_version(&self) -> Version` ### Returns - **Version** - The converted Version object. ``` -------------------------------- ### Implement Display for SemVer Source: https://docs.rs/versions/7.0.0/src/versions/semver.rs.html Formats the SemVer struct into a string representation including pre-release and metadata if present. ```rust impl std::fmt::Display for SemVer { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}.{}.{}", self.major, self.minor, self.patch)?; if let Some(p) = &self.pre_rel { write!(f, "-{}", p)?; } if let Some(m) = &self.meta { write!(f, "+{}", m)?; } Ok(()) } } ``` -------------------------------- ### Implement TryFrom for Version Source: https://docs.rs/versions/7.0.0/src/versions/version.rs.html Provides a TryFrom implementation for string slices to convert them into Version instances. ```rust /// ``` /// use versions::Version; /// /// let orig = "1.2.3.4"; /// let prsd: Version = orig.try_into().unwrap(); /// assert_eq!(orig, prsd.to_string()); /// ``` fn try_from(value: &str) -> Result { Version::from_str(value) } ``` -------------------------------- ### Requirement::new Source: https://docs.rs/versions/7.0.0/versions Parses a version requirement expression for constraint matching. ```APIDOC ## Requirement::new(req: &str) ### Description Parses a version requirement string (e.g., "^1.2.3") into a `Requirement` object. ### Parameters - **req** (&str) - Required - The requirement expression string. ### Example ```rust use versions::Requirement; let req = Requirement::new("^1.2.3").unwrap(); ``` ``` -------------------------------- ### Extract Version Components Source: https://docs.rs/versions/7.0.0/src/versions/versioning.rs.html Shows how to extract specific version components by index using the nth method across different versioning types. ```rust use versions::Versioning; let semver = Versioning::new("1.2.3-r1+git123").unwrap(); assert!(semver.is_ideal()); assert_eq!(Some(1), semver.nth(0)); assert_eq!(Some(2), semver.nth(1)); assert_eq!(Some(3), semver.nth(2)); let version = Versioning::new("1:2.a.4.5.6.7-r1").unwrap(); assert!(version.is_general()); assert_eq!(Some(2), version.nth(0)); assert_eq!(None, version.nth(1)); assert_eq!(Some(4), version.nth(2)); let mess = Versioning::new("1.6a.0+2014+m872b87e73dfb-1").unwrap(); assert!(mess.is_complex()); assert_eq!(Some(1), mess.nth(0)); assert_eq!(None, mess.nth(1)); assert_eq!(Some(0), mess.nth(2)); ``` -------------------------------- ### Requirement::matches Source: https://docs.rs/versions/7.0.0/versions Checks if a version satisfies a requirement. ```APIDOC ## Requirement::matches(&self, version: &Versioning) ### Description Determines if the provided `Versioning` object satisfies the requirement. ### Parameters - **version** (&Versioning) - Required - The version to check against the requirement. ### Example ```rust let req = Requirement::new("^1.2.3").unwrap(); let ver = Versioning::new("1.2.4").unwrap(); assert!(req.matches(&ver)); ``` ``` -------------------------------- ### Implement Ord for SemVer Source: https://docs.rs/versions/7.0.0/src/versions/semver.rs.html Defines version precedence where build metadata is ignored and pre-release versions are lower than normal versions. ```rust impl Ord for SemVer { fn cmp(&self, other: &Self) -> Ordering { let a = (self.major, self.minor, self.patch); let b = (other.major, other.minor, other.patch); match a.cmp(&b) { Less => Less, Greater => Greater, Equal => match (&self.pre_rel, &other.pre_rel) { (None, None) => Equal, (None, _) => Greater, (_, None) => Less, (Some(ap), Some(bp)) => ap.cmp(bp), }, } } } ``` -------------------------------- ### SemVer::new Source: https://docs.rs/versions/7.0.0/src/versions/semver.rs.html Parses a string into a SemVer struct. Returns None if the input does not conform to the SemVer standard. ```APIDOC ## SemVer::new ### Description Parses a string into a SemVer struct. Returns None if the input does not conform to the SemVer standard. ### Signature `pub fn new(s: S) -> Option where S: AsRef` ### Parameters - **s** (AsRef) - The version string to parse. ### Returns - **Option** - The parsed SemVer object or None if parsing fails. ``` -------------------------------- ### Serialize Requirement with Serde Source: https://docs.rs/versions/7.0.0/src/versions/require.rs.html Demonstrates using the Requirement::serialize function as a custom serializer for struct fields. ```rust use versions::Requirement; use serde::Serialize; use serde_json::to_string; #[derive(Serialize)] struct Foo { #[serde(serialize_with = "Requirement::serialize")] requirement: Requirement, // ... } ``` -------------------------------- ### Compare Version Mess Continued Source: https://docs.rs/versions/7.0.0/src/versions/version.rs.html Compares the main parts of versions after the epoch check has been performed. ```rust fn cmp_mess_continued(&self, other: &Mess) -> Ordering { (0..) .find_map( |n| match self.nth(n).and_then(|x| other.nth(n).map(|y| x.cmp(&y))) { // Sane values can't be extracted from one or both of the // arguments. None => Some(self.to_mess().cmp(other)), Some(Greater) => Some(Greater), Some(Less) => Some(Less), // Continue to the next position. Some(Equal) => None, }, ) .unwrap_or_else(|| self.to_mess().cmp(other)) } ``` -------------------------------- ### Version Parsing and Ordering Tests Source: https://docs.rs/versions/7.0.0/src/versions/lib.rs.html Validates parsing of various version strings and their comparison logic. ```rust #[test] fn good_versions() { let goods = vec![ "1", "1.2", "1.0rc0", "1.0rc1", "1.1rc1", "44.0.2403.157-1", "0.25-2", "8.u51-1", "21-2", "7.1p1-1", "20150826-1", "1:0.10.16-3", "8.64.0.81-1", "1:3.20-1", ]; for s in goods { assert!(SemVer::new(s).is_none(), "Shouldn't be SemVer: {}", s); assert_eq!( Some(s.to_string()), Version::new(s).map(|v| format!("{}", v)) ) } } ``` ```rust #[test] fn bad_versions() { let bads = vec!["", "1.2 "]; bads.iter().for_each(|b| assert_eq!(None, Version::new(b))); } ``` ```rust #[test] fn version_ord() { let vs = vec!["0.9.9.9", "1.0.0.0", "1.0.0.1", "2"]; for (a, b) in vs.iter().zip(&vs[1..]) { cmp_versions(a, b); } cmp_versions("1.2-5", "1.2.3-1"); cmp_versions("1.0rc1", "1.0"); cmp_versions("1.0", "1:1.0"); cmp_versions("1.1", "1:1.0"); cmp_versions("1.1", "1:1.1"); } ``` -------------------------------- ### Versioning::new Source: https://docs.rs/versions/7.0.0/versions Parses a version string into a Versioning type, which can represent various versioning styles. ```APIDOC ## Versioning::new(version: &str) ### Description Parses a given version string into a `Versioning` enum variant. ### Parameters - **version** (&str) - Required - The version string to parse. ### Example ```rust use versions::Versioning; let v = Versioning::new("1.2.3").unwrap(); ``` ``` -------------------------------- ### SemVer::new Source: https://docs.rs/versions/7.0.0/versions/struct.SemVer.html Parses a SemVer object from a string input. ```APIDOC ## fn new(s: S) -> Option ### Description Parses a `SemVer` from some input string. ### Parameters - **s** (AsRef) - Required - The input string to parse as a SemVer. ### Returns - **Option** - Returns Some(SemVer) if parsing is successful, otherwise None. ``` -------------------------------- ### Check version compatibility Source: https://docs.rs/versions/7.0.0/src/versions/semver.rs.html Functions to determine if a version satisfies tilde or caret range requirements. ```rust pub(crate) fn matches_tilde(&self, other: &SemVer) -> bool { self.major == other.major && self.minor == other.minor && other.patch >= self.patch } pub(crate) fn matches_caret(&self, other: &SemVer) -> bool { // Two ideal versions are caret-compatible if the first nonzero part of v1 and // v2 are equal and v2's parts right of the first nonzero part are greater than // or equal to v1's. if self.major == 0 && other.major == 0 { // If both major versions are zero, then the first nonzero part is the minor // version. if self.minor == 0 && other.minor == 0 { // If both minor versions are zero, then the first nonzero part is the // patch version. other.patch == self.patch } else { ``` -------------------------------- ### Requirement::from_str Source: https://docs.rs/versions/7.0.0/src/versions/require.rs.html Parses a string into a Requirement object. ```APIDOC ## fn from_str(s: &str) -> Result ### Description Implementation of the FromStr trait to allow parsing a string into a Requirement object. Returns an error if the string contains an illegal operator. ``` -------------------------------- ### Implement PartialEq for SemVer Source: https://docs.rs/versions/7.0.0/src/versions/semver.rs.html Defines equality as matching major, minor, patch, and pre-release fields, excluding metadata. ```rust impl PartialEq for SemVer { fn eq(&self, other: &Self) -> bool { self.major == other.major && self.minor == other.minor && self.patch == other.patch && self.pre_rel == other.pre_rel } } ``` -------------------------------- ### Implement Ord for Version Source: https://docs.rs/versions/7.0.0/src/versions/version.rs.html Defines the comparison logic for Version objects based on epoch, chunks, and release fields. ```rust fn cmp(&self, other: &Self) -> Ordering { let ae = self.epoch.unwrap_or(0); let be = other.epoch.unwrap_or(0); match ae.cmp(&be) { Equal => match self.chunks.cmp(&other.chunks) { Equal => self.release.cmp(&other.release), ord => ord, }, ord => ord, } } } ``` -------------------------------- ### Implement FromStr for SemVer Source: https://docs.rs/versions/7.0.0/src/versions/semver.rs.html Parses a string into a SemVer instance, returning an error if the format is invalid. ```rust impl FromStr for SemVer { type Err = Error; fn from_str(s: &str) -> Result { SemVer::new(s).ok_or_else(|| Error::IllegalSemver(s.to_string())) } } ``` -------------------------------- ### SemVer::to_mess Source: https://docs.rs/versions/7.0.0/src/versions/semver.rs.html Performs a lossless conversion from a SemVer instance to a Mess object. ```APIDOC ## SemVer::to_mess ### Description Performs a lossless conversion from a SemVer instance to a Mess object. ### Signature `pub fn to_mess(&self) -> Mess` ### Returns - **Mess** - The converted Mess object. ``` -------------------------------- ### Verify Version Equality Source: https://docs.rs/versions/7.0.0/src/versions/lib.rs.html Ensures that various version strings are correctly identified as equal to themselves. ```rust #[test] fn equality() { let vers = vec![ "1:3.20.1-1", "1.3.00.25560-1", "150_28-3", "1.0.r15.g3fc772c-5", "0.88-2", ]; for v in vers { let x = Versioning::new(v).unwrap(); assert_eq!(Equal, x.cmp(&x)); } } ``` -------------------------------- ### Implement Display for Sep Source: https://docs.rs/versions/7.0.0/src/versions/mess.rs.html Provides string representation for the Sep enum variants. ```rust impl std::fmt::Display for Sep { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { let c = match self { Sep::Colon => ':', Sep::Hyphen => '-', Sep::Plus => '+', Sep::Underscore => '_', Sep::Tilde => '~', }; write!(f, "{}", c) } } ``` -------------------------------- ### Implement FromStr for Version Source: https://docs.rs/versions/7.0.0/src/versions/version.rs.html Enables parsing a Version from a string slice using the FromStr trait. ```rust impl FromStr for Version { type Err = Error; fn from_str(s: &str) -> Result { Version::new(s).ok_or_else(|| Error::IllegalVersion(s.to_string())) } } ``` -------------------------------- ### Evaluate Version Requirements Source: https://docs.rs/versions/7.0.0/index.html Use the Requirement type to define and check version constraints against parsed versions. ```rust use versions::{Requirement, Versioning}; let req = Requirement::new("^1.2.3").unwrap(); let ver = Versioning::new("1.2.4").unwrap(); assert!(req.matches(&ver)); ``` -------------------------------- ### Convert SemVer to Version Source: https://docs.rs/versions/7.0.0/src/versions/semver.rs.html Performs a lossless conversion from a SemVer instance to a Version object. ```rust use versions::SemVer; let orig = "1.2.3-r1+git123"; let ver = SemVer::new(orig).unwrap().to_version(); assert_eq!("1.2.3-r1+git123", format!("{}", ver)); ``` -------------------------------- ### Define Version Struct Source: https://docs.rs/versions/7.0.0/versions/struct.Version.html The internal structure of the Version type, containing optional epoch, chunks, release, and metadata fields. ```rust pub struct Version { pub epoch: Option, pub chunks: Chunks, pub release: Option, pub meta: Option, } ``` -------------------------------- ### Implement MChunk Logic Source: https://docs.rs/versions/7.0.0/src/versions/mess.rs.html Methods for parsing and accessing text from MChunk variants. ```rust impl MChunk { /// Extract the original `String`, no matter which variant it parsed into. pub fn text(&self) -> &str { match self { MChunk::Digits(_, s) => s, MChunk::Rev(_, s) => s, MChunk::Plain(s) => s, } } pub(crate) fn parse(i: &str) -> IResult<&str, MChunk> { alt((MChunk::digits, MChunk::rev, MChunk::plain)).parse(i) } fn digits(i: &str) -> IResult<&str, MChunk> { let (i, (u, s)) = map_res(recognize(digit1), |s: &str| { s.parse::().map(|u| (u, s)) }) .parse(i)?; let (i, _) = alt((peek(recognize(char('.'))), peek(recognize(Mess::sep)), eof)).parse(i)?; let chunk = MChunk::Digits(u, s.to_string()); Ok((i, chunk)) } fn rev(i: &str) -> IResult<&str, MChunk> { let (i, _) = tag("r")(i)?; let (i, (u, s)) = map_res(recognize(digit1), |s: &str| { s.parse::().map(|u| (u, s)) }) .parse(i)?; let (i, _) = alt((peek(recognize(char('.'))), peek(recognize(Mess::sep)), eof)).parse(i)?; let chunk = MChunk::Rev(u, format!("r{}", s)); Ok((i, chunk)) } fn plain(i: &str) -> IResult<&str, MChunk> { let (i, s) = alphanumeric1(i)?; let chunk = MChunk::Plain(s.to_string()); Ok((i, chunk)) } } ``` -------------------------------- ### Unit Tests for Version Parsing Source: https://docs.rs/versions/7.0.0/src/versions/lib.rs.html Tests for parsing chunks, mess chunks, and various SemVer string formats. ```rust #[cfg(test)] mod tests { use std::str::FromStr; use super::*; #[test] fn chanks() { assert_eq!(Ok(("", Chunk::Numeric(123))), Chunk::parse("123")); assert_eq!( Ok(("", Chunk::Alphanum("123a".to_string()))), Chunk::parse("123a") ); assert_eq!( Ok(("", Chunk::Alphanum("123-456".to_string()))), Chunk::parse("123-456") ); assert_eq!( Ok(("", Chunk::Alphanum("00a".to_string()))), Chunk::parse("00a") ); } #[test] fn mess_chunks() { assert_eq!( Ok(("", MChunk::Digits(1, "1".to_owned()))), MChunk::parse("1") ); assert_eq!( Ok(("", MChunk::Digits(1, "01".to_owned()))), MChunk::parse("01") ); assert_eq!( Ok(("", MChunk::Digits(987, "987".to_owned()))), MChunk::parse("987") ); assert_eq!( Ok(("", MChunk::Rev(1, "r1".to_owned()))), MChunk::parse("r1") ); assert_eq!( Ok(("", MChunk::Rev(1, "r01".to_owned()))), MChunk::parse("r01") ); assert_eq!( Ok(("", MChunk::Rev(987, "r987".to_owned()))), MChunk::parse("r987") ); assert_eq!( Ok(("", MChunk::Plain("abcd".to_owned()))), MChunk::parse("abcd") ); assert_eq!( Ok(("", MChunk::Plain("1r3".to_owned()))), MChunk::parse("1r3") ); assert_eq!( Ok(("", MChunk::Plain("alpha0".to_owned()))), MChunk::parse("alpha0") ); } #[test] fn parse_mess() { assert_eq!( Ok(( "", Mess { chunks: vec![MChunk::Digits(1, "1".to_owned()),], next: None } )), Mess::parse("1") ); } #[test] fn official_semvers() { let goods = vec![ "0.1.0", "1.2.3", "1.2.3-1", "1.2.3-alpha", "1.2.3-alpha.2", "1.2.3+a1b2c3.1", "1.2.3-alpha.2+a1b2c3.1", "1.0.0-x-y-z.-", "1.0.0-alpha+001", "1.0.0+21AF26D3---117B344092BD", ]; for s in goods { assert_eq!( Some(s.to_string()), SemVer::new(s).map(|sv| format!("{}", sv)) ) } } #[test] fn good_semvers() { let goods = vec![ "0.4.8-1", "7.42.13-4", "2.1.16102-2", "2.2.1-b05", "1.11.0+20200830-1", ]; for s in goods { assert_eq!( Some(s.to_string()), SemVer::new(s).map(|sv| format!("{}", sv)) ) } } #[test] fn tricky_semvers() { let v = "1.2.2-00a"; assert_eq!("", SemVer::parse(v).unwrap().0); } } ``` -------------------------------- ### Deserialize Requirement with Serde Source: https://docs.rs/versions/7.0.0/src/versions/require.rs.html Demonstrates using the Requirement::deserialize function as a custom deserializer for struct fields. ```rust use versions::Requirement; use serde::Deserialize; use serde_json::from_str; #[derive(Deserialize)] struct Foo { #[serde(deserialize_with = "Requirement::deserialize")] requirement: Requirement, // ... } let foo: Foo = from_str(r#"{"requirement": ">=1.0.0"}"#).unwrap(); ``` -------------------------------- ### SemVer Ordering Tests Source: https://docs.rs/versions/7.0.0/src/versions/lib.rs.html Tests the ordering of SemVer strings according to the official specification. ```rust #[test] /// The exact example from http://semver.org fn semver_ord() { let svs = vec![ "1.0.0-alpha", "1.0.0-alpha.1", "1.0.0-alpha.beta", "1.0.0-beta", "1.0.0-beta.2", "1.0.0-beta.11", "1.0.0-rc.1", "1.0.0", ]; for (a, b) in svs.iter().zip(&svs[1..]) { let x = SemVer::new(a).unwrap(); let y = SemVer::new(b).unwrap(); assert!(x < y, "{} < {}", x, y); } } ``` -------------------------------- ### SemVer::parse Source: https://docs.rs/versions/7.0.0/versions/struct.SemVer.html The raw nom parser for SemVer. ```APIDOC ## fn parse(i: &str) -> IResult<&str, SemVer> ### Description The raw `nom` parser for `SemVer`. This can be used in combination with other general `nom` parsers. ### Parameters - **i** (&str) - Required - The input string to parse. ### Returns - **IResult<&str, SemVer>** - The result of the nom parsing operation. ``` -------------------------------- ### Match Tilde Version Constraint Source: https://docs.rs/versions/7.0.0/src/versions/version.rs.html Checks if a version matches the tilde constraint logic. ```rust pub(crate) fn matches_tilde(&self, other: &Version) -> bool { if self.chunks.0.len() != other.chunks.0.len() { false } else { // Compare all but the final chunk. let inits_equal = self .chunks .0 .iter() .rev() .skip(1) .rev() .zip(other.chunks.0.iter().rev().skip(1).rev()) .all(|(a, b)| a == b); let last_good = match (self.chunks.0.last(), other.chunks.0.last()) { // TODO: Do our best with strings. Right now, the alpha patch version can be "less" than the // first one and this will still be true (Some(Chunk::Alphanum(_)), Some(Chunk::Alphanum(_))) => true, (Some(Chunk::Numeric(n1)), Some(Chunk::Numeric(n2))) => n2 >= n1, _ => false, }; inits_equal && last_good } } ``` -------------------------------- ### Test unsigned integer parsing Source: https://docs.rs/versions/7.0.0/src/versions/parsers.rs.html Unit test verifying the unsigned integer parser behavior. ```rust #[test] fn unsigned_test() { assert!(unsigned("0").is_ok()); assert!(unsigned("123").is_ok()); match unsigned("06") { Ok(("6", 0)) => {} Ok(_) => panic!("Parsed 06, but gave wrong output"), Err(_) => panic!("Couldn't parse 06"), } } ``` -------------------------------- ### Implement Hash for SemVer Source: https://docs.rs/versions/7.0.0/src/versions/semver.rs.html Hand-implemented Hash trait to ensure consistency with the custom PartialEq implementation. ```rust impl Hash for SemVer { fn hash(&self, state: &mut H) { self.major.hash(state); self.minor.hash(state); self.patch.hash(state); self.pre_rel.hash(state); } } ``` -------------------------------- ### Serialize Requirement to JSON Source: https://docs.rs/versions/7.0.0/src/versions/lib.rs.html Uses serde_json to serialize a Requirement object. Ensure the Requirement is parsed correctly before serialization. ```rust let test_object = SerializableRequirement { req: Requirement::from_str(">=1.2.3").unwrap(), }; let string = serde_json::to_string(&test_object).unwrap(); assert_eq!(string, "{\"req\":\">=1.2.3\"}"); ``` -------------------------------- ### Verify Parsing Sanity Source: https://docs.rs/versions/7.0.0/src/versions/lib.rs.html Checks basic string parsing into unsigned integers. ```rust #[test] fn parsing_sanity() { assert_eq!(Ok(34), "0034".parse::()) } ``` -------------------------------- ### Implement TryFrom for SemVer Source: https://docs.rs/versions/7.0.0/src/versions/semver.rs.html Enables conversion from a string slice to a SemVer instance using the try_into trait. ```rust impl TryFrom<&str> for SemVer { type Error = Error; /// ``` /// use versions::SemVer; /// /// let orig = "1.2.3"; /// let prsd: SemVer = orig.try_into().unwrap(); /// assert_eq!(orig, prsd.to_string()); /// ``` fn try_from(value: &str) -> Result { SemVer::from_str(value) } } ``` -------------------------------- ### Compare Messy Version Strings Source: https://docs.rs/versions/7.0.0/src/versions/lib.rs.html Tests the comparison logic for complex or non-standard version strings using the Mess struct. ```rust for (a, b) in messes.iter().zip(&messes[1..]) { cmp_messes(a, b); } } #[test] fn mess_7zip() { cmp_messes("22.01-ZS-v1.5.5-R2", "22.01-ZS-v1.5.6-R2"); cmp_messes("22.01-ZS-v1.5.5-R2", "24.02-ZS-v1.6.0"); } fn cmp_messes(a: &str, b: &str) { let x = Mess::new(a).unwrap(); let y = Mess::new(b).unwrap(); assert!(x < y, "{} < {}", x, y); assert!(y > x, "{} > {}", x, y); } ``` -------------------------------- ### Parse Version using TryFrom Source: https://docs.rs/versions/7.0.0/versions/struct.Version.html Convert a string slice into a Version object using the TryFrom trait. ```rust use versions::Version; let orig = "1.2.3.4"; let prsd: Version = orig.try_into().unwrap(); assert_eq!(orig, prsd.to_string()); ``` -------------------------------- ### Define the Release struct Source: https://docs.rs/versions/7.0.0/versions/struct.Release.html The primary structure definition for Release, containing a vector of chunks. ```rust pub struct Release(pub Vec); ``` -------------------------------- ### Converting Version to Mess Source: https://docs.rs/versions/7.0.0/src/versions/version.rs.html Performs a lossless conversion from a Version struct to a Mess representation. ```rust use versions::Version; let orig = "1:1.2.3-r1"; let mess = Version::new(orig).unwrap().to_mess(); assert_eq!(orig, format!("{}", mess)); ``` -------------------------------- ### Implement MChunk Traits Source: https://docs.rs/versions/7.0.0/src/versions/mess.rs.html Ordering and display implementations for MChunk, defining how different segment types compare. ```rust impl PartialOrd for MChunk { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Ord for MChunk { fn cmp(&self, other: &Self) -> Ordering { match (self, other) { // Normal cases. (MChunk::Digits(a, _), MChunk::Digits(b, _)) => a.cmp(b), (MChunk::Rev(a, _), MChunk::Rev(b, _)) => a.cmp(b), // If I'm a concrete number and you're just a revision, then I'm greater no matter what. (MChunk::Digits(_, _), MChunk::Rev(_, _)) => Greater, (MChunk::Rev(_, _), MChunk::Digits(_, _)) => Less, // There's no sensible pairing, so we fall back to String-based comparison. (a, b) => a.text().cmp(b.text()), } } } impl std::fmt::Display for MChunk { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { MChunk::Digits(_, s) => write!(f, "{}", s), MChunk::Rev(_, s) => write!(f, "{}", s), MChunk::Plain(s) => write!(f, "{}", s), } } } ```