From 3324d3b7c1c99fa585cd6634fac5a95941acece0 Mon Sep 17 00:00:00 2001 From: Jelmer Vernooij Date: Sun, 22 Sep 2024 22:42:09 +0000 Subject: [PATCH] Add documentation --- src/lib.rs | 366 ++++++++++++++++++++++++------------------------ src/upstream.rs | 17 +++ src/vcs.rs | 6 + src/vendor.rs | 11 +- 4 files changed, 218 insertions(+), 182 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ea8c946..ed53529 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +#![deny(missing_docs)] //! Debian version type, consistent with Section 5.6.12 in the Debian Policy Manual //! //! This structure can be used for validating, dissecting and comparing Debian version strings. @@ -183,6 +184,7 @@ impl PartialEq for Version { impl Eq for Version {} +/// Error parsing a version string #[derive(Debug, PartialEq, Eq)] pub struct ParseError(String); @@ -198,12 +200,13 @@ impl FromStr for Version { type Err = ParseError; fn from_str(text: &str) -> Result { - let (_, epoch, upstream_version, debian_revision) = match regex_captures!( + let (_, epoch, upstream_version, debian_revision) = if let Some(c) = regex_captures!( r"^(?:(\d+):)?([A-Za-z0-9.+:~-]+?)(?:-([A-Za-z0-9+.~]+))?$", text ) { - Some(c) => c, - None => return Err(ParseError(format!("Invalid version string: {}", text))), + c + } else { + return Err(ParseError(format!("Invalid version string: {}", text))); }; let epoch = Some(epoch) @@ -257,7 +260,7 @@ impl Version { ( self.epoch.unwrap_or(0), self.upstream_version.as_str(), - self.debian_revision.as_deref().unwrap_or("0") + self.debian_revision.as_deref().unwrap_or("0"), ) } @@ -323,6 +326,184 @@ impl Version { } } +#[cfg(feature = "sqlx")] +use sqlx::{postgres::PgTypeInfo, Postgres}; + +#[cfg(feature = "sqlx")] +impl sqlx::Type for Version { + fn type_info() -> PgTypeInfo { + PgTypeInfo::with_name("debversion") + } +} + +#[cfg(feature = "sqlx")] +impl sqlx::Encode<'_, Postgres> for Version { + fn encode_by_ref( + &self, + buf: &mut sqlx::postgres::PgArgumentBuffer, + ) -> Result> { + sqlx::Encode::::encode_by_ref(&self.to_string().as_str(), buf) + } +} + +#[cfg(feature = "sqlx")] +impl sqlx::Decode<'_, Postgres> for Version { + fn decode( + value: sqlx::postgres::PgValueRef<'_>, + ) -> Result> { + let s: &str = sqlx::Decode::::decode(value)?; + Ok(s.parse::()?) + } +} + +#[cfg(all(feature = "sqlx", test))] +mod sqlx_tests { + #[test] + fn type_info() { + use super::Version; + use sqlx::postgres::PgTypeInfo; + use sqlx::Type; + + assert_eq!(PgTypeInfo::with_name("debversion"), Version::type_info()); + } +} + +#[cfg(feature = "python-debian")] +use pyo3::prelude::*; + +#[cfg(feature = "python-debian")] +impl FromPyObject<'_> for Version { + fn extract_bound(ob: &Bound) -> PyResult { + let debian_support = Python::import_bound(ob.py(), "debian.debian_support")?; + let version_cls = debian_support.getattr("Version")?; + if !ob.is_instance(&version_cls)? { + return Err(pyo3::exceptions::PyTypeError::new_err("Expected a Version")); + } + Ok(Version { + epoch: ob + .getattr("epoch")? + .extract::>()? + .map(|s| s.parse().unwrap()), + upstream_version: ob.getattr("upstream_version")?.extract::()?, + debian_revision: ob.getattr("debian_revision")?.extract::>()?, + }) + } +} + +#[cfg(feature = "python-debian")] +impl ToPyObject for Version { + fn to_object(&self, py: Python) -> PyObject { + let debian_support = py.import_bound("debian.debian_support").unwrap(); + let version_cls = debian_support.getattr("Version").unwrap(); + version_cls + .call1((self.to_string(),)) + .unwrap() + .to_object(py) + } +} + +#[cfg(feature = "python-debian")] +impl IntoPy for Version { + fn into_py(self, py: Python) -> PyObject { + self.to_object(py) + } +} + +#[cfg(feature = "python-debian")] +mod python_tests { + #[test] + fn test_from_pyobject() { + use super::Version; + use pyo3::prelude::*; + + Python::with_gil(|py| { + let globals = pyo3::types::PyDict::new_bound(py); + globals + .set_item( + "debian_support", + py.import_bound("debian.debian_support").unwrap(), + ) + .unwrap(); + let v = py + .eval_bound("debian_support.Version('1.0-1')", Some(&globals), None) + .unwrap() + .extract::() + .unwrap(); + assert_eq!( + v, + Version { + epoch: None, + upstream_version: "1.0".to_string(), + debian_revision: Some("1".to_string()) + } + ); + }); + } + + #[test] + fn test_to_pyobject() { + use super::Version; + use pyo3::prelude::*; + + Python::with_gil(|py| { + let v = Version { + epoch: Some(1), + upstream_version: "1.0".to_string(), + debian_revision: Some("1".to_string()), + }; + let v = v.to_object(py); + let expected: Version = "1:1.0-1".parse().unwrap(); + assert_eq!(v.extract::(py).unwrap(), expected); + assert_eq!(v.bind(py).get_type().name().unwrap(), "Version"); + }); + } +} + +#[cfg(feature = "serde")] +impl serde::Serialize for Version { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +#[cfg(feature = "serde")] +impl<'de> serde::Deserialize<'de> for Version { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let concatenated: String = String::deserialize(deserializer)?; + concatenated.parse().map_err(serde::de::Error::custom) + } +} + +/// Trait for converting an argument into a Version +pub trait AsVersion { + /// Convert the argument into a Version + fn into_version(self) -> Result; +} + +impl AsVersion for &str { + fn into_version(self) -> Result { + self.parse() + } +} + +impl AsVersion for String { + fn into_version(self) -> Result { + self.parse() + } +} + +impl AsVersion for Version { + fn into_version(self) -> Result { + Ok(self.clone()) + } +} + #[cfg(test)] mod tests { use super::{version_cmp_part, ParseError, Version}; @@ -580,180 +761,3 @@ mod tests { assert!(!"1.0-0".parse::().unwrap().is_native()); } } - -#[cfg(feature = "sqlx")] -use sqlx::{postgres::PgTypeInfo, Postgres}; - -#[cfg(feature = "sqlx")] -impl sqlx::Type for Version { - fn type_info() -> PgTypeInfo { - PgTypeInfo::with_name("debversion") - } -} - -#[cfg(feature = "sqlx")] -impl sqlx::Encode<'_, Postgres> for Version { - fn encode_by_ref( - &self, - buf: &mut sqlx::postgres::PgArgumentBuffer, - ) -> Result> { - sqlx::Encode::::encode_by_ref(&self.to_string().as_str(), buf) - } -} - -#[cfg(feature = "sqlx")] -impl sqlx::Decode<'_, Postgres> for Version { - fn decode( - value: sqlx::postgres::PgValueRef<'_>, - ) -> Result> { - let s: &str = sqlx::Decode::::decode(value)?; - Ok(s.parse::()?) - } -} - -#[cfg(all(feature = "sqlx", test))] -mod sqlx_tests { - #[test] - fn type_info() { - use super::Version; - use sqlx::postgres::PgTypeInfo; - use sqlx::Type; - - assert_eq!(PgTypeInfo::with_name("debversion"), Version::type_info()); - } -} - -#[cfg(feature = "python-debian")] -use pyo3::prelude::*; - -#[cfg(feature = "python-debian")] -impl FromPyObject<'_> for Version { - fn extract_bound(ob: &Bound) -> PyResult { - let debian_support = Python::import_bound(ob.py(), "debian.debian_support")?; - let version_cls = debian_support.getattr("Version")?; - if !ob.is_instance(&version_cls)? { - return Err(pyo3::exceptions::PyTypeError::new_err("Expected a Version")); - } - Ok(Version { - epoch: ob - .getattr("epoch")? - .extract::>()? - .map(|s| s.parse().unwrap()), - upstream_version: ob.getattr("upstream_version")?.extract::()?, - debian_revision: ob.getattr("debian_revision")?.extract::>()?, - }) - } -} - -#[cfg(feature = "python-debian")] -impl ToPyObject for Version { - fn to_object(&self, py: Python) -> PyObject { - let debian_support = py.import_bound("debian.debian_support").unwrap(); - let version_cls = debian_support.getattr("Version").unwrap(); - version_cls - .call1((self.to_string(),)) - .unwrap() - .to_object(py) - } -} - -#[cfg(feature = "python-debian")] -impl IntoPy for Version { - fn into_py(self, py: Python) -> PyObject { - self.to_object(py) - } -} - -#[cfg(feature = "python-debian")] -mod python_tests { - #[test] - fn test_from_pyobject() { - use super::Version; - use pyo3::prelude::*; - - Python::with_gil(|py| { - let globals = pyo3::types::PyDict::new_bound(py); - globals - .set_item( - "debian_support", - py.import_bound("debian.debian_support").unwrap(), - ) - .unwrap(); - let v = py - .eval_bound("debian_support.Version('1.0-1')", Some(&globals), None) - .unwrap() - .extract::() - .unwrap(); - assert_eq!( - v, - Version { - epoch: None, - upstream_version: "1.0".to_string(), - debian_revision: Some("1".to_string()) - } - ); - }); - } - - #[test] - fn test_to_pyobject() { - use super::Version; - use pyo3::prelude::*; - - Python::with_gil(|py| { - let v = Version { - epoch: Some(1), - upstream_version: "1.0".to_string(), - debian_revision: Some("1".to_string()), - }; - let v = v.to_object(py); - let expected: Version = "1:1.0-1".parse().unwrap(); - assert_eq!(v.extract::(py).unwrap(), expected); - assert_eq!(v.bind(py).get_type().name().unwrap(), "Version"); - }); - } -} - -#[cfg(feature = "serde")] -impl serde::Serialize for Version { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_str(&self.to_string()) - } -} - -#[cfg(feature = "serde")] -impl<'de> serde::Deserialize<'de> for Version { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let concatenated: String = String::deserialize(deserializer)?; - concatenated.parse().map_err(serde::de::Error::custom) - } -} - -/// Trait for converting an argument into a Version -pub trait AsVersion { - fn as_version(self) -> Result; -} - -impl AsVersion for &str { - fn as_version(self) -> Result { - self.parse() - } -} - -impl AsVersion for String { - fn as_version(self) -> Result { - self.parse() - } -} - -impl AsVersion for Version { - fn as_version(self) -> Result { - Ok(self.clone()) - } -} diff --git a/src/upstream.rs b/src/upstream.rs index 7e7775a..8a1601e 100644 --- a/src/upstream.rs +++ b/src/upstream.rs @@ -31,21 +31,33 @@ pub fn add_dfsg_suffix(upstream_version: &str, old_upstream_version: Option<&str } #[derive(Debug, Clone, PartialEq)] +/// VCS snapshot information. pub enum VcsSnapshot { + /// Git snapshot information. Git { + /// Date of the snapshot. date: Option, + + /// SHA of the snapshot, usually the first 7 characters. sha: Option, + + /// Snapshot number. snapshot: Option, }, + /// Bazaar snapshot information. Bzr { + /// Revision number, possibly dotted. revno: String, }, + /// Subversion snapshot information. Svn { + /// Revision number. revno: usize, }, } impl VcsSnapshot { + /// Convert the VCS snapshot information to a suffix. fn to_suffix(&self) -> String { match self { VcsSnapshot::Git { @@ -81,8 +93,12 @@ impl VcsSnapshot { } } +/// Direction to add the snapshot. pub enum Direction { + /// Snapshot predates the version. Before, + + /// Snapshot postdates the version. After, } @@ -105,6 +121,7 @@ impl From for &str { } } +/// Get the revision from a version string. pub fn get_revision(version_string: &str) -> (&str, Option<(Direction, VcsSnapshot)>) { if let Some((_, b, s, r)) = lazy_regex::regex_captures!(r"^(.*)([\+~])bzr(\d+)$", version_string) diff --git a/src/vcs.rs b/src/vcs.rs index 592ee8c..e8c62c6 100644 --- a/src/vcs.rs +++ b/src/vcs.rs @@ -1,5 +1,11 @@ +//! Version Control System (VCS) related utilities. use crate::Version; +/// Mangle a version string to be used as a git tag. +/// +/// This function mangles a version string to be used as a git tag, +/// following the Debian version mangling rules described in +/// DEP-14 (https://dep-team.pages.debian.net/deps/dep14/). pub fn mangle_version_for_git(version: &Version) -> String { let version = version.to_string(); // See https://dep-team.pages.debian.net/deps/dep14/ diff --git a/src/vendor.rs b/src/vendor.rs index 0ed8821..22a051c 100644 --- a/src/vendor.rs +++ b/src/vendor.rs @@ -1,8 +1,16 @@ -/// Ideally we wouldn't have a list like this, but unfortunately we do. +//! Vendor enum and related functions. +// Ideally we wouldn't have a list like this, but unfortunately we do. + +/// Vendor enum. #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum Vendor { + /// Debian Debian, + + /// Ubuntu (including derivatives) Ubuntu, + + /// Kali Linux Kali, } @@ -27,6 +35,7 @@ impl std::fmt::Display for Vendor { } } +/// Get the initial Debian revision for a given vendor. pub fn initial_debian_revision(vendor: Vendor) -> &'static str { match vendor { Vendor::Debian => "1",