diff --git a/easytier/src/core.rs b/easytier/src/core.rs index f58c96e6..af96ca95 100644 --- a/easytier/src/core.rs +++ b/easytier/src/core.rs @@ -19,13 +19,15 @@ use crate::{ }; use anyhow::Context; use cidr::IpCidr; -use clap::{Arg, ArgMatches, CommandFactory, FromArgMatches, Parser}; +use clap::{ + Arg, ArgMatches, CommandFactory, FromArgMatches, Parser, builder::TypedValueParser as _, +}; use easytier_core::config::{DhcpConfig, EncryptionAlgorithm, normalize_secure_mode_config}; use guarden::defer; use prost_types::field_descriptor_proto::Type; use rust_i18n::t; use serde_json::{Value, json}; -use std::str::{FromStr, ParseBoolError}; +use std::str::FromStr; use std::{ net::{IpAddr, SocketAddr}, path::PathBuf, @@ -1102,9 +1104,9 @@ fn cli_command() -> clap::Command { value.parse::().map(|value| json!(value)) } - fn negated(value: &str) -> Result { - value.parse::().map(|value| json!(!value)) - } + let parser = clap::builder::BoolishValueParser::new(); + let boolish = parser.map(|v| json!(v)); + let negated = parser.map(|v| json!(!v)); Cli::command().args(Flags::flags().iter().map(|field| { let name = field.name(); @@ -1115,11 +1117,11 @@ fn cli_command() -> clap::Command { .short('u') .num_args(0..=1) .default_missing_value("true") - .value_parser(negated), + .value_parser(negated.clone()), "enable_ipv6" => arg(name, "disable_ipv6") .num_args(0..=1) .default_missing_value("true") - .value_parser(negated), + .value_parser(negated.clone()), // The command line names the algorithm instead of spelling the field. // The values are the schema's own, which the patch reads. "data_compress_algo" => { @@ -1138,7 +1140,7 @@ fn cli_command() -> clap::Command { Type::Bool => arg .num_args(0..=1) .default_missing_value("true") - .value_parser(parsed::), + .value_parser(boolish.clone()), Type::Uint32 => arg.value_parser(parsed::), Type::Uint64 => arg.value_parser(parsed::), // An enum flag takes the name the schema spells, and the patch's @@ -1772,6 +1774,44 @@ enabled = true let patch = parse_flags(&["easytier", "--disable-ipv6"]).unwrap(); assert_eq!(patch.enable_ipv6, Some(false)); + // Boolish tests: accepts 1/0, yes/no, on/off, true/false + assert_eq!( + parse_flags(&["easytier", "--latency-first", "1"]) + .unwrap() + .latency_first, + Some(true) + ); + assert_eq!( + parse_flags(&["easytier", "--latency-first", "0"]) + .unwrap() + .latency_first, + Some(false) + ); + assert_eq!( + parse_flags(&["easytier", "--latency-first", "yes"]) + .unwrap() + .latency_first, + Some(true) + ); + assert_eq!( + parse_flags(&["easytier", "--latency-first", "no"]) + .unwrap() + .latency_first, + Some(false) + ); + assert_eq!( + parse_flags(&["easytier", "--disable-encryption", "1"]) + .unwrap() + .enable_encryption, + Some(false) + ); + assert_eq!( + parse_flags(&["easytier", "--disable-encryption", "0"]) + .unwrap() + .enable_encryption, + Some(true) + ); + // Compression tests: case-insensitive, accepts None/none/zstd/Zstd/ZSTD, rejects invalid let patch = parse_flags(&["easytier", "--compression", "none"]).unwrap(); assert_eq!(