alpm_pkginfo/
cli.rs

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