alpm_buildinfo/
commands.rs

1use std::{
2    fs::{File, create_dir_all},
3    io::{self, IsTerminal, Write},
4    str::FromStr,
5};
6
7use alpm_common::MetadataFile;
8use alpm_types::{SchemaVersion, Sha256Checksum};
9
10use crate::{
11    BuildInfo,
12    BuildInfoV1,
13    BuildInfoV2,
14    cli::{CreateCommand, OutputFormat, ValidateArgs},
15    error::Error,
16};
17
18/// Create a file according to a BUILDINFO schema
19pub fn create_file(command: CreateCommand) -> Result<(), Error> {
20    let (data, output) = match command {
21        CreateCommand::V1 { args } => (
22            BuildInfoV1::new(
23                args.builddate,
24                args.builddir,
25                args.buildenv,
26                SchemaVersion::from_str("1")?,
27                args.installed,
28                args.options,
29                args.packager,
30                args.pkgarch,
31                args.pkgbase,
32                Sha256Checksum::from_str(&args.pkgbuild_sha256sum)?,
33                args.pkgname,
34                args.pkgver,
35            )?
36            .to_string(),
37            args.output,
38        ),
39        CreateCommand::V2 {
40            args,
41            startdir,
42            buildtool,
43            buildtoolver,
44        } => (
45            BuildInfoV2::new(
46                args.builddate,
47                args.builddir,
48                startdir,
49                buildtool,
50                buildtoolver,
51                args.buildenv,
52                SchemaVersion::from_str("2")?,
53                args.installed,
54                args.options,
55                args.packager,
56                args.pkgarch,
57                args.pkgbase,
58                Sha256Checksum::from_str(&args.pkgbuild_sha256sum)?,
59                args.pkgname,
60                args.pkgver,
61            )?
62            .to_string(),
63            args.output,
64        ),
65    };
66
67    // create any parent directories if necessary
68    if let Some(output_dir) = output.0.parent() {
69        create_dir_all(output_dir).map_err(|e| {
70            Error::IoPathError(output_dir.to_path_buf(), "creating output directory", e)
71        })?;
72    }
73
74    let mut out = File::create(&output.0)
75        .map_err(|e| Error::IoPathError(output.0.clone(), "creating output file", e))?;
76
77    let _ = out
78        .write(data.as_bytes())
79        .map_err(|e| Error::IoPathError(output.0, "writing to output file", e))?;
80
81    Ok(())
82}
83
84/// Parses a file according to a BUILDINFO schema.
85///
86/// Returns a serializable BuildInfo if the file is valid, otherwise an error is returned.
87///
88/// NOTE: If a command is piped to this process, the input is read from stdin.
89/// See [`IsTerminal`] for more information about how terminal detection works.
90///
91/// [`IsTerminal`]: https://doc.rust-lang.org/stable/std/io/trait.IsTerminal.html
92pub fn parse(args: ValidateArgs) -> Result<BuildInfo, Error> {
93    if let Some(file) = &args.file {
94        BuildInfo::from_file_with_schema(file, args.schema)
95    } else if !io::stdin().is_terminal() {
96        BuildInfo::from_stdin_with_schema(args.schema)
97    } else {
98        return Err(Error::NoInputFile);
99    }
100}
101
102/// Validate a file according to a BUILDINFO schema.
103///
104/// This is a thin wrapper around [`parse`] that only checks if the file is valid.
105pub fn validate(args: ValidateArgs) -> Result<(), Error> {
106    let _ = parse(args)?;
107    Ok(())
108}
109
110/// Formats a file according to a BUILDINFO schema.
111///
112/// Validates and prints the parsed file in the specified output format to stdout.
113///
114/// The output will be pretty-printed if the `pretty` flag is set to `true` and if the format
115/// supports it.
116pub fn format(args: ValidateArgs, output_format: OutputFormat, pretty: bool) -> Result<(), Error> {
117    let build_info = parse(args)?;
118    match output_format {
119        OutputFormat::Json => {
120            let json = if pretty {
121                serde_json::to_string_pretty(&build_info)?
122            } else {
123                serde_json::to_string(&build_info)?
124            };
125            println!("{json}");
126        }
127    }
128    Ok(())
129}