From d9eae8da3a85ba50e665e9dde59cff060be6869c Mon Sep 17 00:00:00 2001
From: David Runge <dvzrv@archlinux.org>
Date: Sun, 28 May 2023 17:48:01 +0200
Subject: [PATCH 1/3] feat: Derive Architecture from Ord and PartialOrd to
 allow comparison.

---
 src/system.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/system.rs b/src/system.rs
index 60acf12..10fc5f8 100644
--- a/src/system.rs
+++ b/src/system.rs
@@ -31,7 +31,7 @@ use strum_macros::EnumString;
 /// assert_eq!("x86_64_v3", format!("{}", Architecture::X86_64V3));
 /// assert_eq!("x86_64_v4", format!("{}", Architecture::X86_64V4));
 /// ```
-#[derive(Debug, Display, EnumString, Eq, PartialEq)]
+#[derive(Debug, Display, EnumString, Eq, Ord, PartialEq, PartialOrd)]
 #[non_exhaustive]
 pub enum Architecture {
     #[strum(to_string = "aarch64")]
-- 
GitLab


From 25b1001d99e9c2fa8d82140471c1f22118311bf1 Mon Sep 17 00:00:00 2001
From: David Runge <dvzrv@archlinux.org>
Date: Tue, 30 May 2023 20:59:24 +0200
Subject: [PATCH 2/3] feat(Version): Add method to create Version with Pkgrel

Add `with_pkgrel()` to create a new `Version` which is guaranteed to
have a `Pkgrel`.
---
 README.md      |  4 ++++
 src/version.rs | 22 ++++++++++++++++++++++
 2 files changed, 26 insertions(+)

diff --git a/README.md b/README.md
index e290313..ab01fce 100644
--- a/README.md
+++ b/README.md
@@ -153,6 +153,10 @@ let version_a = Version::new("1.0.0").unwrap();
 let version_b = Version::new("1.1.0").unwrap();
 
 assert_eq!(Version::vercmp(&version_a, &version_b), -1);
+
+// create a Version that is guaranteed to have a Pkgrel
+assert!(Version::with_pkgrel("1.0.0-1").is_ok());
+assert!(Version::with_pkgrel("1.0.0").is_err());
 ```
 
 ## Contributing
diff --git a/src/version.rs b/src/version.rs
index f3af4e2..c731bc3 100644
--- a/src/version.rs
+++ b/src/version.rs
@@ -482,6 +482,14 @@ impl Version {
         })
     }
 
+    /// Create a new Version, which is guaranteed to have a Pkgrel
+    pub fn with_pkgrel(version: &str) -> Result<Self, Error> {
+        match Version::new(version) {
+            Ok(version) if version.pkgrel().is_some() => Ok(version),
+            _ => Err(Error::InvalidVersion(version.to_string())),
+        }
+    }
+
     /// Return the optional reference to the Epoch of the Version
     pub fn epoch(&self) -> Option<&Epoch> {
         self.epoch.as_ref()
@@ -649,6 +657,20 @@ mod tests {
         }
     }
 
+    #[rstest]
+    #[case(
+        "1.0.0-1",
+        Ok(Version{
+            pkgver: Pkgver::new("1.0.0".to_string()).unwrap(),
+            pkgrel: Some(Pkgrel::new("1".to_string()).unwrap()),
+            epoch: None,
+        })
+    )]
+    #[case("1.0.0", Err(Error::InvalidVersion("1.0.0".to_string())))]
+    fn version_with_pkgrel(#[case] version: &str, #[case] result: Result<Version, Error>) {
+        assert_eq!(result, Version::with_pkgrel(version));
+    }
+
     #[rstest]
     #[case("1".to_string(), Ok(Epoch(NonZeroUsize::new(1).unwrap())))]
     #[case("0".to_string(), Err(Error::InvalidEpoch("0".to_string())))]
-- 
GitLab


From 6276f82eb10edc368dce441a94962246097f4b8d Mon Sep 17 00:00:00 2001
From: David Runge <dvzrv@archlinux.org>
Date: Sun, 28 May 2023 17:48:20 +0200
Subject: [PATCH 3/3] feat: Implement BuildToolVer type

Implement `BuildToolVer` which tracks a `Version` (that is guaranteed
to have a `Pkgrel`) and an `Architecture` and can be used to denote this
information for a given `BuildTool`.
---
 README.md      | 10 ++++++
 src/error.rs   |  6 ++++
 src/lib.rs     |  1 +
 src/version.rs | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 109 insertions(+)

diff --git a/README.md b/README.md
index ab01fce..8dc879d 100644
--- a/README.md
+++ b/README.md
@@ -127,6 +127,16 @@ assert_eq!(PkgType::from_str("pkg"), Ok(PkgType::Package));
 
 ### Version
 
+The version and CPU architecture of a build tool is tracked using `BuildToolVer`:
+
+```rust
+use alpm_types::BuildToolVer;
+
+let buildtoolver = BuildToolVer::new("1.0.0-1-any").unwrap();
+
+assert_eq!("1.0.0-1-any", format!("{}", buildtoolver));
+```
+
 Schemas of compound types (e.g. those used to describe `.BUILDINFO` or `.PKGINFO` files) need a schema version to version their features. This is what `SchemaVersion` is for:
 
 ```rust
diff --git a/src/error.rs b/src/error.rs
index ff7f681..a00334e 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -6,6 +6,9 @@ use thiserror::Error;
 #[derive(Debug, Error, PartialEq)]
 #[non_exhaustive]
 pub enum Error {
+    /// An invalid CPU architecture
+    #[error("Invalid architecture: {0}")]
+    InvalidArchitecture(String),
     /// An invalid build date (in seconds since the epoch)
     #[error("Invalid build date: {0}")]
     InvalidBuildDate(String),
@@ -18,6 +21,9 @@ pub enum Error {
     /// An invalid BuildTool
     #[error("Invalid buildtool: {0}")]
     InvalidBuildTool(String),
+    /// An invalid BuildToolVer
+    #[error("Invalid buildtool version: {0}")]
+    InvalidBuildToolVer(String),
     /// An invalid compressed file size (in bytes)
     #[error("Invalid compressed size: {0}")]
     InvalidCompressedSize(String),
diff --git a/src/lib.rs b/src/lib.rs
index eff1ba3..0127c8a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -40,6 +40,7 @@ mod system;
 pub use system::Architecture;
 
 mod version;
+pub use version::BuildToolVer;
 pub use version::Epoch;
 pub use version::Pkgrel;
 pub use version::Pkgver;
diff --git a/src/version.rs b/src/version.rs
index c731bc3..d39ccb5 100644
--- a/src/version.rs
+++ b/src/version.rs
@@ -9,8 +9,71 @@ use std::str::FromStr;
 use semver::Version as SemverVersion;
 
 use crate::regex_once;
+use crate::Architecture;
 use crate::Error;
 
+/// The version and architecture of a build tool
+///
+/// `BuildToolVer` is used in conjunction with `BuildTool` to denote the specific build tool a package is built with.
+/// A `BuildToolVer` wraps a `Version` (that is guaranteed to have a `Pkgrel`) and an `Architecture`.
+///
+/// ## Examples
+/// ```
+/// use alpm_types::BuildToolVer;
+///
+/// assert!(BuildToolVer::new("1-1-any").is_ok());
+/// assert!(BuildToolVer::new("1").is_err());
+/// assert!(BuildToolVer::new("1-1-foo").is_err());
+/// ```
+#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)]
+pub struct BuildToolVer {
+    version: Version,
+    architecture: Architecture,
+}
+
+impl BuildToolVer {
+    /// Create a new BuildToolVer and return it in a Result
+    pub fn new(buildtoolver: &str) -> Result<Self, Error> {
+        match buildtoolver.rsplit_once('-') {
+            Some((version, architecture)) => {
+                if let Ok(architecture) = Architecture::from_str(architecture) {
+                    Ok(BuildToolVer {
+                        version: Version::with_pkgrel(version)?,
+                        architecture,
+                    })
+                } else {
+                    Err(Error::InvalidArchitecture(architecture.to_string()))
+                }
+            }
+            None => Err(Error::InvalidBuildToolVer(buildtoolver.to_string())),
+        }
+    }
+
+    /// Return a reference to the Architecture
+    pub fn architecture(&self) -> &Architecture {
+        &self.architecture
+    }
+
+    /// Return a reference to the Version
+    pub fn version(&self) -> &Version {
+        &self.version
+    }
+}
+
+impl FromStr for BuildToolVer {
+    type Err = Error;
+    /// Create an BuildToolVer from a string and return it in a Result
+    fn from_str(input: &str) -> Result<Self, Self::Err> {
+        BuildToolVer::new(input)
+    }
+}
+
+impl Display for BuildToolVer {
+    fn fmt(&self, fmt: &mut Formatter) -> std::fmt::Result {
+        write!(fmt, "{}-{}", self.version, self.architecture)
+    }
+}
+
 /// An epoch of a package
 ///
 /// Epoch is used to indicate the downgrade of a package and is prepended to a version, delimited by a `":"` (e.g. `1:`
@@ -604,6 +667,35 @@ mod tests {
         assert_eq!(result, SchemaVersion::new(version))
     }
 
+    #[rstest]
+    #[case(
+        "1.0.0-1-any",
+        Ok(BuildToolVer{version: Version::new("1.0.0-1").unwrap(), architecture: Architecture::from_str("any").unwrap()}),
+    )]
+    #[case(
+        "1:1.0.0-1-any",
+        Ok(BuildToolVer{version: Version::new("1:1.0.0-1").unwrap(), architecture: Architecture::from_str("any").unwrap()}),
+    )]
+    #[case(
+        "1.0.0",
+        Err(Error::InvalidBuildToolVer("1.0.0".to_string())),
+    )]
+    #[case(
+        "1.0.0-any",
+        Err(Error::InvalidVersion("1.0.0".to_string())),
+    )]
+    #[case(
+        ".1.0.0-1-any",
+        Err(Error::InvalidVersion(".1.0.0-1".to_string())),
+    )]
+    #[case(
+        "1.0.0-1-foo",
+        Err(Error::InvalidArchitecture("foo".to_string())),
+    )]
+    fn buildtoolver_new(#[case] buildtoolver: &str, #[case] result: Result<BuildToolVer, Error>) {
+        assert_eq!(BuildToolVer::new(buildtoolver), result);
+    }
+
     #[rstest]
     #[case(
         SchemaVersion(SemverVersion::new(1, 0, 0)),
-- 
GitLab