alpm_pkginfo/
commands.rs

1//! Command-line functions, that are called by the `alpm-pkginfo` executable.
2
3use std::{
4    fs::{File, create_dir_all},
5    io::{self, IsTerminal, Write},
6    path::PathBuf,
7};
8
9use alpm_common::MetadataFile;
10
11use crate::{
12    Error,
13    PackageInfo,
14    PackageInfoSchema,
15    PackageInfoV1,
16    PackageInfoV2,
17    cli::{CreateCommand, OutputFormat},
18};
19
20/// Create a file according to a PKGINFO schema
21///
22/// ## Errors
23///
24/// Returns an error if one of the provided arguments is not valid, if creating output directory or
25/// file is not possible, or if the output file can not be written to.
26pub fn create_file(command: CreateCommand) -> Result<(), Error> {
27    let (data, output) = match command {
28        CreateCommand::V1 { args } => (
29            PackageInfoV1::new(
30                args.pkgname,
31                args.pkgbase,
32                args.pkgver,
33                args.pkgdesc,
34                args.url,
35                args.builddate,
36                args.packager,
37                args.size,
38                args.arch,
39                args.license,
40                args.replaces,
41                args.group,
42                args.conflict,
43                args.provides,
44                args.backup,
45                args.depend,
46                args.optdepend,
47                args.makedepend,
48                args.checkdepend,
49            )
50            .to_string(),
51            args.output,
52        ),
53        CreateCommand::V2 { args, xdata } => (
54            PackageInfoV2::new(
55                args.pkgname,
56                args.pkgbase,
57                args.pkgver,
58                args.pkgdesc,
59                args.url,
60                args.builddate,
61                args.packager,
62                args.size,
63                args.arch,
64                args.license,
65                args.replaces,
66                args.group,
67                args.conflict,
68                args.provides,
69                args.backup,
70                args.depend,
71                args.optdepend,
72                args.makedepend,
73                args.checkdepend,
74                xdata,
75            )?
76            .to_string(),
77            args.output,
78        ),
79    };
80
81    // create any parent directories if necessary
82    if let Some(output_dir) = output.0.parent() {
83        create_dir_all(output_dir).map_err(|e| {
84            Error::IoPathError(output_dir.to_path_buf(), "creating output directory", e)
85        })?;
86    }
87
88    let mut out = File::create(&output.0)
89        .map_err(|e| Error::IoPathError(output.0.clone(), "creating output file", e))?;
90
91    let _ = out
92        .write(data.as_bytes())
93        .map_err(|e| Error::IoPathError(output.0, "writing to output file", e))?;
94
95    Ok(())
96}
97
98/// Parses a file according to a PKGINFO schema.
99///
100/// Returns a serializable PackageInfo if the file is valid, otherwise an error is returned.
101///
102/// NOTE: If a command is piped to this process, the input is read from stdin.
103/// See [`IsTerminal`] for more information about how terminal detection works.
104///
105/// [`IsTerminal`]: https://doc.rust-lang.org/stable/std/io/trait.IsTerminal.html
106///
107/// ## Errors
108///
109/// Returns an error if the file can not be read, if the file can not be parsed, or if the file is
110/// not valid according to the schema.
111pub fn parse(
112    file: Option<PathBuf>,
113    schema: Option<PackageInfoSchema>,
114) -> Result<PackageInfo, Error> {
115    if let Some(file) = file {
116        PackageInfo::from_file_with_schema(file, schema)
117    } else if !io::stdin().is_terminal() {
118        PackageInfo::from_stdin_with_schema(schema)
119    } else {
120        Err(Error::NoInputFile)
121    }
122}
123
124/// Validate a file according to a PKGINFO schema.
125///
126/// This is a thin wrapper around [`parse`] that only checks if the file is valid.
127///
128/// ## Errors
129///
130/// Returns an error if parsing `file` fails.
131pub fn validate(file: Option<PathBuf>, schema: Option<PackageInfoSchema>) -> Result<(), Error> {
132    let _ = parse(file, schema)?;
133    Ok(())
134}
135
136/// Formats a file according to a PKGINFO schema.
137///
138/// Validates and prints the parsed file in the specified output format to stdout.
139///
140/// The output will be pretty-printed if the `pretty` flag is set to `true` and if the format
141/// supports it.
142///
143/// ## Errors
144///
145/// Returns an error if parsing of `file` fails or if the output format can not be created.
146pub fn format(
147    file: Option<PathBuf>,
148    schema: Option<PackageInfoSchema>,
149    output_format: OutputFormat,
150    pretty: bool,
151) -> Result<(), Error> {
152    let pkg_info = parse(file, schema)?;
153    match output_format {
154        OutputFormat::Json => {
155            println!(
156                "{}",
157                if pretty {
158                    serde_json::to_string_pretty(&pkg_info)?
159                } else {
160                    serde_json::to_string(&pkg_info)?
161                }
162            );
163        }
164    }
165    Ok(())
166}