Skip to content

Commit

Permalink
fix(core): cli argument parser API not exiting on failure, update to …
Browse files Browse the repository at this point in the history
…latest (#1234)
  • Loading branch information
lucasfernog committed Feb 14, 2021
1 parent 54a5a13 commit 772d83e
Show file tree
Hide file tree
Showing 11 changed files with 112 additions and 309 deletions.
235 changes: 2 additions & 233 deletions cli/core/src/helpers/config.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
use json_patch::merge;
use once_cell::sync::Lazy;
use serde::{
de::{Deserializer, Error as DeError, Visitor},
ser::Serializer,
Deserialize, Serialize,
};
use serde::{Deserialize, Serialize};
use serde_json::Value as JsonValue;

use std::{
Expand All @@ -21,229 +17,6 @@ fn config_handle() -> &'static ConfigHandle {
&CONFING_HANDLE
}

/// The embedded server port.
#[derive(PartialEq, Clone, Debug, Deserialize, Serialize)]
pub enum Port {
/// Port with a numeric value.
Value(u16),
/// Random port.
Random,
}

/// The embeddedServer configuration object.
#[derive(PartialEq, Clone, Deserialize, Serialize, Debug)]
#[serde(tag = "embeddedServer", rename_all = "camelCase")]
pub struct EmbeddedServerConfig {
/// The embedded server host.
#[serde(default = "default_host")]
pub host: String,
/// The embedded server port.
/// If it's `random`, we'll generate one at runtime.
#[serde(
default = "default_port",
deserialize_with = "port_deserializer",
serialize_with = "port_serializer"
)]
pub port: Port,
}

fn default_host() -> String {
"http://127.0.0.1".to_string()
}

fn port_serializer<S>(x: &Port, s: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
match x {
Port::Random => s.serialize_str("random"),
Port::Value(val) => s.serialize_u16(*val),
}
}

fn port_deserializer<'de, D>(deserializer: D) -> Result<Port, D::Error>
where
D: Deserializer<'de>,
{
struct PortDeserializer;

impl<'de> Visitor<'de> for PortDeserializer {
type Value = Port;
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str("a port number or the 'random' string")
}

fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: DeError,
{
if value != "random" {
Err(DeError::custom(
"expected a 'random' string or a port number",
))
} else {
Ok(Port::Random)
}
}

fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
where
E: DeError,
{
Ok(Port::Value(value as u16))
}
}

deserializer.deserialize_any(PortDeserializer {})
}

fn default_port() -> Port {
Port::Random
}

fn default_embedded_server() -> EmbeddedServerConfig {
EmbeddedServerConfig {
host: default_host(),
port: default_port(),
}
}

