alpm_pkginfo/
commands.rs

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