alpm_pkginfo/
cli.rs

1//! Command-line argument handling for `alpm-pkginfo`.
2
3use std::{
4    fmt::{Display, Formatter},
5    path::PathBuf,
6    str::FromStr,
7};
8
9use alpm_types::{
10    Architecture,
11    Backup,
12    BuildDate,
13    ExtraData,
14    FullVersion,
15    Group,
16    InstalledSize,
17    License,
18    Name,
19    OptionalDependency,
20    PackageDescription,
21    PackageRelation,
22    Packager,
23    Url,
24};
25use clap::{Args, Parser, Subcommand, ValueEnum};
26use strum::Display;
27
28use crate::{Error, PackageInfoSchema, RelationOrSoname};
29
30/// A type wrapping a PathBuf with a default value
31///
32/// This type is used in circumstances where an output file is required that defaults to
33/// ".PKGINFO"
34#[derive(Clone, Debug)]
35pub struct OutputFile(pub PathBuf);
36
37impl Default for OutputFile {
38    fn default() -> Self {
39        OutputFile(PathBuf::from(".PKGINFO"))
40    }
41}
42
43impl Display for OutputFile {
44    fn fmt(&self, fmt: &mut Formatter) -> std::fmt::Result {
45        write!(fmt, "{}", self.0.display())
46    }
47}
48
49impl FromStr for OutputFile {
50    type Err = Error;
51
52    fn from_str(s: &str) -> Result<Self, Self::Err> {
53        Ok(OutputFile(PathBuf::from(s)))
54    }
55}
56
57/// The command-line interface handling for `alpm-pkginfo`.
58#[derive(Clone, Debug, Parser)]
59#[command(about, author, name = "alpm-pkginfo", version)]
60pub struct Cli {
61    /// The `alpm-pkginfo` commands.
62    #[command(subcommand)]
63    pub command: Command,
64}
65
66/// The `alpm-pkginfo` commands.
67#[allow(clippy::large_enum_variant)]
68#[derive(Clone, Debug, Subcommand)]
69pub enum Command {
70    #[command()]
71    /// Create a PKGINFO file according to a schema
72    ///
73    /// If the input can be validated according to the schema, the program writes a valid PKGINFO
74    /// file and exits with no output and a return code of 0. If the input can not be validated
75    /// according to the schema, an error is emitted on stderr and the program exits with a
76    /// non-zero exit code.
77    Create {
78        /// The `create` command.
79        #[command(subcommand)]
80        command: CreateCommand,
81    },
82    #[command()]
83    /// Validate a PKGINFO file
84    ///
85    /// Validate a PKGINFO file according to a schema.
86    /// If the file can be validated, the program exits with no output and a return code of 0.
87    /// If the file can not be validated, an error is emitted on stderr and the program exits with
88    /// a non-zero exit code.
89    Validate {
90        /// An optional input file path to read from
91        ///
92        /// If no file is specified, stdin is read from and expected to contain PKGINFO data to
93        /// validate.
94        #[arg(value_name = "FILE")]
95        file: Option<PathBuf>,
96
97        /// Provide the PKGINFO schema version to use.
98        ///
99        /// If no schema version is provided, it will be deduced from the file itself.
100        #[arg(short, long, value_name = "VERSION")]
101        schema: Option<PackageInfoSchema>,
102    },
103
104    /// Parse a PKGINFO file and output it in a different file format
105    ///
106    /// If the input can be validated according to a known schema, the program writes the PKGINFO
107    /// data to stdout in a different file format (optionally, a file path to write to may be
108    /// provided) and exits with a return code of 0. Currently only JSON is supported as output
109    /// format. If the input can not be validated according to a known schema, an error is
110    /// emitted on stderr and the program exits with a non-zero exit code.
111    #[command()]
112    Format {
113        /// An optional input file path to read from
114        ///
115        /// If no file path is specified, stdin is read from and expected to contain PKGINFO data
116        /// to format.
117        #[arg(value_name = "FILE")]
118        file: Option<PathBuf>,
119
120        /// Provide the PKGINFO schema version to use.
121        ///
122        /// If no schema version is provided, it will be deduced from the file itself.
123        #[arg(short, long, value_name = "VERSION")]
124        schema: Option<PackageInfoSchema>,
125
126        /// The output format to use
127        ///
128        /// Currently only "json" (the default) is supported
129        #[arg(
130            short,
131            long,
132            value_name = "OUTPUT_FORMAT",
133            default_value_t = OutputFormat::Json
134        )]
135        output_format: OutputFormat,
136
137        /// Pretty-print the output
138        ///
139        /// Has no effect if the output format can not be pretty printed.
140        #[arg(short, long)]
141        pretty: bool,
142    },
143}
144
145/// Arguments for creating a PKGINFO file according to the format version 1 schema
146///
147/// This struct is defined separately for reusing it for v1 and v2 because both share
148/// a set of overlapping fields.
149#[derive(Args, Clone, Debug)]
150pub struct V1CreateArgs {
151    /// The pkgname to use in the PKGINFO
152    ///
153    /// The pkgname must follow the alpm-package-name format (see `man 7 alpm-package-name`).
154    #[arg(env = "PKGINFO_PKGNAME", long, value_name = "PKGNAME")]
155    pub pkgname: Name,
156
157    /// The pkgbase to use in the PKGINFO
158    ///
159    /// The pkgbase must follow the alpm-package-name format (see `man 7 alpm-package-name`).
160    #[arg(env = "PKGINFO_PKGBASE", long, value_name = "PKGBASE")]
161    pub pkgbase: Name,
162
163    /// The pkgver to use in the PKGINFO
164    ///
165    /// The pkgver value must follow the alpm-pkgver format (see `man 7 alpm-pkgver`).
166    #[arg(env = "PKGINFO_PKGVER", long, value_name = "PKGVER")]
167    pub pkgver: FullVersion,
168
169    /// The package description to use in the PKGINFO
170    ///
171    /// The value must follow the format described in the PKGINFO format (see `man 5 PKGINFO`).
172    #[arg(env = "PKGINFO_PKGDESC", long, value_name = "PKGDESC")]
173    pub pkgdesc: PackageDescription,
174
175    /// Provide a url
176    #[arg(env = "PKGINFO_URL", long, value_name = "URL")]
177    pub url: Url,
178
179    /// Provide a builddate
180    #[arg(env = "PKGINFO_BUILDDATE", long, value_name = "BUILDDATE")]
181    pub builddate: BuildDate,
182
183    /// Provide a packager
184    #[arg(env = "PKGINFO_PACKAGER", long, value_name = "PACKAGER")]
185    pub packager: Packager,
186
187    /// Provide a size
188    #[arg(env = "PKGINFO_SIZE", long, value_name = "SIZE")]
189    pub size: InstalledSize,
190
191    /// Provide a architecture
192    #[arg(env = "PKGINFO_ARCH", long, value_name = "ARCH")]
193    pub arch: Architecture,
194
195    /// Provide one or more licenses
196    #[arg(
197        env = "PKGINFO_LICENSE",
198        value_delimiter = ' ',
199        long,
200        value_name = "LICENSE"
201    )]
202    pub license: Vec<License>,
203
204    /// Provide one or more replaces
205    #[arg(
206        env = "PKGINFO_REPLACES",
207        value_delimiter = ' ',
208        long,
209        value_name = "REPLACES"
210    )]
211    pub replaces: Vec<PackageRelation>,
212
213    /// Provide one or more groups
214    #[arg(
215        env = "PKGINFO_GROUP",
216        value_delimiter = ' ',
217        long,
218        value_name = "GROUP"
219    )]
220    pub group: Vec<Group>,
221
222    /// Provide one or more conflicts
223    #[arg(
224        env = "PKGINFO_CONFLICT",
225        value_delimiter = ' ',
226        long,
227        value_name = "CONFLICT"
228    )]
229    pub conflict: Vec<PackageRelation>,
230
231    /// Provide one or more provides
232    #[arg(
233        env = "PKGINFO_PROVIDES",
234        value_delimiter = ' ',
235        long,
236        value_name = "PROVIDES"
237    )]
238    pub provides: Vec<RelationOrSoname>,
239
240    /// Provide one or more backups
241    #[arg(
242        env = "PKGINFO_BACKUP",
243        value_delimiter = ' ',
244        long,
245        value_name = "BACKUP"
246    )]
247    pub backup: Vec<Backup>,
248
249    /// Provide one or more depends
250    #[arg(
251        env = "PKGINFO_DEPEND",
252        value_delimiter = ' ',
253        long,
254        value_name = "DEPEND"
255    )]
256    pub depend: Vec<RelationOrSoname>,
257
258    /// Provide one or more optdepend
259    #[arg(
260        env = "PKGINFO_OPTDEPEND",
261        value_delimiter = ',',
262        long,
263        value_name = "OPTDEPEND"
264    )]
265    pub optdepend: Vec<OptionalDependency>,
266
267    /// Provide one or more makedepend
268    #[arg(
269        env = "PKGINFO_MAKEDEPEND",
270        value_delimiter = ' ',
271        long,
272        value_name = "MAKEDEPEND"
273    )]
274    pub makedepend: Vec<PackageRelation>,
275
276    /// Provide one or more checkdepend
277    #[arg(
278        env = "PKGINFO_CHECKDEPEND",
279        value_delimiter = ' ',
280        long,
281        value_name = "CHECKDEPEND"
282    )]
283    pub checkdepend: Vec<PackageRelation>,
284
285    /// An optional custom file to write to
286    #[arg(default_value_t = OutputFile::default(), env = "PKGINFO_OUTPUT_FILE", value_name = "FILE")]
287    pub output: OutputFile,
288}
289
290/// Create an PKGINFO file according to a schema
291///
292/// If the input can be validated according to the schema, the program exits with no output and
293/// a return code of 0. If the input can not be validated according to the schema, an error
294/// is emitted on stderr and the program exits with a non-zero exit code.
295#[derive(Clone, Debug, Subcommand)]
296pub enum CreateCommand {
297    /// Create a PKGINFO version 1 file
298    V1 {
299        /// Arguments for the `create v1` command.
300        #[command(flatten)]
301        args: V1CreateArgs,
302    },
303    /// Create a PKGINFO version 2 file
304    V2 {
305        /// Arguments for the `create v2` command.
306        #[command(flatten)]
307        args: V1CreateArgs,
308
309        /// Provide one or more Xdata
310        #[arg(env = "PKGINFO_XDATA", long, value_name = "XDATA")]
311        xdata: Vec<ExtraData>,
312    },
313}
314
315/// Output format for the format command
316#[derive(Clone, Debug, Default, Display, ValueEnum)]
317#[non_exhaustive]
318pub enum OutputFormat {
319    /// The JSON output format.
320    #[default]
321    #[strum(to_string = "json")]
322    Json,
323}