123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271 |
- // Copyright 2019-2021 Tauri Programme within The Commons Conservancy
- // SPDX-License-Identifier: Apache-2.0
- // SPDX-License-Identifier: MIT
- //! Types and functions related to CLI arguments.
- use crate::{
- utils::config::{CliArg, CliConfig},
- PackageInfo,
- };
- use clap::{Arg, ArgMatches, ErrorKind};
- use serde::Serialize;
- use serde_json::Value;
- use std::collections::HashMap;
- #[macro_use]
- mod macros;
- mod clapfix {
- //! Compatibility between `clap` 3.0 and 3.1+ without deprecation errors.
- #![allow(deprecated)]
- pub type ClapCommand<'help> = clap::App<'help>;
- pub trait ErrorExt {
- fn kind(&self) -> clap::ErrorKind;
- }
- impl ErrorExt for clap::Error {
- fn kind(&self) -> clap::ErrorKind {
- self.kind
- }
- }
- }
- use clapfix::{ClapCommand as App, ErrorExt};
- /// The resolution of a argument match.
- #[derive(Default, Debug, Serialize)]
- #[non_exhaustive]
- pub struct ArgData {
- /// - [`Value::Bool`] if it's a flag,
- /// - [`Value::Array`] if it's multiple,
- /// - [`Value::String`] if it has value,
- /// - [`Value::Null`] otherwise.
- pub value: Value,
- /// The number of occurrences of the argument.
- /// e.g. `./app --arg 1 --arg 2 --arg 2 3 4` results in three occurrences.
- pub occurrences: u64,
- }
- /// The matched subcommand.
- #[derive(Default, Debug, Serialize)]
- #[non_exhaustive]
- pub struct SubcommandMatches {
- /// The subcommand name.
- pub name: String,
- /// The subcommand argument matches.
- pub matches: Matches,
- }
- /// The argument matches of a command.
- #[derive(Default, Debug, Serialize)]
- #[non_exhaustive]
- pub struct Matches {
- /// Data structure mapping each found arg with its resolution.
- pub args: HashMap<String, ArgData>,
- /// The matched subcommand if found.
- pub subcommand: Option<Box<SubcommandMatches>>,
- }
- impl Matches {
- /// Set a arg match.
- pub(crate) fn set_arg(&mut self, name: String, value: ArgData) {
- self.args.insert(name, value);
- }
- /// Sets the subcommand matches.
- pub(crate) fn set_subcommand(&mut self, name: String, matches: Matches) {
- self.subcommand = Some(Box::new(SubcommandMatches { name, matches }));
- }
- }
- /// Gets the argument matches of the CLI definition.
- ///
- /// This is a low level API. If the application has been built,
- /// prefer [`App::get_cli_matches`](`crate::App#method.get_cli_matches`).
- ///
- /// # Examples
- ///
- /// ```rust,no_run
- /// use tauri::api::cli::get_matches;
- /// tauri::Builder::default()
- /// .setup(|app| {
- /// let matches = get_matches(app.config().tauri.cli.as_ref().unwrap(), app.package_info())?;
- /// Ok(())
- /// });
- /// ```
- pub fn get_matches(cli: &CliConfig, package_info: &PackageInfo) -> crate::api::Result<Matches> {
- let about = cli
- .description()
- .unwrap_or(&package_info.description.to_string())
- .to_string();
- let version = &*package_info.version.to_string();
- let app = get_app(package_info, version, &package_info.name, Some(&about), cli);
- match app.try_get_matches() {
- Ok(matches) => Ok(get_matches_internal(cli, &matches)),
- Err(e) => match ErrorExt::kind(&e) {
- ErrorKind::DisplayHelp => {
- let mut matches = Matches::default();
- let help_text = e.to_string();
- matches.args.insert(
- "help".to_string(),
- ArgData {
- value: Value::String(help_text),
- occurrences: 0,
- },
- );
- Ok(matches)
- }
- ErrorKind::DisplayVersion => {
- let mut matches = Matches::default();
- matches
- .args
- .insert("version".to_string(), Default::default());
- Ok(matches)
- }
- _ => Err(e.into()),
- },
- }
- }
- fn get_matches_internal(config: &CliConfig, matches: &ArgMatches) -> Matches {
- let mut cli_matches = Matches::default();
- map_matches(config, matches, &mut cli_matches);
- if let Some((subcommand_name, subcommand_matches)) = matches.subcommand() {
- let mut subcommand_cli_matches = Matches::default();
- map_matches(
- config.subcommands().unwrap().get(subcommand_name).unwrap(),
- subcommand_matches,
- &mut subcommand_cli_matches,
- );
- cli_matches.set_subcommand(subcommand_name.to_string(), subcommand_cli_matches);
- }
- cli_matches
- }
- fn map_matches(config: &CliConfig, matches: &ArgMatches, cli_matches: &mut Matches) {
- if let Some(args) = config.args() {
- for arg in args {
- let occurrences = matches.occurrences_of(arg.name.clone());
- let value = if occurrences == 0 || !arg.takes_value {
- Value::Bool(occurrences > 0)
- } else if arg.multiple {
- matches
- .values_of(arg.name.clone())
- .map(|v| {
- let mut values = Vec::new();
- for value in v {
- values.push(Value::String(value.to_string()));
- }
- Value::Array(values)
- })
- .unwrap_or(Value::Null)
- } else {
- matches
- .value_of(arg.name.clone())
- .map(|v| Value::String(v.to_string()))
- .unwrap_or(Value::Null)
- };
- cli_matches.set_arg(arg.name.clone(), ArgData { value, occurrences });
- }
- }
- }
- fn get_app<'a>(
- package_info: &'a PackageInfo,
- version: &'a str,
- command_name: &'a str,
- about: Option<&'a String>,
- config: &'a CliConfig,
- ) -> App<'a> {
- let mut app = App::new(command_name)
- .author(package_info.authors)
- .version(version);
- if let Some(about) = about {
- app = app.about(&**about);
- }
- if let Some(long_description) = config.long_description() {
- app = app.long_about(&**long_description);
- }
- if let Some(before_help) = config.before_help() {
- app = app.before_help(&**before_help);
- }
- if let Some(after_help) = config.after_help() {
- app = app.after_help(&**after_help);
- }
- if let Some(args) = config.args() {
- for arg in args {
- let arg_name = arg.name.as_ref();
- app = app.arg(get_arg(arg_name, arg));
- }
- }
- if let Some(subcommands) = config.subcommands() {
- for (subcommand_name, subcommand) in subcommands {
- let clap_subcommand = get_app(
- package_info,
- version,
- subcommand_name,
- subcommand.description(),
- subcommand,
- );
- app = app.subcommand(clap_subcommand);
- }
- }
- app
- }
- fn get_arg<'a>(arg_name: &'a str, arg: &'a CliArg) -> Arg<'a> {
- let mut clap_arg = Arg::new(arg_name);
- if arg.index.is_none() {
- clap_arg = clap_arg.long(arg_name);
- if let Some(short) = arg.short {
- clap_arg = clap_arg.short(short);
- }
- }
- clap_arg = bind_string_arg!(arg, clap_arg, description, help);
- clap_arg = bind_string_arg!(arg, clap_arg, long_description, long_help);
- clap_arg = clap_arg.takes_value(arg.takes_value);
- clap_arg = clap_arg.multiple_values(arg.multiple);
- clap_arg = clap_arg.multiple_occurrences(arg.multiple_occurrences);
- clap_arg = bind_value_arg!(arg, clap_arg, number_of_values);
- clap_arg = bind_string_slice_arg!(arg, clap_arg, possible_values);
- clap_arg = bind_value_arg!(arg, clap_arg, min_values);
- clap_arg = bind_value_arg!(arg, clap_arg, max_values);
- clap_arg = clap_arg.required(arg.required);
- clap_arg = bind_string_arg!(
- arg,
- clap_arg,
- required_unless_present,
- required_unless_present
- );
- clap_arg = bind_string_slice_arg!(arg, clap_arg, required_unless_present_all);
- clap_arg = bind_string_slice_arg!(arg, clap_arg, required_unless_present_any);
- clap_arg = bind_string_arg!(arg, clap_arg, conflicts_with, conflicts_with);
- if let Some(value) = &arg.conflicts_with_all {
- let v: Vec<&str> = value.iter().map(|x| &**x).collect();
- clap_arg = clap_arg.conflicts_with_all(&v);
- }
- clap_arg = bind_string_arg!(arg, clap_arg, requires, requires);
- if let Some(value) = &arg.requires_all {
- let v: Vec<&str> = value.iter().map(|x| &**x).collect();
- clap_arg = clap_arg.requires_all(&v);
- }
- clap_arg = bind_if_arg!(arg, clap_arg, requires_if);
- clap_arg = bind_if_arg!(arg, clap_arg, required_if_eq);
- clap_arg = bind_value_arg!(arg, clap_arg, require_equals);
- clap_arg = bind_value_arg!(arg, clap_arg, index);
- clap_arg
- }
|