alpm_buildinfo/build_info/
v1.rs1use std::{
2 fmt::{Display, Formatter},
3 str::FromStr,
4};
5
6use alpm_common::FileFormatSchema;
7use alpm_types::{
8 Architecture,
9 BuildDate,
10 BuildDirectory,
11 BuildEnvironmentOption,
12 Checksum,
13 InstalledPackage,
14 Name,
15 PackageOption,
16 Packager,
17 SchemaVersion,
18 Version,
19 digests::Sha256,
20};
21use serde_with::{DisplayFromStr, serde_as};
22
23use crate::{BuildInfoSchema, Error};
24
25macro_rules! generate_buildinfo {
27 ($(#[$meta:meta])* $name:ident { $($extra_fields:tt)* }) => {
31 $(#[$meta])*
32 #[serde_as]
33 #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
34 #[serde(deny_unknown_fields)]
35 pub struct $name {
36 #[serde_as(as = "DisplayFromStr")]
37 format: BuildInfoSchema,
38
39 #[serde_as(as = "DisplayFromStr")]
40 pkgname: Name,
41
42 #[serde_as(as = "DisplayFromStr")]
43 pkgbase: Name,
44
45 #[serde_as(as = "DisplayFromStr")]
46 pkgver: Version,
47
48 #[serde_as(as = "DisplayFromStr")]
49 pkgarch: Architecture,
50
51 #[serde_as(as = "DisplayFromStr")]
52 pkgbuild_sha256sum: Checksum<Sha256>,
53
54 #[serde_as(as = "DisplayFromStr")]
55 packager: Packager,
56
57 #[serde_as(as = "DisplayFromStr")]
58 builddate: BuildDate,
59
60 #[serde_as(as = "DisplayFromStr")]
61 builddir: BuildDirectory,
62
63 #[serde_as(as = "Vec<DisplayFromStr>")]
64 #[serde(default)]
65 buildenv: Vec<BuildEnvironmentOption>,
66
67 #[serde_as(as = "Vec<DisplayFromStr>")]
68 #[serde(default)]
69 options: Vec<PackageOption>,
70
71 #[serde_as(as = "Vec<DisplayFromStr>")]
72 #[serde(default)]
73 installed: Vec<InstalledPackage>,
74
75 $($extra_fields)*
76 }
77
78 impl $name {
79 pub fn format(&self) -> &SchemaVersion {
81 self.format.inner()
82 }
83
84 pub fn pkgname(&self) -> &Name {
86 &self.pkgname
87 }
88
89 pub fn pkgbase(&self) -> &Name {
91 &self.pkgbase
92 }
93
94 pub fn pkgver(&self) -> &Version {
96 &self.pkgver
97 }
98
99 pub fn pkgarch(&self) -> &Architecture {
101 &self.pkgarch
102 }
103
104 pub fn pkgbuild_sha256sum(&self) -> &Checksum<Sha256> {
106 &self.pkgbuild_sha256sum
107 }
108
109 pub fn packager(&self) -> &Packager {
111 &self.packager
112 }
113
114 pub fn builddate(&self) -> &BuildDate {
116 &self.builddate
117 }
118
119 pub fn builddir(&self) -> &BuildDirectory {
121 &self.builddir
122 }
123
124 pub fn buildenv(&self) -> &Vec<BuildEnvironmentOption> {
126 &self.buildenv
127 }
128
129 pub fn options(&self) -> &Vec<PackageOption> {
131 &self.options
132 }
133
134 pub fn installed(&self) -> &Vec<InstalledPackage> {
136 &self.installed
137 }
138 }
139 }
140}
141
142pub(crate) use generate_buildinfo;
143
144generate_buildinfo! {
145 BuildInfoV1 {}
181}
182
183impl BuildInfoV1 {
184 #[allow(clippy::too_many_arguments)]
186 pub fn new(
187 builddate: BuildDate,
188 builddir: BuildDirectory,
189 buildenv: Vec<BuildEnvironmentOption>,
190 format: SchemaVersion,
191 installed: Vec<InstalledPackage>,
192 options: Vec<PackageOption>,
193 packager: Packager,
194 pkgarch: Architecture,
195 pkgbase: Name,
196 pkgbuild_sha256sum: Checksum<Sha256>,
197 pkgname: Name,
198 pkgver: Version,
199 ) -> Result<Self, Error> {
200 if format.inner().major != 1 {
201 return Err(Error::WrongSchemaVersion(format));
202 }
203 Ok(BuildInfoV1 {
204 builddate,
205 builddir,
206 buildenv,
207 format: BuildInfoSchema::try_from(format)?,
208 installed,
209 options,
210 packager,
211 pkgarch,
212 pkgbase,
213 pkgbuild_sha256sum,
214 pkgname,
215 pkgver,
216 })
217 }
218}
219
220impl FromStr for BuildInfoV1 {
221 type Err = Error;
222 fn from_str(input: &str) -> Result<BuildInfoV1, Self::Err> {
229 let buildinfo: BuildInfoV1 = alpm_parsers::custom_ini::from_str(input)?;
230 if buildinfo.format().inner().major != 1 {
231 return Err(Error::WrongSchemaVersion(buildinfo.format().clone()));
232 }
233 Ok(buildinfo)
234 }
235}
236
237impl Display for BuildInfoV1 {
238 fn fmt(&self, fmt: &mut Formatter) -> std::fmt::Result {
239 write!(
240 fmt,
241 "format = {}\n\
242 pkgname = {}\n\
243 pkgbase = {}\n\
244 pkgver = {}\n\
245 pkgarch = {}\n\
246 pkgbuild_sha256sum = {}\n\
247 packager = {}\n\
248 builddate = {}\n\
249 builddir = {}\n\
250 {}\n\
251 {}\n\
252 {}\n\
253 ",
254 self.format().inner().major,
255 self.pkgname(),
256 self.pkgbase(),
257 self.pkgver(),
258 self.pkgarch(),
259 self.pkgbuild_sha256sum(),
260 self.packager(),
261 self.builddate(),
262 self.builddir(),
263 self.buildenv()
264 .iter()
265 .map(|v| format!("buildenv = {v}"))
266 .collect::<Vec<String>>()
267 .join("\n"),
268 self.options()
269 .iter()
270 .map(|v| format!("options = {v}"))
271 .collect::<Vec<String>>()
272 .join("\n"),
273 self.installed()
274 .iter()
275 .map(|v| format!("installed = {v}"))
276 .collect::<Vec<String>>()
277 .join("\n"),
278 )
279 }
280}
281
282#[cfg(test)]
283mod tests {
284 use rstest::{fixture, rstest};
285 use testresult::TestResult;
286
287 use super::*;
288
289 #[fixture]
290 fn valid_buildinfov1() -> String {
291 r#"builddate = 1
292builddir = /build
293buildenv = envfoo
294buildenv = envbar
295format = 1
296installed = bar-1.2.3-1-any
297installed = beh-2.2.3-4-any
298options = some_option
299options = !other_option
300packager = Foobar McFooface <foobar@mcfooface.org>
301pkgarch = any
302pkgbase = foo
303pkgbuild_sha256sum = b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c
304pkgname = foo
305pkgver = 1:1.0.0-1
306"#
307 .to_string()
308 }
309
310 #[rstest]
311 fn buildinfov1_from_str(valid_buildinfov1: String) -> TestResult {
312 BuildInfoV1::from_str(&valid_buildinfov1)?;
313 Ok(())
314 }
315
316 #[rstest]
317 fn buildinfov1() -> TestResult {
318 BuildInfoV1::new(
319 1,
320 BuildDirectory::from_str("/build")?,
321 vec![BuildEnvironmentOption::new("some")?],
322 SchemaVersion::from_str("1")?,
323 vec![InstalledPackage::from_str("bar-1:1.0.0-2-any")?],
324 vec![PackageOption::new("buildoption")?],
325 Packager::from_str("Foobar McFooface <foobar@mcfooface.org>")?,
326 Architecture::Any,
327 Name::new("foo")?,
328 Checksum::<Sha256>::calculate_from("foo"),
329 Name::new("foo")?,
330 Version::from_str("1:1.0.0-1")?,
331 )?;
332 Ok(())
333 }
334
335 #[rstest]
336 fn buildinfov1_invalid_schemaversion() -> TestResult {
337 assert!(
338 BuildInfoV1::new(
339 1,
340 BuildDirectory::from_str("/build")?,
341 vec![BuildEnvironmentOption::new("some")?],
342 SchemaVersion::from_str("2")?,
343 vec![InstalledPackage::from_str("bar-1:1.0.0-2-any")?],
344 vec![PackageOption::new("buildoption")?],
345 Packager::from_str("Foobar McFooface <foobar@mcfooface.org>")?,
346 Architecture::Any,
347 Name::new("foo")?,
348 Checksum::<Sha256>::calculate_from("foo"),
349 Name::new("foo")?,
350 Version::from_str("1:1.0.0-1")?,
351 )
352 .is_err()
353 );
354 Ok(())
355 }
356
357 #[rstest]
358 #[case("builddate = 2")]
359 #[case("builddir = /build2")]
360 #[case("format = 1")]
361 #[case("packager = Foobar McFooface <foobar@mcfooface.org>")]
362 #[case("pkgarch = any")]
363 #[case("pkgbase = foo")]
364 #[case("pkgbuild_sha256sum = b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c")]
365 #[case("pkgname = foo")]
366 #[case("pkgver = 1:1.0.0-1")]
367 fn buildinfov1_from_str_duplicate_fail(mut valid_buildinfov1: String, #[case] duplicate: &str) {
368 valid_buildinfov1.push_str(duplicate);
369 assert!(BuildInfoV1::from_str(&valid_buildinfov1).is_err());
370 }
371}