use std::fmt::Display;
use std::fmt::Formatter;
use std::str::FromStr;
use alpm_types::digests::Sha256;
use alpm_types::Architecture;
use alpm_types::BuildDate;
use alpm_types::BuildDir;
use alpm_types::BuildEnv;
use alpm_types::BuildTool;
use alpm_types::BuildToolVersion;
use alpm_types::Checksum;
use alpm_types::InstalledPackage;
use alpm_types::Name;
use alpm_types::PackageOption;
use alpm_types::Packager;
use alpm_types::SchemaVersion;
use alpm_types::StartDir;
use alpm_types::Version;
use serde_with::serde_as;
use serde_with::DisplayFromStr;
use crate::buildinfo_v1::generate_buildinfo;
use crate::schema::Schema;
use crate::Error;
generate_buildinfo! {
BuildInfoV2 {
#[serde_as(as = "DisplayFromStr")]
startdir: StartDir,
#[serde_as(as = "DisplayFromStr")]
buildtool: BuildTool,
#[serde_as(as = "DisplayFromStr")]
buildtoolver: BuildToolVersion,
}
}
impl BuildInfoV2 {
#[allow(clippy::too_many_arguments)]
pub fn new(
builddate: BuildDate,
builddir: BuildDir,
startdir: StartDir,
buildtool: BuildTool,
buildtoolver: BuildToolVersion,
buildenv: Vec<BuildEnv>,
format: SchemaVersion,
installed: Vec<InstalledPackage>,
options: Vec<PackageOption>,
packager: Packager,
pkgarch: Architecture,
pkgbase: Name,
pkgbuild_sha256sum: Checksum<Sha256>,
pkgname: Name,
pkgver: Version,
) -> Result<Self, Error> {
if format.inner().major != 2 {
return Err(Error::WrongSchemaVersion(format));
}
Ok(BuildInfoV2 {
builddate,
builddir,
buildenv,
format: format.try_into()?,
installed,
options,
packager,
pkgarch,
pkgbase,
pkgbuild_sha256sum,
pkgname,
pkgver,
startdir,
buildtool,
buildtoolver,
})
}
pub fn startdir(&self) -> &StartDir {
&self.startdir
}
pub fn buildtool(&self) -> &BuildTool {
&self.buildtool
}
pub fn buildtoolver(&self) -> &BuildToolVersion {
&self.buildtoolver
}
}
impl FromStr for BuildInfoV2 {
type Err = Error;
fn from_str(input: &str) -> Result<BuildInfoV2, Self::Err> {
let buildinfo: BuildInfoV2 = alpm_parsers::custom_ini::from_str(input)?;
if buildinfo.format().inner().major != 2 {
return Err(Error::WrongSchemaVersion(buildinfo.format().clone()));
}
Ok(buildinfo)
}
}
impl Display for BuildInfoV2 {
fn fmt(&self, fmt: &mut Formatter) -> std::fmt::Result {
write!(
fmt,
"format = {}\n\
pkgname = {}\n\
pkgbase = {}\n\
pkgver = {}\n\
pkgarch = {}\n\
pkgbuild_sha256sum = {}\n\
packager = {}\n\
builddate = {}\n\
builddir = {}\n\
startdir = {}\n\
buildtool = {}\n\
buildtoolver = {}\n\
{}\n\
{}\n\
{}\n\
",
self.format().inner().major,
self.pkgname(),
self.pkgbase(),
self.pkgver(),
self.pkgarch(),
self.pkgbuild_sha256sum(),
self.packager(),
self.builddate(),
self.builddir(),
self.startdir(),
self.buildtool(),
self.buildtoolver(),
self.buildenv()
.iter()
.map(|v| format!("buildenv = {v}"))
.collect::<Vec<String>>()
.join("\n"),
self.options()
.iter()
.map(|v| format!("options = {v}"))
.collect::<Vec<String>>()
.join("\n"),
self.installed()
.iter()
.map(|v| format!("installed = {v}"))
.collect::<Vec<String>>()
.join("\n"),
)
}
}
#[cfg(test)]
mod tests {
use rstest::rstest;
use testresult::TestResult;
use super::*;
const VALID_BUILDINFOV2_CASE1: &str = r#"
builddate = 1
builddir = /build
startdir = /startdir/
buildtool = devtools
buildtoolver = 1:1.2.1-1-any
buildenv = envfoo
buildenv = envbar
format = 2
installed = bar-1.2.3-1-any
installed = beh-2.2.3-4-any
options = some_option
options = !other_option
packager = Foobar McFooface <foobar@mcfooface.org>
pkgarch = any
pkgbase = foo
pkgbuild_sha256sum = b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c
pkgname = foo
pkgver = 1:1.0.0-1
"#;
const VALID_BUILDINFOV2_CASE2: &str = r#"
builddate = 1
builddir = /build
startdir = /startdir/
buildtool = devtools
buildtoolver = 1:1.2.1-1-any
buildenv = envfoo
format = 2
installed = bar-1.2.3-1-any
options = some_option
packager = Foobar McFooface <foobar@mcfooface.org>
pkgarch = any
pkgbase = foo
pkgbuild_sha256sum = b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c
pkgname = foo
pkgver = 1:1.0.0-1
"#;
#[rstest]
#[case(VALID_BUILDINFOV2_CASE1)]
#[case(VALID_BUILDINFOV2_CASE2)]
fn buildinfov2_from_str(#[case] buildinfo: &str) -> TestResult {
BuildInfoV2::from_str(buildinfo)?;
Ok(())
}
#[rstest]
fn buildinfov2() -> TestResult {
BuildInfoV2::new(
1,
BuildDir::from_str("/build")?,
StartDir::from_str("/startdir/")?,
BuildTool::from_str("devtools")?,
BuildToolVersion::from_str("1:1.2.1-1-any")?,
vec![BuildEnv::new("some")?],
SchemaVersion::from_str("2")?,
vec![InstalledPackage::from_str("bar-1:1.0.0-2-any")?],
vec![PackageOption::new("buildoption")?],
Packager::from_str("Foobar McFooface <foobar@mcfooface.org>")?,
Architecture::Any,
Name::new("foo".to_string())?,
Checksum::<Sha256>::calculate_from("foo"),
Name::new("foo".to_string())?,
Version::from_str("1:1.0.0-1")?,
)?;
Ok(())
}
#[rstest]
fn buildinfov2_invalid_schemaversion() -> TestResult {
assert!(BuildInfoV2::new(
1,
BuildDir::from_str("/build")?,
StartDir::from_str("/startdir/")?,
BuildTool::from_str("devtools")?,
BuildToolVersion::from_str("1:1.2.1-1-any")?,
vec![BuildEnv::new("some")?],
SchemaVersion::from_str("1")?,
vec![InstalledPackage::from_str("bar-1:1.0.0-2-any")?],
vec![PackageOption::new("buildoption")?],
Packager::from_str("Foobar McFooface <foobar@mcfooface.org>")?,
Architecture::Any,
Name::new("foo".to_string())?,
Checksum::<Sha256>::calculate_from("foo"),
Name::new("foo".to_string())?,
Version::from_str("1:1.0.0-1")?,
)
.is_err());
Ok(())
}
#[rstest]
#[case("builddate = 2")]
#[case("builddir = /build2")]
#[case("startdir = /startdir2/")]
#[case("buildtool = devtools2")]
#[case("buildtoolver = 1:1.2.1-2-any")]
#[case("format = 1")]
#[case("packager = Foobar McFooface <foobar@mcfooface.org>")]
#[case("pkgarch = any")]
#[case("pkgbase = foo")]
#[case("pkgbuild_sha256sum = b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c")]
#[case("pkgname = foo")]
#[case("pkgver = 1:1.0.0-1")]
fn buildinfov2_from_str_duplicate_fail(#[case] duplicate: &str) {
let mut buildinfov2 = VALID_BUILDINFOV2_CASE1.to_string();
buildinfov2.push_str(duplicate);
assert!(BuildInfoV2::from_str(&buildinfov2).is_err());
}
}