/// A CLI argument definition
#[derive(PartialEq, Clone, Deserialize, Serialize, Debug, Default)]
#[serde(rename_all = "camelCase")]
pub struct CliArg {
/// The short version of the argument, without the preceding -.
///
/// NOTE: Any leading - characters will be stripped, and only the first non - character will be used as the short version.
pub short: Option<char>,
/// The unique argument name
pub name: String,
/// The argument description which will be shown on the help information.
/// Typically, this is a short (one line) description of the arg.
pub description: Option<String>,
/// The argument long description which will be shown on the help information.
/// Typically this a more detailed (multi-line) message that describes the argument.
pub long_description: Option<String>,
/// Specifies that the argument takes a value at run time.
///
/// NOTE: values for arguments may be specified in any of the following methods
/// - Using a space such as -o value or --option value
/// - Using an equals and no space such as -o=value or --option=value
/// - Use a short and no space such as -ovalue
pub takes_value: Option<bool>,
/// Specifies that the argument may appear more than once.
///
/// - For flags, this results in the number of occurrences of the flag being recorded.
/// For example -ddd or -d -d -d would count as three occurrences.
/// - For options there is a distinct difference in multiple occurrences vs multiple values.
/// For example, --opt val1 val2 is one occurrence, but two values. Whereas --opt val1 --opt val2 is two occurrences.
pub multiple: Option<bool>,
///
pub multiple_occurrences: Option<bool>,
///
pub number_of_values: Option<u64>,
/// Specifies a list of possible values for this argument.
/// At runtime, the CLI verifies that only one of the specified values was used, or fails with an error message.
pub possible_values: Option<Vec<String>>,
/// Specifies the minimum number of values for this argument.
/// For example, if you had a -f <file> argument where you wanted at least 2 'files',
/// you would set `minValues: 2`, and this argument would be satisfied if the user provided, 2 or more values.
pub min_values: Option<u64>,
/// Specifies the maximum number of values are for this argument.
/// For example, if you had a -f <file> argument where you wanted up to 3 'files',
/// you would set .max_values(3), and this argument would be satisfied if the user provided, 1, 2, or 3 values.
pub max_values: Option<u64>,
/// Sets whether or not the argument is required by default.
///
/// - Required by default means it is required, when no other conflicting rules have been evaluated
/// - Conflicting rules take precedence over being required.
pub required: Option<bool>,
/// Sets an arg that override this arg's required setting
/// i.e. this arg will be required unless this other argument is present.
pub required_unless: Option<String>,
/// Sets args that override this arg's required setting
/// i.e. this arg will be required unless all these other arguments are present.
pub required_unless_all: Option<Vec<String>>,
/// Sets args that override this arg's required setting
/// i.e. this arg will be required unless at least one of these other arguments are present.
pub required_unless_one: Option<Vec<String>>,
/// Sets a conflicting argument by name
/// i.e. when using this argument, the following argument can't be present and vice versa.
pub conflicts_with: Option<String>,
/// The same as conflictsWith but allows specifying multiple two-way conflicts per argument.
pub conflicts_with_all: Option<Vec<String>>,
/// Tets an argument by name that is required when this one is present
/// i.e. when using this argument, the following argument must be present.
pub requires: Option<String>,
/// Sts multiple arguments by names that are required when this one is present
/// i.e. when using this argument, the following arguments must be present.
pub requires_all: Option<Vec<String>>,
/// Allows a conditional requirement with the signature [arg, value]
/// the requirement will only become valid if `arg`'s value equals `${value}`.
pub requires_if: Option<Vec<String>>,
/// Allows specifying that an argument is required conditionally with the signature [arg, value]
/// the requirement will only become valid if the `arg`'s value equals `${value}`.
pub required_if: Option<Vec<String>>,
/// Requires that options use the --option=val syntax
/// i.e. an equals between the option and associated value.
pub require_equals: Option<bool>,
/// The positional argument index, starting at 1.
///
/// The index refers to position according to other positional argument.
/// It does not define position in the argument list as a whole. When utilized with multiple=true,
/// only the last positional argument may be defined as multiple (i.e. the one with the highest index).
pub index: Option<u64>,
}

/// The CLI root command definition.
#[derive(PartialEq, Clone, Deserialize, Serialize, Debug)]
#[serde(tag = "cli", rename_all = "camelCase")]
pub struct CliConfig {
description: Option<String>,
long_description: Option<String>,
before_help: Option<String>,
after_help: Option<String>,
args: Option<Vec<CliArg>>,
subcommands: Option<HashMap<String, CliConfig>>,
}

#[allow(dead_code)]
impl CliConfig {
/// List of args for the command
pub fn args(&self) -> Option<&Vec<CliArg>> {
self.args.as_ref()
}

/// List of subcommands of this command
pub fn subcommands(&self) -> Option<&HashMap<String, CliConfig>> {
self.subcommands.as_ref()
}

/// Command description which will be shown on the help information.
pub fn description(&self) -> Option<&String> {
self.description.as_ref()
}

/// Command long description which will be shown on the help information.
pub fn long_description(&self) -> Option<&String> {
self.description.as_ref()
}

/// Adds additional help information to be displayed in addition to auto-generated help.
/// This information is displayed before the auto-generated help information.
/// This is often used for header information.
pub fn before_help(&self) -> Option<&String> {
self.before_help.as_ref()
}

/// Adds additional help information to be displayed in addition to auto-generated help.
/// This information is displayed after the auto-generated help information.
/// This is often used to describe how to use the arguments, or caveats to be noted.
pub fn after_help(&self) -> Option<&String> {
self.after_help.as_ref()
}
}

