Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

shift polymorphism to version_parts #21

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "version-compare"
version = "0.0.10"
authors = ["timvisee <[email protected]>"]
authors = ["timvisee <[email protected]>", "msarahan <[email protected]>"]
license = "MIT"
readme = "README.md"
homepage = "https://github.com/timvisee/version-compare"
Expand All @@ -18,3 +18,7 @@ travis-ci = { repository = "timvisee/version-compare" }
[features]
default = []
dev = []

[dependencies]
lazy_static = "1.4.0"
regex = "1.3.1"
1 change: 1 addition & 0 deletions src/custom_parts/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod pep440;
102 changes: 102 additions & 0 deletions src/custom_parts/pep440.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use std::cmp::Ordering;
use std::fmt;
use regex::Regex;

#[derive(Debug, Copy, Clone)]
pub struct PEP440String<'a> {
pre: i16,
alpha: &'a str,
post: i16,
}

impl<'a> PEP440String<'a> {
fn new(input: &'a str) -> PEP440String {
lazy_static! {
static ref RE: Regex = Regex::new(r"^(\d*)([a-zA-Z]*)(\d*)").unwrap();
}

let caps = RE.captures(input).unwrap();
let pre: i16 = caps.get(1).map_or(0, |m| match m.as_str().is_empty() {
true => 0,
false => m.as_str().parse().unwrap()
});
let alpha = caps.get(2).map_or("", |m| m.as_str());
let post: i16 = caps.get(3).map_or(0, |m| match m.as_str().is_empty() {
true => 0,
false => m.as_str().parse().unwrap()
});

PEP440String{ pre, alpha, post }
}

pub fn empty() -> PEP440String<'a> {
PEP440String {pre: 0, alpha: "", post: 0}
}
}

fn compare_pep440_str<'a>(left: &'a str, right: &'a str) -> Option<Ordering> {
lazy_static! { static ref DEV_RE: Regex = Regex::new("dev").unwrap(); }
lazy_static! { static ref POST_RE: Regex = Regex::new("post").unwrap(); }

let is_dev = (DEV_RE.is_match(left), DEV_RE.is_match(right));
let is_post = (POST_RE.is_match(left), POST_RE.is_match(right));

let str_match = left.partial_cmp(right);
match str_match {
Some(Ordering::Equal) => Some(Ordering::Equal),
_ => match is_dev {
(false, true) => Some(Ordering::Greater),
(true, false) => Some(Ordering::Less),
_ => match is_post {
(true, true) => Some(Ordering::Equal),
(false, true) => Some(Ordering::Less),
(true, false) => Some(Ordering::Greater),
// this is the final fallback to lexicographic sorting, if neither
// dev nor post are in effect
(false, false) => left.partial_cmp(right),
}
}
}
}

impl<'a> PartialOrd for PEP440String<'a> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match self.pre.partial_cmp(&other.pre) {
Some(Ordering::Greater) => Some(Ordering::Greater),
Some(Ordering::Less) => Some(Ordering::Less),
Some(Ordering::Equal) => match compare_pep440_str(self.alpha, &other.alpha) {
Some(Ordering::Equal) => self.post.partial_cmp(&other.post),
Some(Ordering::Greater) => Some(Ordering::Greater),
Some(Ordering::Less) => Some(Ordering::Less),
_ => panic!()
}
_ => panic!()
}
}
}

impl<'a> PartialEq for PEP440String<'a> {
fn eq(&self, other: &Self) -> bool {
self.partial_cmp(&other).unwrap() == Ordering::Equal
}
}

impl<'a> fmt::Display for PEP440String<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}{}{}", self.pre, self.alpha, self.post)
}
}

#[cfg_attr(tarpaulin, skip)]
#[cfg(test)]
mod tests {
use super::PEP440String;

#[test]
fn compare_implict_leading_zero() {
assert_eq!(PEP440String::new("0dev"), PEP440String::new("dev"));
// epoch of any value trumps integer (priority)
// assert!(VersionPart::Epoch(value: 0) > VersionPart::Integer(value: 1);
// assert!(Version::Epoch{0} > Version::String{"abc"});
}
}
12 changes: 8 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,19 +50,23 @@
//!
//!
//! _[View complete README](https://github.com/timvisee/version-compare/blob/master/README.md)_
//!
#[macro_use] extern crate lazy_static;

pub mod comp_op;
pub mod version;
pub mod version_compare;
pub mod version_manifest;
pub mod version_part;
pub mod parsers;
// Ideally no one interacts directly with the parts
mod version_part;
mod custom_parts;

#[cfg(test)]
mod test;

// Reexports
pub use crate::comp_op::CompOp;
pub use crate::version::Version;
pub use crate::version_compare::VersionCompare;
pub use crate::version_manifest::VersionManifest;
pub use crate::version_part::VersionPart;
pub use crate::version_compare::VersionCompare;
pub use crate::parsers::default::default_parser;
Empty file added src/parsers/conda.rs
Empty file.
39 changes: 39 additions & 0 deletions src/parsers/default.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use crate::version_part::VersionPart;

