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,
- }
- }
- }
-}