From 19abbd9eebdb95388f5839b81d5d657c719022f9 Mon Sep 17 00:00:00 2001 From: Allstreamer <48365544+Allstreamer@users.noreply.github.com> Date: Mon, 15 Aug 2022 22:16:06 +0200 Subject: [PATCH 1/5] Moved changes from #20 - Moved changes from #20 to 25-add-python-version-parsing-pep-440 branch --- Cargo.lock | 111 +++++++++ Cargo.toml | 6 +- src/main.rs | 11 +- src/package_version.rs | 526 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 652 insertions(+), 2 deletions(-) create mode 100644 src/package_version.rs diff --git a/Cargo.lock b/Cargo.lock index 7c897e7..469af1b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,15 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + [[package]] name = "anyhow" version = "1.0.59" @@ -149,6 +158,17 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "encoding_rs" version = "0.8.31" @@ -453,6 +473,12 @@ version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +[[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.5.3" @@ -492,6 +518,16 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nom" +version = "7.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "num_cpus" version = "1.13.1" @@ -592,6 +628,25 @@ version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" +[[package]] +name = "pomsky" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2325e4138bcceff534b0149cb30d08099d2b08f15c65b7bdf7c33b16c5907d4d" +dependencies = [ + "nom", + "thiserror", +] + +[[package]] +name = "pomsky-macro" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5997a1ccb23752151a0463191b5b88fa678ce39fa711c64b2a211ede9e1d8166" +dependencies = [ + "pomsky", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -643,6 +698,23 @@ dependencies = [ "bitflags", ] +[[package]] +name = "regex" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" + [[package]] name = "remove_dir_all" version = "0.5.3" @@ -695,7 +767,12 @@ version = "0.0.1" dependencies = [ "anyhow", "clap", + "derivative", + "lazy_static", + "pomsky-macro", + "regex", "reqwest", + "serde", "strum", ] @@ -755,6 +832,20 @@ name = "serde" version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc855a42c7967b7c369eb5860f7164ef1f6f81c20c7cc1141f2a604e18723b03" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f2122636b9fe3b81f1cb25099fcf2d3f542cdb1d45940d56c713158884a05da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "serde_json" @@ -866,6 +957,26 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" +[[package]] +name = "thiserror" +version = "1.0.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5f6586b7f764adc0231f4c79be7b920e766bb2f3e51b3661cdb263828f19994" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12bafc5b54507e0149cdf1b145a5d80ab80a90bcd9275df43d4fff68460f6c21" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tinyvec" version = "1.6.0" diff --git a/Cargo.toml b/Cargo.toml index 16cfda1..05644a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,10 @@ anyhow = { version = "1.0.58", features = ["backtrace"] } clap = { version = "3.2", features = ["derive"] } reqwest = { version = "0.11", features = ["blocking", "json"] } strum = { version = "0.24.1", features = ["derive"] } - +serde = { version = "1", features = ["derive"] } +regex = { version = "1" } +lazy_static = { version = "1.4.0" } +pomsky-macro = { version = "0.6.0" } +derivative = { version = "2.2.0" } [dev-dependencies] diff --git a/src/main.rs b/src/main.rs index fca4f23..74dafa1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,11 @@ use clap::{AppSettings, Parser}; +#[macro_use] +extern crate derivative; + +mod package_version; +use package_version::PackageVersion; + /// Python package manager written in Rust #[derive(Parser, Debug)] #[clap(global_setting = AppSettings::DeriveDisplayOrder)] @@ -43,7 +49,10 @@ enum Opt { Help {}, } -fn download_package(_package_name: String, _package_index: &str) {} +fn download_package(_package_name: String, _package_index: &str) { + // Version usage example + let _ = PackageVersion::new("v1.0"); +} fn main() { let opt = Opt::parse(); diff --git a/src/package_version.rs b/src/package_version.rs new file mode 100644 index 0000000..f8a7a66 --- /dev/null +++ b/src/package_version.rs @@ -0,0 +1,526 @@ +//! # Handling of pep-440 +//! +//! This module implements Pythons Package versioning system read more at +use anyhow::Result; +use lazy_static::lazy_static; +use pomsky_macro::pomsky; +use serde::{Deserialize, Serialize}; +use std::{cmp::Ordering, fmt}; + + +/// Rulex version of +/// Python's pep-440 Regex () +static VALIDATION_REGEX: &str = pomsky!( +// Version String may start with v +// Example: +// v1.0 +"v"? + +// Version String may include an epoch ! +// Example: +// 1!1.0 +(:epoch(['0'-'9']+)'!')? + +// Version String must include major and minor version . +// Example: +// 1.0 +:release(['0'-'9']+("."['0'-'9']+)*) + +// Version String may include Pre-Header +// Example: +// 1.0.preview-2 +// 1.0.rc2 +// 1.0beta2 +:pre( + ["-" "_" "."]? + + :pre_l( + ("preview"|"alpha"|"beta"|"pre"|"rc"|"a"|"b"|"c") + ) + + ["-" "_" "."]? + + :pre_n(['0'-'9']+)? +)? + +// Version String may include Post-Header +// Examples: +// 1.0-9 +// 1.0-post2 +// 1.0.post.2 +:post( + "-" + :post_n1(['0'-'9']+) + + | + + ["-" "_" "."]? + :post_l("post" | "rev" | "r") + ["-" "_" "."]? + :post_n2(['0'-'9']+)? +)? + +// Version string may include Dev-header +// Example: +// 1.0-dev3 +// 1.0dev4 +// 1.0_dev_9 +:dev( + ["-" "_" "."]? + :dev_l("dev") + ["-" "_" "."]? + :dev_n(['0'-'9']+)? +)? + +// Version string may include Local Version +// Local version must start with + +// Example: +// 1.0+this.can.say.anything.as.long.as.its.a.letter.or.number.231241 +( +"+" +:local( + ['a'-'z' '0'-'9']+ + ((["-" "_" "."] ['a'-'z' '0'-'9']+)+)? +) +)? +); + +/// # Pep-440 Developmental release identifier +#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, PartialOrd)] +pub struct DevHead { + dev_num: Option, +} + +/// Pep-440 Post-Release Identifier Keyword +#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)] +pub enum PostHead { + Post, + Rev, +} + +impl PartialOrd for PostHead { + fn partial_cmp(&self, _other: &Self) -> Option { + Some(Ordering::Equal) + } +} + +/// # Pep-440 Post-Release identifier +#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)] +pub struct PostHeader { + pub post_head: Option, + pub post_num: Option, +} + +impl PartialOrd for PostHeader { + fn partial_cmp(&self, other: &Self) -> Option { + if self.post_num == other.post_num { + return Some(Ordering::Equal); + } + + if self.post_num.is_none() && other.post_num.is_some() { + return Some(Ordering::Less); + } else if self.post_num.is_some() && other.post_num.is_none() { + return Some(Ordering::Greater); + } + + if self.post_num < other.post_num { + Some(Ordering::Less) + } else { + Some(Ordering::Greater) + } + } +} + +/// # Pep-440 Pre-Release identifier +#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, PartialOrd)] +pub enum PreHeader { + /// Present in versions like 1.1beta1 or 1.0b1 both are represented the same way + /// ``` + /// PreHeader::Beta(Some(1)) + /// ``` + Beta(Option), + /// Present in versions like 1.0alpha2 or 1.0a2 both are represented the same way + /// ``` + /// PreHeader::Alpha(Some(2)) + /// ``` + Alpha(Option), + /// Present in versions like 1.1pre3 + /// ``` + /// PreHeader::Preview(Some(3)) + /// ``` + Preview(Option), + /// Present in versions like 1.1-rc-4 or 1.1c-4 + /// ``` + /// PreHeader::ReleaseCanidate(Some(4)) + /// ``` + ReleaseCanidate(Option), +} + +/// Pep-440 Release numbers +#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, PartialOrd)] +pub struct ReleaseHeader { + /// Major release such as 1.0 or breaking changes + pub major: u32, + /// Minor release Such as new functionality + pub minor: u32, +} + +/// Pep-440 Compliant versioning system +/// +/// This struct is sorted so that PartialOrd +/// corretly interpets priority +/// +/// Lower == More important +/// +/// # Example Usage +/// ``` +/// let _ = PackageVersion::new("v1.0"); +/// ``` +#[derive(Derivative, Debug, Serialize, Deserialize)] +#[derivative(PartialOrd, PartialEq)] +pub struct PackageVersion { + #[derivative(PartialOrd = "ignore", PartialEq = "ignore")] + pub original: String, + + /// # Pep-440 Local version identifier + /// Local version sorting will have to be it's own issue + /// since there are no limits to what a local version can be + /// + /// For those who can read regex here it is for the local version: + /// `[a-z0-9]+(?:(?:[\-_.][a-z0-9]+)+)?` + /// + /// Here in Rulex: + /// ``` + /// ['a'-'z' '0'-'9']+ + /// ((["-" "_" "."] ['a'-'z' '0'-'9']+)+)? + /// ``` + #[derivative(PartialOrd = "ignore", PartialEq = "ignore")] + pub local: Option, + + /// # Pep-440 Developmental release identifier + pub dev: Option, + + /// # Pep-440 Post-Release identifier + pub post: Option, + + /// # Pep-440 Pre-Release identifier + pub pre: Option, + + /// # Pep-440 Release number + pub release: ReleaseHeader, + + /// # Pep-440 Version-Epoch + pub epoch: Option, +} + +impl PackageVersion { + pub fn new(version: &str) -> Result { + lazy_static! { + // Safe to unwrap since Regex is predefined + // Regex as defined in PEP-0440 + static ref VERSION_VALIDATIOR: regex::Regex = + regex::Regex::new(VALIDATION_REGEX).unwrap(); + } + + // Capture each group of the regex + // Groups are: + // epoch, release, pre, pre_l, pre_n, post, post_l, post_n1, post_n2, + // dev, dev_l, dev_n, local + let version_match = match VERSION_VALIDATIOR.captures(version) { + Some(v) => v, + None => anyhow::bail!("Failed to decode version {}", version), + }; + + let epoch: Option = match version_match.name("epoch") { + // Convert Epoch String to Epoch Number + Some(v) => Some(v.as_str().parse::()?), + None => None, + }; + + let release: ReleaseHeader = match version_match.name("release") { + Some(v) => { + // Does Release String contain minor version + if v.as_str().contains('.') { + let split: Vec<&str> = v.as_str().split('.').into_iter().collect(); + ReleaseHeader { + major: split[0].parse::()?, + minor: split[1].parse::()?, + } + } else { + ReleaseHeader { + major: v.as_str().parse::()?, + minor: 0, + } + } + } + // There always has to be at least a major version + None => anyhow::bail!("Failed to decode version {}", version), + }; + + let pre: Option = match version_match.name("pre") { + Some(_) => { + let pre_n = match version_match.name("pre_n") { + Some(v) => Some(v.as_str().parse::()?), + None => None, + }; + + // Should be safe to unwrap since we already checked if pre has a value + // since pre_n has to exist + match version_match.name("pre_l").unwrap().as_str() { + "alpha" => Some(PreHeader::Alpha(pre_n)), + "a" => Some(PreHeader::Alpha(pre_n)), + "beta" => Some(PreHeader::Beta(pre_n)), + "b" => Some(PreHeader::Beta(pre_n)), + "rc" => Some(PreHeader::ReleaseCanidate(pre_n)), + "c" => Some(PreHeader::ReleaseCanidate(pre_n)), + "preview" => Some(PreHeader::Preview(pre_n)), + "pre" => Some(PreHeader::Preview(pre_n)), + _ => None, + } + } + None => None, + }; + + let post: Option = match version_match.name("post") { + Some(_) => { + let post_num: Option = match version_match.name("post_n1") { + Some(v) => Some(v.as_str().parse::()?), + None => match version_match.name("post_n2") { + Some(v) => Some(v.as_str().parse::()?), + _ => None, + }, + }; + + let post_head: Option = match version_match.name("post_l") { + Some(v) => { + match v.as_str() { + "post" => Some(PostHead::Post), + "rev" => Some(PostHead::Rev), + "r" => Some(PostHead::Rev), + // This branch Should be impossible (see regex-group post_l) + _ => None, + } + } + None => None, + }; + + Some(PostHeader { + post_head, + post_num, + }) + } + None => None, + }; + + let dev: Option = match version_match.name("dev") { + Some(_) => { + let dev_num = match version_match.name("dev_n") { + Some(v) => Some(v.as_str().parse::()?), + None => None, + }; + Some(DevHead { dev_num }) + } + None => None, + }; + + let local: Option = + version_match.name("local").map(|v| v.as_str().to_string()); + + Ok(Self { + original: version.to_string(), + epoch, + release, + pre, + post, + dev, + local, + }) + } +} + +impl fmt::Display for PackageVersion { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.original) + } +} + +#[cfg(test)] +mod tests { + use std::fmt::Debug; + + use crate::package_version::PackageVersion; + use anyhow::bail; + use anyhow::Result; + + use super::DevHead; + use super::PostHead; + use super::PostHeader; + use super::PreHeader; + use super::ReleaseHeader; + + fn check_a_greater(a: T, b: T) -> Result<()> + where + T: PartialEq + PartialOrd + Debug, + { + if a <= b { + bail!( + "Failed Less Than or Equal Check for A: {:?} \n<=\n B: {:?}", + a, + b + ) + } + Ok(()) + } + + #[test] + fn check_pep440_ordering() -> Result<()> { + check_a_greater( + PackageVersion::new("v1!1.0-preview-921.post-516.dev-241+yeah.this.is.the.problem.with.local.versions")?, + PackageVersion::new("1.0")?, + )?; + Ok(()) + } + + #[test] + fn check_release_ordering() -> Result<()> { + check_a_greater( + ReleaseHeader { major: 1, minor: 0 }, + ReleaseHeader { major: 0, minor: 0 }, + )?; + check_a_greater( + ReleaseHeader { major: 1, minor: 1 }, + ReleaseHeader { major: 1, minor: 0 }, + )?; + check_a_greater( + ReleaseHeader { major: 2, minor: 1 }, + ReleaseHeader { + major: 1, + minor: 52, + }, + )?; + Ok(()) + } + + #[test] + fn check_pre_ordering() -> Result<()> { + check_a_greater(PreHeader::ReleaseCanidate(None), PreHeader::Preview(None))?; + check_a_greater(PreHeader::Preview(None), PreHeader::Alpha(None))?; + check_a_greater(PreHeader::Alpha(None), PreHeader::Beta(None))?; + + check_a_greater( + PreHeader::ReleaseCanidate(Some(2)), + PreHeader::ReleaseCanidate(Some(1)), + )?; + check_a_greater(PreHeader::Preview(Some(50)), PreHeader::Preview(Some(3)))?; + check_a_greater(PreHeader::Alpha(Some(504)), PreHeader::Alpha(Some(0)))?; + check_a_greater(PreHeader::Beta(Some(1234)), PreHeader::Beta(Some(1)))?; + + check_a_greater( + PreHeader::ReleaseCanidate(Some(1)), + PreHeader::Beta(Some(45067885)), + )?; + Ok(()) + } + + #[test] + fn check_post_ordering() -> Result<()> { + check_a_greater( + PostHeader { + post_head: Some(PostHead::Post), + post_num: Some(0), + }, + PostHeader { + post_head: Some(PostHead::Post), + post_num: None, + }, + )?; + check_a_greater( + PostHeader { + post_head: Some(PostHead::Post), + post_num: Some(1), + }, + PostHeader { + post_head: Some(PostHead::Post), + post_num: Some(0), + }, + )?; + Ok(()) + } + + #[test] + fn check_dev_ordering() -> Result<()> { + check_a_greater(DevHead { dev_num: Some(0) }, DevHead { dev_num: None })?; + check_a_greater(DevHead { dev_num: Some(1) }, DevHead { dev_num: Some(0) })?; + Ok(()) + } + + #[test] + fn check_pep440_equality() -> Result<()> { + assert_eq!( + PackageVersion::new("1.0a1")?, + PackageVersion::new("1.0alpha1")? + ); + assert_eq!( + PackageVersion::new("1.0b")?, + PackageVersion::new("1.0beta")? + ); + assert_eq!(PackageVersion::new("1.0r")?, PackageVersion::new("1.0rev")?); + assert_eq!(PackageVersion::new("1.0c")?, PackageVersion::new("1.0rc")?); + assert_eq!(PackageVersion::new("v1.0")?, PackageVersion::new("1.0")?); + Ok(()) + } + + #[test] + fn check_pep440() { + // list of every example mentioned in pep-440 + let versions = vec![ + "1.0", + "v1.1", + "2.0", + "2013.10", + "2014.04", + "1!1.0", + "1!1.1", + "1!2.0", + "2!1.0.pre0", + "1.0.dev456", + "1.0a1", + "1.0a2.dev456", + "1.0a12.dev456", + "1.0a12", + "1.0b1.dev456", + "1.0b2", + "1.0b2.post345.dev456", + "1.0b2.post345", + "1.0rc1.dev456", + "1.0rc1", + "1.0", + "1.0+abc.5", + "1.0+abc.7", + "1.0+5", + "1.0.post456.dev34", + "1.0.post456", + "1.0.15", + "1.1.dev1", + ]; + + for version in versions { + match PackageVersion::new(version) { + Ok(_v) => continue, + Err(e) => panic!("Oh no {}", e), + } + } + } + + #[test] + fn check_pep440_negative() { + let versions = vec!["not a version"]; + + for version in versions { + match PackageVersion::new(version) { + Ok(v) => panic!("Oh no {}", v), + Err(_e) => continue, + } + } + } +} \ No newline at end of file From 091d1cea4a6175a5c51e61c44ab64f995869fbee Mon Sep 17 00:00:00 2001 From: Allstreamer <48365544+Allstreamer@users.noreply.github.com> Date: Tue, 16 Aug 2022 13:10:24 +0200 Subject: [PATCH 2/5] Formatted package_version.rs - Fixed naming - `VERSION_VALIDATIOR` to `VERSION_VALIDATOR` - `ReleaseCanidate` to `ReleaseCandidate` - Did some manual formatting - Ran cargo fmt --- src/package_version.rs | 257 +++++++++++++++++++++-------------------- 1 file changed, 129 insertions(+), 128 deletions(-) diff --git a/src/package_version.rs b/src/package_version.rs index f8a7a66..45ce126 100644 --- a/src/package_version.rs +++ b/src/package_version.rs @@ -1,97 +1,98 @@ -//! # Handling of pep-440 -//! -//! This module implements Pythons Package versioning system read more at +//! # Handling of `PEP-440` +//! +//! This module implements Pythons Package versioning system. +//! Read more at use anyhow::Result; use lazy_static::lazy_static; use pomsky_macro::pomsky; use serde::{Deserialize, Serialize}; use std::{cmp::Ordering, fmt}; - -/// Rulex version of -/// Python's pep-440 Regex () +/// Rulex version of +/// Python's PEP-440 Regex +/// () static VALIDATION_REGEX: &str = pomsky!( -// Version String may start with v -// Example: -// v1.0 -"v"? - -// Version String may include an epoch ! -// Example: -// 1!1.0 -(:epoch(['0'-'9']+)'!')? - -// Version String must include major and minor version . -// Example: -// 1.0 -:release(['0'-'9']+("."['0'-'9']+)*) - -// Version String may include Pre-Header -// Example: -// 1.0.preview-2 -// 1.0.rc2 -// 1.0beta2 -:pre( - ["-" "_" "."]? - - :pre_l( - ("preview"|"alpha"|"beta"|"pre"|"rc"|"a"|"b"|"c") + // Version String may start with v + // Example: + // v1.0 + "v"? + + // Version String may include an epoch ! + // Example: + // 1!1.0 + (:epoch(['0'-'9']+)'!')? + + // Version String must include major and minor version . + // Example: + // 1.0 + :release(['0'-'9']+("."['0'-'9']+)*) + + // Version String may include Pre-Header + // Example: + // 1.0.preview-2 + // 1.0.rc2 + // 1.0beta2 + :pre( + ["-" "_" "."]? + + :pre_l( + ("preview"|"alpha"|"beta"|"pre"|"rc"|"a"|"b"|"c") + ) + + ["-" "_" "."]? + + :pre_n(['0'-'9']+)? + )? + + // Version String may include Post-Header + // Examples: + // 1.0-9 + // 1.0-post2 + // 1.0.post.2 + :post( + "-" + :post_n1(['0'-'9']+) + + | + + ["-" "_" "."]? + :post_l("post" | "rev" | "r") + ["-" "_" "."]? + :post_n2(['0'-'9']+)? + )? + + // Version string may include Dev-header + // Example: + // 1.0-dev3 + // 1.0dev4 + // 1.0_dev_9 + :dev( + ["-" "_" "."]? + :dev_l("dev") + ["-" "_" "."]? + :dev_n(['0'-'9']+)? + )? + + // Version string may include Local Version + // Local version must start with + + // Example: + // 1.0+this.can.say.anything.as.long.as.its.a.letter.or.number.231241 + ( + "+" + :local( + ['a'-'z' '0'-'9']+ + ((["-" "_" "."] ['a'-'z' '0'-'9']+)+)? ) - - ["-" "_" "."]? - - :pre_n(['0'-'9']+)? -)? - -// Version String may include Post-Header -// Examples: -// 1.0-9 -// 1.0-post2 -// 1.0.post.2 -:post( - "-" - :post_n1(['0'-'9']+) - - | - - ["-" "_" "."]? - :post_l("post" | "rev" | "r") - ["-" "_" "."]? - :post_n2(['0'-'9']+)? -)? - -// Version string may include Dev-header -// Example: -// 1.0-dev3 -// 1.0dev4 -// 1.0_dev_9 -:dev( - ["-" "_" "."]? - :dev_l("dev") - ["-" "_" "."]? - :dev_n(['0'-'9']+)? -)? - -// Version string may include Local Version -// Local version must start with + -// Example: -// 1.0+this.can.say.anything.as.long.as.its.a.letter.or.number.231241 -( -"+" -:local( - ['a'-'z' '0'-'9']+ - ((["-" "_" "."] ['a'-'z' '0'-'9']+)+)? -) -)? + )? ); -/// # Pep-440 Developmental release identifier +/// # `PEP-440` Developmental release identifier #[derive(Debug, Serialize, Deserialize, Eq, PartialEq, PartialOrd)] pub struct DevHead { dev_num: Option, } -/// Pep-440 Post-Release Identifier Keyword +/// `PEP-440` Post-Release Identifier Keyword #[derive(Debug, Serialize, Deserialize, Eq, PartialEq)] pub enum PostHead { Post, @@ -104,7 +105,7 @@ impl PartialOrd for PostHead { } } -/// # Pep-440 Post-Release identifier +/// # `PEP-440` Post-Release identifier #[derive(Debug, Serialize, Deserialize, Eq, PartialEq)] pub struct PostHeader { pub post_head: Option, @@ -131,7 +132,7 @@ impl PartialOrd for PostHeader { } } -/// # Pep-440 Pre-Release identifier +/// # `PEP-440` Pre-Release identifier #[derive(Debug, Serialize, Deserialize, Eq, PartialEq, PartialOrd)] pub enum PreHeader { /// Present in versions like 1.1beta1 or 1.0b1 both are represented the same way @@ -151,12 +152,12 @@ pub enum PreHeader { Preview(Option), /// Present in versions like 1.1-rc-4 or 1.1c-4 /// ``` - /// PreHeader::ReleaseCanidate(Some(4)) + /// PreHeader::ReleaseCandidate(Some(4)) /// ``` - ReleaseCanidate(Option), + ReleaseCandidate(Option), } -/// Pep-440 Release numbers +/// `PEP-440` Release numbers #[derive(Debug, Serialize, Deserialize, Eq, PartialEq, PartialOrd)] pub struct ReleaseHeader { /// Major release such as 1.0 or breaking changes @@ -165,10 +166,10 @@ pub struct ReleaseHeader { pub minor: u32, } -/// Pep-440 Compliant versioning system -/// +/// `PEP-440` Compliant versioning system +/// /// This struct is sorted so that PartialOrd -/// corretly interpets priority +/// correctly interprets priority /// /// Lower == More important /// @@ -182,7 +183,7 @@ pub struct PackageVersion { #[derivative(PartialOrd = "ignore", PartialEq = "ignore")] pub original: String, - /// # Pep-440 Local version identifier + /// # `PEP-440` Local version identifier /// Local version sorting will have to be it's own issue /// since there are no limits to what a local version can be /// @@ -197,19 +198,19 @@ pub struct PackageVersion { #[derivative(PartialOrd = "ignore", PartialEq = "ignore")] pub local: Option, - /// # Pep-440 Developmental release identifier + /// # `PEP-440` Developmental release identifier pub dev: Option, - /// # Pep-440 Post-Release identifier + /// # `PEP-440` Post-Release identifier pub post: Option, - /// # Pep-440 Pre-Release identifier + /// # `PEP-440` Pre-Release identifier pub pre: Option, - /// # Pep-440 Release number + /// # `PEP-440` Release number pub release: ReleaseHeader, - /// # Pep-440 Version-Epoch + /// # `PEP-440` Version-Epoch pub epoch: Option, } @@ -218,7 +219,7 @@ impl PackageVersion { lazy_static! { // Safe to unwrap since Regex is predefined // Regex as defined in PEP-0440 - static ref VERSION_VALIDATIOR: regex::Regex = + static ref VERSION_VALIDATOR: regex::Regex = regex::Regex::new(VALIDATION_REGEX).unwrap(); } @@ -226,7 +227,7 @@ impl PackageVersion { // Groups are: // epoch, release, pre, pre_l, pre_n, post, post_l, post_n1, post_n2, // dev, dev_l, dev_n, local - let version_match = match VERSION_VALIDATIOR.captures(version) { + let version_match = match VERSION_VALIDATOR.captures(version) { Some(v) => v, None => anyhow::bail!("Failed to decode version {}", version), }; @@ -265,14 +266,14 @@ impl PackageVersion { }; // Should be safe to unwrap since we already checked if pre has a value - // since pre_n has to exist + // since pre_n has to exist match version_match.name("pre_l").unwrap().as_str() { "alpha" => Some(PreHeader::Alpha(pre_n)), "a" => Some(PreHeader::Alpha(pre_n)), "beta" => Some(PreHeader::Beta(pre_n)), "b" => Some(PreHeader::Beta(pre_n)), - "rc" => Some(PreHeader::ReleaseCanidate(pre_n)), - "c" => Some(PreHeader::ReleaseCanidate(pre_n)), + "rc" => Some(PreHeader::ReleaseCandidate(pre_n)), + "c" => Some(PreHeader::ReleaseCandidate(pre_n)), "preview" => Some(PreHeader::Preview(pre_n)), "pre" => Some(PreHeader::Preview(pre_n)), _ => None, @@ -358,7 +359,7 @@ mod tests { use super::PreHeader; use super::ReleaseHeader; - fn check_a_greater(a: T, b: T) -> Result<()> + fn test_a_greater(a: T, b: T) -> Result<()> where T: PartialEq + PartialOrd + Debug, { @@ -373,8 +374,8 @@ mod tests { } #[test] - fn check_pep440_ordering() -> Result<()> { - check_a_greater( + fn test_pep440_ordering() -> Result<()> { + test_a_greater( PackageVersion::new("v1!1.0-preview-921.post-516.dev-241+yeah.this.is.the.problem.with.local.versions")?, PackageVersion::new("1.0")?, )?; @@ -382,16 +383,16 @@ mod tests { } #[test] - fn check_release_ordering() -> Result<()> { - check_a_greater( + fn test_release_ordering() -> Result<()> { + test_a_greater( ReleaseHeader { major: 1, minor: 0 }, ReleaseHeader { major: 0, minor: 0 }, )?; - check_a_greater( + test_a_greater( ReleaseHeader { major: 1, minor: 1 }, ReleaseHeader { major: 1, minor: 0 }, )?; - check_a_greater( + test_a_greater( ReleaseHeader { major: 2, minor: 1 }, ReleaseHeader { major: 1, @@ -402,29 +403,29 @@ mod tests { } #[test] - fn check_pre_ordering() -> Result<()> { - check_a_greater(PreHeader::ReleaseCanidate(None), PreHeader::Preview(None))?; - check_a_greater(PreHeader::Preview(None), PreHeader::Alpha(None))?; - check_a_greater(PreHeader::Alpha(None), PreHeader::Beta(None))?; - - check_a_greater( - PreHeader::ReleaseCanidate(Some(2)), - PreHeader::ReleaseCanidate(Some(1)), + fn test_pre_ordering() -> Result<()> { + test_a_greater(PreHeader::ReleaseCandidate(None), PreHeader::Preview(None))?; + test_a_greater(PreHeader::Preview(None), PreHeader::Alpha(None))?; + test_a_greater(PreHeader::Alpha(None), PreHeader::Beta(None))?; + + test_a_greater( + PreHeader::ReleaseCandidate(Some(2)), + PreHeader::ReleaseCandidate(Some(1)), )?; - check_a_greater(PreHeader::Preview(Some(50)), PreHeader::Preview(Some(3)))?; - check_a_greater(PreHeader::Alpha(Some(504)), PreHeader::Alpha(Some(0)))?; - check_a_greater(PreHeader::Beta(Some(1234)), PreHeader::Beta(Some(1)))?; + test_a_greater(PreHeader::Preview(Some(50)), PreHeader::Preview(Some(3)))?; + test_a_greater(PreHeader::Alpha(Some(504)), PreHeader::Alpha(Some(0)))?; + test_a_greater(PreHeader::Beta(Some(1234)), PreHeader::Beta(Some(1)))?; - check_a_greater( - PreHeader::ReleaseCanidate(Some(1)), + test_a_greater( + PreHeader::ReleaseCandidate(Some(1)), PreHeader::Beta(Some(45067885)), )?; Ok(()) } #[test] - fn check_post_ordering() -> Result<()> { - check_a_greater( + fn test_post_ordering() -> Result<()> { + test_a_greater( PostHeader { post_head: Some(PostHead::Post), post_num: Some(0), @@ -434,7 +435,7 @@ mod tests { post_num: None, }, )?; - check_a_greater( + test_a_greater( PostHeader { post_head: Some(PostHead::Post), post_num: Some(1), @@ -448,14 +449,14 @@ mod tests { } #[test] - fn check_dev_ordering() -> Result<()> { - check_a_greater(DevHead { dev_num: Some(0) }, DevHead { dev_num: None })?; - check_a_greater(DevHead { dev_num: Some(1) }, DevHead { dev_num: Some(0) })?; + fn test_dev_ordering() -> Result<()> { + test_a_greater(DevHead { dev_num: Some(0) }, DevHead { dev_num: None })?; + test_a_greater(DevHead { dev_num: Some(1) }, DevHead { dev_num: Some(0) })?; Ok(()) } #[test] - fn check_pep440_equality() -> Result<()> { + fn test_pep440_equality() -> Result<()> { assert_eq!( PackageVersion::new("1.0a1")?, PackageVersion::new("1.0alpha1")? @@ -471,7 +472,7 @@ mod tests { } #[test] - fn check_pep440() { + fn test_pep440() { // list of every example mentioned in pep-440 let versions = vec![ "1.0", @@ -513,7 +514,7 @@ mod tests { } #[test] - fn check_pep440_negative() { + fn test_pep440_negative() { let versions = vec!["not a version"]; for version in versions { @@ -523,4 +524,4 @@ mod tests { } } } -} \ No newline at end of file +} From 3d1a0abb275ecab8d2b13bda50dea059581b5a66 Mon Sep 17 00:00:00 2001 From: Allstreamer <48365544+Allstreamer@users.noreply.github.com> Date: Thu, 18 Aug 2022 07:42:22 +0200 Subject: [PATCH 3/5] Create CHANGELOG.md - Created base template changelog including dev branch changes --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..4552316 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,11 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] +### Added +- Created changelog +- Created Basic CLI functionality using CLAP 36198f7e568233eff0518179ea5152ee854c8083 & 8a1284b0fe765752e81672bacfa6db882592ec8f +- Created Base Project 003d7cb0196201b876c40f28af0760a0b86ec9df \ No newline at end of file From 5d26bd38ff9320a498edfa9cfa590e64eb992958 Mon Sep 17 00:00:00 2001 From: Allstreamer <48365544+Allstreamer@users.noreply.github.com> Date: Thu, 18 Aug 2022 07:43:09 +0200 Subject: [PATCH 4/5] Revert "Create CHANGELOG.md" This reverts commit 3d1a0abb275ecab8d2b13bda50dea059581b5a66. --- CHANGELOG.md | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 4552316..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,11 +0,0 @@ -# Changelog -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [Unreleased] -### Added -- Created changelog -- Created Basic CLI functionality using CLAP 36198f7e568233eff0518179ea5152ee854c8083 & 8a1284b0fe765752e81672bacfa6db882592ec8f -- Created Base Project 003d7cb0196201b876c40f28af0760a0b86ec9df \ No newline at end of file From 7d1f36810118297067ed6a6b9e6c221e3a437eb4 Mon Sep 17 00:00:00 2001 From: Allstreamer <48365544+Allstreamer@users.noreply.github.com> Date: Wed, 14 Sep 2022 10:10:18 +0200 Subject: [PATCH 5/5] Add pyver - Removed Old dependencies - Add pyver crate - Update usages - Removed package_version --- Cargo.lock | 19 +- Cargo.toml | 5 +- src/main.rs | 6 +- src/package_version.rs | 527 ----------------------------------------- 4 files changed, 17 insertions(+), 540 deletions(-) delete mode 100644 src/package_version.rs diff --git a/Cargo.lock b/Cargo.lock index 469af1b..95e3e77 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -680,6 +680,20 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "pyver" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8437765dce7370ae968d4094568a3ce57a8b74fd51197e9b665560a1ae183d9" +dependencies = [ + "anyhow", + "derivative", + "lazy_static", + "pomsky-macro", + "regex", + "serde", +] + [[package]] name = "quote" version = "1.0.20" @@ -767,10 +781,7 @@ version = "0.0.1" dependencies = [ "anyhow", "clap", - "derivative", - "lazy_static", - "pomsky-macro", - "regex", + "pyver", "reqwest", "serde", "strum", diff --git a/Cargo.toml b/Cargo.toml index 05644a8..9ef2509 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,9 +16,6 @@ clap = { version = "3.2", features = ["derive"] } reqwest = { version = "0.11", features = ["blocking", "json"] } strum = { version = "0.24.1", features = ["derive"] } serde = { version = "1", features = ["derive"] } -regex = { version = "1" } -lazy_static = { version = "1.4.0" } -pomsky-macro = { version = "0.6.0" } -derivative = { version = "2.2.0" } +pyver = { version = "1" } [dev-dependencies] diff --git a/src/main.rs b/src/main.rs index 74dafa1..e16a942 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,6 @@ use clap::{AppSettings, Parser}; -#[macro_use] -extern crate derivative; - -mod package_version; -use package_version::PackageVersion; +use pyver::PackageVersion; /// Python package manager written in Rust #[derive(Parser, Debug)] diff --git a/src/package_version.rs b/src/package_version.rs deleted file mode 100644 index 45ce126..0000000 --- a/src/package_version.rs +++ /dev/null @@ -1,527 +0,0 @@ -//! # Handling of `PEP-440` -//! -//! This module implements Pythons Package versioning system. -//! Read more at -use anyhow::Result; -use lazy_static::lazy_static; -use pomsky_macro::pomsky; -use serde::{Deserialize, Serialize}; -use std::{cmp::Ordering, fmt}; - -/// Rulex version of -/// Python's PEP-440 Regex -/// () -static VALIDATION_REGEX: &str = pomsky!( - // Version String may start with v - // Example: - // v1.0 - "v"? - - // Version String may include an epoch ! - // Example: - // 1!1.0 - (:epoch(['0'-'9']+)'!')? - - // Version String must include major and minor version . - // Example: - // 1.0 - :release(['0'-'9']+("."['0'-'9']+)*) - - // Version String may include Pre-Header - // Example: - // 1.0.preview-2 - // 1.0.rc2 - // 1.0beta2 - :pre( - ["-" "_" "."]? - - :pre_l( - ("preview"|"alpha"|"beta"|"pre"|"rc"|"a"|"b"|"c") - ) - - ["-" "_" "."]? - - :pre_n(['0'-'9']+)? - )? - - // Version String may include Post-Header - // Examples: - // 1.0-9 - // 1.0-post2 - // 1.0.post.2 - :post( - "-" - :post_n1(['0'-'9']+) - - | - - ["-" "_" "."]? - :post_l("post" | "rev" | "r") - ["-" "_" "."]? - :post_n2(['0'-'9']+)? - )? - - // Version string may include Dev-header - // Example: - // 1.0-dev3 - // 1.0dev4 - // 1.0_dev_9 - :dev( - ["-" "_" "."]? - :dev_l("dev") - ["-" "_" "."]? - :dev_n(['0'-'9']+)? - )? - - // Version string may include Local Version - // Local version must start with + - // Example: - // 1.0+this.can.say.anything.as.long.as.its.a.letter.or.number.231241 - ( - "+" - :local( - ['a'-'z' '0'-'9']+ - ((["-" "_" "."] ['a'-'z' '0'-'9']+)+)? - ) - )? -); - -/// # `PEP-440` Developmental release identifier -#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, PartialOrd)] -pub struct DevHead { - dev_num: Option, -} - -/// `PEP-440` Post-Release Identifier Keyword -#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)] -pub enum PostHead { - Post, - Rev, -} - -impl PartialOrd for PostHead { - fn partial_cmp(&self, _other: &Self) -> Option { - Some(Ordering::Equal) - } -} - -/// # `PEP-440` Post-Release identifier -#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)] -pub struct PostHeader { - pub post_head: Option, - pub post_num: Option, -} - -impl PartialOrd for PostHeader { - fn partial_cmp(&self, other: &Self) -> Option { - if self.post_num == other.post_num { - return Some(Ordering::Equal); - } - - if self.post_num.is_none() && other.post_num.is_some() { - return Some(Ordering::Less); - } else if self.post_num.is_some() && other.post_num.is_none() { - return Some(Ordering::Greater); - } - - if self.post_num < other.post_num { - Some(Ordering::Less) - } else { - Some(Ordering::Greater) - } - } -} - -/// # `PEP-440` Pre-Release identifier -#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, PartialOrd)] -pub enum PreHeader { - /// Present in versions like 1.1beta1 or 1.0b1 both are represented the same way - /// ``` - /// PreHeader::Beta(Some(1)) - /// ``` - Beta(Option), - /// Present in versions like 1.0alpha2 or 1.0a2 both are represented the same way - /// ``` - /// PreHeader::Alpha(Some(2)) - /// ``` - Alpha(Option), - /// Present in versions like 1.1pre3 - /// ``` - /// PreHeader::Preview(Some(3)) - /// ``` - Preview(Option), - /// Present in versions like 1.1-rc-4 or 1.1c-4 - /// ``` - /// PreHeader::ReleaseCandidate(Some(4)) - /// ``` - ReleaseCandidate(Option), -} - -/// `PEP-440` Release numbers -#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, PartialOrd)] -pub struct ReleaseHeader { - /// Major release such as 1.0 or breaking changes - pub major: u32, - /// Minor release Such as new functionality - pub minor: u32, -} - -/// `PEP-440` Compliant versioning system -/// -/// This struct is sorted so that PartialOrd -/// correctly interprets priority -/// -/// Lower == More important -/// -/// # Example Usage -/// ``` -/// let _ = PackageVersion::new("v1.0"); -/// ``` -#[derive(Derivative, Debug, Serialize, Deserialize)] -#[derivative(PartialOrd, PartialEq)] -pub struct PackageVersion { - #[derivative(PartialOrd = "ignore", PartialEq = "ignore")] - pub original: String, - - /// # `PEP-440` Local version identifier - /// Local version sorting will have to be it's own issue - /// since there are no limits to what a local version can be - /// - /// For those who can read regex here it is for the local version: - /// `[a-z0-9]+(?:(?:[\-_.][a-z0-9]+)+)?` - /// - /// Here in Rulex: - /// ``` - /// ['a'-'z' '0'-'9']+ - /// ((["-" "_" "."] ['a'-'z' '0'-'9']+)+)? - /// ``` - #[derivative(PartialOrd = "ignore", PartialEq = "ignore")] - pub local: Option, - - /// # `PEP-440` Developmental release identifier - pub dev: Option, - - /// # `PEP-440` Post-Release identifier - pub post: Option, - - /// # `PEP-440` Pre-Release identifier - pub pre: Option, - - /// # `PEP-440` Release number - pub release: ReleaseHeader, - - /// # `PEP-440` Version-Epoch - pub epoch: Option, -} - -impl PackageVersion { - pub fn new(version: &str) -> Result { - lazy_static! { - // Safe to unwrap since Regex is predefined - // Regex as defined in PEP-0440 - static ref VERSION_VALIDATOR: regex::Regex = - regex::Regex::new(VALIDATION_REGEX).unwrap(); - } - - // Capture each group of the regex - // Groups are: - // epoch, release, pre, pre_l, pre_n, post, post_l, post_n1, post_n2, - // dev, dev_l, dev_n, local - let version_match = match VERSION_VALIDATOR.captures(version) { - Some(v) => v, - None => anyhow::bail!("Failed to decode version {}", version), - }; - - let epoch: Option = match version_match.name("epoch") { - // Convert Epoch String to Epoch Number - Some(v) => Some(v.as_str().parse::()?), - None => None, - }; - - let release: ReleaseHeader = match version_match.name("release") { - Some(v) => { - // Does Release String contain minor version - if v.as_str().contains('.') { - let split: Vec<&str> = v.as_str().split('.').into_iter().collect(); - ReleaseHeader { - major: split[0].parse::()?, - minor: split[1].parse::()?, - } - } else { - ReleaseHeader { - major: v.as_str().parse::()?, - minor: 0, - } - } - } - // There always has to be at least a major version - None => anyhow::bail!("Failed to decode version {}", version), - }; - - let pre: Option = match version_match.name("pre") { - Some(_) => { - let pre_n = match version_match.name("pre_n") { - Some(v) => Some(v.as_str().parse::()?), - None => None, - }; - - // Should be safe to unwrap since we already checked if pre has a value - // since pre_n has to exist - match version_match.name("pre_l").unwrap().as_str() { - "alpha" => Some(PreHeader::Alpha(pre_n)), - "a" => Some(PreHeader::Alpha(pre_n)), - "beta" => Some(PreHeader::Beta(pre_n)), - "b" => Some(PreHeader::Beta(pre_n)), - "rc" => Some(PreHeader::ReleaseCandidate(pre_n)), - "c" => Some(PreHeader::ReleaseCandidate(pre_n)), - "preview" => Some(PreHeader::Preview(pre_n)), - "pre" => Some(PreHeader::Preview(pre_n)), - _ => None, - } - } - None => None, - }; - - let post: Option = match version_match.name("post") { - Some(_) => { - let post_num: Option = match version_match.name("post_n1") { - Some(v) => Some(v.as_str().parse::()?), - None => match version_match.name("post_n2") { - Some(v) => Some(v.as_str().parse::()?), - _ => None, - }, - }; - - let post_head: Option = match version_match.name("post_l") { - Some(v) => { - match v.as_str() { - "post" => Some(PostHead::Post), - "rev" => Some(PostHead::Rev), - "r" => Some(PostHead::Rev), - // This branch Should be impossible (see regex-group post_l) - _ => None, - } - } - None => None, - }; - - Some(PostHeader { - post_head, - post_num, - }) - } - None => None, - }; - - let dev: Option = match version_match.name("dev") { - Some(_) => { - let dev_num = match version_match.name("dev_n") { - Some(v) => Some(v.as_str().parse::()?), - None => None, - }; - Some(DevHead { dev_num }) - } - None => None, - }; - - let local: Option = - version_match.name("local").map(|v| v.as_str().to_string()); - - Ok(Self { - original: version.to_string(), - epoch, - release, - pre, - post, - dev, - local, - }) - } -} - -impl fmt::Display for PackageVersion { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.original) - } -} - -#[cfg(test)] -mod tests { - use std::fmt::Debug; - - use crate::package_version::PackageVersion; - use anyhow::bail; - use anyhow::Result; - - use super::DevHead; - use super::PostHead; - use super::PostHeader; - use super::PreHeader; - use super::ReleaseHeader; - - fn test_a_greater(a: T, b: T) -> Result<()> - where - T: PartialEq + PartialOrd + Debug, - { - if a <= b { - bail!( - "Failed Less Than or Equal Check for A: {:?} \n<=\n B: {:?}", - a, - b - ) - } - Ok(()) - } - - #[test] - fn test_pep440_ordering() -> Result<()> { - test_a_greater( - PackageVersion::new("v1!1.0-preview-921.post-516.dev-241+yeah.this.is.the.problem.with.local.versions")?, - PackageVersion::new("1.0")?, - )?; - Ok(()) - } - - #[test] - fn test_release_ordering() -> Result<()> { - test_a_greater( - ReleaseHeader { major: 1, minor: 0 }, - ReleaseHeader { major: 0, minor: 0 }, - )?; - test_a_greater( - ReleaseHeader { major: 1, minor: 1 }, - ReleaseHeader { major: 1, minor: 0 }, - )?; - test_a_greater( - ReleaseHeader { major: 2, minor: 1 }, - ReleaseHeader { - major: 1, - minor: 52, - }, - )?; - Ok(()) - } - - #[test] - fn test_pre_ordering() -> Result<()> { - test_a_greater(PreHeader::ReleaseCandidate(None), PreHeader::Preview(None))?; - test_a_greater(PreHeader::Preview(None), PreHeader::Alpha(None))?; - test_a_greater(PreHeader::Alpha(None), PreHeader::Beta(None))?; - - test_a_greater( - PreHeader::ReleaseCandidate(Some(2)), - PreHeader::ReleaseCandidate(Some(1)), - )?; - test_a_greater(PreHeader::Preview(Some(50)), PreHeader::Preview(Some(3)))?; - test_a_greater(PreHeader::Alpha(Some(504)), PreHeader::Alpha(Some(0)))?; - test_a_greater(PreHeader::Beta(Some(1234)), PreHeader::Beta(Some(1)))?; - - test_a_greater( - PreHeader::ReleaseCandidate(Some(1)), - PreHeader::Beta(Some(45067885)), - )?; - Ok(()) - } - - #[test] - fn test_post_ordering() -> Result<()> { - test_a_greater( - PostHeader { - post_head: Some(PostHead::Post), - post_num: Some(0), - }, - PostHeader { - post_head: Some(PostHead::Post), - post_num: None, - }, - )?; - test_a_greater( - PostHeader { - post_head: Some(PostHead::Post), - post_num: Some(1), - }, - PostHeader { - post_head: Some(PostHead::Post), - post_num: Some(0), - }, - )?; - Ok(()) - } - - #[test] - fn test_dev_ordering() -> Result<()> { - test_a_greater(DevHead { dev_num: Some(0) }, DevHead { dev_num: None })?; - test_a_greater(DevHead { dev_num: Some(1) }, DevHead { dev_num: Some(0) })?; - Ok(()) - } - - #[test] - fn test_pep440_equality() -> Result<()> { - assert_eq!( - PackageVersion::new("1.0a1")?, - PackageVersion::new("1.0alpha1")? - ); - assert_eq!( - PackageVersion::new("1.0b")?, - PackageVersion::new("1.0beta")? - ); - assert_eq!(PackageVersion::new("1.0r")?, PackageVersion::new("1.0rev")?); - assert_eq!(PackageVersion::new("1.0c")?, PackageVersion::new("1.0rc")?); - assert_eq!(PackageVersion::new("v1.0")?, PackageVersion::new("1.0")?); - Ok(()) - } - - #[test] - fn test_pep440() { - // list of every example mentioned in pep-440 - let versions = vec![ - "1.0", - "v1.1", - "2.0", - "2013.10", - "2014.04", - "1!1.0", - "1!1.1", - "1!2.0", - "2!1.0.pre0", - "1.0.dev456", - "1.0a1", - "1.0a2.dev456", - "1.0a12.dev456", - "1.0a12", - "1.0b1.dev456", - "1.0b2", - "1.0b2.post345.dev456", - "1.0b2.post345", - "1.0rc1.dev456", - "1.0rc1", - "1.0", - "1.0+abc.5", - "1.0+abc.7", - "1.0+5", - "1.0.post456.dev34", - "1.0.post456", - "1.0.15", - "1.1.dev1", - ]; - - for version in versions { - match PackageVersion::new(version) { - Ok(_v) => continue, - Err(e) => panic!("Oh no {}", e), - } - } - } - - #[test] - fn test_pep440_negative() { - let versions = vec!["not a version"]; - - for version in versions { - match PackageVersion::new(version) { - Ok(v) => panic!("Oh no {}", v), - Err(_e) => continue, - } - } - } -}