/// The bundler configuration object.
#[derive(PartialEq, Clone, Deserialize, Serialize, Debug)]
#[serde(tag = "bundle", rename_all = "camelCase")]
Expand All @@ -265,12 +38,9 @@ fn default_bundle() -> BundleConfig {
#[derive(PartialEq, Clone, Deserialize, Serialize, Debug)]
#[serde(tag = "tauri", rename_all = "camelCase")]
pub struct TauriConfig {
/// The embeddedServer configuration.
#[serde(default = "default_embedded_server")]
pub embedded_server: EmbeddedServerConfig,
/// The CLI configuration.
#[serde(default)]
pub cli: Option<CliConfig>,
pub cli: Option<JsonValue>,
/// The bundler configuration.
#[serde(default = "default_bundle")]
pub bundle: BundleConfig,
Expand Down Expand Up @@ -320,7 +90,6 @@ pub struct Config {

fn default_tauri() -> TauriConfig {
TauriConfig {
embedded_server: default_embedded_server(),
cli: None,
bundle: default_bundle(),
allowlist: Default::default(),
Expand Down
8 changes: 4 additions & 4 deletions cli/tauri.js/src/types/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
"description": "sets whether or not the argument is required by default\nrequired by default means it is required, when no other conflicting rules have been evaluated\nconflicting rules take precedence over being required.",
"type": "boolean"
},
"requiredIf": {
"requiredIfEq": {
"additionalItems": {
"anyOf": [
{
Expand All @@ -86,18 +86,18 @@
"minItems": 2,
"type": "array"
},
"requiredUnless": {
"requiredUnlessPresent": {
"description": "sets an arg that override this arg's required setting\ni.e. this arg will be required unless this other argument is present",
"type": "string"
},
"requiredUnlessAll": {
"requiredUnlessPresentAll": {
"description": "sets args that override this arg's required setting\ni.e. this arg will be required unless all these other arguments are present",
"items": {
"type": "string"
},
"type": "array"
},
"requiredUnlessOne": {
"requiredUnlessPresentAny": {
"description": "sets args that override this arg's required setting\ni.e. this arg will be required unless at least one of these other arguments are present",
"items": {
"type": "string"
Expand Down
8 changes: 4 additions & 4 deletions cli/tauri.js/src/types/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,17 @@ export interface CliArg {
* sets an arg that override this arg's required setting
* i.e. this arg will be required unless this other argument is present
*/
requiredUnless?: string
requiredUnlessPresent?: string
/**
* sets args that override this arg's required setting
* i.e. this arg will be required unless all these other arguments are present
*/
requiredUnlessAll?: string[]
requiredUnlessPresentAll?: string[]
/**
* sets args that override this arg's required setting
* i.e. this arg will be required unless at least one of these other arguments are present
*/
requiredUnlessOne?: string[]
requiredUnlessPresentAny?: string[]
/**
* sets a conflicting argument by name
* i.e. when using this argument, the following argument can't be present and vice versa
Expand Down Expand Up @@ -102,7 +102,7 @@ export interface CliArg {
* allows specifying that an argument is required conditionally with the signature [arg: string, value: string]
* the requirement will only become valid if the `arg`'s value equals `${value}`.
*/
requiredIf?: [string, string]
requiredIfEq?: [string, string]
/**
* requires that options use the --option=val syntax
* i.e. an equals between the option and associated value
Expand Down
8 changes: 4 additions & 4 deletions cli/tauri.js/src/types/config.validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export const TauriConfigSchema = {
'sets whether or not the argument is required by default\nrequired by default means it is required, when no other conflicting rules have been evaluated\nconflicting rules take precedence over being required.',
type: 'boolean'
},
requiredIf: {
requiredIfEq: {
additionalItems: {
anyOf: [
{
Expand All @@ -116,20 +116,20 @@ export const TauriConfigSchema = {
minItems: 2,
type: 'array'
},
requiredUnless: {
requiredUnlessPresent: {
description:
"sets an arg that override this arg's required setting\ni.e. this arg will be required unless this other argument is present",
type: 'string'
},
requiredUnlessAll: {
requiredUnlessPresentAll: {
description:
"sets args that override this arg's required setting\ni.e. this arg will be required unless all these other arguments are present",
items: {
type: 'string'
},
type: 'array'
},
requiredUnlessOne: {
requiredUnlessPresentAny: {
description:
"sets args that override this arg's required setting\ni.e. this arg will be required unless at least one of these other arguments are present",
items: {
Expand Down
2 changes: 1 addition & 1 deletion tauri-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ tauri-dialog = "0.1.0"
attohttpc = { version = "0.16.1", features = [ "json", "form" ] }
http = "0.2"
tauri-utils = { version = "0.5", path = "../tauri-utils" }
clap = { git = "https://github.com/clap-rs/clap", rev = "1a276f8", version = "=3.0.0-beta.1", optional = true }
clap = { version = "=3.0.0-beta.2", optional = true }
notify-rust = { version = "4.2.2", optional = true }
once_cell = "1.5.2"

Expand Down

0 comments on commit 772d83e

Please sign in to comment.