/// Split the given version string, in it's version parts.
/// TODO: Move this method to some sort of helper class, maybe as part of `VersionPart`.
pub fn default_parser(
version: &str,
) -> Option<Vec<VersionPart>> {
// Split the version string, and create a vector to put the parts in
// TODO: split at specific separators instead
let split = version.split(|c| !char::is_alphanumeric(c));
let mut parts = Vec::new();

// Loop over the parts, and parse them
for part in split {
// Skip empty parts
if part.is_empty() {
continue;
}

// Try to parse the value as an number
match part.parse::<i32>() {
Ok(number) => {
// Push the number part to the vector, and set the has number flag
parts.push(VersionPart::Integer(number));
}
Err(_) => {
// Push the text part to the vector
parts.push(VersionPart::LexicographicString(part));
}
}
}

if parts.is_empty() && version.is_empty() {
parts.push(VersionPart::Empty);
}

// Return the list of parts
Some(parts)
}
2 changes: 2 additions & 0 deletions src/parsers/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod conda;
pub mod default;
11 changes: 7 additions & 4 deletions src/test/test_version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ pub const TEST_VERSIONS: &'static [TestVersion] = &[
TestVersion("0.0.0", 3),
TestVersion("1.0.0", 3),
TestVersion("0.0.1", 3),
TestVersion("", 0),
// Empty strings still have length 1 - the Empty component
TestVersion("", 1),
TestVersion(".", 0),
TestVersion("...", 0),
TestVersion("1.2.dev", 3),
Expand All @@ -32,9 +33,11 @@ pub const TEST_VERSIONS: &'static [TestVersion] = &[

/// List of version numbers that contain errors with metadata for dynamic tests
pub const TEST_VERSIONS_ERROR: &'static [TestVersion] = &[
TestVersion("abc", 1),
TestVersion("alpha.dev.snapshot", 3),
TestVersion("test. .snapshot", 3),
// 3 alpha parts - fine
// TestVersion("abc", 1),
// 3 alpha parts - fine
// TestVersion("alpha.dev.snapshot", 3),
//TestVersion("test. .snapshot", 3),
// TODO: broken case, decide what to do here
// TestVersion("$", 1),
];
47 changes: 24 additions & 23 deletions src/test/test_version_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ pub const TEST_VERSION_SETS: &'static [TestVersionSet] = &[
TestVersionSet("0.0.0", "0", CompOp::Eq),
TestVersionSet("0", "0.0.0", CompOp::Eq),
TestVersionSet("", "", CompOp::Eq),
TestVersionSet("", "0.0", CompOp::Eq),
TestVersionSet("0.0", "", CompOp::Eq),
// Empty is lower than anything
TestVersionSet("", "0.0", CompOp::Lt),
TestVersionSet("0.0", "", CompOp::Gt),
TestVersionSet("", "0.1", CompOp::Lt),
TestVersionSet("0.1", "", CompOp::Gt),
TestVersionSet("1.2.3", "1.2.3", CompOp::Eq),
Expand All @@ -37,31 +38,31 @@ pub const TEST_VERSION_SETS: &'static [TestVersionSet] = &[
TestVersionSet("1.1.2", "1.1.30-dev", CompOp::Lt),
TestVersionSet("1.2.3", "1.2.3.alpha", CompOp::Gt),
TestVersionSet("1.2.3", "1.2.3-dev", CompOp::Gt),
TestVersionSet("1.2.3.dev", "1.2.3.alpha", CompOp::Eq),
TestVersionSet("1.2.3-dev", "1.2.3-alpha", CompOp::Eq),
TestVersionSet("1.2.3.dev", "1.2.3.alpha", CompOp::Gt),
TestVersionSet("1.2.3-dev", "1.2.3-alpha", CompOp::Gt),
TestVersionSet("1.2.3.dev.1", "1.2.3.alpha", CompOp::Gt),
TestVersionSet("1.2.3-dev-1", "1.2.3-alpha", CompOp::Gt),
TestVersionSet("version-compare 3.2.0 / build 0932", "3.2.5", CompOp::Lt),
TestVersionSet("version-compare 3.2.0 / build 0932", "3.1.1", CompOp::Gt),
TestVersionSet(
"version-compare 1.4.1 / build 0043",
"version-compare 1.4.1 / build 0043",
CompOp::Eq,
),
TestVersionSet(
"version-compare 1.4.1 / build 0042",
"version-compare 1.4.1 / build 0043",
CompOp::Lt,
),
// TODO: inspect these cases
TestVersionSet("snapshot.1.2.3", "1.2.3.alpha", CompOp::Lt),
TestVersionSet("snapshot-1.2.3", "1.2.3-alpha", CompOp::Lt),
// TestVersionSet("version-compare 3.2.0 / build 0932", "3.2.5", CompOp::Lt),
// TestVersionSet("version-compare 3.2.0 / build 0932", "3.1.1", CompOp::Gt),
// TestVersionSet(
// "version-compare 1.4.1 / build 0043",
// "version-compare 1.4.1 / build 0043",
// CompOp::Eq,
// ),
// TestVersionSet(
// "version-compare 1.4.1 / build 0042",
// "version-compare 1.4.1 / build 0043",
// CompOp::Lt,
// ),
// // TODO: inspect these cases
// TestVersionSet("snapshot.1.2.3", "1.2.3.alpha", CompOp::Lt),
// TestVersionSet("snapshot-1.2.3", "1.2.3-alpha", CompOp::Lt),
];

/// List of invalid version sets for dynamic tests
pub const TEST_VERSION_SETS_ERROR: &'static [TestVersionSet] = &[
TestVersionSet("1.2.3", "1.2.3", CompOp::Lt),
TestVersionSet("1.2", "1.2.0.0", CompOp::Ne),
TestVersionSet("1.2.3.dev", "dev", CompOp::Eq),
TestVersionSet("snapshot", "1", CompOp::Lt),
TestVersionSet("1.2.3", "1.2.3", CompOp::Lt), // Eq
TestVersionSet("1.2", "1.2.0.0", CompOp::Ne), // Eq
TestVersionSet("1.2.3.dev", "dev", CompOp::Eq), // NEq
TestVersionSet("snapshot", "1", CompOp::Gt), // Lt
];
Loading