use std::{
fmt::{Display, Formatter},
str::FromStr,
};
use serde::Serialize;
#[cfg(doc)]
use crate::BuildTool;
use crate::{Architecture, Error, FullVersion, MinimalVersion, Version};
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize)]
pub enum BuildToolVersion {
Makepkg(MinimalVersion),
DevTools {
version: FullVersion,
architecture: Architecture,
},
}
impl BuildToolVersion {
pub fn architecture(&self) -> Option<Architecture> {
if let Self::DevTools {
version: _,
architecture,
} = self
{
Some(architecture.clone())
} else {
None
}
}
pub fn version(&self) -> Version {
match self {
Self::Makepkg(version) => Version::from(version),
Self::DevTools {
version,
architecture: _,
} => Version::from(version),
}
}
}
impl FromStr for BuildToolVersion {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.rsplit_once('-') {
Some((version, architecture)) => Ok(BuildToolVersion::DevTools {
version: FullVersion::from_str(version)?,
architecture: Architecture::from_str(architecture)?,
}),
None => Ok(BuildToolVersion::Makepkg(MinimalVersion::from_str(s)?)),
}
}
}
impl Display for BuildToolVersion {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Makepkg(version) => write!(f, "{version}"),
Self::DevTools {
version,
architecture,
} => write!(f, "{version}-{architecture}"),
}
}
}
#[cfg(test)]
mod tests {
use rstest::rstest;
use testresult::TestResult;
use super::*;
#[rstest]
#[case::devtools_full(
"1.0.0-1-any",
Ok(BuildToolVersion::DevTools{version: FullVersion::from_str("1.0.0-1")?, architecture: Architecture::from_str("any")?}),
)]
#[case::devtools_full_with_epoch(
"1:1.0.0-1-any",
Ok(BuildToolVersion::DevTools{version: FullVersion::from_str("1:1.0.0-1")?, architecture: Architecture::from_str("any")?}),
)]
#[case::makepkg_minimal(
"1.0.0",
Ok(BuildToolVersion::Makepkg(MinimalVersion::from_str("1.0.0")?)),
)]
#[case::makepkg_minimal_with_epoch(
"1:1.0.0",
Ok(BuildToolVersion::Makepkg(MinimalVersion::from_str("1:1.0.0")?)),
)]
#[case::minimal_version_with_architecture("1.0.0-any",
Err(Error::ParseError(
"1.0.0\n^\nexpected alpm-pkgver string, followed by a '-' and an alpm-pkgrel string".to_string()
))
)]
#[case::minimal_version_with_epoch_and_architecture(
"1:1.0.0-any",
Err(Error::ParseError(
"1:1.0.0\n ^\nexpected alpm-pkgver string, followed by a '-' and an alpm-pkgrel string".to_string()
))
)]
fn valid_buildtoolver_new(
#[case] input: &str,
#[case] expected: Result<BuildToolVersion, Error>,
) -> TestResult {
let parse_result = BuildToolVersion::from_str(input);
assert_eq!(
parse_result, expected,
"Expected '{expected:?}' when parsing '{input}' but got '{parse_result:?}'"
);
Ok(())
}
#[rstest]
#[case::minimal_version_with_architecture(
"1.0.0-any",
Error::ParseError(
"1.0.0\n^\nexpected alpm-pkgver string, followed by a '-' and an alpm-pkgrel string".to_string()
)
)]
#[case::minimal_version_with_unknown_architecture(
"1.0.0-foo",
Error::ParseError(
"1.0.0\n^\nexpected alpm-pkgver string, followed by a '-' and an alpm-pkgrel string".to_string()
)
)]
fn invalid_buildtoolver_new(#[case] buildtoolver: &str, #[case] expected: Error) {
assert_eq!(
BuildToolVersion::from_str(buildtoolver),
Err(expected),
"Expected error during parse of buildtoolver '{buildtoolver}'"
);
}
#[rstest]
#[case("ß-1-any", "invalid pkgver character")]
fn invalid_buildtoolver_badpkgver(#[case] buildtoolver: &str, #[case] err_snippet: &str) {
let Err(Error::ParseError(err_msg)) = BuildToolVersion::from_str(buildtoolver) else {
panic!("'{buildtoolver}' erroneously parsed as BuildToolVersion")
};
assert!(
err_msg.contains(err_snippet),
"Error:\n=====\n{err_msg}\n=====\nshould contain snippet:\n\n{err_snippet}"
);
}
}