diff --git a/Cargo.lock b/Cargo.lock index 584b5b6e301b..c8a8a1eb2c2a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -704,19 +704,28 @@ dependencies = [ [[package]] name = "clap" -version = "3.0.10" +version = "3.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a30c3bf9ff12dfe5dae53f0a96e0febcd18420d1c0e7fad77796d9d5c4b5375" +checksum = "d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b" dependencies = [ "bitflags", + "clap_lex", "indexmap", "lazy_static", - "os_str_bytes", "strsim", "terminal_size", "textwrap", ] +[[package]] +name = "clap_lex" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "cmake" version = "0.1.999" @@ -3753,9 +3762,6 @@ name = "os_str_bytes" version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "029d8d0b2f198229de29dca79676f2738ff952edf3fde542eb8bf94d8c21b435" -dependencies = [ - "memchr", -] [[package]] name = "osclientcerts-static" @@ -5071,9 +5077,9 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.14.2" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80" +checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" dependencies = [ "terminal_size", ] diff --git a/testing/geckodriver/Cargo.toml b/testing/geckodriver/Cargo.toml index 582706b79cab..853d31b170b7 100644 --- a/testing/geckodriver/Cargo.toml +++ b/testing/geckodriver/Cargo.toml @@ -12,7 +12,7 @@ edition = "2018" [dependencies] base64 = "0.12" chrono = "0.4.6" -clap = { version = "3", default-features = false, features = ["cargo", "std", "suggestions", "wrap_help"] } +clap = { version = "3.1", default-features = false, features = ["cargo", "std", "suggestions", "wrap_help"] } hyper = "0.13" lazy_static = "1.0" log = { version = "0.4", features = ["std"] } diff --git a/testing/geckodriver/src/main.rs b/testing/geckodriver/src/main.rs index ea2044ee8789..7742b3643a3b 100644 --- a/testing/geckodriver/src/main.rs +++ b/testing/geckodriver/src/main.rs @@ -34,7 +34,7 @@ use std::path::PathBuf; use std::result; use std::str::FromStr; -use clap::{App, AppSettings, Arg}; +use clap::{AppSettings, Arg, Command}; macro_rules! try_opt { ($expr:expr, $err_type:expr, $err_msg:expr) => {{ @@ -234,8 +234,8 @@ fn get_allowed_origins(allow_origins: Option) -> Result, .unwrap_or_else(|| Ok(vec![])) } -fn parse_args(app: &mut App) -> ProgramResult { - let args = app.try_get_matches_from_mut(env::args())?; +fn parse_args(cmd: &mut Command) -> ProgramResult { + let args = cmd.try_get_matches_from_mut(env::args())?; if args.is_present("help") { return Ok(Operation::Help); @@ -340,9 +340,9 @@ fn parse_args(app: &mut App) -> ProgramResult { }) } -fn inner_main(app: &mut App) -> ProgramResult<()> { - match parse_args(app)? { - Operation::Help => print_help(app), +fn inner_main(cmd: &mut Command) -> ProgramResult<()> { + match parse_args(cmd)? { + Operation::Help => print_help(cmd), Operation::Version => print_version(), Operation::Server { @@ -381,16 +381,16 @@ fn inner_main(app: &mut App) -> ProgramResult<()> { fn main() { use std::process::exit; - let mut app = make_app(); + let mut cmd = make_command(); // use std::process:Termination when it graduates - exit(match inner_main(&mut app) { + exit(match inner_main(&mut cmd) { Ok(_) => EXIT_SUCCESS, Err(e) => { eprintln!("{}: {}", get_program_name(), e); if !e.help_included() { - print_help(&mut app); + print_help(&mut cmd); } e.exit_code() @@ -398,8 +398,8 @@ fn main() { }); } -fn make_app<'a>() -> App<'a> { - App::new(format!("geckodriver {}", build::build_info())) +fn make_command<'a>() -> Command<'a> { + Command::new(format!("geckodriver {}", build::build_info())) .setting(AppSettings::NoAutoHelp) .setting(AppSettings::NoAutoVersion) .about("WebDriver implementation for Firefox") @@ -525,8 +525,8 @@ fn get_program_name() -> String { env::args().next().unwrap() } -fn print_help(app: &mut App) { - app.print_help().ok(); +fn print_help(cmd: &mut Command) { + cmd.print_help().ok(); println!(); } diff --git a/third_party/rust/clap/.cargo-checksum.json b/third_party/rust/clap/.cargo-checksum.json index f835936e9775..dc82b6d7c8f6 100644 --- a/third_party/rust/clap/.cargo-checksum.json +++ b/third_party/rust/clap/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.lock":"67d366e477c95557534095f1d90f3f3da040c2beefd464b5ca7807c55737de79","Cargo.toml":"9eb2eff2e26845f75b0f4f8aee222031d261a43d9efded6fcbe0b9eab470f233","LICENSE-APACHE":"c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4","LICENSE-MIT":"6725d1437fc6c77301f2ff0e7d52914cf4f9509213e1078dc77d9356dbe6eac5","README.md":"3318dc5f9c3c236fa609561f48d87631f1ba32a52467a71883e8caa62529dfbd","benches/01_default.rs":"f0be47b9a370aa729fbe36cb7845153ffbc64d3699bb50b74424e89a4ae48d4a","benches/02_simple.rs":"1ed8d0eb3ae627db67141197f0d4fe22d2bdaa449782f095d511c5178922e077","benches/03_complex.rs":"8866bee6de558b1fdf226814f077903e7c1f4709c314c5dc224f8f02285eb3d7","benches/04_new_help.rs":"a1ac5a6cde8eb23006b878ad4b20cab3359656666d23dac51263e73330fe72ac","benches/05_ripgrep.rs":"3680076a8b7e471fd36ac2bfdc6cc3070aeea227baf41038d73bfadca0f8233a","benches/06_rustup.rs":"f120229525421e44e4f92e8ed913af5287890f89bb8ac47d4a9250c9f3195cc6","examples/README.md":"1f79959991f9e39abb6eb39e0848222c703d2675d071b0c61769f7e4da40f502","examples/cargo-example-derive.md":"53a35e48cd04dfa10d70f99eb6d97d0d74110958f7f9e4ffc460fca87a256a73","examples/cargo-example-derive.rs":"950a1b91d795c626005639f84c3cd8ca133dc470b0f153364cdf5f7e75eaa697","examples/cargo-example.md":"1f270b0a0ac9122feb9cdac0887c1c1bcf1b654fed27147871c9cfe71851a4d9","examples/cargo-example.rs":"259712cb859a71a9d60a788bc1eb94100099d5d449a3bed34906c3c9decd9580","examples/demo.md":"3e986afca0863142fdd4cc092b5969fba32a70fac8adf84a55bd652f4b30eedb","examples/demo.rs":"2bf20d203a1f35fbdcae25690e4e9f55938a485ab92f909822decc0d2f3a6c10","examples/derive_ref/README.md":"a92db7ace75503220a8f2943cca2dcd86109b498a206f0251cf8724a4b7aca69","examples/derive_ref/custom-bool.md":"4aa41737326d7591a223d3b98b6c19dada4b62d12ac9d7aee44edadf71286fb0","examples/derive_ref/custom-bool.rs":"82b52b9fd902007a5b075445efac5bbc10f59e1a32433abe70850769b99a00cf","examples/escaped-positional-derive.md":"b2190ada5ad2634c3ceb7f9d98293512628762545f9519579cbee9867c6a406a","examples/escaped-positional-derive.rs":"0784fc1a0407053065bd231ba20d7c7d7c0225ef5ee48892b60632ce87dceb48","examples/escaped-positional.md":"b6a5c103f5cea1464b5ac8e63e1964f032a4dbb4a908e45b9b391f4857fe2684","examples/escaped-positional.rs":"472d84586bff5eb48fb10c0c8eca214935c0f3c7499df3df0a232c75e94278df","examples/git-derive.md":"08acb33eb9309365d4f2d8ae703b2273c697e41dffe8bf8c76701724e420ecd9","examples/git-derive.rs":"f05043fd233de1f81209fd36f3883767f18c4d633d5664cea874756bcbef8751","examples/git.md":"53651572035f35076fd1fb26556eb90dc0074959cd70dc568384165f213da11d","examples/git.rs":"656d05727abac3e7a0906565eab91751d266b743f889e7b4fdab687283f55f96","examples/keyvalue-derive.md":"d0d4751a234108c70ea839182d79c261a25b05d3b818584d96a375330de0165e","examples/keyvalue-derive.rs":"2455cc4a3d38bccbbe44e865b6d8fd0bc32be8bf3a74ff6c607489ceeebf6473","examples/multicall-busybox.md":"f755cf1a92930b42e6a920ba16a68cd08e1d16810d649f22b74d52ecee6a998b","examples/multicall-busybox.rs":"4762b1dfd518fd6959b3aee5721837f62d75f7e2dc139019c200cbced56815b8","examples/multicall-hostname.md":"6fbc7bbd9dd94e9b8bfdd9cae85909fbe8091c3580f94a9ac2189bbe66efb3cc","examples/multicall-hostname.rs":"17428c0c95af69ec561e4a3472c3e6862fd42bc9ee5fad896dae81b501a34ce9","examples/pacman.md":"870633ad60db3c736d95378ad04964393f76c56fd0f7dc90a8f444ce51ad4b3a","examples/pacman.rs":"28d6b264042090d1063a28587f2ac2b8a32b2ff6a048a72ac7f03c0fa57d0f8f","examples/tutorial_builder/01_quick.rs":"77cee52cf769850c331dcb7b09ab4ca5a79886ed2a5876f0da961761532d9c1c","examples/tutorial_builder/02_app_settings.rs":"60ba38141c703a2a5012fcd7195a9b8726496b50aaacbc1180a79a5b3a3ed5ee","examples/tutorial_builder/02_apps.rs":"e4323a1210b30996c937282a0fc28490a6e2ed8f5d31300a6d68b36f567d456c","examples/tutorial_builder/02_crate.rs":"48ac80a522338bf311608989e3402847b2bd0e2777c3b66775455133b34148b6","examples/tutorial_builder/03_01_flag_bool.rs":"9e96dd8fde99265b7e147f044cf55f1eb7a3c8228e29be3368c3d44210d9cec1","examples/tutorial_builder/03_01_flag_count.rs":"fe2a2300cace0d7bb55f47124f622c642546100a18676dc8aa88c5fb434b1e9c","examples/tutorial_builder/03_02_option.rs":"d146fd8af68818db087de474e2863171c19d32847f3147c75a054ed93c548b3d","examples/tutorial_builder/03_03_positional.rs":"ee4057bb14416b80eff1ac6a26adaca28dff601501dca0204bd5d9af3aaff033","examples/tutorial_builder/03_04_subcommands.rs":"f57e11b4082a23887741eaf4869a60fd8a4e8bcf60d63e6047120b8a9182829f","examples/tutorial_builder/03_05_default_values.rs":"883e2f2be05046e6ff5de9a9069609846247b1e2cd5b75b19633295b5d05b392","examples/tutorial_builder/04_01_enum.rs":"25fbd7fff15164ab868964b9983e05dac7a27a27e72f2643482df4f95a4a5a42","examples/tutorial_builder/04_01_possible.rs":"4782a6f95770a093217fdb257100898d93282d6feaed5ae0db951dd1ccdbaac2","examples/tutorial_builder/04_02_validate.rs":"a66118adb631947131807a07bcf468adfb69b5ab8b789ec73a57ca2bdd085de4","examples/tutorial_builder/04_03_relations.rs":"8c7922f5cef0505a344daefd8e514ed821cb94d0dd070cc78070a3158a9b03ab","examples/tutorial_builder/04_04_custom.rs":"291fd0d6ee571f04cb121a320158c72c85867b4be90729593b0d3fbb6bdc4c3c","examples/tutorial_builder/05_01_assert.rs":"03a2e2ebe949d4ba0cdb435c6ffd91501f9724faa71b24d3eecb1edbd45f728d","examples/tutorial_builder/README.md":"53b102e904ea3ffd563353b7f9e696f4b110b6c0b8e00fd91acd664651c68116","examples/tutorial_derive/01_quick.rs":"5687707ceae990e488037e86f479e9a49a8d905b76601e8460424eeb095cbd76","examples/tutorial_derive/02_app_settings.rs":"3076d14899573716310e8b463d91492b443b7060ea50843972c42a9342381cf7","examples/tutorial_derive/02_apps.rs":"5c71c66b2a6ad0d57c47e911fc7d9ea9163064b89ee2ece610202b133241c3e5","examples/tutorial_derive/02_crate.rs":"345f4e9a7ba384d6008c1686009b1ba05ec646252fb56efdd02d8efd0b80d851","examples/tutorial_derive/03_01_flag_bool.rs":"8fb058ef030429ff601509e2d0f67279522d8c8639b5bafee29eaa83e983b0ba","examples/tutorial_derive/03_01_flag_count.rs":"edc54dcf8631ff60438b89871d7e92f1a97077ae4293271af2c279ec53a3d0bc","examples/tutorial_derive/03_02_option.rs":"cc9487c64a71ed272ee3ec210a87fde5cded839cb64548f0bab0a3f0c44186fe","examples/tutorial_derive/03_03_positional.rs":"092d843366f4959f917bcbeef3a521c033c623ba25dc8f8f167e9cc218024d7d","examples/tutorial_derive/03_04_subcommands.rs":"1a9ea5dee9856dddbaba2342ed295d64eac53f90bc6c8c0d6ee8ab7f318edc0e","examples/tutorial_derive/03_05_default_values.rs":"28dacd9836fe525dab37b0095c8df40d8fa0c19457c0d09ac32f0f7b8972baeb","examples/tutorial_derive/04_01_enum.rs":"12fbe926afa371c633e36324c9e18976ac8b375939f7d637c9fcfabb0833a297","examples/tutorial_derive/04_02_validate.rs":"ecd08d395fd455cb4a167c29279dabfa0be13cc19d70cd9a9fe199725a3efe83","examples/tutorial_derive/04_03_relations.rs":"ede16700d983aa08081d29054cd946237c67951e3c7f3ee92d432a7abcb3952e","examples/tutorial_derive/04_04_custom.rs":"8c4d6849430aeb9fb76e0cea33d7815a41e144b918844e14fc9e62d3ce9058a9","examples/tutorial_derive/05_01_assert.rs":"75c61f1281291d7c7d1483964d6c04185daed94bc2700050963b6e5dee12795e","examples/tutorial_derive/README.md":"c4892eadcaf0ea425945d4b82f1a18a949a671fad849217ed1db8922605f0032","src/build/app/debug_asserts.rs":"c2de27dda3c5e1908accfdd097762bf1f6f93ab04af8eedf78abc443b87e13e5","src/build/app/mod.rs":"8a60a94c75f777bacaab3fc1b997b4c1089169d3c6c39a16974bb3376e734c22","src/build/app/settings.rs":"439b5a139d50832736da7f30e1748e8035475a2bfdbd7942322826b1b078f54c","src/build/app/tests.rs":"59ff6cdaed75eb3a6832453499e6c6abff5b55c9ab9b3533c154c9c35c81acbd","src/build/arg/debug_asserts.rs":"c8c9556d497a7b81d5caaf9c51594dfa1a1ced40bb4f9565590b76ba926755db","src/build/arg/mod.rs":"c67d625fbc378fcc8e58a380a4c457553937e3fe0d56654db84c31ebb10fd08c","src/build/arg/possible_value.rs":"8ed9410b2384ff342484ed2dc2597226a4e721e5fae814cfeaec1912d185bea0","src/build/arg/regex.rs":"b18891310186ecdcca9bb611833b7188cc80fe72e9fef27b63fa076b880a1acb","src/build/arg/settings.rs":"5533c231f3a74670282ee6441e01492b8f8059cf7c57b08a82acf0a74f330d34","src/build/arg/tests.rs":"d3320cb8317db0473289dad5137ca6ff2f0ef31c2aa4cdbfd549d88853ec5b74","src/build/arg/value_hint.rs":"e40604c01b58815f118118f4f3de481354779e1a41925cd27a9ae186b4da6f96","src/build/arg_group.rs":"c3e0c04c677769fa043bb7d7311ca922943c022f1d9069814fbea49e02a092a7","src/build/macros.rs":"904e42e72c49107ae324f45d5df24fbff13b9c7d4589f1a4ae38594add704434","src/build/mod.rs":"62de0f151e54f2a9d563c006108156882649fdda86f849a8f4b25f9508190051","src/build/usage_parser.rs":"9218cdfbbcfd7b55ec767036f5d21e07edcd098ea3e9f62cfba14d05e95a7bd3","src/derive.rs":"2b7180eeef3ef4be3111cdedf28885e75d3833210cdad23166d49864e24af4fc","src/lib.rs":"2955ccfdf911f6614a216ad3e352bd8b4a09ff894c8513ad8f6deb3142d54cd2","src/macros.rs":"03a1540909c4c472301bbc162afede5255e21dba73404e2035af29b57ad48a21","src/mkeymap.rs":"b3f695aa04f2c7e1e951cd6dbd545b229f361fa0fc97ebff1ba03879f52d4fd8","src/output/fmt.rs":"b6ec41c78ce226004b3867b5013a89c0c7015309334ad4949ae579685d62a96d","src/output/help.rs":"9b455854abbc1499efca8d785ab4a4a88ec2e07b12319071c08478dccebbbf9f","src/output/mod.rs":"3a61f89a4568e95114a3ab839da8f3c4c92de2a228549308f5d1be076a474917","src/output/usage.rs":"42100920ce0173ee215f0be840b1c13344ce54074d0febb01ccbab8c2cb845b2","src/parse/arg_matcher.rs":"f9b978330e82b390beddcf821b2ab307a10066c55907b8517561b19a7dd3bd94","src/parse/errors.rs":"ccc178376caae59088a306fb980d797f6b2ec1942403c6c0b601345f5e0cf350","src/parse/features/mod.rs":"6ed075e97af56bff22f22ed1ee83ff6479360e05f9d3661a3145f822c242b694","src/parse/features/suggestions.rs":"06709bf7a19abdc230445d17d9fa96bb0840bbff5ab1b4d0a050de4aeb89d292","src/parse/matches/arg_matches.rs":"0b91cd7ea3723873b3a5a274c56f2883fac6c6b007499ad8b72757cc94b16027","src/parse/matches/matched_arg.rs":"2abbdc7ab5976f11d8105299f9cff9bba42f3c5dc3d1f61dfa4896f62535ee06","src/parse/matches/mod.rs":"26f478e97311f8c758dfd12b85ea811f3954ca9486504027daa0c488ad5957c1","src/parse/mod.rs":"d57a14b8732fbd4aa0789cc221a8a9058ece265dbf1c2df7cd329f48860febee","src/parse/parser.rs":"d60c343720c29e8604603094a64d86a0c17e38dff77dd4ac9f8022fa65265d27","src/parse/validator.rs":"d6dcb4c87726cf2d9e865ef4a01cb9c2571a8a62b852cf3de95ad0ba01f13d0d","src/util/color.rs":"e207e5850ac71a88a032a42734414721d0a71581eb94023c64721d804c5dd00c","src/util/fnv.rs":"82492d91d990f38b62de8b3c3e67f1fad55919117b1f448aa28acf6d21919fd7","src/util/graph.rs":"f35396b6e2a427377dcbbca69b1b98737d89684a3834cfda98cbf8cc70ff9c2f","src/util/id.rs":"fc498c65887385d92a51359a2f72556c2e508ee49b8cac4b3f827512795d690d","src/util/mod.rs":"702c2567286d80f9ccb4e6910cda690965f23b21a93c4cd286f37e7890e92783","src/util/str_to_bool.rs":"f9302078014c7b8f493811f2bc0bcbb982c33efde6c5ca98bea479781c1501b7"},"package":"7a30c3bf9ff12dfe5dae53f0a96e0febcd18420d1c0e7fad77796d9d5c4b5375"} \ No newline at end of file +{"files":{"Cargo.lock":"2ec13fd0e12fedb834d38d41b9a8f5256d072bb72bd1dc9b2c82a5bce261baf9","Cargo.toml":"3029ba78c9f735e2603be18b3388585509f58830facfdf6db6ebce2c6b062158","LICENSE-APACHE":"c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4","LICENSE-MIT":"6725d1437fc6c77301f2ff0e7d52914cf4f9509213e1078dc77d9356dbe6eac5","README.md":"f717b799574b78231a95049906acfadaefc010b1ef02897967c4cbb89cca9102","benches/01_default.rs":"d65df6622531917affdba219552236425f78a05712b796d82e9b5f6634aaf1cd","benches/02_simple.rs":"2c0653407241aa6d9ad13ac65a0e91a212c55b5842a5a025fbed795510709936","benches/03_complex.rs":"d74a8995066b1bbaa126bbedafa1b0e07be141ed5416be1a4131c263d4b50763","benches/04_new_help.rs":"82c7e6c7a2e29239131ec6358d87203019e6c6c81eb3c38fbc2f563607b7413d","benches/05_ripgrep.rs":"a36f2e5dad4edbfcf521e6b900e932d5b10de903ba1c560f45962f9f4a9d0863","benches/06_rustup.rs":"9397f81671043fb062d55b0066f4769f2e3c627fe7781b48f78440448bcc1ee3","examples/README.md":"3da9187b15988b99103f585ba6199d12c5a43441dd9bf3dc3225c211debbb5b1","examples/cargo-example-derive.md":"53a35e48cd04dfa10d70f99eb6d97d0d74110958f7f9e4ffc460fca87a256a73","examples/cargo-example-derive.rs":"2c08a8f93dc0bd4205323956d23e38f50f69846aece19edf749c341145c62d32","examples/cargo-example.md":"1f270b0a0ac9122feb9cdac0887c1c1bcf1b654fed27147871c9cfe71851a4d9","examples/cargo-example.rs":"675d39e5837ca71acb1d662f1f43e6704d19ac1f9b52800e54bbeacde6a0c47b","examples/demo.md":"3e986afca0863142fdd4cc092b5969fba32a70fac8adf84a55bd652f4b30eedb","examples/demo.rs":"2bf20d203a1f35fbdcae25690e4e9f55938a485ab92f909822decc0d2f3a6c10","examples/derive_ref/README.md":"4be9d9468ba4b5759d4fa8ca3e62695d5b3c654ab0542b22fec116aa857af983","examples/derive_ref/augment_args.rs":"de176a7978d2a45ce06a746cdc13806319f1999b41e9e7849bfb16839b33376e","examples/derive_ref/augment_subcommands.rs":"f0b4738ff01af53507f99754fa87ed244daeb95f7864b3c5463392d393f7ce2b","examples/derive_ref/custom-bool.md":"4aa41737326d7591a223d3b98b6c19dada4b62d12ac9d7aee44edadf71286fb0","examples/derive_ref/custom-bool.rs":"82b52b9fd902007a5b075445efac5bbc10f59e1a32433abe70850769b99a00cf","examples/derive_ref/flatten_hand_args.rs":"0565b6ef2077ed7ec09e8bbad85c8c9a20585111672b901d588fb6bdf7344f98","examples/derive_ref/hand_subcommand.rs":"288b9edfe70e017be8d1f5c11c8e294284f239748441d147a5a2f5282438c04f","examples/derive_ref/interop_tests.md":"3a357e82cfc155f71e0348be33f78295aa77839c3e3a5b7c1783cbd1259e6a61","examples/escaped-positional-derive.md":"b2190ada5ad2634c3ceb7f9d98293512628762545f9519579cbee9867c6a406a","examples/escaped-positional-derive.rs":"0784fc1a0407053065bd231ba20d7c7d7c0225ef5ee48892b60632ce87dceb48","examples/escaped-positional.md":"b6a5c103f5cea1464b5ac8e63e1964f032a4dbb4a908e45b9b391f4857fe2684","examples/escaped-positional.rs":"30220741cab1fbd6be8184e84b0715ca7d3db46e6002aa008d07600eee5eaf72","examples/git-derive.md":"1a20f30d1c6df28d7e48b527b0726e9440f54d55a1e0b9e059b2322a7960ee72","examples/git-derive.rs":"fbfa7c557155ff15ded43f5e4bd011e0dff874f7bc705993e3c6df50e5e18558","examples/git.md":"acb3a44f954a6e2160f8989952981fb4baea8b97b921e4227c67896bc64d5e4a","examples/git.rs":"f00c7857c81bdab373bce292eb9733e35a7d358729d829c6228f7a96bcef2d20","examples/multicall-busybox.md":"3cb9d00ad110fc99c298dac4aa471e2f7a304aecef51d2eda67f5fbf36c41293","examples/multicall-busybox.rs":"cee5c19664b7fe04a2ee15912337cc733042e35e4527d3d2aeddd686fd0e416e","examples/multicall-hostname.md":"8c2d95fa8b940d4f788f96cf6b08b81bb2541bf12adff63bdff90dfc24567c7e","examples/multicall-hostname.rs":"56002453334cdc2ce66b36b8c192535a6d25d2b93cb27761443cb0d7a5bfc75a","examples/pacman.md":"e583ef465e55fa2fffef3b121e4a90835b8905ef3ffe2ec9ae91702832b7392d","examples/pacman.rs":"9bc96e2bda8cb05e32ce2875c7895727035e91851c51feb666438fe91a67b942","examples/repl.rs":"2596ad5550c7516b39bb1c256698c47760289ad01c71d3fb5d8994b0aeb1432e","examples/tutorial_builder/01_quick.rs":"e77b7d13339c32175f8557872905a58f7e7cb12011af406db818003d58ff8383","examples/tutorial_builder/02_app_settings.rs":"e75c4ff25944d232a81a45e51cf537bb0953365029432bb412148088478117e9","examples/tutorial_builder/02_apps.rs":"c894e5532188a69b4f70da76cbd3ba0da0f2ef99b8cc166921d33e227d2097e2","examples/tutorial_builder/02_crate.rs":"d11d8b67d5970ffd1959920340feea9eb0ef28ef785c4aedfcbc856b196d15ae","examples/tutorial_builder/03_01_flag_bool.rs":"335fde46e6671f346475992ef354a65940031cd289797c633ba1ead945a50064","examples/tutorial_builder/03_01_flag_count.rs":"2fb0c286b6736814327e89691d4dff963d67ec2e21ca1d9eb0011104e54f3fb7","examples/tutorial_builder/03_02_option.rs":"f885febca66261de1fd9bdf644eee414640b366fcd40a5636dbe6b8b86ad79ab","examples/tutorial_builder/03_03_positional.rs":"7fde3b689cec6fe461a9d846edd0771bde38ecd140578872725f4b4662ec2702","examples/tutorial_builder/03_04_subcommands.rs":"74ce4e38f817e4b93ace297141761345a7613e2c2346d57d9d3b70ac1a246764","examples/tutorial_builder/03_05_default_values.rs":"f34dd35da83306ae272082ee4f950350e72e203d47351087c7c6ae4126fc8283","examples/tutorial_builder/04_01_enum.rs":"dbf7887694141402f1ffc3d1a1903ccccbead6ce7376396b12fe98763f4fb563","examples/tutorial_builder/04_01_possible.rs":"3fb3dfc39b94552d498e968c22ea55b7c2dd855c0147993b163f5e1188ccc36c","examples/tutorial_builder/04_02_parse.rs":"e2671f487c805bf5f27de87afa25a2ee18163a2feade1712c3bbbb3870af659d","examples/tutorial_builder/04_02_validate.rs":"1d4ca583cd17db81e1e2c0bd48f344527933859d2c6415421b44af78930d7c95","examples/tutorial_builder/04_03_relations.rs":"aafb5c8c3ae034046993d2d8dcd68642106aee07f1512d670bd1dea01e2fa07c","examples/tutorial_builder/04_04_custom.rs":"a35a895764d7a119273bca536297dc8f2f6ba7600b975ebb31ad72760cf6cf04","examples/tutorial_builder/05_01_assert.rs":"9dee6679af27136d3a9ca517a21f799d9e294167c7465a2b7c265fad4c9daf9c","examples/tutorial_builder/README.md":"f055e67f6aa693bb92bbae76f4bb50ceb0e10b07ab8728af0ddbea94a6c88bee","examples/tutorial_derive/01_quick.rs":"f0ea261accd0a8ecce82ddd4cc4f93e6efd9940300edfa972459d6caca05e506","examples/tutorial_derive/02_app_settings.rs":"43290668f00675ce43f1209f800892fa664ab0d9c57b42bbc470c5843ce66811","examples/tutorial_derive/02_apps.rs":"5c71c66b2a6ad0d57c47e911fc7d9ea9163064b89ee2ece610202b133241c3e5","examples/tutorial_derive/02_crate.rs":"345f4e9a7ba384d6008c1686009b1ba05ec646252fb56efdd02d8efd0b80d851","examples/tutorial_derive/03_01_flag_bool.rs":"8fb058ef030429ff601509e2d0f67279522d8c8639b5bafee29eaa83e983b0ba","examples/tutorial_derive/03_01_flag_count.rs":"edc54dcf8631ff60438b89871d7e92f1a97077ae4293271af2c279ec53a3d0bc","examples/tutorial_derive/03_02_option.rs":"cc9487c64a71ed272ee3ec210a87fde5cded839cb64548f0bab0a3f0c44186fe","examples/tutorial_derive/03_03_positional.rs":"092d843366f4959f917bcbeef3a521c033c623ba25dc8f8f167e9cc218024d7d","examples/tutorial_derive/03_04_subcommands.rs":"1f157b1cd8c9e1661bba1083555b77bcf0de4b0398ca1775e19f53a72a737df2","examples/tutorial_derive/03_04_subcommands_alt.rs":"6a019fbdf26c0a513304e95154bfc5b1e0564c3886e2d0b0feb1ddf6032e0c31","examples/tutorial_derive/03_05_default_values.rs":"28dacd9836fe525dab37b0095c8df40d8fa0c19457c0d09ac32f0f7b8972baeb","examples/tutorial_derive/04_01_enum.rs":"12fbe926afa371c633e36324c9e18976ac8b375939f7d637c9fcfabb0833a297","examples/tutorial_derive/04_02_parse.rs":"ecd08d395fd455cb4a167c29279dabfa0be13cc19d70cd9a9fe199725a3efe83","examples/tutorial_derive/04_02_validate.rs":"e4992869879af8a69097ef4fdcd1e9a5cbf1d174dcd9dfa53d1d93fa33eba128","examples/tutorial_derive/04_03_relations.rs":"ede16700d983aa08081d29054cd946237c67951e3c7f3ee92d432a7abcb3952e","examples/tutorial_derive/04_04_custom.rs":"bc0fc3838c42b394abf6a4e73b70933f0290b3ed4151ff349bafed4a5815d52f","examples/tutorial_derive/05_01_assert.rs":"ded7bb91cd293dce6e60fcf4c9208eaac799b9d90e4f30598e5a8510427a71d7","examples/tutorial_derive/README.md":"da4929d79505f8c2f09c88d5e555cafeb39e96da407bcb3cd97102893f8f7670","examples/typed-derive.md":"cf3ee8313709937cc9c5462e3826cca8679946a658d684e8a36e66b0950100a7","examples/typed-derive.rs":"5b7b5cc6d9f6f10be55bb2df8d8d8b8ed9484c0b7d8f1054b5d8a9697ea394f2","src/bin/stdio-fixture.rs":"e8f10afbfe1857bcf986d2f55be0ca19a50c905d9f650c6e1fd42dbacf219b04","src/build/app_settings.rs":"91fe424d11e5220e2000f3a25b367a6ea9cfbd6a93a47b8c53916d0f260d90ae","src/build/arg.rs":"dd26efc3ac7f250fc130fa77d2d4a31fd3c2d37ea6c169f5404d94aab7bc9de3","src/build/arg_group.rs":"27993e229092327be9e98bbcd0a033b23eb83ff4581efd39c53111a01a6c84fb","src/build/arg_predicate.rs":"8c9fd14780cd42465f32f2e8927b97c06e686d09b5927ba3cd5bbd792af7a13d","src/build/arg_settings.rs":"1eb03498d18e02874a4c534a75b204ef4ede9e10f72f7ba73e584a002537fcd3","src/build/command.rs":"3dda34aeb37d4cf623881d16e1081824c966df6a454aa2b4d2cec6e109a4dfce","src/build/debug_asserts.rs":"2a461f4e9b6ae98442a249a8de49b2777dc01dfe16fc8369f6999885ed1af687","src/build/macros.rs":"904e42e72c49107ae324f45d5df24fbff13b9c7d4589f1a4ae38594add704434","src/build/mod.rs":"c876a1f5bfc7d7545f1019c38520342ed681c427a2d2f2ccbac5f10c3612e9ca","src/build/possible_value.rs":"9f46be1a5a6d407f1c316dab763a528d538e3e29f6bff8883c4a39f98c79d8fb","src/build/regex.rs":"b18891310186ecdcca9bb611833b7188cc80fe72e9fef27b63fa076b880a1acb","src/build/tests.rs":"995c7d6be608b94c6f392f1356eba0cb8adb9ca661ffeebf7ebe6e8ad01a868c","src/build/usage_parser.rs":"9f5c0900932ce4d0e371f28f3d1fe7a04bd705508b0476c3bb2b035a55c078c3","src/build/value_hint.rs":"0dd974d495c313430898d28b79991a70983c9aa44e88fa9aa1776d3308935438","src/derive.rs":"cec2f22de80e768b52899dbabc7e3892813d464dd23325af4c15deef8326e73f","src/error/context.rs":"1dc561c7877d7f9b90f04bb7915744f0b3cc4ac2aa928ae100b2825a97077142","src/error/kind.rs":"ea521d41a60d0a53e305d6a7555a772253757bebe2734a5d17539fe66bcc2e27","src/error/mod.rs":"f92cc86699a1bff0f8d0ea1f02b4f6073acf01ee77303729ed75a354b9f57814","src/lib.rs":"cb455acb0c34043a716d4416cc43d247300c6a8226820c0655d8632817271f5a","src/macros.rs":"d8b4f47680b573624a62b2862b48d075d23d9b7d23b1785a21430f09c94efa9c","src/mkeymap.rs":"a5faf57bce951b03b06a25d2d63d28b04bef5dc9694e80e7519cc379b5f3d537","src/output/fmt.rs":"efb741b5854500d946c6c09763ece1badc2b9cd3efa616cb58012039836c2b7f","src/output/help.rs":"20f8267c059f40ef0ecdfe7e0b5c6599a3449e2394778717780d133a0fc2b124","src/output/mod.rs":"3a61f89a4568e95114a3ab839da8f3c4c92de2a228549308f5d1be076a474917","src/output/usage.rs":"da6833909eda05553eafbef3c7f06e48e5a76d28cfa82266ddad987dc572de2b","src/parse/arg_matcher.rs":"d8c8ae95fe663c7612931309aaafcd6d40e99b44765c46c6d636dbc2899903f3","src/parse/features/mod.rs":"6ed075e97af56bff22f22ed1ee83ff6479360e05f9d3661a3145f822c242b694","src/parse/features/suggestions.rs":"f1f78c9d359c66989f904314f6a0398ee616ac69e089b3e164d375d6b5873355","src/parse/matches/arg_matches.rs":"121ce832e8b03fb1d60b986aa8caa43219d32fac93bd435b1d04c5e079eeb031","src/parse/matches/matched_arg.rs":"58658f2461e5498bcd1cc996bac916ce7aa95600bfee23786f23995a9724fa19","src/parse/matches/mod.rs":"8144a1b46739f0048a0973a180b133a1ebdbed946514acbb17c79d0ec75e607a","src/parse/matches/value_source.rs":"e6a477ae36f2f7155960239a97b7d142eef4eb227c41c9eefea147289547d713","src/parse/mod.rs":"e9c2621a1d666d75c511f98704c18ffc9ba8d821debd55877e284c98a177e47a","src/parse/parser.rs":"ded330fd39af09b0b424f192b22d76ff8b24ce4d9acbb368cc61c34c544e6df2","src/parse/validator.rs":"bc512f3b9c2b94351f8502d04bb36e26abbcf4135ef443aa9a131a101c49fd93","src/util/color.rs":"63df5e1cda1001b4c536dc0b04e09dd85dae6d317e34e660abbe3c71e17eaa29","src/util/fnv.rs":"82492d91d990f38b62de8b3c3e67f1fad55919117b1f448aa28acf6d21919fd7","src/util/graph.rs":"f35396b6e2a427377dcbbca69b1b98737d89684a3834cfda98cbf8cc70ff9c2f","src/util/id.rs":"fc498c65887385d92a51359a2f72556c2e508ee49b8cac4b3f827512795d690d","src/util/mod.rs":"702c2567286d80f9ccb4e6910cda690965f23b21a93c4cd286f37e7890e92783","src/util/str_to_bool.rs":"f9302078014c7b8f493811f2bc0bcbb982c33efde6c5ca98bea479781c1501b7"},"package":"d2dbdf4bdacb33466e854ce889eee8dfd5729abf7ccd7664d0a2d60cd384440b"} \ No newline at end of file diff --git a/third_party/rust/clap/Cargo.lock b/third_party/rust/clap/Cargo.lock index ada324eb3de6..921f081e3b2d 100644 --- a/third_party/rust/clap/Cargo.lock +++ b/third_party/rust/clap/Cargo.lock @@ -39,15 +39,15 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" -version = "0.3.63" +version = "0.3.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "321629d8ba6513061f26707241fa9bc89524ff1cd7a915a97ef0c62c666ce1b6" +checksum = "11a17d453482a265fd5f8479f2a3f405566e6ca627837aaddb85af8b1ab8ef61" dependencies = [ "addr2line", "cc", @@ -78,9 +78,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.8.0" +version = "3.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c" +checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" [[package]] name = "bytes" @@ -99,9 +99,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.72" +version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" [[package]] name = "cfg-if" @@ -111,9 +111,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "2.33.3" +version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ "bitflags", "textwrap 0.11.0", @@ -122,22 +122,25 @@ dependencies = [ [[package]] name = "clap" -version = "3.0.10" +version = "3.1.18" dependencies = [ "atty", "backtrace", "bitflags", "clap_derive", + "clap_lex", "criterion", + "humantime", "indexmap", "lazy_static", - "os_str_bytes", "regex", "rustversion", + "shlex", + "snapbox", "strsim", "termcolor", "terminal_size", - "textwrap 0.14.2", + "textwrap 0.15.0", "trybuild", "trycmd", "unicase", @@ -146,9 +149,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "3.0.6" +version = "3.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "517358c28fcef6607bf6f76108e02afad7e82297d132a6b846dcc1fc3efcd153" +checksum = "25320346e922cffe59c0bbc5410c8d8784509efb321488971081313cb1e1a33c" dependencies = [ "heck", "proc-macro-error", @@ -158,20 +161,29 @@ dependencies = [ ] [[package]] -name = "combine" -version = "4.6.2" +name = "clap_lex" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2b2f5d0ee456f3928812dfc8c6d9a1d592b98678f6d56db9b0cd2b7bc6c8db5" +checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "combine" +version = "4.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a604e93b79d1808327a6fca85a6f2d69de66461e7620f5a4cbf5fb4d1d7c948" dependencies = [ "bytes", "memchr", ] [[package]] -name = "concolor-control" -version = "0.0.7" +name = "concolor" +version = "0.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7104119c2f80d887239879d0c50e033cd40eac9a3f3561e0684ba7d5d654f4da" +checksum = "015267563b1df20adccdd00cb05257b1dfbea70a04928e9cf88ffb850c1a40af" dependencies = [ "atty", "bitflags", @@ -180,9 +192,9 @@ dependencies = [ [[package]] name = "concolor-query" -version = "0.0.4" +version = "0.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad159cc964ac8f9d407cbc0aa44b02436c054b541f2b4b5f06972e1efdc54bc7" +checksum = "d6417fe6fc03a8b533fd2177742eeb39a90c7233eedec7bac96d4d6b69a09449" [[package]] name = "criterion" @@ -192,7 +204,7 @@ checksum = "1604dafd25fba2fe2d5895a9da139f8dc9b319a5fe5354ca137cbbce4e178d10" dependencies = [ "atty", "cast", - "clap 2.33.3", + "clap 2.34.0", "criterion-plot", "csv", "itertools", @@ -222,9 +234,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.1" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" +checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53" dependencies = [ "cfg-if", "crossbeam-utils", @@ -243,10 +255,11 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.5" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" +checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c" dependencies = [ + "autocfg", "cfg-if", "crossbeam-utils", "lazy_static", @@ -256,9 +269,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" +checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" dependencies = [ "cfg-if", "lazy_static", @@ -272,7 +285,7 @@ checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" dependencies = [ "bstr", "csv-core", - "itoa", + "itoa 0.4.8", "ryu", "serde", ] @@ -286,12 +299,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "difflib" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" - [[package]] name = "either" version = "1.6.1" @@ -357,9 +364,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "humantime-serde" -version = "1.0.1" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac34a56cfd4acddb469cc7fff187ed5ac36f498ba085caf8bbc725e3ff474058" +checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" dependencies = [ "humantime", "serde", @@ -367,9 +374,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" +checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" dependencies = [ "autocfg", "hashbrown", @@ -377,9 +384,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.10.1" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" dependencies = [ "either", ] @@ -391,21 +398,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" [[package]] -name = "js-sys" -version = "0.3.55" +name = "itoa" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" -dependencies = [ - "wasm-bindgen", -] +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" [[package]] -name = "kstring" -version = "1.0.6" +name = "js-sys" +version = "0.3.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b310ccceade8121d7d77fee406160e457c2f4e7c7982d589da3499bc7ea4526" +checksum = "671a26f820db17c2a2750743f1dd03bafd15b98c9f30c7c2628c024c05d73397" dependencies = [ - "serde", + "wasm-bindgen", ] [[package]] @@ -416,9 +420,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.107" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219" +checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b" [[package]] name = "linked-hash-map" @@ -428,36 +432,35 @@ checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" [[package]] name = "log" -version = "0.4.14" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if", ] [[package]] name = "memchr" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memoffset" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" dependencies = [ "autocfg", ] [[package]] name = "miniz_oxide" -version = "0.4.4" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082" dependencies = [ "adler", - "autocfg", ] [[package]] @@ -468,18 +471,18 @@ checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" [[package]] name = "num-traits" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" -version = "1.13.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" dependencies = [ "hermit-abi", "libc", @@ -487,18 +490,18 @@ dependencies = [ [[package]] name = "object" -version = "0.27.1" +version = "0.28.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9" +checksum = "40bec70ba014595f99f7aa110b84331ffe1ee9aece7fe6f387cc7e3ecda4d456" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" +checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" [[package]] name = "oorandom" @@ -508,9 +511,9 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "os_pipe" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e3492ebca331b895fe23ed427dce2013d9b2e00c45964f12040b0db38b8ab27" +checksum = "2c92f2b54f081d635c77e7120862d48db8e91f7f21cef23ab1b4fe9971c59f55" dependencies = [ "libc", "winapi", @@ -521,9 +524,6 @@ name = "os_str_bytes" version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" -dependencies = [ - "memchr", -] [[package]] name = "plotters" @@ -579,27 +579,27 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.32" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" +checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" dependencies = [ "unicode-xid", ] [[package]] name = "quote" -version = "1.0.10" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" +checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" dependencies = [ "proc-macro2", ] [[package]] name = "rayon" -version = "1.5.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" +checksum = "fd249e82c21598a9a426a4e00dd7adc1d640b22445ec8545feef801d1a74c221" dependencies = [ "autocfg", "crossbeam-deque", @@ -609,22 +609,21 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.9.1" +version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" +checksum = "9f51245e1e62e1f1629cbfec37b5793bbabcaeb90f30e94d2ba03564687353e4" dependencies = [ "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", - "lazy_static", "num_cpus", ] [[package]] name = "regex" -version = "1.5.4" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" dependencies = [ "aho-corasick", "memchr", @@ -660,15 +659,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088" +checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f" [[package]] name = "ryu" -version = "1.0.5" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" [[package]] name = "same-file" @@ -687,15 +686,15 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "semver" -version = "1.0.4" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012" +checksum = "8cb243bdfdb5936c8dc3c45762a19d12ab4550cdc753bc247637d4ec35a040fd" [[package]] name = "serde" -version = "1.0.130" +version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" +checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" dependencies = [ "serde_derive", ] @@ -712,9 +711,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.130" +version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" +checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" dependencies = [ "proc-macro2", "quote", @@ -723,11 +722,11 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.69" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e466864e431129c7e0d3476b92f20458e5879919a0596c6472738d9fa2d342f8" +checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" dependencies = [ - "itoa", + "itoa 1.0.1", "ryu", "serde", ] @@ -738,6 +737,33 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" +[[package]] +name = "similar" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e24979f63a11545f5f2c60141afe249d4f19f84581ea2138065e400941d83d3" + +[[package]] +name = "snapbox" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "767a1d5da232b6959cd1bd5c9e8db8a7cce09c3038e89deedb49a549a2aefd93" +dependencies = [ + "concolor", + "normalize-line-endings", + "os_pipe", + "similar", + "snapbox-macros", + "wait-timeout", + "yansi", +] + +[[package]] +name = "snapbox-macros" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c01dea7e04cbb27ef4c86e9922184608185f7cd95c1763bc30d727cda4a5e930" + [[package]] name = "strsim" version = "0.10.0" @@ -746,9 +772,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.81" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966" +checksum = "7ff7c592601f11445996a06f8ad0c27f094a58857c2f89e97974ab9235b92c52" dependencies = [ "proc-macro2", "quote", @@ -757,9 +783,9 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" dependencies = [ "winapi-util", ] @@ -785,9 +811,9 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.14.2" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80" +checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" dependencies = [ "terminal_size", "unicode-width", @@ -805,35 +831,35 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.12.3" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f2d523490d6542d4ffa1f1a7cae4d0c9f2ee634bf4e779b0f5ff5fde74dd4b6" +checksum = "ba98375fd631b83696f87c64e4ed8e29e6a1f3404d6aed95fa95163bad38e705" dependencies = [ "combine", "indexmap", "itertools", - "kstring", "serde", ] [[package]] name = "trybuild" -version = "1.0.52" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "150e726dc059e6fbd4fce3288f5bb3cf70128cf63b0dde23b938a3cad810fb23" +checksum = "7fc92f558afb6d1d7c6f175eb8d615b8ef49c227543e68e19c123d4ee43d8a7d" dependencies = [ "glob", - "lazy_static", + "once_cell", "serde", + "serde_derive", "serde_json", "termcolor", "toml", @@ -841,24 +867,19 @@ dependencies = [ [[package]] name = "trycmd" -version = "0.9.0" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a03bb057469ddca02e598e6c84e771bfdc464f188803d3101f133d6643e638d" +checksum = "ffb4185126cc904642173a54c185083f410c86d1202ada6761aacf7c40829f13" dependencies = [ - "concolor-control", - "difflib", "escargot", "glob", "humantime", "humantime-serde", - "normalize-line-endings", - "os_pipe", "rayon", "serde", "shlex", + "snapbox", "toml_edit", - "wait-timeout", - "yansi", ] [[package]] @@ -878,15 +899,15 @@ checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" [[package]] name = "unicode-xid" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" [[package]] name = "version_check" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wait-timeout" @@ -910,9 +931,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.78" +version = "0.2.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" +checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -920,9 +941,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.78" +version = "0.2.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" +checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4" dependencies = [ "bumpalo", "lazy_static", @@ -935,9 +956,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.78" +version = "0.2.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" +checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -945,9 +966,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.78" +version = "0.2.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" +checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b" dependencies = [ "proc-macro2", "quote", @@ -958,15 +979,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.78" +version = "0.2.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" +checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744" [[package]] name = "web-sys" -version = "0.3.55" +version = "0.3.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" +checksum = "7b17e741662c70c8bd24ac5c5b18de314a2c26c32bf8346ee1e6f53de919c283" dependencies = [ "js-sys", "wasm-bindgen", @@ -1014,6 +1035,6 @@ dependencies = [ [[package]] name = "yansi" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc79f4a1e39857fc00c3f662cbf2651c771f00e9c15fe2abc341806bd46bd71" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" diff --git a/third_party/rust/clap/Cargo.toml b/third_party/rust/clap/Cargo.toml index bce95fe3e0f0..e1ced84be1ef 100644 --- a/third_party/rust/clap/Cargo.toml +++ b/third_party/rust/clap/Cargo.toml @@ -12,19 +12,40 @@ [package] edition = "2018" name = "clap" -version = "3.0.10" -include = ["build.rs", "src/**/*", "Cargo.toml", "LICENSE*", "README.md", "benches/**/*", "examples/**/*"] +version = "3.1.18" +include = [ + "build.rs", + "src/**/*", + "Cargo.toml", + "LICENSE*", + "README.md", + "benches/**/*", + "examples/**/*", +] description = "A simple to use, efficient, and full-featured Command Line Argument Parser" documentation = "https://docs.rs/clap/" readme = "README.md" -keywords = ["argument", "cli", "arg", "parser", "parse"] +keywords = [ + "argument", + "cli", + "arg", + "parser", + "parse", +] categories = ["command-line-interface"] license = "MIT OR Apache-2.0" repository = "https://github.com/clap-rs/clap" + [package.metadata.docs.rs] -cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples=examples"] features = ["unstable-doc"] -rustdoc-args = ["--cfg", "docsrs"] +rustdoc-args = [ + "--cfg", + "docsrs", +] +cargo-args = [ + "-Zunstable-options", + "-Zrustdoc-scrape-examples=examples", +] [package.metadata.playground] features = ["unstable-doc"] @@ -35,54 +56,60 @@ tag-name = "v{{version}}" [[package.metadata.release.pre-release-replacements]] file = "CHANGELOG.md" -min = 1 -replace = "{{version}}" search = "Unreleased" - -[[package.metadata.release.pre-release-replacements]] -exactly = 1 -file = "CHANGELOG.md" -replace = "...{{tag_name}}" -search = "\\.\\.\\.HEAD" - -[[package.metadata.release.pre-release-replacements]] -file = "CHANGELOG.md" +replace = "{{version}}" min = 1 -replace = "{{date}}" + +[[package.metadata.release.pre-release-replacements]] +file = "CHANGELOG.md" +search = '\.\.\.HEAD' +replace = "...{{tag_name}}" +exactly = 1 + +[[package.metadata.release.pre-release-replacements]] +file = "CHANGELOG.md" search = "ReleaseDate" +replace = "{{date}}" +min = 1 [[package.metadata.release.pre-release-replacements]] -exactly = 1 file = "CHANGELOG.md" -replace = "\n## [Unreleased] - ReleaseDate\n" search = "" +replace = """ + +## [Unreleased] - ReleaseDate +""" +exactly = 1 [[package.metadata.release.pre-release-replacements]] -exactly = 1 file = "CHANGELOG.md" -replace = "\n[Unreleased]: https://github.com/clap-rs/clap/compare/{{tag_name}}...HEAD" search = "" - -[[package.metadata.release.pre-release-replacements]] -exactly = 9 -file = "README.md" -prerelease = true -replace = "github.com/clap-rs/clap/blob/{{tag_name}}/" -search = "github.com/clap-rs/clap/blob/[^/]+/" - -[[package.metadata.release.pre-release-replacements]] +replace = """ + +[Unreleased]: https://github.com/clap-rs/clap/compare/{{tag_name}}...HEAD""" exactly = 1 -file = "README.md" -prerelease = true -replace = "version = \"{{version}}\"" -search = "version = \"[a-z0-9\\.-]+\"" [[package.metadata.release.pre-release-replacements]] -exactly = 4 -file = "src/derive.rs" -prerelease = true -replace = "github.com/clap-rs/clap/blob/{{tag_name}}/" +file = "README.md" search = "github.com/clap-rs/clap/blob/[^/]+/" +replace = "github.com/clap-rs/clap/blob/{{tag_name}}/" +exactly = 13 +prerelease = true + +[[package.metadata.release.pre-release-replacements]] +file = "README.md" +search = 'version = "[a-z0-9\.-]+"' +replace = "version = \"{{version}}\"" +exactly = 1 +prerelease = true + +[[package.metadata.release.pre-release-replacements]] +file = "src/derive.rs" +search = "github.com/clap-rs/clap/blob/[^/]+/" +replace = "github.com/clap-rs/clap/blob/{{tag_name}}/" +exactly = 4 +prerelease = true + [profile.bench] lto = true codegen-units = 1 @@ -118,7 +145,7 @@ name = "git-derive" required-features = ["derive"] [[example]] -name = "keyvalue-derive" +name = "typed-derive" required-features = ["derive"] [[example]] @@ -131,6 +158,11 @@ name = "hostname" path = "examples/multicall-hostname.rs" required-features = ["unstable-multicall"] +[[example]] +name = "repl" +path = "examples/repl.rs" +required-features = ["unstable-multicall"] + [[example]] name = "01_quick" path = "examples/tutorial_builder/01_quick.rs" @@ -139,7 +171,6 @@ required-features = ["cargo"] [[example]] name = "02_apps" path = "examples/tutorial_builder/02_apps.rs" -required-features = ["cargo"] [[example]] name = "02_crate" @@ -189,7 +220,15 @@ required-features = ["cargo"] [[example]] name = "04_01_enum" path = "examples/tutorial_builder/04_01_enum.rs" -required-features = ["cargo", "derive"] +required-features = [ + "cargo", + "derive", +] + +[[example]] +name = "04_02_parse" +path = "examples/tutorial_builder/04_02_parse.rs" +required-features = ["cargo"] [[example]] name = "04_02_validate" @@ -257,6 +296,11 @@ name = "03_04_subcommands_derive" path = "examples/tutorial_derive/03_04_subcommands.rs" required-features = ["derive"] +[[example]] +name = "03_04_subcommands_alt_derive" +path = "examples/tutorial_derive/03_04_subcommands_alt.rs" +required-features = ["derive"] + [[example]] name = "03_05_default_values_derive" path = "examples/tutorial_derive/03_05_default_values.rs" @@ -267,6 +311,11 @@ name = "04_01_enum_derive" path = "examples/tutorial_derive/04_01_enum.rs" required-features = ["derive"] +[[example]] +name = "04_02_parse_derive" +path = "examples/tutorial_derive/04_02_parse.rs" +required-features = ["derive"] + [[example]] name = "04_02_validate_derive" path = "examples/tutorial_derive/04_02_validate.rs" @@ -293,6 +342,26 @@ name = "custom-bool" path = "examples/derive_ref/custom-bool.rs" required-features = ["derive"] +[[example]] +name = "interop_augment_args" +path = "examples/derive_ref/augment_args.rs" +required-features = ["derive"] + +[[example]] +name = "interop_augment_subcommands" +path = "examples/derive_ref/augment_subcommands.rs" +required-features = ["derive"] + +[[example]] +name = "interop_hand_subcommand" +path = "examples/derive_ref/hand_subcommand.rs" +required-features = ["derive"] + +[[example]] +name = "interop_flatten_hand_args" +path = "examples/derive_ref/flatten_hand_args.rs" +required-features = ["derive"] + [[bench]] name = "01_default" path = "benches/01_default.rs" @@ -322,6 +391,7 @@ harness = false name = "06_rustup" path = "benches/06_rustup.rs" harness = false + [dependencies.atty] version = "0.2" optional = true @@ -334,9 +404,12 @@ optional = true version = "1.2" [dependencies.clap_derive] -version = "3.0.0" +version = "=3.1.18" optional = true +[dependencies.clap_lex] +version = "0.2.0" + [dependencies.indexmap] version = "1.0" @@ -344,9 +417,6 @@ version = "1.0" version = "1" optional = true -[dependencies.os_str_bytes] -version = "6.0" - [dependencies.regex] version = "1.0" optional = true @@ -364,7 +434,7 @@ version = "0.1.12" optional = true [dependencies.textwrap] -version = "0.14.0" +version = "0.15.0" features = [] default-features = false @@ -375,9 +445,13 @@ optional = true [dependencies.yaml-rust] version = "0.4.1" optional = true + [dev-dependencies.criterion] version = "0.3.2" +[dev-dependencies.humantime] +version = "2" + [dev-dependencies.lazy_static] version = "1" @@ -387,27 +461,68 @@ version = "1.0" [dev-dependencies.rustversion] version = "1" +[dev-dependencies.shlex] +version = "1.1.0" + +[dev-dependencies.snapbox] +version = "0.2.9" + [dev-dependencies.trybuild] version = "1.0.18" [dev-dependencies.trycmd] -version = "0.9" -features = ["color-auto", "diff", "examples"] +version = "0.13" +features = [ + "color-auto", + "diff", + "examples", +] default-features = false [features] cargo = ["lazy_static"] -color = ["atty", "termcolor"] -debug = ["clap_derive/debug", "backtrace"] -default = ["std", "color", "suggestions"] -derive = ["clap_derive", "lazy_static"] +color = [ + "atty", + "termcolor", +] +debug = [ + "clap_derive/debug", + "backtrace", +] +default = [ + "std", + "color", + "suggestions", +] +derive = [ + "clap_derive", + "lazy_static", +] env = [] std = ["indexmap/std"] suggestions = ["strsim"] -unicode = ["textwrap/unicode-width", "unicase"] -unstable-doc = ["derive", "cargo", "wrap_help", "yaml", "env", "unicode", "regex", "unstable-replace", "unstable-multicall", "unstable-grouped"] +unicode = [ + "textwrap/unicode-width", + "unicase", +] +unstable-doc = [ + "derive", + "cargo", + "wrap_help", + "yaml", + "env", + "unicode", + "regex", + "unstable-replace", + "unstable-multicall", + "unstable-grouped", +] unstable-grouped = [] unstable-multicall = [] unstable-replace = [] -wrap_help = ["terminal_size", "textwrap/terminal_size"] +unstable-v4 = ["clap_derive/unstable-v4"] +wrap_help = [ + "terminal_size", + "textwrap/terminal_size", +] yaml = ["yaml-rust"] diff --git a/third_party/rust/clap/README.md b/third_party/rust/clap/README.md index 808568427931..ee6d9f3d8c6c 100644 --- a/third_party/rust/clap/README.md +++ b/third_party/rust/clap/README.md @@ -5,8 +5,8 @@ [![Crates.io](https://img.shields.io/crates/v/clap?style=flat-square)](https://crates.io/crates/clap) [![Crates.io](https://img.shields.io/crates/d/clap?style=flat-square)](https://crates.io/crates/clap) -[![License](https://img.shields.io/badge/license-Apache%202.0-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/v3.0.10/LICENSE-APACHE) -[![License](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/v3.0.10/LICENSE-MIT) +[![License](https://img.shields.io/badge/license-Apache%202.0-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/v3.1.18/LICENSE-APACHE) +[![License](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/v3.1.18/LICENSE-MIT) [![Build Status](https://img.shields.io/github/workflow/status/clap-rs/clap/CI/staging?style=flat-square)](https://github.com/clap-rs/clap/actions/workflows/ci.yml?query=branch%3Astaging) [![Coverage Status](https://img.shields.io/coveralls/github/clap-rs/clap/master?style=flat-square)](https://coveralls.io/github/clap-rs/clap?branch=master) [![Contributors](https://img.shields.io/github/contributors/clap-rs/clap?style=flat-square)](https://github.com/clap-rs/clap/graphs/contributors) @@ -14,15 +14,15 @@ Dual-licensed under [Apache 2.0](LICENSE-APACHE) or [MIT](LICENSE-MIT). 1. [About](#about) -2. Tutorial: [Builder API](https://github.com/clap-rs/clap/blob/v3.0.10/examples/tutorial_builder/README.md), [Derive API](https://github.com/clap-rs/clap/blob/v3.0.10/examples/tutorial_derive/README.md) -3. [Examples](https://github.com/clap-rs/clap/blob/v3.0.10/examples/README.md) +2. Tutorial: [Builder API](https://github.com/clap-rs/clap/blob/v3.1.18/examples/tutorial_builder/README.md), [Derive API](https://github.com/clap-rs/clap/blob/v3.1.18/examples/tutorial_derive/README.md) +3. [Examples](https://github.com/clap-rs/clap/blob/v3.1.18/examples/README.md) 4. [API Reference](https://docs.rs/clap) - - [Derive Reference](https://github.com/clap-rs/clap/blob/v3.0.10/examples/derive_ref/README.md) + - [Derive Reference](https://github.com/clap-rs/clap/blob/v3.1.18/examples/derive_ref/README.md) - [Feature Flags](#feature-flags) -5. [CHANGELOG](https://github.com/clap-rs/clap/blob/v3.0.10/CHANGELOG.md) -6. [FAQ](https://github.com/clap-rs/clap/blob/v3.0.10/docs/FAQ.md) +5. [CHANGELOG](https://github.com/clap-rs/clap/blob/v3.1.18/CHANGELOG.md) +6. [FAQ](https://github.com/clap-rs/clap/blob/v3.1.18/docs/FAQ.md) 7. [Questions & Discussions](https://github.com/clap-rs/clap/discussions) -8. [Contributing](https://github.com/clap-rs/clap/blob/v3.0.10/CONTRIBUTING.md) +8. [Contributing](https://github.com/clap-rs/clap/blob/v3.1.18/CONTRIBUTING.md) 8. [Sponsors](#sponsors) ## About @@ -31,6 +31,10 @@ Create your command-line parser, with all of the bells and whistles, declarative ### Example +This uses our +[Derive API](https://github.com/clap-rs/clap/blob/v3.1.18/examples/tutorial_derive/README.md) +which provides access to the [Builder API](https://github.com/clap-rs/clap/blob/v3.1.18/examples/tutorial_builder/README.md) as attributes on a `struct`: + ```rust,no_run use clap::Parser; @@ -59,13 +63,12 @@ fn main() { Add this to `Cargo.toml`: ```toml [dependencies] -clap = { version = "3.0.10", features = ["derive"] } +clap = { version = "3.1.18", features = ["derive"] } ``` ```bash $ demo --help clap [..] - -A simple to use, efficient, and full-featured Command Line Argument Parser +Simple program to greet a person USAGE: demo[EXE] [OPTIONS] --name @@ -98,10 +101,27 @@ get. Check out the [argparse-benchmarks](https://github.com/rust-cli/argparse-benchmarks-rs) for CLI parsers optimized for other use cases. +### Selecting an API + +Why use the declarative [Derive API](https://github.com/clap-rs/clap/blob/v3.1.18/examples/tutorial_derive/README.md): +- Easier to read, write, and modify +- Easier to keep the argument declaration and reading of argument in sync +- Easier to reuse, e.g. [clap-verbosity-flag](https://crates.io/crates/clap-verbosity-flag) + +Why use the procedural [Builder API](https://github.com/clap-rs/clap/blob/v3.1.18/examples/tutorial_builder/README.md): +- Faster compile times if you aren't already using other procedural macros +- More flexible, e.g. you can look up how many times an argument showed up, + what its values were, and what were the indexes of those values. The Derive + API can only report presence, number of occurrences, or values but no indices + or combinations of data. + ### Related Projects - [wild](https://crates.io/crates/wild) for supporting wildcards (`*`) on Windows like you do Linux - [argfile](https://crates.io/crates/argfile) for loading additional arguments from a file (aka response files) +- [shadow-rs](https://crates.io/crates/shadow-rs) for generating `Command::long_version` +- [clap_lex](https://crates.io/crates/clap_lex) for a lighter-weight, battle-tested CLI parser +- [clap_mangen](https://crates.io/crates/clap_mangen) for generating man page source (roff) - [clap_complete](https://crates.io/crates/clap_complete) for shell completion support - [clap-verbosity-flag](https://crates.io/crates/clap-verbosity-flag) - [clap-cargo](https://crates.io/crates/clap-cargo) @@ -131,9 +151,10 @@ CLI parsers optimized for other use cases. **Warning:** These may contain breaking changes between minor releases. -* **unstable-replace**: Enable [`App::replace`](https://github.com/clap-rs/clap/issues/2836) -* **unstable-multicall**: Enable [`AppSettings::Multicall`](https://github.com/clap-rs/clap/issues/2861) +* **unstable-replace**: Enable [`Command::replace`](https://github.com/clap-rs/clap/issues/2836) +* **unstable-multicall**: Enable [`Command::multicall`](https://github.com/clap-rs/clap/issues/2861) * **unstable-grouped**: Enable [`ArgMatches::grouped_values_of`](https://github.com/clap-rs/clap/issues/2924) +* **unstable-v4**: Preview features which will be stable on the v4.0 release ## Sponsors diff --git a/third_party/rust/clap/benches/01_default.rs b/third_party/rust/clap/benches/01_default.rs index 07e3ad218f80..620b32b818fb 100644 --- a/third_party/rust/clap/benches/01_default.rs +++ b/third_party/rust/clap/benches/01_default.rs @@ -1,13 +1,13 @@ -use clap::App; +use clap::Command; use criterion::{criterion_group, criterion_main, Criterion}; pub fn build_empty(c: &mut Criterion) { - c.bench_function("build_empty", |b| b.iter(|| App::new("claptests"))); + c.bench_function("build_empty", |b| b.iter(|| Command::new("claptests"))); } pub fn parse_empty(c: &mut Criterion) { c.bench_function("parse_empty", |b| { - b.iter(|| App::new("claptests").get_matches_from(vec![""])) + b.iter(|| Command::new("claptests").get_matches_from(vec![""])) }); } diff --git a/third_party/rust/clap/benches/02_simple.rs b/third_party/rust/clap/benches/02_simple.rs index 46a725bbb5ef..667d02f873c8 100644 --- a/third_party/rust/clap/benches/02_simple.rs +++ b/third_party/rust/clap/benches/02_simple.rs @@ -1,9 +1,9 @@ -use clap::{arg, App, Arg}; +use clap::{arg, Arg, Command}; use criterion::{criterion_group, criterion_main, Criterion}; macro_rules! create_app { () => {{ - App::new("claptests") + Command::new("claptests") .version("0.1") .about("tests clap library") .author("Kevin K. ") @@ -19,7 +19,7 @@ pub fn build_simple(c: &mut Criterion) { pub fn build_with_flag(c: &mut Criterion) { c.bench_function("build_with_flag", |b| { - b.iter(|| App::new("claptests").arg(arg!(-s --some "something"))) + b.iter(|| Command::new("claptests").arg(arg!(-s --some "something"))) }); } @@ -27,14 +27,14 @@ pub fn build_with_flag_ref(c: &mut Criterion) { c.bench_function("build_with_flag_ref", |b| { b.iter(|| { let arg = arg!(-s --some "something"); - App::new("claptests").arg(&arg) + Command::new("claptests").arg(&arg) }) }); } pub fn build_with_opt(c: &mut Criterion) { c.bench_function("build_with_opt", |b| { - b.iter(|| App::new("claptests").arg(arg!(-s --some "something"))) + b.iter(|| Command::new("claptests").arg(arg!(-s --some "something"))) }); } @@ -42,14 +42,14 @@ pub fn build_with_opt_ref(c: &mut Criterion) { c.bench_function("build_with_opt_ref", |b| { b.iter(|| { let arg = arg!(-s --some "something"); - App::new("claptests").arg(&arg) + Command::new("claptests").arg(&arg) }) }); } pub fn build_with_pos(c: &mut Criterion) { c.bench_function("build_with_pos", |b| { - b.iter(|| App::new("claptests").arg(Arg::new("some"))) + b.iter(|| Command::new("claptests").arg(Arg::new("some"))) }); } @@ -57,7 +57,7 @@ pub fn build_with_pos_ref(c: &mut Criterion) { c.bench_function("build_with_pos_ref", |b| { b.iter(|| { let arg = Arg::new("some"); - App::new("claptests").arg(&arg) + Command::new("claptests").arg(&arg) }) }); } diff --git a/third_party/rust/clap/benches/03_complex.rs b/third_party/rust/clap/benches/03_complex.rs index 377d60ccc21a..3ac81bb15dc7 100644 --- a/third_party/rust/clap/benches/03_complex.rs +++ b/third_party/rust/clap/benches/03_complex.rs @@ -1,4 +1,4 @@ -use clap::{arg, App, AppSettings, Arg}; +use clap::{arg, Arg, Command}; use criterion::{criterion_group, criterion_main, Criterion}; static OPT3_VALS: [&str; 2] = ["fast", "slow"]; @@ -6,7 +6,7 @@ static POS3_VALS: [&str; 2] = ["vi", "emacs"]; macro_rules! create_app { () => {{ - App::new("claptests") + Command::new("claptests") .version("0.1") .about("tests clap library") .author("Kevin K. ") @@ -35,7 +35,7 @@ macro_rules! create_app { arg!(--maxvals3 ... "Tests 3 max vals").max_values(3).multiple_values(true).required(false), ]) .subcommand( - App::new("subcmd") + Command::new("subcmd") .about("tests subcommands") .version("0.1") .author("Kevin K. ") @@ -48,7 +48,7 @@ macro_rules! create_app { pub fn build_from_builder(c: &mut Criterion) { c.bench_function("build_from_builder", |b| { b.iter(|| { - App::new("claptests") + Command::new("claptests") .version("0.1") .about("tests clap library") .author("Kevin K. ") @@ -141,7 +141,7 @@ pub fn build_from_builder(c: &mut Criterion) { .max_values(3), ) .subcommand( - App::new("subcmd") + Command::new("subcmd") .about("tests subcommands") .version("0.1") .author("Kevin K. ") @@ -257,7 +257,7 @@ pub fn parse_args_negate_scs(c: &mut Criterion) { c.bench_function("parse_args_negate_scs", |b| { b.iter(|| { create_app!() - .setting(AppSettings::ArgsNegateSubcommands) + .args_conflicts_with_subcommands(true) .get_matches_from(vec![ "myprog", "arg1", diff --git a/third_party/rust/clap/benches/04_new_help.rs b/third_party/rust/clap/benches/04_new_help.rs index 7a8717304a3f..83b14fb70681 100644 --- a/third_party/rust/clap/benches/04_new_help.rs +++ b/third_party/rust/clap/benches/04_new_help.rs @@ -1,17 +1,17 @@ -use clap::App; +use clap::Command; use clap::{arg, Arg}; use criterion::{criterion_group, criterion_main, Criterion}; use std::io::Cursor; -fn build_help(app: &mut App) -> String { +fn build_help(cmd: &mut Command) -> String { let mut buf = Cursor::new(Vec::with_capacity(50)); - app.write_help(&mut buf).unwrap(); + cmd.write_help(&mut buf).unwrap(); let content = buf.into_inner(); String::from_utf8(content).unwrap() } -fn app_example1<'c>() -> App<'c> { - App::new("MyApp") +fn app_example1<'c>() -> Command<'c> { + Command::new("MyApp") .version("1.0") .author("Kevin K. ") .about("Does awesome things") @@ -24,21 +24,21 @@ fn app_example1<'c>() -> App<'c> { .arg(arg!( "Sets an optional output file")) .arg(arg!(d: -d ... "Turn debugging information on")) .subcommand( - App::new("test") + Command::new("test") .about("does testing things") .arg(arg!(-l --list "lists test values")), ) } -fn app_example2<'c>() -> App<'c> { - App::new("MyApp") +fn app_example2<'c>() -> Command<'c> { + Command::new("MyApp") .version("1.0") .author("Kevin K. ") .about("Does awesome things") } -fn app_example3<'c>() -> App<'c> { - App::new("MyApp") +fn app_example3<'c>() -> Command<'c> { + Command::new("MyApp") .arg( Arg::new("debug") .help("turn on debugging information") @@ -64,8 +64,8 @@ fn app_example3<'c>() -> App<'c> { ) } -fn app_example4<'c>() -> App<'c> { - App::new("MyApp") +fn app_example4<'c>() -> Command<'c> { + Command::new("MyApp") .about("Parses an input file to do awesome things") .version("1.0") .author("Kevin K. ") @@ -89,8 +89,8 @@ fn app_example4<'c>() -> App<'c> { ) } -fn app_example5<'c>() -> App<'c> { - App::new("MyApp").arg( +fn app_example5<'c>() -> Command<'c> { + Command::new("MyApp").arg( Arg::new("awesome") .help("turns up the awesome") .short('a') @@ -99,8 +99,8 @@ fn app_example5<'c>() -> App<'c> { ) } -fn app_example6<'c>() -> App<'c> { - App::new("MyApp") +fn app_example6<'c>() -> Command<'c> { + Command::new("MyApp") .arg( Arg::new("input") .help("the input file to use") @@ -111,8 +111,8 @@ fn app_example6<'c>() -> App<'c> { .arg(Arg::new("config").help("the config file to use").index(2)) } -fn app_example7<'c>() -> App<'c> { - App::new("MyApp") +fn app_example7<'c>() -> Command<'c> { + Command::new("MyApp") .arg(Arg::new("config")) .arg(Arg::new("output")) .arg( @@ -129,8 +129,8 @@ fn app_example7<'c>() -> App<'c> { ) } -fn app_example8<'c>() -> App<'c> { - App::new("MyApp") +fn app_example8<'c>() -> Command<'c> { + Command::new("MyApp") .arg(Arg::new("config")) .arg(Arg::new("output")) .arg( @@ -147,8 +147,8 @@ fn app_example8<'c>() -> App<'c> { ) } -fn app_example10<'c>() -> App<'c> { - App::new("myapp").about("does awesome things").arg( +fn app_example10<'c>() -> Command<'c> { + Command::new("myapp").about("does awesome things").arg( Arg::new("CONFIG") .help("The config file to use (default is \"config.json\")") .short('c') @@ -157,53 +157,53 @@ fn app_example10<'c>() -> App<'c> { } pub fn example1(c: &mut Criterion) { - let mut app = app_example1(); - c.bench_function("example1", |b| b.iter(|| build_help(&mut app))); + let mut cmd = app_example1(); + c.bench_function("example1", |b| b.iter(|| build_help(&mut cmd))); } pub fn example2(c: &mut Criterion) { - let mut app = app_example2(); - c.bench_function("example2", |b| b.iter(|| build_help(&mut app))); + let mut cmd = app_example2(); + c.bench_function("example2", |b| b.iter(|| build_help(&mut cmd))); } pub fn example3(c: &mut Criterion) { - let mut app = app_example3(); - c.bench_function("example3", |b| b.iter(|| build_help(&mut app))); + let mut cmd = app_example3(); + c.bench_function("example3", |b| b.iter(|| build_help(&mut cmd))); } pub fn example4(c: &mut Criterion) { - let mut app = app_example4(); - c.bench_function("example4", |b| b.iter(|| build_help(&mut app))); + let mut cmd = app_example4(); + c.bench_function("example4", |b| b.iter(|| build_help(&mut cmd))); } pub fn example5(c: &mut Criterion) { - let mut app = app_example5(); - c.bench_function("example5", |b| b.iter(|| build_help(&mut app))); + let mut cmd = app_example5(); + c.bench_function("example5", |b| b.iter(|| build_help(&mut cmd))); } pub fn example6(c: &mut Criterion) { - let mut app = app_example6(); - c.bench_function("example6", |b| b.iter(|| build_help(&mut app))); + let mut cmd = app_example6(); + c.bench_function("example6", |b| b.iter(|| build_help(&mut cmd))); } pub fn example7(c: &mut Criterion) { - let mut app = app_example7(); - c.bench_function("example7", |b| b.iter(|| build_help(&mut app))); + let mut cmd = app_example7(); + c.bench_function("example7", |b| b.iter(|| build_help(&mut cmd))); } pub fn example8(c: &mut Criterion) { - let mut app = app_example8(); - c.bench_function("example8", |b| b.iter(|| build_help(&mut app))); + let mut cmd = app_example8(); + c.bench_function("example8", |b| b.iter(|| build_help(&mut cmd))); } pub fn example10(c: &mut Criterion) { - let mut app = app_example10(); - c.bench_function("example10", |b| b.iter(|| build_help(&mut app))); + let mut cmd = app_example10(); + c.bench_function("example10", |b| b.iter(|| build_help(&mut cmd))); } pub fn example4_template(c: &mut Criterion) { - let mut app = app_example4().help_template("{bin} {version}\n{author}\n{about}\n\nUSAGE:\n {usage}\n\nOPTIONS:\n{options}\n\nARGS:\n{args}\n"); - c.bench_function("example4_template", |b| b.iter(|| build_help(&mut app))); + let mut cmd = app_example4().help_template("{bin} {version}\n{author}\n{about}\n\nUSAGE:\n {usage}\n\nOPTIONS:\n{options}\n\nARGS:\n{args}\n"); + c.bench_function("example4_template", |b| b.iter(|| build_help(&mut cmd))); } criterion_group!( diff --git a/third_party/rust/clap/benches/05_ripgrep.rs b/third_party/rust/clap/benches/05_ripgrep.rs index 3a7e4de6b4ed..7d5cab696495 100644 --- a/third_party/rust/clap/benches/05_ripgrep.rs +++ b/third_party/rust/clap/benches/05_ripgrep.rs @@ -3,7 +3,7 @@ // // CLI used is adapted from ripgrep 48a8a3a691220f9e5b2b08f4051abe8655ea7e8a -use clap::{App, Arg}; +use clap::{Arg, Command}; use criterion::{criterion_group, criterion_main, Criterion}; use std::collections::HashMap; use std::io::Cursor; @@ -19,13 +19,13 @@ pub fn build_rg_with_long_help(c: &mut Criterion) { } pub fn write_rg_short_help(c: &mut Criterion) { - let mut app = app_short(); - c.bench_function("write_rg_short_help", |b| b.iter(|| build_help(&mut app))); + let mut cmd = app_short(); + c.bench_function("write_rg_short_help", |b| b.iter(|| build_help(&mut cmd))); } pub fn write_rg_long_help(c: &mut Criterion) { - let mut app = app_long(); - c.bench_function("write_rg_long_help", |b| b.iter(|| build_help(&mut app))); + let mut cmd = app_long(); + c.bench_function("write_rg_long_help", |b| b.iter(|| build_help(&mut cmd))); } pub fn parse_rg(c: &mut Criterion) { @@ -270,19 +270,19 @@ OPTIONS: {options}"; /// Build a clap application with short help strings. -fn app_short() -> App<'static> { - app(false, |k| USAGES[k].short) +fn app_short() -> Command<'static> { + cmd(false, |k| USAGES[k].short) } /// Build a clap application with long help strings. -fn app_long() -> App<'static> { - app(true, |k| USAGES[k].long) +fn app_long() -> Command<'static> { + cmd(true, |k| USAGES[k].long) } /// Build the help text of an application. -fn build_help(app: &mut App) -> String { +fn build_help(cmd: &mut Command) -> String { let mut buf = Cursor::new(Vec::with_capacity(50)); - app.write_help(&mut buf).unwrap(); + cmd.write_help(&mut buf).unwrap(); let content = buf.into_inner(); String::from_utf8(content).unwrap() } @@ -290,18 +290,18 @@ fn build_help(app: &mut App) -> String { /// Build a clap application parameterized by usage strings. /// /// The function given should take a clap argument name and return a help -/// string. `app` will panic if a usage string is not defined. +/// string. `cmd` will panic if a usage string is not defined. /// /// This is an intentionally stand-alone module so that it can be used easily /// in a `build.rs` script to build shell completion files. -fn app(_next_line_help: bool, doc: F) -> App<'static> +fn cmd(_next_line_help: bool, doc: F) -> Command<'static> where F: Fn(&'static str) -> &'static str, { let arg = |name| Arg::new(name).help(doc(name)); let flag = |name| arg(name).long(name); - App::new("ripgrep") + Command::new("ripgrep") .author("BurntSushi") // simulating since it's only a bench .version("0.4.0") // Simulating .about(ABOUT) diff --git a/third_party/rust/clap/benches/06_rustup.rs b/third_party/rust/clap/benches/06_rustup.rs index 0f23f16d271a..8d9c406e40df 100644 --- a/third_party/rust/clap/benches/06_rustup.rs +++ b/third_party/rust/clap/benches/06_rustup.rs @@ -2,7 +2,7 @@ // // CLI used is from rustup 408ed84f0e50511ed44a405dd91365e5da588790 -use clap::{App, AppSettings, Arg, ArgGroup}; +use clap::{AppSettings, Arg, ArgGroup, Command}; use criterion::{criterion_group, criterion_main, Criterion}; pub fn build_rustup(c: &mut Criterion) { @@ -21,8 +21,8 @@ pub fn parse_rustup_with_sc(c: &mut Criterion) { }); } -fn build_cli() -> App<'static> { - App::new("rustup") +fn build_cli() -> Command<'static> { + Command::new("rustup") .version("0.9.0") // Simulating .about("The Rust toolchain installer") .after_help(RUSTUP_HELP) @@ -35,19 +35,19 @@ fn build_cli() -> App<'static> { .long("verbose"), ) .subcommand( - App::new("show") + Command::new("show") .about("Show the active and installed toolchains") .after_help(SHOW_HELP), ) .subcommand( - App::new("install") + Command::new("install") .about("Update Rust toolchains") .after_help(TOOLCHAIN_INSTALL_HELP) - .setting(AppSettings::Hidden) // synonym for 'toolchain install' + .hide(true) // synonym for 'toolchain install' .arg(Arg::new("toolchain").required(true)), ) .subcommand( - App::new("update") + Command::new("update") .about("Update Rust toolchains") .after_help(UPDATE_HELP) .arg(Arg::new("toolchain").required(true)) @@ -59,104 +59,104 @@ fn build_cli() -> App<'static> { ), ) .subcommand( - App::new("default") + Command::new("default") .about("Set the default toolchain") .after_help(DEFAULT_HELP) .arg(Arg::new("toolchain").required(true)), ) .subcommand( - App::new("toolchain") + Command::new("toolchain") .about("Modify or query the installed toolchains") .after_help(TOOLCHAIN_HELP) .setting(AppSettings::DeriveDisplayOrder) // .setting(AppSettings::SubcommandRequiredElseHelp) - .subcommand(App::new("list").about("List installed toolchains")) + .subcommand(Command::new("list").about("List installed toolchains")) .subcommand( - App::new("install") + Command::new("install") .about("Install or update a given toolchain") .arg(Arg::new("toolchain").required(true)), ) .subcommand( - App::new("uninstall") + Command::new("uninstall") .about("Uninstall a toolchain") .arg(Arg::new("toolchain").required(true)), ) .subcommand( - App::new("link") + Command::new("link") .about("Create a custom toolchain by symlinking to a directory") .arg(Arg::new("toolchain").required(true)) .arg(Arg::new("path").required(true)), ) .subcommand( - App::new("update") - .setting(AppSettings::Hidden) // synonym for 'install' + Command::new("update") + .hide(true) // synonym for 'install' .arg(Arg::new("toolchain").required(true)), ) .subcommand( - App::new("add") - .setting(AppSettings::Hidden) // synonym for 'install' + Command::new("add") + .hide(true) // synonym for 'install' .arg(Arg::new("toolchain").required(true)), ) .subcommand( - App::new("remove") - .setting(AppSettings::Hidden) // synonym for 'uninstall' + Command::new("remove") + .hide(true) // synonym for 'uninstall' .arg(Arg::new("toolchain").required(true)), ), ) .subcommand( - App::new("target") + Command::new("target") .about("Modify a toolchain's supported targets") .setting(AppSettings::DeriveDisplayOrder) // .setting(AppSettings::SubcommandRequiredElseHelp) .subcommand( - App::new("list") + Command::new("list") .about("List installed and available targets") .arg(Arg::new("toolchain").long("toolchain").takes_value(true)), ) .subcommand( - App::new("add") + Command::new("add") .about("Add a target to a Rust toolchain") .arg(Arg::new("target").required(true)) .arg(Arg::new("toolchain").long("toolchain").takes_value(true)), ) .subcommand( - App::new("remove") + Command::new("remove") .about("Remove a target from a Rust toolchain") .arg(Arg::new("target").required(true)) .arg(Arg::new("toolchain").long("toolchain").takes_value(true)), ) .subcommand( - App::new("install") - .setting(AppSettings::Hidden) // synonym for 'add' + Command::new("install") + .hide(true) // synonym for 'add' .arg(Arg::new("target").required(true)) .arg(Arg::new("toolchain").long("toolchain").takes_value(true)), ) .subcommand( - App::new("uninstall") - .setting(AppSettings::Hidden) // synonym for 'remove' + Command::new("uninstall") + .hide(true) // synonym for 'remove' .arg(Arg::new("target").required(true)) .arg(Arg::new("toolchain").long("toolchain").takes_value(true)), ), ) .subcommand( - App::new("component") + Command::new("component") .about("Modify a toolchain's installed components") .setting(AppSettings::DeriveDisplayOrder) // .setting(AppSettings::SubcommandRequiredElseHelp) .subcommand( - App::new("list") + Command::new("list") .about("List installed and available components") .arg(Arg::new("toolchain").long("toolchain").takes_value(true)), ) .subcommand( - App::new("add") + Command::new("add") .about("Add a component to a Rust toolchain") .arg(Arg::new("component").required(true)) .arg(Arg::new("toolchain").long("toolchain").takes_value(true)) .arg(Arg::new("target").long("target").takes_value(true)), ) .subcommand( - App::new("remove") + Command::new("remove") .about("Remove a component from a Rust toolchain") .arg(Arg::new("component").required(true)) .arg(Arg::new("toolchain").long("toolchain").takes_value(true)) @@ -164,19 +164,19 @@ fn build_cli() -> App<'static> { ), ) .subcommand( - App::new("override") + Command::new("override") .about("Modify directory toolchain overrides") .after_help(OVERRIDE_HELP) .setting(AppSettings::DeriveDisplayOrder) // .setting(AppSettings::SubcommandRequiredElseHelp) - .subcommand(App::new("list").about("List directory toolchain overrides")) + .subcommand(Command::new("list").about("List directory toolchain overrides")) .subcommand( - App::new("set") + Command::new("set") .about("Set the override toolchain for a directory") .arg(Arg::new("toolchain").required(true)), ) .subcommand( - App::new("unset") + Command::new("unset") .about("Remove the override toolchain for a directory") .after_help(OVERRIDE_UNSET_HELP) .arg( @@ -192,13 +192,13 @@ fn build_cli() -> App<'static> { ), ) .subcommand( - App::new("add") - .setting(AppSettings::Hidden) // synonym for 'set' + Command::new("add") + .hide(true) // synonym for 'set' .arg(Arg::new("toolchain").required(true)), ) .subcommand( - App::new("remove") - .setting(AppSettings::Hidden) // synonym for 'unset' + Command::new("remove") + .hide(true) // synonym for 'unset' .about("Remove the override toolchain for a directory") .arg(Arg::new("path").long("path").takes_value(true)) .arg( @@ -209,10 +209,10 @@ fn build_cli() -> App<'static> { ), ) .subcommand( - App::new("run") + Command::new("run") .about("Run a command with an environment configured for a given toolchain") .after_help(RUN_HELP) - .setting(AppSettings::TrailingVarArg) + .trailing_var_arg(true) .arg(Arg::new("toolchain").required(true)) .arg( Arg::new("command") @@ -223,12 +223,12 @@ fn build_cli() -> App<'static> { ), ) .subcommand( - App::new("which") + Command::new("which") .about("Display which binary will be run for a given command") .arg(Arg::new("command").required(true)), ) .subcommand( - App::new("doc") + Command::new("doc") .about("Open the documentation for the current toolchain") .after_help(DOC_HELP) .arg( @@ -244,38 +244,42 @@ fn build_cli() -> App<'static> { .group(ArgGroup::new("page").args(&["book", "std"])), ) .subcommand( - App::new("man") + Command::new("man") .about("View the man page for a given command") .arg(Arg::new("command").required(true)) .arg(Arg::new("toolchain").long("toolchain").takes_value(true)), ) .subcommand( - App::new("self") + Command::new("self") .about("Modify the rustup installation") .setting(AppSettings::DeriveDisplayOrder) - .subcommand(App::new("update").about("Download and install updates to rustup")) + .subcommand(Command::new("update").about("Download and install updates to rustup")) .subcommand( - App::new("uninstall") + Command::new("uninstall") .about("Uninstall rustup.") .arg(Arg::new("no-prompt").short('y')), ) - .subcommand(App::new("upgrade-data").about("Upgrade the internal data format.")), + .subcommand( + Command::new("upgrade-data").about("Upgrade the internal data format."), + ), ) .subcommand( - App::new("telemetry") + Command::new("telemetry") .about("rustup telemetry commands") - .setting(AppSettings::Hidden) + .hide(true) .setting(AppSettings::DeriveDisplayOrder) - .subcommand(App::new("enable").about("Enable rustup telemetry")) - .subcommand(App::new("disable").about("Disable rustup telemetry")) - .subcommand(App::new("analyze").about("Analyze stored telemetry")), + .subcommand(Command::new("enable").about("Enable rustup telemetry")) + .subcommand(Command::new("disable").about("Disable rustup telemetry")) + .subcommand(Command::new("analyze").about("Analyze stored telemetry")), ) .subcommand( - App::new("set").about("Alter rustup settings").subcommand( - App::new("default-host") - .about("The triple used to identify toolchains when not specified") - .arg(Arg::new("host_triple").required(true)), - ), + Command::new("set") + .about("Alter rustup settings") + .subcommand( + Command::new("default-host") + .about("The triple used to identify toolchains when not specified") + .arg(Arg::new("host_triple").required(true)), + ), ) } diff --git a/third_party/rust/clap/examples/README.md b/third_party/rust/clap/examples/README.md index 946822ddb08a..42ba88991a50 100644 --- a/third_party/rust/clap/examples/README.md +++ b/third_party/rust/clap/examples/README.md @@ -1,14 +1,34 @@ # Examples - Basic demo: [derive](demo.md) -- Key-value pair arguments: [derive](keyvalue-derive.md) +- Typed arguments: [derive](typed-derive.md) + - Topics: + - Custom `parse()` - Custom cargo command: [builder](cargo-example.md), [derive](cargo-example-derive.md) + - Topics: + - Subcommands + - Cargo plugins - git-like interface: [builder](git.md), [derive](git-derive.md) + - Topics: + - Subcommands + - External subcommands + - Optional subcommands + - Default subcommands - pacman-like interface: [builder](pacman.md) + - Topics: + - Flag subcommands + - Conflicting arguments - Escaped positionals with `--`: [builder](escaped-positional.md), [derive](escaped-positional-derive.md) - Multi-call - busybox: [builder](multicall-busybox.md) + - Topics: + - Subcommands - hostname: [builder](multicall-hostname.md) + - Topics: + - Subcommands +- repl: [builder](repl.rs) + - Topics: + - Read-Eval-Print Loops / Custom command lines ## Contributing @@ -17,4 +37,4 @@ New examples: - Testing: Ensure there is a markdown file with [trycmd](https://docs.rs/trycmd) syntax - Link the `.md` file from here -See also the general [CONTRIBUTING](../../CONTRIBUTING.md). +See also the general [CONTRIBUTING](../CONTRIBUTING.md). diff --git a/third_party/rust/clap/examples/cargo-example-derive.rs b/third_party/rust/clap/examples/cargo-example-derive.rs index c714480e0edc..1ee0a02551f1 100644 --- a/third_party/rust/clap/examples/cargo-example-derive.rs +++ b/third_party/rust/clap/examples/cargo-example-derive.rs @@ -1,3 +1,5 @@ +// Note: this requires the `derive` feature + use clap::Parser; #[derive(Parser)] diff --git a/third_party/rust/clap/examples/cargo-example.rs b/third_party/rust/clap/examples/cargo-example.rs index 742697a02149..149bf599184d 100644 --- a/third_party/rust/clap/examples/cargo-example.rs +++ b/third_party/rust/clap/examples/cargo-example.rs @@ -1,15 +1,17 @@ +// Note: this requires the `cargo` feature + fn main() { - let app = clap::App::new("cargo") + let cmd = clap::Command::new("cargo") .bin_name("cargo") - .setting(clap::AppSettings::SubcommandRequired) + .subcommand_required(true) .subcommand( - clap::app_from_crate!().name("example").arg( + clap::command!("example").arg( clap::arg!(--"manifest-path" ) .required(false) .allow_invalid_utf8(true), ), ); - let matches = app.get_matches(); + let matches = cmd.get_matches(); let matches = match matches.subcommand() { Some(("example", matches)) => matches, _ => unreachable!("clap should ensure we don't get here"), diff --git a/third_party/rust/clap/examples/derive_ref/README.md b/third_party/rust/clap/examples/derive_ref/README.md index fbc88a913b57..11001dde85f8 100644 --- a/third_party/rust/clap/examples/derive_ref/README.md +++ b/third_party/rust/clap/examples/derive_ref/README.md @@ -1,14 +1,16 @@ # Derive Reference 1. [Overview](#overview) -2. [Raw Attributes](#raw-attributes) -3. [Magic Attributes](#magic-attributes) - 1. [App Attributes](#app-attributes) - 2. [Arg Attributes](#arg-attributes) - 3. [Arg Types](#arg-types) +2. [Attributes](#attributes) + 1. [Terminology](#terminology) + 2. [Command Attributes](#command-attributes) + 3. [Arg Attributes](#arg-attributes) 4. [Arg Enum Attributes](#arg-enum-attributes) 5. [Possible Value Attributes](#possible-value-attributes) - 6. [Doc Comments](#doc-comments) +3. [Arg Types](#arg-types) +4. [Doc Comments](#doc-comments) +5. [Tips](#tips) +6. [Mixing Builder and Derive APIS](#mixing-builder-and-derive-apis) ## Overview @@ -16,7 +18,7 @@ To derive `clap` types, you need to enable the `derive` feature flag. See [demo.rs](../demo.rs) and [demo.md](../demo.md) for a brief example. -Let's start by breaking down what can go where: +Let's start by breaking down the anatomy of the derive attributes: ```rust use clap::{Parser, Args, Subcommand, ArgEnum}; @@ -26,7 +28,10 @@ use clap::{Parser, Args, Subcommand, ArgEnum}; struct Cli { /// Doc comment #[clap(ARG ATTRIBUTE)] - field: Type, + field: UserType, + + #[clap(arg_enum, ARG ATTRIBUTE...)] + field: EnumValues, #[clap(flatten)] delegate: Struct, @@ -41,7 +46,7 @@ struct Cli { struct Struct { /// Doc comment #[clap(ARG ATTRIBUTE)] - field: Type, + field: UserType, } /// Doc comment @@ -57,14 +62,14 @@ enum Command { Variant2 { /// Doc comment #[clap(ARG ATTRIBUTE)] - field: Type, + field: UserType, } } /// Doc comment #[derive(ArgEnum)] #[clap(ARG ENUM ATTRIBUTE)] -enum Mode { +enum EnumValues { /// Doc comment #[clap(POSSIBLE VALUE ATTRIBUTE)] Variant1, @@ -78,14 +83,18 @@ fn main() { - `Parser` parses arguments into a `struct` (arguments) or `enum` (subcommands). - `Args` allows defining a set of re-usable arguments that get merged into their parent container. - `Subcommand` defines available subcommands. + - Subcommand arguments can be defined in a struct-variant or automatically flattened with a tuple-variant. - `ArgEnum` allows parsing a value directly into an `enum`, erroring on unsupported values. + - The derive doesn't work on enums that contain non-unit variants, unless they are skipped See also the [tutorial](../tutorial_derive/README.md) and [examples](../README.md). -## Raw Attributes +## Attributes + +### Terminology **Raw attributes** are forwarded directly to the underlying `clap` builder. Any -`App`, `Arg`, or `PossibleValue` method can be used as an attribute. +`Command`, `Arg`, or `PossibleValue` method can be used as an attribute. Raw attributes come in two different syntaxes: ```rust @@ -101,38 +110,46 @@ Raw attributes come in two different syntaxes: As long as `method_name` is not one of the magical methods - it will be translated into a mere method call. -## Magic Attributes - **Magic attributes** have post-processing done to them, whether that is - Providing of defaults - Special behavior is triggered off of it -### App Attributes +Magic attributes are more constrained in the syntax they support, usually just +` = ` though some use `()` instead. See the specific +magic attributes documentation for details. This allows users to access the +raw behavior of an attribute via `()` syntax. -These correspond to a `clap::App` which is used for both top-level parsers and +**NOTE:** Some attributes are inferred from [Arg Types](#arg-types) and [Doc +Comments](#doc-comments). Explicit attributes take precedence over inferred +attributes. + +### Command Attributes + +These correspond to a `clap::Command` which is used for both top-level parsers and when defining subcommands. -In addition to the raw attributes, the following magic attributes are supported: -- `name = `: `clap::App::name` +**Magic attributes:** +- `name = `: `clap::Command::name` - When not present: [crate `name`](https://doc.rust-lang.org/cargo/reference/manifest.html#the-name-field) (`Parser` container), variant name (`Subcommand` variant) -- `version [= ]`: `clap::App::version` +- `version [= ]`: `clap::Command::version` - When not present: no version set - Without ``: defaults to [crate `version`](https://doc.rust-lang.org/cargo/reference/manifest.html#the-version-field) -- `author [= ]`: `clap::App::author` +- `author [= ]`: `clap::Command::author` - When not present: no author set - Without ``: defaults to [crate `authors`](https://doc.rust-lang.org/cargo/reference/manifest.html#the-authors-field) -- `about [= ]`: `clap::App::about` +- `about [= ]`: `clap::Command::about` - When not present: [Doc comment summary](#doc-comments) - Without ``: [crate `description`](https://doc.rust-lang.org/cargo/reference/manifest.html#the-description-field) (`Parser` container) - **TIP:** When a doc comment is also present, you most likely want to add `#[clap(long_about = None)]` to clear the doc comment so only `about` gets shown with both `-h` and `--help`. -- `long_about = `: `clap::App::long_about` +- `long_about = `: `clap::Command::long_about` - When not present: [Doc comment](#doc-comments) if there is a blank line, else nothing - `verbatim_doc_comment`: Minimizes pre-processing when converting doc comments to `about` / `long_about` -- `help_heading`: `clap::App::help_heading` +- `next_display_order`: `clap::Command::next_display_order` +- `next_help_heading`: `clap::Command::next_help_heading` - When `flatten`ing `Args`, this is scoped to just the args in this struct and any struct `flatten`ed into it -- `rename_all = `: Override default field / variant name case conversion for `App::name` / `Arg::name` +- `rename_all = `: Override default field / variant name case conversion for `Command::name` / `Arg::name` - When not present: `kebab-case` - Available values: `camelCase`, `kebab-case`, `PascalCase`, `SCREAMING_SNAKE_CASE`, `snake_case`, `lower`, `UPPER`, `verbatim` - `rename_all_env = `: Override default field name case conversion for env variables for `clap::Arg::env` @@ -143,14 +160,17 @@ And for `Subcommand` variants: - `skip`: Ignore this variant - `flatten`: Delegates to the variant for more subcommands (must implement `Subcommand`) - `subcommand`: Nest subcommands under the current set of subcommands (must implement `Subcommand`) -- `external_subcommand`: `clap::AppSettings::AllowExternalSubcommand` +- `external_subcommand`: `clap::Command::allow_external_subcommand(true)` - Variant must be either `Variant(Vec)` or `Variant(Vec)` +**Raw attributes:** Any [`Command` method](https://docs.rs/clap/latest/clap/type.Command.html) can also be used as an attribute, see [Terminology](#terminology) for syntax. +- e.g. `#[clap(arg_required_else_help(true))]` would translate to `cmd.arg_required_else_help(true)` + ### Arg Attributes These correspond to a `clap::Arg`. -In addition to the raw attributes, the following magic attributes are supported: +**Magic attributes**: - `name = `: `clap::Arg::new` - When not present: case-converted field name is used - `help = `: `clap::Arg::help` @@ -164,20 +184,23 @@ In addition to the raw attributes, the following magic attributes are supported: - `long [= ]`: `clap::Arg::long` - When not present: no long set - Without ``: defaults to the case-converted field name -- `env [= ]`: `clap::Arg::env` +- `env [= ]`: `clap::Arg::env` (needs `env` feature enabled) - When not present: no env set - Without ``: defaults to the case-converted field name - `flatten`: Delegates to the field for more arguments (must implement `Args`) - Only `help_heading` can be used with `flatten`. See [clap-rs/clap#3269](https://github.com/clap-rs/clap/issues/3269) for why arg attributes are not generally supported. - - **Tip:** Though we do apply a flattened `Args`'s Parent App Attributes, this - makes reuse harder. Generally prefer putting the app attributes on the `Parser` + - **Tip:** Though we do apply a flattened `Args`'s Parent Command Attributes, this + makes reuse harder. Generally prefer putting the cmd attributes on the `Parser` or on the flattened field. - `subcommand`: Delegates definition of subcommands to the field (must implement `Subcommand`) - When `Option`, the subcommand becomes optional -- `from_global`: Read a `clap::Arg::global` argument (raw attribute), regardless of what subcommand you are in -- `parse( [= ])` `clap::Arg::validator` +- `from_global`: Read a `clap::Arg::global` argument (raw attribute), regardless of what subcommand you are in +- `parse( [= ])`: `clap::Arg::validator` and `clap::ArgMatches::values_of_t` + - Default: `try_from_str` + - Warning: for `Path` / `OsString`, be sure to use `try_from_os_str` + - See [Arg Types](#arg-types) for more details - `arg_enum`: Parse the value using the `ArgEnum` trait - `skip [= ]`: Ignore this field, filling in with `` - Without ``: fills the field with `Default::default()` @@ -185,14 +208,39 @@ In addition to the raw attributes, the following magic attributes are supported: - `default_value_t [= ]`: `clap::Arg::default_value` and `clap::Arg::required(false)` - Requires `std::fmt::Display` or `#[clap(arg_enum)]` - Without ``, relies on `Default::default()` +- `default_value_os_t [= ]`: `clap::Arg::default_value_os` and `clap::Arg::required(false)` + - Requires `std::convert::Into` or `#[clap(arg_enum)]` + - Without ``, relies on `Default::default()` -### Arg Types +**Raw attributes:** Any [`Arg` method](https://docs.rs/clap/latest/clap/struct.Arg.html) can also be used as an attribute, see [Terminology](#terminology) for syntax. +- e.g. `#[clap(max_values(3))]` would translate to `arg.max_values(3)` + +### Arg Enum Attributes + +- `rename_all = `: Override default field / variant name case conversion for `PossibleValue::new` + - When not present: `kebab-case` + - Available values: `camelCase`, `kebab-case`, `PascalCase`, `SCREAMING_SNAKE_CASE`, `snake_case`, `lower`, `UPPER`, `verbatim` + +### Possible Value Attributes + +These correspond to a `clap::PossibleValue`. + +**Magic attributes**: +- `name = `: `clap::PossibleValue::new` + - When not present: case-converted field name is used +- `help = `: `clap::PossibleValue::help` + - When not present: [Doc comment summary](#doc-comments) + +**Raw attributes:** Any [`PossibleValue` method](https://docs.rs/clap/latest/clap/struct.PossibleValue.html) can also be used as an attribute, see [Terminology](#terminology) for syntax. +- e.g. `#[clap(alias("foo"))]` would translate to `pv.alias("foo")` + +## Arg Types `clap` assumes some intent based on the type used: | Type | Effect | Implies | |---------------------|--------------------------------------|------------------------------------------------------------------| -| `bool` | flag | `#[clap(parser(from_flag))]` | +| `bool` | flag | `#[clap(parse(from_flag))]` | | `Option` | optional argument | `.takes_value(true).required(false)` | | `Option>` | optional value for optional argument | `.takes_value(true).required(false).min_values(0).max_values(1)` | | `T` | required argument | `.takes_value(true).required(!has_default)` | @@ -224,33 +272,20 @@ Notes: - `from_occurrences`: - Implies `arg.takes_value(false).multiple_occurrences(true)` - Reads from `clap::ArgMatches::occurrences_of` rather than a `value_of` function + - Note: operations on values, like `default_value`, are unlikely to do what you want - `from_flag` - Implies `arg.takes_value(false)` - Reads from `clap::ArgMatches::is_present` rather than a `value_of` function + - Note: operations on values, like `default_value`, are unlikely to do what you want **Warning:** - To support non-UTF8 paths, you must use `parse(from_os_str)`, otherwise `clap` will use `clap::ArgMatches::value_of` with `PathBuf::FromStr`. -### Arg Enum Attributes - -- `rename_all = `: Override default field / variant name case conversion for `PossibleValue::new` - - When not present: `kebab-case` - - Available values: `camelCase`, `kebab-case`, `PascalCase`, `SCREAMING_SNAKE_CASE`, `snake_case`, `lower`, `UPPER`, `verbatim` - -### Possible Value Attributes - -These correspond to a `clap::PossibleValue`. - -- `name = `: `clap::PossibleValue::new` - - When not present: case-converted field name is used -- `help = `: `clap::PossibleValue::help` - - When not present: [Doc comment summary](#doc-comments) - -### Doc Comments +## Doc Comments In clap, help messages for the whole binary can be specified -via [`App::about`] and [`App::long_about`] while help messages +via [`Command::about`] and [`Command::long_about`] while help messages for individual arguments can be specified via [`Arg::help`] and [`Arg::long_help`]". `long_*` variants are used when user calls the program with @@ -283,15 +318,15 @@ struct Foo { **NOTE:** Attributes have priority over doc comments! -**Top level doc comments always generate `App::about/long_about` calls!** -If you really want to use the `App::about/long_about` methods (you likely don't), +**Top level doc comments always generate `Command::about/long_about` calls!** +If you really want to use the `Command::about/long_about` methods (you likely don't), use the `about` / `long_about` attributes to override the calls generated from the doc comment. To clear `long_about`, you can use `#[clap(long_about = None)]`. **TIP:** Set `#![deny(missing_docs)]` to catch missing `--help` documentation at compile time. -#### Pre-processing +### Pre-processing ```rust # use clap::Parser; @@ -317,10 +352,10 @@ A doc comment consists of three parts: - A blank line (whitespace only) - Detailed description, all the rest -The summary corresponds with `App::about` / `Arg::help`. When a blank line is -present, the whole doc comment will be passed to `App::long_about` / -`Arg::long_help`. Or in other words, a doc may result in just a `App::about` / -`Arg::help` or `App::about` / `Arg::help` and `App::long_about` / +The summary corresponds with `Command::about` / `Arg::help`. When a blank line is +present, the whole doc comment will be passed to `Command::long_about` / +`Arg::long_help`. Or in other words, a doc may result in just a `Command::about` / +`Arg::help` or `Command::about` / `Arg::help` and `Command::long_about` / `Arg::long_help` In addition, when `verbatim_doc_comment` is not present, `clap` applies some preprocessing, including: @@ -349,3 +384,46 @@ them. - Remove one leading space from each line, even if this attribute is present, to allow for a space between `///` and the content. - Remove leading and trailing blank lines + +## Tips + +- To get access to a `Command` call `CommandFactory::command` (implemented when deriving `Parser`) +- Proactively check for bad `Command` configurations by calling `Command::debug_assert` in a test ([example](../tutorial_derive/05_01_assert.rs)) + +## Mixing Builder and Derive APIs + +The builder and derive APIs do not live in isolation. They can work together, which is especially helpful if some arguments can be specified at compile-time while others must be specified at runtime. + +### Using derived arguments in a builder application + +*[Jump to source](augment_args.rs)* + +When using the derive API, you can `#[clap(flatten)]` a struct deriving `Args` into a struct deriving `Args` or `Parser`. This example shows how you can augment a `Command` instance created using the builder API with `Args` created using the derive API. + +It uses the `Args::augment_args` method to add the arguments to the `Command` instance. + +Crates such as [clap-verbosity-flag](https://github.com/rust-cli/clap-verbosity-flag) provide structs that implement `Args` or `Parser`. Without the technique shown in this example, it would not be possible to use such crates with the builder API. `augment_args` to the rescue! + +### Using derived subcommands in a builder application + +*[Jump to source](augment_subcommands.rs)* + +When using the derive API, you can use `#[clap(subcommand)]` inside the struct to add subcommands. The type of the field is usually an enum that derived `Parser`. However, you can also add the subcommands in that enum to a `Command` instance created with the builder API. + +It uses the `Subcommand::augment_subcommands` method to add the subcommands to the `Command` instance. + +### Adding hand-implemented subcommands to a derived application + +*[Jump to source](hand_subcommand.rs)* + +When using the derive API, you can use `#[clap(subcommand)]` inside the struct to add subcommands. The type of the field is usually an enum that derived `Parser`. However, you can also implement the `Subcommand` trait manually on this enum (or any other type) and it can still be used inside the struct created with the derive API. The implementation of the `Subcommand` trait will use the builder API to add the subcommands to the `Command` instance created behind the scenes for you by the derive API. + +Notice how in the previous example we used `augment_subcommands` on an enum that derived `Parser`, whereas now we implement `augment_subcommands` ourselves, but the derive API calls it automatically since we used the `#[clap(subcommand)]` attribute. + +### Flattening hand-implemented args into a derived application + +*[Jump to source](flatten_hand_args.rs)* + +When using the derive API, you can use `#[clap(flatten)]` inside the struct to add arguments as if they were added directly to the containing struct. The type of the field is usually an struct that derived `Args`. However, you can also implement the `Args` trait manually on this struct (or any other type) and it can still be used inside the struct created with the derive API. The implementation of the `Args` trait will use the builder API to add the arguments to the `Command` instance created behind the scenes for you by the derive API. + +Notice how in the example 1 we used `augment_args` on the struct that derived `Parser`, whereas now we implement `augment_args` ourselves, but the derive API calls it automatically since we used the `#[clap(flatten)]` attribute. diff --git a/third_party/rust/clap/examples/derive_ref/augment_args.rs b/third_party/rust/clap/examples/derive_ref/augment_args.rs new file mode 100644 index 000000000000..59765ad61f48 --- /dev/null +++ b/third_party/rust/clap/examples/derive_ref/augment_args.rs @@ -0,0 +1,27 @@ +use clap::{arg, Args as _, Command, FromArgMatches as _, Parser}; + +#[derive(Parser, Debug)] +struct DerivedArgs { + #[clap(short, long)] + derived: bool, +} + +fn main() { + let cli = Command::new("CLI").arg(arg!(-b - -built)); + // Augment built args with derived args + let cli = DerivedArgs::augment_args(cli); + + let matches = cli.get_matches(); + println!("Value of built: {:?}", matches.is_present("built")); + println!( + "Value of derived via ArgMatches: {:?}", + matches.is_present("derived") + ); + + // Since DerivedArgs implements FromArgMatches, we can extract it from the unstructured ArgMatches. + // This is the main benefit of using derived arguments. + let derived_matches = DerivedArgs::from_arg_matches(&matches) + .map_err(|err| err.exit()) + .unwrap(); + println!("Value of derived: {:#?}", derived_matches); +} diff --git a/third_party/rust/clap/examples/derive_ref/augment_subcommands.rs b/third_party/rust/clap/examples/derive_ref/augment_subcommands.rs new file mode 100644 index 000000000000..299c7f8cf690 --- /dev/null +++ b/third_party/rust/clap/examples/derive_ref/augment_subcommands.rs @@ -0,0 +1,21 @@ +use clap::{Command, FromArgMatches as _, Parser, Subcommand as _}; + +#[derive(Parser, Debug)] +enum Subcommands { + Derived { + #[clap(short, long)] + derived_flag: bool, + }, +} + +fn main() { + let cli = Command::new("Built CLI"); + // Augment with derived subcommands + let cli = Subcommands::augment_subcommands(cli); + + let matches = cli.get_matches(); + let derived_subcommands = Subcommands::from_arg_matches(&matches) + .map_err(|err| err.exit()) + .unwrap(); + println!("Derived subcommands: {:#?}", derived_subcommands); +} diff --git a/third_party/rust/clap/examples/derive_ref/flatten_hand_args.rs b/third_party/rust/clap/examples/derive_ref/flatten_hand_args.rs new file mode 100644 index 000000000000..2969457e1893 --- /dev/null +++ b/third_party/rust/clap/examples/derive_ref/flatten_hand_args.rs @@ -0,0 +1,53 @@ +use clap::error::Error; +use clap::{Arg, ArgMatches, Args, Command, FromArgMatches, Parser}; + +#[derive(Debug)] +struct CliArgs { + foo: bool, + bar: bool, + quuz: Option, +} + +impl FromArgMatches for CliArgs { + fn from_arg_matches(matches: &ArgMatches) -> Result { + Ok(Self { + foo: matches.is_present("foo"), + bar: matches.is_present("bar"), + quuz: matches.value_of("quuz").map(|quuz| quuz.to_owned()), + }) + } + fn update_from_arg_matches(&mut self, matches: &ArgMatches) -> Result<(), Error> { + self.foo |= matches.is_present("foo"); + self.bar |= matches.is_present("bar"); + if let Some(quuz) = matches.value_of("quuz") { + self.quuz = Some(quuz.to_owned()); + } + Ok(()) + } +} + +impl Args for CliArgs { + fn augment_args(cmd: Command<'_>) -> Command<'_> { + cmd.arg(Arg::new("foo").short('f').long("foo")) + .arg(Arg::new("bar").short('b').long("bar")) + .arg(Arg::new("quuz").short('q').long("quuz").takes_value(true)) + } + fn augment_args_for_update(cmd: Command<'_>) -> Command<'_> { + cmd.arg(Arg::new("foo").short('f').long("foo")) + .arg(Arg::new("bar").short('b').long("bar")) + .arg(Arg::new("quuz").short('q').long("quuz").takes_value(true)) + } +} + +#[derive(Parser, Debug)] +struct Cli { + #[clap(short, long)] + top_level: bool, + #[clap(flatten)] + more_args: CliArgs, +} + +fn main() { + let args = Cli::parse(); + println!("{:#?}", args); +} diff --git a/third_party/rust/clap/examples/derive_ref/hand_subcommand.rs b/third_party/rust/clap/examples/derive_ref/hand_subcommand.rs new file mode 100644 index 000000000000..5afd94132718 --- /dev/null +++ b/third_party/rust/clap/examples/derive_ref/hand_subcommand.rs @@ -0,0 +1,79 @@ +use clap::error::{Error, ErrorKind}; +use clap::{ArgMatches, Args as _, Command, FromArgMatches, Parser, Subcommand}; + +#[derive(Parser, Debug)] +struct AddArgs { + name: Vec, +} +#[derive(Parser, Debug)] +struct RemoveArgs { + #[clap(short, long)] + force: bool, + name: Vec, +} + +#[derive(Debug)] +enum CliSub { + Add(AddArgs), + Remove(RemoveArgs), +} + +impl FromArgMatches for CliSub { + fn from_arg_matches(matches: &ArgMatches) -> Result { + match matches.subcommand() { + Some(("add", args)) => Ok(Self::Add(AddArgs::from_arg_matches(args)?)), + Some(("remove", args)) => Ok(Self::Remove(RemoveArgs::from_arg_matches(args)?)), + Some((_, _)) => Err(Error::raw( + ErrorKind::UnrecognizedSubcommand, + "Valid subcommands are `add` and `remove`", + )), + None => Err(Error::raw( + ErrorKind::MissingSubcommand, + "Valid subcommands are `add` and `remove`", + )), + } + } + fn update_from_arg_matches(&mut self, matches: &ArgMatches) -> Result<(), Error> { + match matches.subcommand() { + Some(("add", args)) => *self = Self::Add(AddArgs::from_arg_matches(args)?), + Some(("remove", args)) => *self = Self::Remove(RemoveArgs::from_arg_matches(args)?), + Some((_, _)) => { + return Err(Error::raw( + ErrorKind::UnrecognizedSubcommand, + "Valid subcommands are `add` and `remove`", + )) + } + None => (), + }; + Ok(()) + } +} + +impl Subcommand for CliSub { + fn augment_subcommands(cmd: Command<'_>) -> Command<'_> { + cmd.subcommand(AddArgs::augment_args(Command::new("add"))) + .subcommand(RemoveArgs::augment_args(Command::new("remove"))) + .subcommand_required(true) + } + fn augment_subcommands_for_update(cmd: Command<'_>) -> Command<'_> { + cmd.subcommand(AddArgs::augment_args(Command::new("add"))) + .subcommand(RemoveArgs::augment_args(Command::new("remove"))) + .subcommand_required(true) + } + fn has_subcommand(name: &str) -> bool { + matches!(name, "add" | "remove") + } +} + +#[derive(Parser, Debug)] +struct Cli { + #[clap(short, long)] + top_level: bool, + #[clap(subcommand)] + subcommand: CliSub, +} + +fn main() { + let args = Cli::parse(); + println!("{:#?}", args); +} diff --git a/third_party/rust/clap/examples/derive_ref/interop_tests.md b/third_party/rust/clap/examples/derive_ref/interop_tests.md new file mode 100644 index 000000000000..746fe1878d76 --- /dev/null +++ b/third_party/rust/clap/examples/derive_ref/interop_tests.md @@ -0,0 +1,256 @@ +Following are tests for the interop examples in this directory. + +## Augment Args + +```console +$ interop_augment_args +Value of built: false +Value of derived via ArgMatches: false +Value of derived: DerivedArgs { + derived: false, +} + +``` + +```console +$ interop_augment_args -b --derived +Value of built: true +Value of derived via ArgMatches: true +Value of derived: DerivedArgs { + derived: true, +} + +``` + +```console +$ interop_augment_args -d --built +Value of built: true +Value of derived via ArgMatches: true +Value of derived: DerivedArgs { + derived: true, +} + +``` + +```console +$ interop_augment_args --unknown +? failed +error: Found argument '--unknown' which wasn't expected, or isn't valid in this context + + If you tried to supply `--unknown` as a value rather than a flag, use `-- --unknown` + +USAGE: + interop_augment_args[EXE] [OPTIONS] + +For more information try --help + +``` + +## Augment Subcommands + +```console +$ interop_augment_subcommands +? failed +error: A subcommand is required but one was not provided. +``` + +```console +$ interop_augment_subcommands derived +Derived subcommands: Derived { + derived_flag: false, +} + +``` + +```console +$ interop_augment_subcommands derived --derived-flag +Derived subcommands: Derived { + derived_flag: true, +} + +``` + +```console +$ interop_augment_subcommands derived --unknown +? failed +error: Found argument '--unknown' which wasn't expected, or isn't valid in this context + + If you tried to supply `--unknown` as a value rather than a flag, use `-- --unknown` + +USAGE: + interop_augment_subcommands[EXE] derived [OPTIONS] + +For more information try --help + +``` + +```console +$ interop_augment_subcommands unknown +? failed +error: Found argument 'unknown' which wasn't expected, or isn't valid in this context + +USAGE: + interop_augment_subcommands[EXE] [SUBCOMMAND] + +For more information try --help + +``` + +## Hand-Implemented Subcommand + +```console +$ interop_hand_subcommand +? failed +error: 'interop_hand_subcommand[EXE]' requires a subcommand but one was not provided + +USAGE: + interop_hand_subcommand[EXE] [OPTIONS] + +For more information try --help + +``` + +```console +$ interop_hand_subcommand add +Cli { + top_level: false, + subcommand: Add( + AddArgs { + name: [], + }, + ), +} + +``` + +```console +$ interop_hand_subcommand add a b c +Cli { + top_level: false, + subcommand: Add( + AddArgs { + name: [ + "a", + "b", + "c", + ], + }, + ), +} + +``` + +```console +$ interop_hand_subcommand add --unknown +? failed +error: Found argument '--unknown' which wasn't expected, or isn't valid in this context + + If you tried to supply `--unknown` as a value rather than a flag, use `-- --unknown` + +USAGE: + interop_hand_subcommand[EXE] add [NAME]... + +For more information try --help + +``` + +```console +$ interop_hand_subcommand remove +Cli { + top_level: false, + subcommand: Remove( + RemoveArgs { + force: false, + name: [], + }, + ), +} + +``` + +```console +$ interop_hand_subcommand remove --force a b c +Cli { + top_level: false, + subcommand: Remove( + RemoveArgs { + force: true, + name: [ + "a", + "b", + "c", + ], + }, + ), +} + +``` + +```console +$ interop_hand_subcommand unknown +? failed +error: Found argument 'unknown' which wasn't expected, or isn't valid in this context + +USAGE: + interop_hand_subcommand[EXE] [OPTIONS] + +For more information try --help + +``` + +## Flatten Hand-Implemented Args + +```console +$ interop_flatten_hand_args +Cli { + top_level: false, + more_args: CliArgs { + foo: false, + bar: false, + quuz: None, + }, +} + +``` + +```console +$ interop_flatten_hand_args -f --bar +Cli { + top_level: false, + more_args: CliArgs { + foo: true, + bar: true, + quuz: None, + }, +} + +``` + +```console +$ interop_flatten_hand_args --quuz abc +Cli { + top_level: false, + more_args: CliArgs { + foo: false, + bar: false, + quuz: Some( + "abc", + ), + }, +} + +``` + +```console +$ interop_flatten_hand_args --unknown +? failed +error: Found argument '--unknown' which wasn't expected, or isn't valid in this context + + If you tried to supply `--unknown` as a value rather than a flag, use `-- --unknown` + +USAGE: + interop_flatten_hand_args[EXE] [OPTIONS] + +For more information try --help + +``` diff --git a/third_party/rust/clap/examples/escaped-positional.rs b/third_party/rust/clap/examples/escaped-positional.rs index 814e2a7cbdf4..f6d61bd9651c 100644 --- a/third_party/rust/clap/examples/escaped-positional.rs +++ b/third_party/rust/clap/examples/escaped-positional.rs @@ -1,9 +1,9 @@ // Note: this requires the `cargo` feature -use clap::{app_from_crate, arg}; +use clap::{arg, command}; fn main() { - let matches = app_from_crate!() + let matches = command!() .arg(arg!(eff: -f)) .arg(arg!(pea: -p ).required(false)) .arg( diff --git a/third_party/rust/clap/examples/git-derive.md b/third_party/rust/clap/examples/git-derive.md index d33ce19bcada..dc27776f5823 100644 --- a/third_party/rust/clap/examples/git-derive.md +++ b/third_party/rust/clap/examples/git-derive.md @@ -22,6 +22,7 @@ SUBCOMMANDS: clone Clones repos help Print this message or the help of the given subcommand(s) push pushes things + stash $ git-derive help git @@ -38,6 +39,7 @@ SUBCOMMANDS: clone Clones repos help Print this message or the help of the given subcommand(s) push pushes things + stash $ git-derive help add git-derive[EXE]-add @@ -75,6 +77,61 @@ Adding ["Cargo.toml", "Cargo.lock"] ``` +Default subcommand: +```console +$ git-derive stash -h +git-derive[EXE]-stash + +USAGE: + git-derive[EXE] stash [OPTIONS] + git-derive[EXE] stash + +OPTIONS: + -h, --help Print help information + -m, --message + +SUBCOMMANDS: + apply + help Print this message or the help of the given subcommand(s) + pop + push + +$ git-derive stash push -h +git-derive[EXE]-stash-push + +USAGE: + git-derive[EXE] stash push [OPTIONS] + +OPTIONS: + -h, --help Print help information + -m, --message + +$ git-derive stash pop -h +git-derive[EXE]-stash-pop + +USAGE: + git-derive[EXE] stash pop [STASH] + +ARGS: + + +OPTIONS: + -h, --help Print help information + +$ git-derive stash -m "Prototype" +Pushing StashPush { message: Some("Prototype") } + +$ git-derive stash pop +Popping None + +$ git-derive stash push -m "Prototype" +Pushing StashPush { message: Some("Prototype") } + +$ git-derive stash pop +Popping None + +``` + External subcommands: ```console $ git-derive custom-tool arg1 --foo bar diff --git a/third_party/rust/clap/examples/git-derive.rs b/third_party/rust/clap/examples/git-derive.rs index 4f0cf80e07e9..7e44edb1aff0 100644 --- a/third_party/rust/clap/examples/git-derive.rs +++ b/third_party/rust/clap/examples/git-derive.rs @@ -3,10 +3,10 @@ use std::ffi::OsString; use std::path::PathBuf; -use clap::{AppSettings, Parser, Subcommand}; +use clap::{Args, Parser, Subcommand}; /// A fictional versioning CLI -#[derive(Parser)] +#[derive(Debug, Parser)] #[clap(name = "git")] #[clap(about = "A fictional versioning CLI", long_about = None)] struct Cli { @@ -14,35 +14,59 @@ struct Cli { command: Commands, } -#[derive(Subcommand)] +#[derive(Debug, Subcommand)] enum Commands { /// Clones repos - #[clap(setting(AppSettings::ArgRequiredElseHelp))] + #[clap(arg_required_else_help = true)] Clone { /// The remote to clone remote: String, }, /// pushes things - #[clap(setting(AppSettings::ArgRequiredElseHelp))] + #[clap(arg_required_else_help = true)] Push { /// The remote to target remote: String, }, /// adds things - #[clap(setting(AppSettings::ArgRequiredElseHelp))] + #[clap(arg_required_else_help = true)] Add { /// Stuff to add #[clap(required = true, parse(from_os_str))] path: Vec, }, + Stash(Stash), #[clap(external_subcommand)] External(Vec), } +#[derive(Debug, Args)] +#[clap(args_conflicts_with_subcommands = true)] +struct Stash { + #[clap(subcommand)] + command: Option, + + #[clap(flatten)] + push: StashPush, +} + +#[derive(Debug, Subcommand)] +enum StashCommands { + Push(StashPush), + Pop { stash: Option }, + Apply { stash: Option }, +} + +#[derive(Debug, Args)] +struct StashPush { + #[clap(short, long)] + message: Option, +} + fn main() { let args = Cli::parse(); - match &args.command { + match args.command { Commands::Clone { remote } => { println!("Cloning {}", remote); } @@ -52,6 +76,20 @@ fn main() { Commands::Add { path } => { println!("Adding {:?}", path); } + Commands::Stash(stash) => { + let stash_cmd = stash.command.unwrap_or(StashCommands::Push(stash.push)); + match stash_cmd { + StashCommands::Push(push) => { + println!("Pushing {:?}", push); + } + StashCommands::Pop { stash } => { + println!("Popping {:?}", stash); + } + StashCommands::Apply { stash } => { + println!("Applying {:?}", stash); + } + } + } Commands::External(args) => { println!("Calling out to {:?} with {:?}", &args[0], &args[1..]); } diff --git a/third_party/rust/clap/examples/git.md b/third_party/rust/clap/examples/git.md index 64c2f9413e1c..2cdfe653b3c5 100644 --- a/third_party/rust/clap/examples/git.md +++ b/third_party/rust/clap/examples/git.md @@ -20,6 +20,7 @@ SUBCOMMANDS: clone Clones repos help Print this message or the help of the given subcommand(s) push pushes things + stash $ git help git @@ -36,6 +37,7 @@ SUBCOMMANDS: clone Clones repos help Print this message or the help of the given subcommand(s) push pushes things + stash $ git help add git[EXE]-add @@ -73,6 +75,61 @@ Adding ["Cargo.toml", "Cargo.lock"] ``` +Default subcommand: +```console +$ git stash -h +git[EXE]-stash + +USAGE: + git[EXE] stash [OPTIONS] + git[EXE] stash + +OPTIONS: + -h, --help Print help information + -m, --message + +SUBCOMMANDS: + apply + help Print this message or the help of the given subcommand(s) + pop + push + +$ git stash push -h +git[EXE]-stash-push + +USAGE: + git[EXE] stash push [OPTIONS] + +OPTIONS: + -h, --help Print help information + -m, --message + +$ git stash pop -h +git[EXE]-stash-pop + +USAGE: + git[EXE] stash pop [STASH] + +ARGS: + + +OPTIONS: + -h, --help Print help information + +$ git stash -m "Prototype" +Pushing Some("Prototype") + +$ git stash pop +Popping None + +$ git stash push -m "Prototype" +Pushing Some("Prototype") + +$ git stash pop +Popping None + +``` + External subcommands: ```console $ git custom-tool arg1 --foo bar diff --git a/third_party/rust/clap/examples/git.rs b/third_party/rust/clap/examples/git.rs index 77394dccb302..1ced54a90eab 100644 --- a/third_party/rust/clap/examples/git.rs +++ b/third_party/rust/clap/examples/git.rs @@ -1,32 +1,50 @@ +// Note: this requires the `cargo` feature + use std::path::PathBuf; -use clap::{arg, App, AppSettings}; +use clap::{arg, Command}; -fn main() { - let matches = App::new("git") +fn cli() -> Command<'static> { + Command::new("git") .about("A fictional versioning CLI") - .setting(AppSettings::SubcommandRequiredElseHelp) - .setting(AppSettings::AllowExternalSubcommands) - .setting(AppSettings::AllowInvalidUtf8ForExternalSubcommands) + .subcommand_required(true) + .arg_required_else_help(true) + .allow_external_subcommands(true) + .allow_invalid_utf8_for_external_subcommands(true) .subcommand( - App::new("clone") + Command::new("clone") .about("Clones repos") .arg(arg!( "The remote to clone")) - .setting(AppSettings::ArgRequiredElseHelp), + .arg_required_else_help(true), ) .subcommand( - App::new("push") + Command::new("push") .about("pushes things") .arg(arg!( "The remote to target")) - .setting(AppSettings::ArgRequiredElseHelp), + .arg_required_else_help(true), ) .subcommand( - App::new("add") + Command::new("add") .about("adds things") - .setting(AppSettings::ArgRequiredElseHelp) + .arg_required_else_help(true) .arg(arg!( ... "Stuff to add").allow_invalid_utf8(true)), ) - .get_matches(); + .subcommand( + Command::new("stash") + .args_conflicts_with_subcommands(true) + .args(push_args()) + .subcommand(Command::new("push").args(push_args())) + .subcommand(Command::new("pop").arg(arg!([STASH]))) + .subcommand(Command::new("apply").arg(arg!([STASH]))), + ) +} + +fn push_args() -> Vec> { + vec![arg!(-m --message ).required(false)] +} + +fn main() { + let matches = cli().get_matches(); match matches.subcommand() { Some(("clone", sub_matches)) => { @@ -49,6 +67,26 @@ fn main() { .collect::>(); println!("Adding {:?}", paths); } + Some(("stash", sub_matches)) => { + let stash_command = sub_matches.subcommand().unwrap_or(("push", sub_matches)); + match stash_command { + ("apply", sub_matches) => { + let stash = sub_matches.value_of("STASH"); + println!("Applying {:?}", stash); + } + ("pop", sub_matches) => { + let stash = sub_matches.value_of("STASH"); + println!("Popping {:?}", stash); + } + ("push", sub_matches) => { + let message = sub_matches.value_of("message"); + println!("Pushing {:?}", message); + } + (name, _) => { + unreachable!("Unsupported subcommand `{}`", name) + } + } + } Some((ext, sub_matches)) => { let args = sub_matches .values_of_os("") diff --git a/third_party/rust/clap/examples/keyvalue-derive.md b/third_party/rust/clap/examples/keyvalue-derive.md deleted file mode 100644 index 65748bc39253..000000000000 --- a/third_party/rust/clap/examples/keyvalue-derive.md +++ /dev/null @@ -1,31 +0,0 @@ -*Jump to [source](keyvalue-derive.rs)* - -**This requires enabling the `derive` feature flag.** - -```console -$ keyvalue-derive --help -clap - -USAGE: - keyvalue-derive[EXE] [OPTIONS] - -OPTIONS: - -D - -h, --help Print help information - -$ keyvalue-derive -D Foo=10 -D Alice=30 -Args { defines: [("Foo", 10), ("Alice", 30)] } - -$ keyvalue-derive -D Foo -? failed -error: Invalid value for '-D ': invalid KEY=value: no `=` found in `Foo` - -For more information try --help - -$ keyvalue-derive -D Foo=Bar -? failed -error: Invalid value for '-D ': invalid digit found in string - -For more information try --help - -``` diff --git a/third_party/rust/clap/examples/multicall-busybox.md b/third_party/rust/clap/examples/multicall-busybox.md index 040b0088fe98..a09418403f7e 100644 --- a/third_party/rust/clap/examples/multicall-busybox.md +++ b/third_party/rust/clap/examples/multicall-busybox.md @@ -2,7 +2,7 @@ Example of a busybox-style multicall program -See the documentation for clap::AppSettings::Multicall for rationale. +See the documentation for `clap::Command::multicall` for rationale. This example omits every command except true and false, which are the most trivial to implement, diff --git a/third_party/rust/clap/examples/multicall-busybox.rs b/third_party/rust/clap/examples/multicall-busybox.rs index 9f07aef195a3..30865a8303ff 100644 --- a/third_party/rust/clap/examples/multicall-busybox.rs +++ b/third_party/rust/clap/examples/multicall-busybox.rs @@ -1,20 +1,22 @@ +// Note: this requires the `unstable-multicall` feature + use std::process::exit; -use clap::{App, AppSettings, Arg}; +use clap::{Arg, Command}; -fn applet_commands() -> [App<'static>; 2] { +fn applet_commands() -> [Command<'static>; 2] { [ - App::new("true").about("does nothing successfully"), - App::new("false").about("does nothing unsuccessfully"), + Command::new("true").about("does nothing successfully"), + Command::new("false").about("does nothing unsuccessfully"), ] } fn main() { - let app = App::new(env!("CARGO_CRATE_NAME")) - .setting(AppSettings::Multicall) + let cmd = Command::new(env!("CARGO_CRATE_NAME")) + .multicall(true) .subcommand( - App::new("busybox") - .setting(AppSettings::ArgRequiredElseHelp) + Command::new("busybox") + .arg_required_else_help(true) .subcommand_value_name("APPLET") .subcommand_help_heading("APPLETS") .arg( @@ -24,13 +26,13 @@ fn main() { .exclusive(true) .takes_value(true) .default_missing_value("/usr/local/bin") - .use_delimiter(false), + .use_value_delimiter(false), ) .subcommands(applet_commands()), ) .subcommands(applet_commands()); - let matches = app.get_matches(); + let matches = cmd.get_matches(); let mut subcommand = matches.subcommand(); if let Some(("busybox", cmd)) = subcommand { if cmd.occurrences_of("install") > 0 { diff --git a/third_party/rust/clap/examples/multicall-hostname.md b/third_party/rust/clap/examples/multicall-hostname.md index a59aa0ab8cd0..9e17ca16cbc7 100644 --- a/third_party/rust/clap/examples/multicall-hostname.md +++ b/third_party/rust/clap/examples/multicall-hostname.md @@ -2,7 +2,7 @@ Example of a `hostname-style` multicall program -See the documentation for clap::AppSettings::Multicall for rationale. +See the documentation for `clap::Command::multicall` for rationale. This example omits the implementation of displaying address config diff --git a/third_party/rust/clap/examples/multicall-hostname.rs b/third_party/rust/clap/examples/multicall-hostname.rs index 904c70035073..2c89a14484ab 100644 --- a/third_party/rust/clap/examples/multicall-hostname.rs +++ b/third_party/rust/clap/examples/multicall-hostname.rs @@ -1,16 +1,17 @@ -use clap::{App, AppSettings}; +// Note: this requires the `unstable-multicall` feature + +use clap::Command; fn main() { - let app = App::new(env!("CARGO_CRATE_NAME")) - .setting(AppSettings::ArgRequiredElseHelp) + let cmd = Command::new(env!("CARGO_CRATE_NAME")) + .multicall(true) + .arg_required_else_help(true) .subcommand_value_name("APPLET") .subcommand_help_heading("APPLETS") - .subcommand(App::new("hostname").about("show hostname part of FQDN")) - .subcommand(App::new("dnsdomainname").about("show domain name part of FQDN")); + .subcommand(Command::new("hostname").about("show hostname part of FQDN")) + .subcommand(Command::new("dnsdomainname").about("show domain name part of FQDN")); - let app = app.setting(AppSettings::Multicall); - - match app.get_matches().subcommand_name() { + match cmd.get_matches().subcommand_name() { Some("hostname") => println!("www"), Some("dnsdomainname") => println!("example.com"), _ => unreachable!("parser should ensure only valid subcommand names are used"), diff --git a/third_party/rust/clap/examples/pacman.md b/third_party/rust/clap/examples/pacman.md index daa26dc8b216..7f6c5a7d3396 100644 --- a/third_party/rust/clap/examples/pacman.md +++ b/third_party/rust/clap/examples/pacman.md @@ -34,5 +34,54 @@ Searching for name... ``` *(users can "stack" short subcommands with short flags or with other short flag subcommands)* +In the help, this looks like: +```console +$ pacman -h +pacman 5.2.1 +Pacman Development Team +package manager utility + +USAGE: + pacman[EXE] + +OPTIONS: + -h, --help Print help information + -V, --version Print version information + +SUBCOMMANDS: + help Print this message or the help of the given subcommand(s) + query -Q --query Query the package database. + sync -S --sync Synchronize packages. + +$ pacman -S -h +pacman[EXE]-sync +Synchronize packages. + +USAGE: + pacman[EXE] {sync|--sync|-S} [OPTIONS] [--] [package]... + +ARGS: + ... packages + +OPTIONS: + -h, --help Print help information + -i, --info view package information + -s, --search ... search remote repositories for matching strings + +``` + +And errors: +```console +$ pacman -S -s foo -i bar +? failed +error: The argument '--search ...' cannot be used with '--info' + +USAGE: + pacman[EXE] {sync|--sync|-S} --search ... ... + +For more information try --help + +``` + **NOTE:** Keep in mind that subcommands, flags, and long flags are *case sensitive*: `-Q` and `-q` are different flags/subcommands. For example, you can have both `-Q` subcommand and `-q` flag, and they will be properly disambiguated. Let's make a quick program to illustrate. diff --git a/third_party/rust/clap/examples/pacman.rs b/third_party/rust/clap/examples/pacman.rs index 5b42f0d5b23f..c088a8fe916d 100644 --- a/third_party/rust/clap/examples/pacman.rs +++ b/third_party/rust/clap/examples/pacman.rs @@ -1,16 +1,17 @@ -use clap::{App, AppSettings, Arg}; +use clap::{Arg, Command}; fn main() { - let matches = App::new("pacman") + let matches = Command::new("pacman") .about("package manager utility") .version("5.2.1") - .setting(AppSettings::SubcommandRequiredElseHelp) + .subcommand_required(true) + .arg_required_else_help(true) .author("Pacman Development Team") // Query subcommand // // Only a few of its arguments are implemented below. .subcommand( - App::new("query") + Command::new("query") .short_flag('Q') .long_flag("query") .about("Query the package database.") @@ -37,7 +38,7 @@ fn main() { // // Only a few of its arguments are implemented below. .subcommand( - App::new("sync") + Command::new("sync") .short_flag('S') .long_flag("sync") .about("Synchronize packages.") diff --git a/third_party/rust/clap/examples/repl.rs b/third_party/rust/clap/examples/repl.rs new file mode 100644 index 000000000000..f1b65c3090f1 --- /dev/null +++ b/third_party/rust/clap/examples/repl.rs @@ -0,0 +1,94 @@ +// Note: this requires the `unstable-multicall` feature + +use std::io::Write; + +use clap::Command; + +fn main() -> Result<(), String> { + loop { + let line = readline()?; + let line = line.trim(); + if line.is_empty() { + continue; + } + + match respond(line) { + Ok(quit) => { + if quit { + break; + } + } + Err(err) => { + write!(std::io::stdout(), "{}", err).map_err(|e| e.to_string())?; + std::io::stdout().flush().map_err(|e| e.to_string())?; + } + } + } + + Ok(()) +} + +fn respond(line: &str) -> Result { + let args = shlex::split(line).ok_or("error: Invalid quoting")?; + let matches = cli() + .try_get_matches_from(&args) + .map_err(|e| e.to_string())?; + match matches.subcommand() { + Some(("ping", _matches)) => { + write!(std::io::stdout(), "Pong").map_err(|e| e.to_string())?; + std::io::stdout().flush().map_err(|e| e.to_string())?; + } + Some(("quit", _matches)) => { + write!(std::io::stdout(), "Exiting ...").map_err(|e| e.to_string())?; + std::io::stdout().flush().map_err(|e| e.to_string())?; + return Ok(true); + } + Some((name, _matches)) => unimplemented!("{}", name), + None => unreachable!("subcommand required"), + } + + Ok(false) +} + +fn cli() -> Command<'static> { + // strip out usage + const PARSER_TEMPLATE: &str = "\ + {all-args} + "; + // strip out name/version + const APPLET_TEMPLATE: &str = "\ + {about-with-newline}\n\ + {usage-heading}\n {usage}\n\ + \n\ + {all-args}{after-help}\ + "; + + Command::new("repl") + .multicall(true) + .arg_required_else_help(true) + .subcommand_required(true) + .subcommand_value_name("APPLET") + .subcommand_help_heading("APPLETS") + .help_template(PARSER_TEMPLATE) + .subcommand( + Command::new("ping") + .about("Get a response") + .help_template(APPLET_TEMPLATE), + ) + .subcommand( + Command::new("quit") + .alias("exit") + .about("Quit the REPL") + .help_template(APPLET_TEMPLATE), + ) +} + +fn readline() -> Result { + write!(std::io::stdout(), "$ ").map_err(|e| e.to_string())?; + std::io::stdout().flush().map_err(|e| e.to_string())?; + let mut buffer = String::new(); + std::io::stdin() + .read_line(&mut buffer) + .map_err(|e| e.to_string())?; + Ok(buffer) +} diff --git a/third_party/rust/clap/examples/tutorial_builder/01_quick.rs b/third_party/rust/clap/examples/tutorial_builder/01_quick.rs index f14a91814355..7ab5081dfc64 100644 --- a/third_party/rust/clap/examples/tutorial_builder/01_quick.rs +++ b/third_party/rust/clap/examples/tutorial_builder/01_quick.rs @@ -1,8 +1,10 @@ -use clap::{app_from_crate, arg, App}; +// Note: this requires the `cargo` feature + +use clap::{arg, command, Command}; use std::path::Path; fn main() { - let matches = app_from_crate!() + let matches = command!() .arg(arg!([name] "Optional name to operate on")) .arg( arg!( @@ -17,7 +19,7 @@ fn main() { -d --debug ... "Turn debugging information on" )) .subcommand( - App::new("test") + Command::new("test") .about("does testing things") .arg(arg!(-l --list "lists test values")), ) @@ -43,7 +45,7 @@ fn main() { } // You can check for the existence of subcommands, and if found use their - // matches just as you would the top level app + // matches just as you would the top level cmd if let Some(matches) = matches.subcommand_matches("test") { // "$ myapp test" was run if matches.is_present("list") { diff --git a/third_party/rust/clap/examples/tutorial_builder/02_app_settings.rs b/third_party/rust/clap/examples/tutorial_builder/02_app_settings.rs index 6cfca11f6f84..3485f588eb97 100644 --- a/third_party/rust/clap/examples/tutorial_builder/02_app_settings.rs +++ b/third_party/rust/clap/examples/tutorial_builder/02_app_settings.rs @@ -1,10 +1,12 @@ -use clap::{app_from_crate, arg, AppSettings}; +// Note: this requires the `cargo` feature + +use clap::{arg, command, AppSettings}; fn main() { - let matches = app_from_crate!() - .global_setting(AppSettings::AllArgsOverrideSelf) + let matches = command!() + .args_override_self(true) .global_setting(AppSettings::DeriveDisplayOrder) - .global_setting(AppSettings::AllowNegativeNumbers) + .allow_negative_numbers(true) .arg(arg!(--two )) .arg(arg!(--one )) .get_matches(); diff --git a/third_party/rust/clap/examples/tutorial_builder/02_apps.rs b/third_party/rust/clap/examples/tutorial_builder/02_apps.rs index b607b7f19a79..c9422d64a3a0 100644 --- a/third_party/rust/clap/examples/tutorial_builder/02_apps.rs +++ b/third_party/rust/clap/examples/tutorial_builder/02_apps.rs @@ -1,7 +1,7 @@ -use clap::{arg, App}; +use clap::{arg, Command}; fn main() { - let matches = App::new("MyApp") + let matches = Command::new("MyApp") .version("1.0") .author("Kevin K. ") .about("Does awesome things") diff --git a/third_party/rust/clap/examples/tutorial_builder/02_crate.rs b/third_party/rust/clap/examples/tutorial_builder/02_crate.rs index c36299dab62e..df214f3f5cc5 100644 --- a/third_party/rust/clap/examples/tutorial_builder/02_crate.rs +++ b/third_party/rust/clap/examples/tutorial_builder/02_crate.rs @@ -1,7 +1,9 @@ -use clap::{app_from_crate, arg}; +// Note: this requires the `cargo` feature + +use clap::{arg, command}; fn main() { - let matches = app_from_crate!() + let matches = command!() .arg(arg!(--two )) .arg(arg!(--one )) .get_matches(); diff --git a/third_party/rust/clap/examples/tutorial_builder/03_01_flag_bool.rs b/third_party/rust/clap/examples/tutorial_builder/03_01_flag_bool.rs index f19b65b88ee9..a8d85c4c2ee8 100644 --- a/third_party/rust/clap/examples/tutorial_builder/03_01_flag_bool.rs +++ b/third_party/rust/clap/examples/tutorial_builder/03_01_flag_bool.rs @@ -1,7 +1,9 @@ -use clap::{app_from_crate, arg}; +// Note: this requires the `cargo` feature + +use clap::{arg, command}; fn main() { - let matches = app_from_crate!().arg(arg!(-v - -verbose)).get_matches(); + let matches = command!().arg(arg!(-v - -verbose)).get_matches(); println!("verbose: {:?}", matches.is_present("verbose")); } diff --git a/third_party/rust/clap/examples/tutorial_builder/03_01_flag_count.rs b/third_party/rust/clap/examples/tutorial_builder/03_01_flag_count.rs index 6fc15b26816f..c5532c07a44d 100644 --- a/third_party/rust/clap/examples/tutorial_builder/03_01_flag_count.rs +++ b/third_party/rust/clap/examples/tutorial_builder/03_01_flag_count.rs @@ -1,7 +1,9 @@ -use clap::{app_from_crate, arg}; +// Note: this requires the `cargo` feature + +use clap::{arg, command}; fn main() { - let matches = app_from_crate!().arg(arg!(-v --verbose ...)).get_matches(); + let matches = command!().arg(arg!(-v --verbose ...)).get_matches(); println!("verbose: {:?}", matches.occurrences_of("verbose")); } diff --git a/third_party/rust/clap/examples/tutorial_builder/03_02_option.rs b/third_party/rust/clap/examples/tutorial_builder/03_02_option.rs index d03afe0f7b41..29ad60a99c13 100644 --- a/third_party/rust/clap/examples/tutorial_builder/03_02_option.rs +++ b/third_party/rust/clap/examples/tutorial_builder/03_02_option.rs @@ -1,7 +1,9 @@ -use clap::{app_from_crate, arg}; +// Note: this requires the `cargo` feature + +use clap::{arg, command}; fn main() { - let matches = app_from_crate!() + let matches = command!() .arg(arg!(-n --name ).required(false)) .get_matches(); diff --git a/third_party/rust/clap/examples/tutorial_builder/03_03_positional.rs b/third_party/rust/clap/examples/tutorial_builder/03_03_positional.rs index ea77ccc2ea2c..03bfab8e05a9 100644 --- a/third_party/rust/clap/examples/tutorial_builder/03_03_positional.rs +++ b/third_party/rust/clap/examples/tutorial_builder/03_03_positional.rs @@ -1,7 +1,9 @@ -use clap::{app_from_crate, arg}; +// Note: this requires the `cargo` feature + +use clap::{arg, command}; fn main() { - let matches = app_from_crate!().arg(arg!([NAME])).get_matches(); + let matches = command!().arg(arg!([NAME])).get_matches(); println!("NAME: {:?}", matches.value_of("NAME")); } diff --git a/third_party/rust/clap/examples/tutorial_builder/03_04_subcommands.rs b/third_party/rust/clap/examples/tutorial_builder/03_04_subcommands.rs index f414ccd5dfb1..1ab79e714d26 100644 --- a/third_party/rust/clap/examples/tutorial_builder/03_04_subcommands.rs +++ b/third_party/rust/clap/examples/tutorial_builder/03_04_subcommands.rs @@ -1,12 +1,14 @@ -use clap::{app_from_crate, arg, App, AppSettings}; +// Note: this requires the `cargo` feature + +use clap::{arg, command, Command}; fn main() { - let matches = app_from_crate!() - .global_setting(AppSettings::PropagateVersion) - .global_setting(AppSettings::UseLongFormatForHelpSubcommand) - .setting(AppSettings::SubcommandRequiredElseHelp) + let matches = command!() + .propagate_version(true) + .subcommand_required(true) + .arg_required_else_help(true) .subcommand( - App::new("add") + Command::new("add") .about("Adds files to myapp") .arg(arg!([NAME])), ) @@ -17,6 +19,6 @@ fn main() { "'myapp add' was used, name is: {:?}", sub_matches.value_of("NAME") ), - _ => unreachable!(), + _ => unreachable!("Exhausted list of subcommands and subcommand_required prevents `None`"), } } diff --git a/third_party/rust/clap/examples/tutorial_builder/03_05_default_values.rs b/third_party/rust/clap/examples/tutorial_builder/03_05_default_values.rs index 28bef52b6d87..f88e3953c8d6 100644 --- a/third_party/rust/clap/examples/tutorial_builder/03_05_default_values.rs +++ b/third_party/rust/clap/examples/tutorial_builder/03_05_default_values.rs @@ -1,7 +1,9 @@ -use clap::{app_from_crate, arg}; +// Note: this requires the `cargo` feature + +use clap::{arg, command}; fn main() { - let matches = app_from_crate!() + let matches = command!() .arg(arg!([NAME]).default_value("alice")) .get_matches(); diff --git a/third_party/rust/clap/examples/tutorial_builder/04_01_enum.rs b/third_party/rust/clap/examples/tutorial_builder/04_01_enum.rs index c3ac8be1bdd7..128b3cdc4a35 100644 --- a/third_party/rust/clap/examples/tutorial_builder/04_01_enum.rs +++ b/third_party/rust/clap/examples/tutorial_builder/04_01_enum.rs @@ -1,4 +1,6 @@ -use clap::{app_from_crate, arg, ArgEnum, PossibleValue}; +// Note: this requires the `cargo` feature + +use clap::{arg, command, ArgEnum, PossibleValue}; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ArgEnum)] enum Mode { @@ -37,7 +39,7 @@ impl std::str::FromStr for Mode { } fn main() { - let matches = app_from_crate!() + let matches = command!() .arg( arg!() .help("What mode to run the program in") diff --git a/third_party/rust/clap/examples/tutorial_builder/04_01_possible.rs b/third_party/rust/clap/examples/tutorial_builder/04_01_possible.rs index 33ca131a1379..77160392a3d6 100644 --- a/third_party/rust/clap/examples/tutorial_builder/04_01_possible.rs +++ b/third_party/rust/clap/examples/tutorial_builder/04_01_possible.rs @@ -1,7 +1,9 @@ -use clap::{app_from_crate, arg}; +// Note: this requires the `cargo` feature + +use clap::{arg, command}; fn main() { - let matches = app_from_crate!() + let matches = command!() .arg( arg!() .help("What mode to run the program in") diff --git a/third_party/rust/clap/examples/tutorial_builder/04_02_parse.rs b/third_party/rust/clap/examples/tutorial_builder/04_02_parse.rs new file mode 100644 index 000000000000..e354af3a8a0e --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_builder/04_02_parse.rs @@ -0,0 +1,19 @@ +// Note: this requires the `cargo` feature + +use clap::{arg, command}; + +fn main() { + let matches = command!() + .arg( + arg!() + .help("Network port to use") + .validator(|s| s.parse::()), + ) + .get_matches(); + + // Note, it's safe to call unwrap() because the arg is required + let port: usize = matches + .value_of_t("PORT") + .expect("'PORT' is required and parsing will fail if its missing"); + println!("PORT = {}", port); +} diff --git a/third_party/rust/clap/examples/tutorial_builder/04_02_validate.rs b/third_party/rust/clap/examples/tutorial_builder/04_02_validate.rs index ff2bebbe8336..a49f869e0017 100644 --- a/third_party/rust/clap/examples/tutorial_builder/04_02_validate.rs +++ b/third_party/rust/clap/examples/tutorial_builder/04_02_validate.rs @@ -1,11 +1,15 @@ -use clap::{app_from_crate, arg}; +// Note: this requires the `cargo` feature + +use std::ops::RangeInclusive; + +use clap::{arg, command}; fn main() { - let matches = app_from_crate!() + let matches = command!() .arg( arg!() .help("Network port to use") - .validator(|s| s.parse::()), + .validator(port_in_range), ) .get_matches(); @@ -15,3 +19,20 @@ fn main() { .expect("'PORT' is required and parsing will fail if its missing"); println!("PORT = {}", port); } + +const PORT_RANGE: RangeInclusive = 1..=65535; + +fn port_in_range(s: &str) -> Result<(), String> { + let port: usize = s + .parse() + .map_err(|_| format!("`{}` isn't a port number", s))?; + if PORT_RANGE.contains(&port) { + Ok(()) + } else { + Err(format!( + "Port not in range {}-{}", + PORT_RANGE.start(), + PORT_RANGE.end() + )) + } +} diff --git a/third_party/rust/clap/examples/tutorial_builder/04_03_relations.rs b/third_party/rust/clap/examples/tutorial_builder/04_03_relations.rs index fdd74411b38d..495bf365cc94 100644 --- a/third_party/rust/clap/examples/tutorial_builder/04_03_relations.rs +++ b/third_party/rust/clap/examples/tutorial_builder/04_03_relations.rs @@ -1,8 +1,10 @@ -use clap::{app_from_crate, arg, ArgGroup}; +// Note: this requires the `cargo` feature + +use clap::{arg, command, ArgGroup}; fn main() { // Create application like normal - let matches = app_from_crate!() + let matches = command!() // Add the version arguments .arg(arg!(--"set-ver" "set version manually").required(false)) .arg(arg!(--major "auto inc major")) diff --git a/third_party/rust/clap/examples/tutorial_builder/04_04_custom.rs b/third_party/rust/clap/examples/tutorial_builder/04_04_custom.rs index 2a4e9b8556c2..6993329d32d2 100644 --- a/third_party/rust/clap/examples/tutorial_builder/04_04_custom.rs +++ b/third_party/rust/clap/examples/tutorial_builder/04_04_custom.rs @@ -1,8 +1,10 @@ -use clap::{app_from_crate, arg, ErrorKind}; +// Note: this requires the `cargo` feature + +use clap::{arg, command, ErrorKind}; fn main() { // Create application like normal - let mut app = app_from_crate!() + let mut cmd = command!() // Add the version arguments .arg(arg!(--"set-ver" "set version manually").required(false)) .arg(arg!(--major "auto inc major")) @@ -15,7 +17,7 @@ fn main() { // Now let's assume we have a -c [config] argument which requires one of // (but **not** both) the "input" arguments .arg(arg!(config: -c ).required(false)); - let matches = app.get_matches_mut(); + let matches = cmd.get_matches_mut(); // Let's assume the old version 1.2.3 let mut major = 1; @@ -26,7 +28,7 @@ fn main() { let version = if let Some(ver) = matches.value_of("set-ver") { if matches.is_present("major") || matches.is_present("minor") || matches.is_present("patch") { - app.error( + cmd.error( ErrorKind::ArgumentConflict, "Can't do relative and absolute version change", ) @@ -45,9 +47,9 @@ fn main() { (false, true, false) => minor += 1, (false, false, true) => patch += 1, _ => { - app.error( + cmd.error( ErrorKind::ArgumentConflict, - "Cam only modify one version field", + "Can only modify one version field", ) .exit(); } @@ -63,7 +65,7 @@ fn main() { .value_of("INPUT_FILE") .or_else(|| matches.value_of("spec-in")) .unwrap_or_else(|| { - app.error( + cmd.error( ErrorKind::MissingRequiredArgument, "INPUT_FILE or --spec-in is required when using --config", ) diff --git a/third_party/rust/clap/examples/tutorial_builder/05_01_assert.rs b/third_party/rust/clap/examples/tutorial_builder/05_01_assert.rs index 034367e79647..f40f6ebd209b 100644 --- a/third_party/rust/clap/examples/tutorial_builder/05_01_assert.rs +++ b/third_party/rust/clap/examples/tutorial_builder/05_01_assert.rs @@ -1,7 +1,9 @@ -use clap::{app_from_crate, arg}; +// Note: this requires the `cargo` feature + +use clap::{arg, command}; fn main() { - let matches = app().get_matches(); + let matches = cmd().get_matches(); // Note, it's safe to call unwrap() because the arg is required let port: usize = matches @@ -10,8 +12,8 @@ fn main() { println!("PORT = {}", port); } -fn app() -> clap::App<'static> { - app_from_crate!().arg( +fn cmd() -> clap::Command<'static> { + command!().arg( arg!() .help("Network port to use") .validator(|s| s.parse::()), @@ -20,5 +22,5 @@ fn app() -> clap::App<'static> { #[test] fn verify_app() { - app().debug_assert(); + cmd().debug_assert(); } diff --git a/third_party/rust/clap/examples/tutorial_builder/README.md b/third_party/rust/clap/examples/tutorial_builder/README.md index 2679806f926b..fcbda4b3a0e9 100644 --- a/third_party/rust/clap/examples/tutorial_builder/README.md +++ b/third_party/rust/clap/examples/tutorial_builder/README.md @@ -5,9 +5,9 @@ 1. [Quick Start](#quick-start) 2. [Configuring the Parser](#configuring-the-parser) 3. [Adding Arguments](#adding-arguments) - 1. [Flags](#flags) + 1. [Positionals](#positionals) 2. [Options](#options) - 3. [Positionals](#positionals) + 3. [Flags](#flags) 4. [Subcommands](#subcommands) 5. [Defaults](#defaults) 4. Validation @@ -63,7 +63,7 @@ Not printing testing lists... ## Configuring the Parser -You use the `App` the start building a parser. +You use the `Command` the start building a parser. [Example:](02_apps.rs) ```console @@ -86,7 +86,7 @@ MyApp 1.0 ``` -You can use `app_from_crate!()` to fill these fields in from your `Cargo.toml` +You can use `command!()` to fill these fields in from your `Cargo.toml` file. **This requires the `cargo` feature flag.** [Example:](02_crate.rs) @@ -109,9 +109,7 @@ clap [..] ``` -You can use `AppSettings` to change the application level behavior of clap. You -can apply the setting to the top level command (`app.setting()`) or to it and -all subcommands (`app.global_setting()`). +You can use `Command` methods to change the application level behavior of clap. [Example:](02_app_settings.rs) ```console @@ -136,9 +134,78 @@ one: "-3" ## Adding Arguments +### Positionals + +You can have users specify values by their position on the command-line: + +[Example:](03_03_positional.rs) +```console +$ 03_03_positional --help +clap [..] +A simple to use, efficient, and full-featured Command Line Argument Parser + +USAGE: + 03_03_positional[EXE] [NAME] + +ARGS: + + +OPTIONS: + -h, --help Print help information + -V, --version Print version information + +$ 03_03_positional +NAME: None + +$ 03_03_positional bob +NAME: Some("bob") + +``` + +### Options + +You can name your arguments with a flag: +- Order doesn't matter +- They can be optional +- Intent is clearer + +[Example:](03_02_option.rs) +```console +$ 03_02_option --help +clap [..] +A simple to use, efficient, and full-featured Command Line Argument Parser + +USAGE: + 03_02_option[EXE] [OPTIONS] + +OPTIONS: + -h, --help Print help information + -n, --name + -V, --version Print version information + +$ 03_02_option +name: None + +$ 03_02_option --name bob +name: Some("bob") + +$ 03_02_option --name=bob +name: Some("bob") + +$ 03_02_option -n bob +name: Some("bob") + +$ 03_02_option -n=bob +name: Some("bob") + +$ 03_02_option -nbob +name: Some("bob") + +``` + ### Flags -Flags are switches that can be on/off: +Flags can also be switches that can be on/off: [Example:](03_01_flag_bool.rs) ```console @@ -198,96 +265,14 @@ verbose: 2 ``` -### Options - -Flags can also accept a value. - -[Example:](03_02_option.rs) -```console -$ 03_02_option --help -clap [..] -A simple to use, efficient, and full-featured Command Line Argument Parser - -USAGE: - 03_02_option[EXE] [OPTIONS] - -OPTIONS: - -h, --help Print help information - -n, --name - -V, --version Print version information - -$ 03_02_option -name: None - -$ 03_02_option --name bob -name: Some("bob") - -$ 03_02_option --name=bob -name: Some("bob") - -$ 03_02_option -n bob -name: Some("bob") - -$ 03_02_option -n=bob -name: Some("bob") - -$ 03_02_option -nbob -name: Some("bob") - -``` - -### Positionals - -Or you can have users specify values by their position on the command-line: - -[Example:](03_03_positional.rs) -```console -$ 03_03_positional --help -clap [..] -A simple to use, efficient, and full-featured Command Line Argument Parser - -USAGE: - 03_03_positional[EXE] [NAME] - -ARGS: - - -OPTIONS: - -h, --help Print help information - -V, --version Print version information - -$ 03_03_positional -NAME: None - -$ 03_03_positional bob -NAME: Some("bob") - -``` - ### Subcommands -Subcommands are defined as `App`s that get added via `App::subcommand`. Each +Subcommands are defined as `Command`s that get added via `Command::subcommand`. Each instance of a Subcommand can have its own version, author(s), Args, and even its own subcommands. [Example:](03_04_subcommands.rs) ```console -$ 03_04_subcommands -? failed -clap [..] -A simple to use, efficient, and full-featured Command Line Argument Parser - -USAGE: - 03_04_subcommands[EXE] - -OPTIONS: - -h, --help Print help information - -V, --version Print version information - -SUBCOMMANDS: - add Adds files to myapp - help Print this message or the help of the given subcommand(s) - $ 03_04_subcommands help clap [..] A simple to use, efficient, and full-featured Command Line Argument Parser @@ -322,7 +307,27 @@ $ 03_04_subcommands add bob ``` -Because we set `AppSettings::PropagateVersion`: +Because we set `Command::arg_required_else_help`: +```console +$ 03_04_subcommands +? failed +clap [..] +A simple to use, efficient, and full-featured Command Line Argument Parser + +USAGE: + 03_04_subcommands[EXE] + +OPTIONS: + -h, --help Print help information + -V, --version Print version information + +SUBCOMMANDS: + add Adds files to myapp + help Print this message or the help of the given subcommand(s) + +``` + +Because we set `Command::propagate_version`: ```console $ 03_04_subcommands --version clap [..] @@ -445,7 +450,36 @@ For more information try --help ### Validated values -More generally, you can validate and parse into any data type. +More generally, you can parse into any data type. + +[Example:](04_02_parse.rs) +```console +$ 04_02_parse --help +clap [..] +A simple to use, efficient, and full-featured Command Line Argument Parser + +USAGE: + 04_02_parse[EXE] + +ARGS: + Network port to use + +OPTIONS: + -h, --help Print help information + -V, --version Print version information + +$ 04_02_parse 22 +PORT = 22 + +$ 04_02_parse foobar +? failed +error: Invalid value "foobar" for '': invalid digit found in string + +For more information try --help + +``` + +A custom validator can be used to improve the error messages or provide additional validation: [Example:](04_02_validate.rs) ```console @@ -468,7 +502,13 @@ PORT = 22 $ 04_02_validate foobar ? failed -error: Invalid value for '': invalid digit found in string +error: Invalid value "foobar" for '': `foobar` isn't a port number + +For more information try --help + +$ 04_02_validate 0 +? failed +error: Invalid value "0" for '': Port not in range 1-65535 For more information try --help @@ -574,7 +614,7 @@ OPTIONS: $ 04_04_custom ? failed -error: Cam only modify one version field +error: Can only modify one version field USAGE: 04_04_custom[EXE] [OPTIONS] [INPUT_FILE] @@ -586,7 +626,7 @@ Version: 2.2.3 $ 04_04_custom --major --minor ? failed -error: Cam only modify one version field +error: Can only modify one version field USAGE: 04_04_custom[EXE] [OPTIONS] [INPUT_FILE] @@ -611,11 +651,13 @@ Doing work using input input.txt and config config.toml ## Tips -- Proactively check for bad `App` configurations by calling `App::debug_assert` ([example](05_01_assert.rs)) +- For more complex demonstration of features, see our [examples](../README.md). +- Proactively check for bad `Command` configurations by calling `Command::debug_assert` in a test ([example](05_01_assert.rs)) ## Contributing New example code: +- Please update the corresponding section in the [derive tutorial](../tutorial_derive/README.md) - Building: They must be added to [Cargo.toml](../../Cargo.toml) with the appropriate `required-features`. - Testing: Ensure there is a markdown file with [trycmd](https://docs.rs/trycmd) syntax (generally they'll go in here). diff --git a/third_party/rust/clap/examples/tutorial_derive/01_quick.rs b/third_party/rust/clap/examples/tutorial_derive/01_quick.rs index 9bd1c52ccc60..f840b8a9b2f9 100644 --- a/third_party/rust/clap/examples/tutorial_derive/01_quick.rs +++ b/third_party/rust/clap/examples/tutorial_derive/01_quick.rs @@ -52,7 +52,7 @@ fn main() { } // You can check for the existence of subcommands, and if found use their - // matches just as you would the top level app + // matches just as you would the top level cmd match &cli.command { Some(Commands::Test { list }) => { if *list { diff --git a/third_party/rust/clap/examples/tutorial_derive/02_app_settings.rs b/third_party/rust/clap/examples/tutorial_derive/02_app_settings.rs index 6c9fbd73cb98..bccd353f6029 100644 --- a/third_party/rust/clap/examples/tutorial_derive/02_app_settings.rs +++ b/third_party/rust/clap/examples/tutorial_derive/02_app_settings.rs @@ -2,9 +2,9 @@ use clap::{AppSettings, Parser}; #[derive(Parser)] #[clap(author, version, about, long_about = None)] -#[clap(global_setting(AppSettings::AllArgsOverrideSelf))] +#[clap(args_override_self = true)] +#[clap(allow_negative_numbers = true)] #[clap(global_setting(AppSettings::DeriveDisplayOrder))] -#[clap(global_setting(AppSettings::AllowNegativeNumbers))] struct Cli { #[clap(long)] two: String, diff --git a/third_party/rust/clap/examples/tutorial_derive/03_04_subcommands.rs b/third_party/rust/clap/examples/tutorial_derive/03_04_subcommands.rs index 8e272dcdd0a2..86cf444c21bc 100644 --- a/third_party/rust/clap/examples/tutorial_derive/03_04_subcommands.rs +++ b/third_party/rust/clap/examples/tutorial_derive/03_04_subcommands.rs @@ -1,9 +1,8 @@ -use clap::{AppSettings, Parser, Subcommand}; +use clap::{Parser, Subcommand}; #[derive(Parser)] #[clap(author, version, about, long_about = None)] -#[clap(global_setting(AppSettings::PropagateVersion))] -#[clap(global_setting(AppSettings::UseLongFormatForHelpSubcommand))] +#[clap(propagate_version = true)] struct Cli { #[clap(subcommand)] command: Commands, @@ -19,7 +18,7 @@ fn main() { let cli = Cli::parse(); // You can check for the existence of subcommands, and if found use their - // matches just as you would the top level app + // matches just as you would the top level cmd match &cli.command { Commands::Add { name } => { println!("'myapp add' was used, name is: {:?}", name) diff --git a/third_party/rust/clap/examples/tutorial_derive/03_04_subcommands_alt.rs b/third_party/rust/clap/examples/tutorial_derive/03_04_subcommands_alt.rs new file mode 100644 index 000000000000..0a5b60682d42 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/03_04_subcommands_alt.rs @@ -0,0 +1,32 @@ +use clap::{Args, Parser, Subcommand}; + +#[derive(Parser)] +#[clap(author, version, about, long_about = None)] +#[clap(propagate_version = true)] +struct Cli { + #[clap(subcommand)] + command: Commands, +} + +#[derive(Subcommand)] +enum Commands { + /// Adds files to myapp + Add(Add), +} + +#[derive(Args)] +struct Add { + name: Option, +} + +fn main() { + let cli = Cli::parse(); + + // You can check for the existence of subcommands, and if found use their + // matches just as you would the top level cmd + match &cli.command { + Commands::Add(name) => { + println!("'myapp add' was used, name is: {:?}", name.name) + } + } +} diff --git a/third_party/rust/clap/examples/tutorial_derive/04_02_parse.rs b/third_party/rust/clap/examples/tutorial_derive/04_02_parse.rs new file mode 100644 index 000000000000..5f4cbadc0452 --- /dev/null +++ b/third_party/rust/clap/examples/tutorial_derive/04_02_parse.rs @@ -0,0 +1,15 @@ +use clap::Parser; + +#[derive(Parser)] +#[clap(author, version, about, long_about = None)] +struct Cli { + /// Network port to use + #[clap(parse(try_from_str))] + port: usize, +} + +fn main() { + let cli = Cli::parse(); + + println!("PORT = {}", cli.port); +} diff --git a/third_party/rust/clap/examples/tutorial_derive/04_02_validate.rs b/third_party/rust/clap/examples/tutorial_derive/04_02_validate.rs index 5f4cbadc0452..434f40c869ff 100644 --- a/third_party/rust/clap/examples/tutorial_derive/04_02_validate.rs +++ b/third_party/rust/clap/examples/tutorial_derive/04_02_validate.rs @@ -1,10 +1,12 @@ +use std::ops::RangeInclusive; + use clap::Parser; #[derive(Parser)] #[clap(author, version, about, long_about = None)] struct Cli { /// Network port to use - #[clap(parse(try_from_str))] + #[clap(parse(try_from_str=port_in_range))] port: usize, } @@ -13,3 +15,20 @@ fn main() { println!("PORT = {}", cli.port); } + +const PORT_RANGE: RangeInclusive = 1..=65535; + +fn port_in_range(s: &str) -> Result { + let port: usize = s + .parse() + .map_err(|_| format!("`{}` isn't a port number", s))?; + if PORT_RANGE.contains(&port) { + Ok(port) + } else { + Err(format!( + "Port not in range {}-{}", + PORT_RANGE.start(), + PORT_RANGE.end() + )) + } +} diff --git a/third_party/rust/clap/examples/tutorial_derive/04_04_custom.rs b/third_party/rust/clap/examples/tutorial_derive/04_04_custom.rs index e3c1387d4621..a03345b82973 100644 --- a/third_party/rust/clap/examples/tutorial_derive/04_04_custom.rs +++ b/third_party/rust/clap/examples/tutorial_derive/04_04_custom.rs @@ -1,4 +1,4 @@ -use clap::{ErrorKind, IntoApp, Parser}; +use clap::{CommandFactory, ErrorKind, Parser}; #[derive(Parser)] #[clap(author, version, about, long_about = None)] @@ -41,8 +41,8 @@ fn main() { // See if --set-ver was used to set the version manually let version = if let Some(ver) = cli.set_ver.as_deref() { if cli.major || cli.minor || cli.patch { - let mut app = Cli::into_app(); - app.error( + let mut cmd = Cli::command(); + cmd.error( ErrorKind::ArgumentConflict, "Can't do relative and absolute version change", ) @@ -57,10 +57,10 @@ fn main() { (false, true, false) => minor += 1, (false, false, true) => patch += 1, _ => { - let mut app = Cli::into_app(); - app.error( + let mut cmd = Cli::command(); + cmd.error( ErrorKind::ArgumentConflict, - "Cam only modify one version field", + "Can only modify one version field", ) .exit(); } @@ -80,8 +80,8 @@ fn main() { // 'or' is preferred to 'or_else' here since `Option::as_deref` is 'const' .or(cli.spec_in.as_deref()) .unwrap_or_else(|| { - let mut app = Cli::into_app(); - app.error( + let mut cmd = Cli::command(); + cmd.error( ErrorKind::MissingRequiredArgument, "INPUT_FILE or --spec-in is required when using --config", ) diff --git a/third_party/rust/clap/examples/tutorial_derive/05_01_assert.rs b/third_party/rust/clap/examples/tutorial_derive/05_01_assert.rs index 52dd5d2cbaba..12fdba9b9232 100644 --- a/third_party/rust/clap/examples/tutorial_derive/05_01_assert.rs +++ b/third_party/rust/clap/examples/tutorial_derive/05_01_assert.rs @@ -16,6 +16,6 @@ fn main() { #[test] fn verify_app() { - use clap::IntoApp; - Cli::into_app().debug_assert() + use clap::CommandFactory; + Cli::command().debug_assert() } diff --git a/third_party/rust/clap/examples/tutorial_derive/README.md b/third_party/rust/clap/examples/tutorial_derive/README.md index 14e627fae401..706e76c09e73 100644 --- a/third_party/rust/clap/examples/tutorial_derive/README.md +++ b/third_party/rust/clap/examples/tutorial_derive/README.md @@ -5,9 +5,9 @@ 1. [Quick Start](#quick-start) 2. [Configuring the Parser](#configuring-the-parser) 3. [Adding Arguments](#adding-arguments) - 1. [Flags](#flags) + 1. [Positionals](#positionals) 2. [Options](#options) - 3. [Positionals](#positionals) + 3. [Flags](#flags) 4. [Subcommands](#subcommands) 5. [Defaults](#defaults) 4. Validation @@ -66,7 +66,7 @@ In addition to this tutorial, see the [derive reference](../derive_ref/README.md ## Configuring the Parser -You use the `App` the start building a parser. +You use derive `Parser` the start building a parser. [Example:](02_apps.rs) ```console @@ -89,7 +89,7 @@ MyApp 1.0 ``` -You can use `app_from_crate!()` to fill these fields in from your `Cargo.toml` file. +You can use `#[clap(author, version, about)]` attribute defaults to fill these fields in from your `Cargo.toml` file. [Example:](02_crate.rs) ```console @@ -111,9 +111,7 @@ clap [..] ``` -You can use `AppSettings` to change the application level behavior of clap. You -can apply the setting to the top level command (`app.setting()`) or to it and -all subcommands (`app.global_setting()`). +You can use attributes to change the application level behavior of clap. Any `Command` builder function can be used as an attribute. [Example:](02_app_settings.rs) ```console @@ -138,71 +136,44 @@ one: "-3" ## Adding Arguments -### Flags +### Positionals -Flags are switches that can be on/off: +You can have users specify values by their position on the command-line: -[Example:](03_01_flag_bool.rs) +[Example:](03_03_positional.rs) ```console -$ 03_01_flag_bool_derive --help +$ 03_03_positional_derive --help clap [..] A simple to use, efficient, and full-featured Command Line Argument Parser USAGE: - 03_01_flag_bool_derive[EXE] [OPTIONS] + 03_03_positional_derive[EXE] [NAME] + +ARGS: + OPTIONS: -h, --help Print help information - -v, --verbose -V, --version Print version information -$ 03_01_flag_bool_derive -verbose: false +$ 03_03_positional_derive +name: None -$ 03_01_flag_bool_derive --verbose -verbose: true - -$ 03_01_flag_bool_derive --verbose --verbose -? failed -error: The argument '--verbose' was provided more than once, but cannot be used multiple times - -USAGE: - 03_01_flag_bool_derive[EXE] [OPTIONS] - -For more information try --help - -``` - -Or counted. - -[Example:](03_01_flag_count.rs) -```console -$ 03_01_flag_count_derive --help -clap [..] -A simple to use, efficient, and full-featured Command Line Argument Parser - -USAGE: - 03_01_flag_count_derive[EXE] [OPTIONS] - -OPTIONS: - -h, --help Print help information - -v, --verbose - -V, --version Print version information - -$ 03_01_flag_count_derive -verbose: 0 - -$ 03_01_flag_count_derive --verbose -verbose: 1 - -$ 03_01_flag_count_derive --verbose --verbose -verbose: 2 +$ 03_03_positional_derive bob +name: Some("bob") ``` ### Options -Flags can also accept a value. +You can name your arguments with a flag: +- Order doesn't matter +- They can be optional +- Intent is clearer + +The `#[clap(short = 'c')]` and `#[clap(long = "name")]` attributes that define +the flags are `Arg` methods that are derived from the field name when no value +is specified (`#[clap(short)]` and `#[clap(long)]`). [Example:](03_02_option.rs) ```console @@ -238,58 +209,78 @@ name: Some("bob") ``` -### Positionals +### Flags -Or you can have users specify values by their position on the command-line: +Flags can also be switches that can be on/off. This is enabled via the +`#[clap(parse(from_flag)]` attribute though this is implied when the field is a +`bool`. -[Example:](03_03_positional.rs) +[Example:](03_01_flag_bool.rs) ```console -$ 03_03_positional_derive --help +$ 03_01_flag_bool_derive --help clap [..] A simple to use, efficient, and full-featured Command Line Argument Parser USAGE: - 03_03_positional_derive[EXE] [NAME] - -ARGS: - + 03_01_flag_bool_derive[EXE] [OPTIONS] OPTIONS: -h, --help Print help information + -v, --verbose -V, --version Print version information -$ 03_03_positional_derive -name: None +$ 03_01_flag_bool_derive +verbose: false -$ 03_03_positional_derive bob -name: Some("bob") +$ 03_01_flag_bool_derive --verbose +verbose: true + +$ 03_01_flag_bool_derive --verbose --verbose +? failed +error: The argument '--verbose' was provided more than once, but cannot be used multiple times + +USAGE: + 03_01_flag_bool_derive[EXE] [OPTIONS] + +For more information try --help + +``` + +Or counted with `#[clap(parse(from_occurrences))]`: + +[Example:](03_01_flag_count.rs) +```console +$ 03_01_flag_count_derive --help +clap [..] +A simple to use, efficient, and full-featured Command Line Argument Parser + +USAGE: + 03_01_flag_count_derive[EXE] [OPTIONS] + +OPTIONS: + -h, --help Print help information + -v, --verbose + -V, --version Print version information + +$ 03_01_flag_count_derive +verbose: 0 + +$ 03_01_flag_count_derive --verbose +verbose: 1 + +$ 03_01_flag_count_derive --verbose --verbose +verbose: 2 ``` ### Subcommands -Subcommands are defined as `App`s that get added via `App::subcommand`. Each +Subcommands are derived with `#[derive(Subcommand)]` and be added via `#[clap(subcommand)]` attribute. Each instance of a Subcommand can have its own version, author(s), Args, and even its own subcommands. [Example:](03_04_subcommands.rs) ```console -$ 03_04_subcommands_derive -? failed -clap [..] -A simple to use, efficient, and full-featured Command Line Argument Parser - -USAGE: - 03_04_subcommands_derive[EXE] - -OPTIONS: - -h, --help Print help information - -V, --version Print version information - -SUBCOMMANDS: - add Adds files to myapp - help Print this message or the help of the given subcommand(s) - $ 03_04_subcommands_derive help clap [..] A simple to use, efficient, and full-featured Command Line Argument Parser @@ -324,7 +315,31 @@ $ 03_04_subcommands_derive add bob ``` -Because we set `AppSettings::PropagateVersion`: +Above, we used a struct-variant to define the `add` subcommand. Alternatively, +you can +[use a struct for your subcommand's arguments](03_04_subcommands_alt.rs). + +Because we used `command: Commands` instead of `command: Option`: +```console +$ 03_04_subcommands_derive +? failed +clap [..] +A simple to use, efficient, and full-featured Command Line Argument Parser + +USAGE: + 03_04_subcommands_derive[EXE] + +OPTIONS: + -h, --help Print help information + -V, --version Print version information + +SUBCOMMANDS: + add Adds files to myapp + help Print this message or the help of the given subcommand(s) + +``` + +Because we added `#[clap(propagate_version = true)]`: ```console $ 03_04_subcommands_derive --version clap [..] @@ -337,8 +352,8 @@ $ 03_04_subcommands_derive add --version ### Defaults We've previously showed that arguments can be `required` or optional. When -optional, you work with a `Option` and can `unwrap_or`. Alternatively, you can -set `Arg::default_value`. +optional, you work with an `Option` and can `unwrap_or`. Alternatively, you can +set `#[clap(default_value_t)]`. [Example:](03_05_default_values.rs) ```console @@ -413,6 +428,35 @@ For more information try --help More generally, you can validate and parse into any data type. +[Example:](04_02_parse.rs) +```console +$ 04_02_parse_derive --help +clap [..] +A simple to use, efficient, and full-featured Command Line Argument Parser + +USAGE: + 04_02_parse_derive[EXE] + +ARGS: + Network port to use + +OPTIONS: + -h, --help Print help information + -V, --version Print version information + +$ 04_02_parse_derive 22 +PORT = 22 + +$ 04_02_parse_derive foobar +? failed +error: Invalid value "foobar" for '': invalid digit found in string + +For more information try --help + +``` + +A custom parser can be used to improve the error messages or provide additional validation: + [Example:](04_02_validate.rs) ```console $ 04_02_validate_derive --help @@ -434,7 +478,13 @@ PORT = 22 $ 04_02_validate_derive foobar ? failed -error: Invalid value for '': invalid digit found in string +error: Invalid value "foobar" for '': `foobar` isn't a port number + +For more information try --help + +$ 04_02_validate_derive 0 +? failed +error: Invalid value "0" for '': Port not in range 1-65535 For more information try --help @@ -540,7 +590,7 @@ OPTIONS: $ 04_04_custom_derive ? failed -error: Cam only modify one version field +error: Can only modify one version field USAGE: clap [OPTIONS] [INPUT_FILE] @@ -552,7 +602,7 @@ Version: 2.2.3 $ 04_04_custom_derive --major --minor ? failed -error: Cam only modify one version field +error: Can only modify one version field USAGE: clap [OPTIONS] [INPUT_FILE] @@ -577,11 +627,15 @@ Doing work using input input.txt and config config.toml ## Tips -- Proactively check for bad `App` configurations by calling `App::debug_assert` ([example](05_01_assert.rs)) +- For more complex demonstration of features, see our [examples](../README.md). +- See the [derive reference](../derive_ref/README.md) to understand how to use + anything in the [builder API](https://docs.rs/clap/) in the derive API. +- Proactively check for bad `Command` configurations by calling `Command::debug_assert` in a test ([example](05_01_assert.rs)) ## Contributing New example code: +- Please update the corresponding section in the [builder tutorial](../tutorial_builder/README.md) - Building: They must be added to [Cargo.toml](../../Cargo.toml) with the appropriate `required-features`. - Testing: Ensure there is a markdown file with [trycmd](https://docs.rs/trycmd) syntax (generally they'll go in here). diff --git a/third_party/rust/clap/examples/typed-derive.md b/third_party/rust/clap/examples/typed-derive.md new file mode 100644 index 000000000000..7c61f70d9a9c --- /dev/null +++ b/third_party/rust/clap/examples/typed-derive.md @@ -0,0 +1,86 @@ +*Jump to [source](typed-derive.rs)* + +**This requires enabling the `derive` feature flag.** + +Help: +```console +$ typed-derive --help +clap + +USAGE: + typed-derive[EXE] [OPTIONS] + +OPTIONS: + --bind Handle IP addresses + -D Hand-written parser for tuples + -h, --help Print help information + -I Allow invalid UTF-8 paths + -O Implicitly using `std::str::FromStr` + --sleep Allow human-readable durations + +``` + +Optimization-level (number) +```console +$ typed-derive -O 1 +Args { optimization: Some(1), include: None, bind: None, sleep: None, defines: [] } + +$ typed-derive -O plaid +? failed +error: Invalid value "plaid" for '-O ': invalid digit found in string + +For more information try --help + +``` + +Include (path) +```console +$ typed-derive -I../hello +Args { optimization: None, include: Some("../hello"), bind: None, sleep: None, defines: [] } + +``` + +IP Address +```console +$ typed-derive --bind 192.0.0.1 +Args { optimization: None, include: None, bind: Some(192.0.0.1), sleep: None, defines: [] } + +$ typed-derive --bind localhost +? failed +error: Invalid value "localhost" for '--bind ': invalid IP address syntax + +For more information try --help + +``` + +Time +```console +$ typed-derive --sleep 10s +Args { optimization: None, include: None, bind: None, sleep: Some(Duration(10s)), defines: [] } + +$ typed-derive --sleep forever +? failed +error: Invalid value "forever" for '--sleep ': expected number at 0 + +For more information try --help + +``` + +Defines (key-value pairs) +```console +$ typed-derive -D Foo=10 -D Alice=30 +Args { optimization: None, include: None, bind: None, sleep: None, defines: [("Foo", 10), ("Alice", 30)] } + +$ typed-derive -D Foo +? failed +error: Invalid value "Foo" for '-D ': invalid KEY=value: no `=` found in `Foo` + +For more information try --help + +$ typed-derive -D Foo=Bar +? failed +error: Invalid value "Foo=Bar" for '-D ': invalid digit found in string + +For more information try --help + +``` diff --git a/third_party/rust/clap/examples/keyvalue-derive.rs b/third_party/rust/clap/examples/typed-derive.rs similarity index 60% rename from third_party/rust/clap/examples/keyvalue-derive.rs rename to third_party/rust/clap/examples/typed-derive.rs index c31a1a79bd20..237bbe15e8d5 100644 --- a/third_party/rust/clap/examples/keyvalue-derive.rs +++ b/third_party/rust/clap/examples/typed-derive.rs @@ -5,6 +5,23 @@ use std::error::Error; #[derive(Parser, Debug)] struct Args { + /// Implicitly using `std::str::FromStr` + #[clap(short = 'O')] + optimization: Option, + + /// Allow invalid UTF-8 paths + #[clap(short = 'I', parse(from_os_str), value_name = "DIR", value_hint = clap::ValueHint::DirPath)] + include: Option, + + /// Handle IP addresses + #[clap(long)] + bind: Option, + + /// Allow human-readable durations + #[clap(long)] + sleep: Option, + + /// Hand-written parser for tuples #[clap(short = 'D', parse(try_from_str = parse_key_val), multiple_occurrences(true))] defines: Vec<(String, i32)>, } diff --git a/third_party/rust/clap/src/bin/stdio-fixture.rs b/third_party/rust/clap/src/bin/stdio-fixture.rs new file mode 100644 index 000000000000..e3f34b41a22a --- /dev/null +++ b/third_party/rust/clap/src/bin/stdio-fixture.rs @@ -0,0 +1,14 @@ +fn main() { + let cmd = clap::Command::new("stdio-fixture") + .version("1.0") + .long_version("1.0 - a2132c") + .arg_required_else_help(true) + .subcommand(clap::Command::new("more")) + .arg( + clap::Arg::new("verbose") + .long("verbose") + .help("log") + .long_help("more log"), + ); + cmd.get_matches(); +} diff --git a/third_party/rust/clap/src/build/app/debug_asserts.rs b/third_party/rust/clap/src/build/app/debug_asserts.rs deleted file mode 100644 index f96c355490f0..000000000000 --- a/third_party/rust/clap/src/build/app/debug_asserts.rs +++ /dev/null @@ -1,582 +0,0 @@ -use crate::{ - build::arg::{debug_asserts::assert_arg, ArgProvider}, - mkeymap::KeyType, - util::Id, - App, AppSettings, Arg, ArgSettings, ValueHint, -}; -use std::cmp::Ordering; - -pub(crate) fn assert_app(app: &App) { - debug!("App::_debug_asserts"); - - let mut short_flags = vec![]; - let mut long_flags = vec![]; - - // Invalid version flag settings - if app.version.is_none() && app.long_version.is_none() { - // PropagateVersion is meaningless if there is no version - assert!( - !app.settings.is_set(AppSettings::PropagateVersion), - "App {}: No version information via App::version or App::long_version to propagate", - app.get_name(), - ); - - // Used `App::mut_arg("version", ..) but did not provide any version information to display - let has_mutated_version = app - .args - .args() - .any(|x| x.id == Id::version_hash() && x.provider == ArgProvider::GeneratedMutated); - - if has_mutated_version { - assert!(app.settings.is_set(AppSettings::NoAutoVersion), - "App {}: Used App::mut_arg(\"version\", ..) without providing App::version, App::long_version or using AppSettings::NoAutoVersion" - ,app.get_name() - ); - } - } - - for sc in &app.subcommands { - if let Some(s) = sc.short_flag.as_ref() { - short_flags.push(Flag::App(format!("-{}", s), &sc.name)); - } - - for (short_alias, _) in &sc.short_flag_aliases { - short_flags.push(Flag::App(format!("-{}", short_alias), &sc.name)); - } - - if let Some(l) = sc.long_flag.as_ref() { - long_flags.push(Flag::App(format!("--{}", l), &sc.name)); - } - - for (long_alias, _) in &sc.long_flag_aliases { - long_flags.push(Flag::App(format!("--{}", long_alias), &sc.name)); - } - } - - for arg in app.args.args() { - assert_arg(arg); - - if let Some(s) = arg.short.as_ref() { - short_flags.push(Flag::Arg(format!("-{}", s), &*arg.name)); - } - - for (short_alias, _) in &arg.short_aliases { - short_flags.push(Flag::Arg(format!("-{}", short_alias), arg.name)); - } - - if let Some(l) = arg.long.as_ref() { - long_flags.push(Flag::Arg(format!("--{}", l), &*arg.name)); - } - - for (long_alias, _) in &arg.aliases { - long_flags.push(Flag::Arg(format!("--{}", long_alias), arg.name)); - } - - // Name conflicts - assert!( - app.two_args_of(|x| x.id == arg.id).is_none(), - "App {}: Argument names must be unique, but '{}' is in use by more than one argument or group", - app.get_name(), - arg.name, - ); - - // Long conflicts - if let Some(l) = arg.long { - if let Some((first, second)) = app.two_args_of(|x| x.long == Some(l)) { - panic!( - "App {}: Long option names must be unique for each argument, \ - but '--{}' is in use by both '{}' and '{}'", - app.get_name(), - l, - first.name, - second.name - ) - } - } - - // Short conflicts - if let Some(s) = arg.short { - if let Some((first, second)) = app.two_args_of(|x| x.short == Some(s)) { - panic!( - "App {}: Short option names must be unique for each argument, \ - but '-{}' is in use by both '{}' and '{}'", - app.get_name(), - s, - first.name, - second.name - ) - } - } - - // Index conflicts - if let Some(idx) = arg.index { - if let Some((first, second)) = - app.two_args_of(|x| x.is_positional() && x.index == Some(idx)) - { - panic!( - "App {}: Argument '{}' has the same index as '{}' \ - and they are both positional arguments\n\n\t \ - Use Arg::multiple_values(true) to allow one \ - positional argument to take multiple values", - app.get_name(), - first.name, - second.name - ) - } - } - - // requires, r_if, r_unless - for req in &arg.requires { - assert!( - app.id_exists(&req.1), - "App {}: Argument or group '{:?}' specified in 'requires*' for '{}' does not exist", - app.get_name(), - req.1, - arg.name, - ); - } - - for req in &arg.r_ifs { - assert!( - app.id_exists(&req.0), - "App {}: Argument or group '{:?}' specified in 'required_if_eq*' for '{}' does not exist", - app.get_name(), - req.0, - arg.name - ); - } - - for req in &arg.r_ifs_all { - assert!( - app.id_exists(&req.0), - "App {}: Argument or group '{:?}' specified in 'required_if_eq_all' for '{}' does not exist", - app.get_name(), - req.0, - arg.name - ); - } - - for req in &arg.r_unless { - assert!( - app.id_exists(req), - "App {}: Argument or group '{:?}' specified in 'required_unless*' for '{}' does not exist", - app.get_name(), - req, - arg.name, - ); - } - - // blacklist - for req in &arg.blacklist { - assert!( - app.id_exists(req), - "App {}: Argument or group '{:?}' specified in 'conflicts_with*' for '{}' does not exist", - app.get_name(), - req, - arg.name, - ); - } - - if arg.is_set(ArgSettings::Last) { - assert!( - arg.long.is_none(), - "App {}: Flags or Options cannot have last(true) set. '{}' has both a long and last(true) set.", - app.get_name(), - arg.name - ); - assert!( - arg.short.is_none(), - "App {}: Flags or Options cannot have last(true) set. '{}' has both a short and last(true) set.", - app.get_name(), - arg.name - ); - } - - assert!( - !(arg.is_set(ArgSettings::Required) && arg.get_global()), - "App {}: Global arguments cannot be required.\n\n\t'{}' is marked as both global and required", - app.get_name(), - arg.name - ); - - // validators - assert!( - arg.validator.is_none() || arg.validator_os.is_none(), - "App {}: Argument '{}' has both `validator` and `validator_os` set which is not allowed", - app.get_name(), - arg.name - ); - - if arg.value_hint == ValueHint::CommandWithArguments { - assert!( - arg.is_positional(), - "App {}: Argument '{}' has hint CommandWithArguments and must be positional.", - app.get_name(), - arg.name - ); - - assert!( - app.is_set(AppSettings::TrailingVarArg), - "App {}: Positional argument '{}' has hint CommandWithArguments, so App must have TrailingVarArg set.", - app.get_name(), - arg.name - ); - } - } - - for group in &app.groups { - // Name conflicts - assert!( - app.groups.iter().filter(|x| x.id == group.id).count() < 2, - "App {}: Argument group name must be unique\n\n\t'{}' is already in use", - app.get_name(), - group.name, - ); - - // Groups should not have naming conflicts with Args - assert!( - !app.args.args().any(|x| x.id == group.id), - "App {}: Argument group name '{}' must not conflict with argument name", - app.get_name(), - group.name, - ); - - // Required groups should have at least one arg without default values - if group.required && !group.args.is_empty() { - assert!( - group.args.iter().any(|arg| { - app.args - .args() - .any(|x| x.id == *arg && x.default_vals.is_empty()) - }), - "App {}: Argument group '{}' is required but all of it's arguments have a default value.", - app.get_name(), - group.name - ) - } - - for arg in &group.args { - // Args listed inside groups should exist - assert!( - app.args.args().any(|x| x.id == *arg), - "App {}: Argument group '{}' contains non-existent argument '{:?}'", - app.get_name(), - group.name, - arg - ); - } - } - - // Conflicts between flags and subcommands - - long_flags.sort_unstable(); - short_flags.sort_unstable(); - - detect_duplicate_flags(&long_flags, "long"); - detect_duplicate_flags(&short_flags, "short"); - - _verify_positionals(app); - - if let Some(help_template) = app.template { - assert!( - !help_template.contains("{flags}"), - "App {}: {}", - app.get_name(), - "`{flags}` template variable was removed in clap3, they are now included in `{options}`", - ); - assert!( - !help_template.contains("{unified}"), - "App {}: {}", - app.get_name(), - "`{unified}` template variable was removed in clap3, use `{options}` instead" - ); - } - - app._panic_on_missing_help(app.g_settings.is_set(AppSettings::HelpExpected)); - assert_app_flags(app); -} - -#[derive(Eq)] -enum Flag<'a> { - App(String, &'a str), - Arg(String, &'a str), -} - -impl PartialEq for Flag<'_> { - fn eq(&self, other: &Flag) -> bool { - self.cmp(other) == Ordering::Equal - } -} - -impl PartialOrd for Flag<'_> { - fn partial_cmp(&self, other: &Flag) -> Option { - use Flag::*; - - match (self, other) { - (App(s1, _), App(s2, _)) - | (Arg(s1, _), Arg(s2, _)) - | (App(s1, _), Arg(s2, _)) - | (Arg(s1, _), App(s2, _)) => { - if s1 == s2 { - Some(Ordering::Equal) - } else { - s1.partial_cmp(s2) - } - } - } - } -} - -impl Ord for Flag<'_> { - fn cmp(&self, other: &Self) -> Ordering { - self.partial_cmp(other).unwrap() - } -} - -fn detect_duplicate_flags(flags: &[Flag], short_or_long: &str) { - use Flag::*; - - for (one, two) in find_duplicates(flags) { - match (one, two) { - (App(flag, one), App(_, another)) if one != another => panic!( - "the '{}' {} flag is specified for both '{}' and '{}' subcommands", - flag, short_or_long, one, another - ), - - (Arg(flag, one), Arg(_, another)) if one != another => panic!( - "{} option names must be unique, but '{}' is in use by both '{}' and '{}'", - short_or_long, flag, one, another - ), - - (Arg(flag, arg), App(_, sub)) | (App(flag, sub), Arg(_, arg)) => panic!( - "the '{}' {} flag for the '{}' argument conflicts with the short flag \ - for '{}' subcommand", - flag, short_or_long, arg, sub - ), - - _ => {} - } - } -} - -/// Find duplicates in a sorted array. -/// -/// The algorithm is simple: the array is sorted, duplicates -/// must be placed next to each other, we can check only adjacent elements. -fn find_duplicates(slice: &[T]) -> impl Iterator { - slice.windows(2).filter_map(|w| { - if w[0] == w[1] { - Some((&w[0], &w[1])) - } else { - None - } - }) -} - -fn assert_app_flags(app: &App) { - use AppSettings::*; - - macro_rules! checker { - ($a:ident requires $($b:ident)|+) => { - if app.is_set($a) { - let mut s = String::new(); - - $( - if !app.is_set($b) { - s.push_str(&format!(" AppSettings::{} is required when AppSettings::{} is set.\n", std::stringify!($b), std::stringify!($a))); - } - )+ - - if !s.is_empty() { - panic!("{}", s) - } - } - }; - ($a:ident conflicts $($b:ident)|+) => { - if app.is_set($a) { - let mut s = String::new(); - - $( - if app.is_set($b) { - s.push_str(&format!(" AppSettings::{} conflicts with AppSettings::{}.\n", std::stringify!($b), std::stringify!($a))); - } - )+ - - if !s.is_empty() { - panic!("{}\n{}", app.get_name(), s) - } - } - }; - } - - checker!(AllowInvalidUtf8ForExternalSubcommands requires AllowExternalSubcommands); - #[cfg(feature = "unstable-multicall")] - checker!(Multicall conflicts NoBinaryName); -} - -#[cfg(debug_assertions)] -fn _verify_positionals(app: &App) -> bool { - debug!("App::_verify_positionals"); - // Because you must wait until all arguments have been supplied, this is the first chance - // to make assertions on positional argument indexes - // - // First we verify that the index highest supplied index, is equal to the number of - // positional arguments to verify there are no gaps (i.e. supplying an index of 1 and 3 - // but no 2) - - let highest_idx = app - .args - .keys() - .filter_map(|x| { - if let KeyType::Position(n) = x { - Some(*n) - } else { - None - } - }) - .max() - .unwrap_or(0); - - let num_p = app.args.keys().filter(|x| x.is_position()).count(); - - assert!( - highest_idx == num_p, - "Found positional argument whose index is {} but there \ - are only {} positional arguments defined", - highest_idx, - num_p - ); - - // Next we verify that only the highest index has takes multiple arguments (if any) - let only_highest = |a: &Arg| a.is_multiple() && (a.index.unwrap_or(0) != highest_idx); - if app.get_positionals().any(only_highest) { - // First we make sure if there is a positional that allows multiple values - // the one before it (second to last) has one of these: - // * a value terminator - // * ArgSettings::Last - // * The last arg is Required - - // We can't pass the closure (it.next()) to the macro directly because each call to - // find() (iterator, not macro) gets called repeatedly. - let last = &app.args[&KeyType::Position(highest_idx)]; - let second_to_last = &app.args[&KeyType::Position(highest_idx - 1)]; - - // Either the final positional is required - // Or the second to last has a terminator or .last(true) set - let ok = last.is_set(ArgSettings::Required) - || (second_to_last.terminator.is_some() || second_to_last.is_set(ArgSettings::Last)) - || last.is_set(ArgSettings::Last); - assert!( - ok, - "When using a positional argument with .multiple_values(true) that is *not the \ - last* positional argument, the last positional argument (i.e. the one \ - with the highest index) *must* have .required(true) or .last(true) set." - ); - - // We make sure if the second to last is Multiple the last is ArgSettings::Last - let ok = second_to_last.is_multiple() || last.is_set(ArgSettings::Last); - assert!( - ok, - "Only the last positional argument, or second to last positional \ - argument may be set to .multiple_values(true)" - ); - - // Next we check how many have both Multiple and not a specific number of values set - let count = app - .get_positionals() - .filter(|p| { - p.settings.is_set(ArgSettings::MultipleOccurrences) - || (p.settings.is_set(ArgSettings::MultipleValues) && p.num_vals.is_none()) - }) - .count(); - let ok = count <= 1 - || (last.is_set(ArgSettings::Last) - && last.is_multiple() - && second_to_last.is_multiple() - && count == 2); - assert!( - ok, - "Only one positional argument with .multiple_values(true) set is allowed per \ - command, unless the second one also has .last(true) set" - ); - } - - let mut found = false; - - if app.is_set(AppSettings::AllowMissingPositional) { - // Check that if a required positional argument is found, all positions with a lower - // index are also required. - let mut foundx2 = false; - - for p in app.get_positionals() { - if foundx2 && !p.is_set(ArgSettings::Required) { - assert!( - p.is_set(ArgSettings::Required), - "Found non-required positional argument with a lower \ - index than a required positional argument by two or more: {:?} \ - index {:?}", - p.name, - p.index - ); - } else if p.is_set(ArgSettings::Required) && !p.is_set(ArgSettings::Last) { - // Args that .last(true) don't count since they can be required and have - // positionals with a lower index that aren't required - // Imagine: prog [opt1] -- - // Both of these are valid invocations: - // $ prog r1 -- r2 - // $ prog r1 o1 -- r2 - if found { - foundx2 = true; - continue; - } - found = true; - continue; - } else { - found = false; - } - } - } else { - // Check that if a required positional argument is found, all positions with a lower - // index are also required - for p in (1..=num_p).rev().filter_map(|n| app.args.get(&n)) { - if found { - assert!( - p.is_set(ArgSettings::Required), - "Found non-required positional argument with a lower \ - index than a required positional argument: {:?} index {:?}", - p.name, - p.index - ); - } else if p.is_set(ArgSettings::Required) && !p.is_set(ArgSettings::Last) { - // Args that .last(true) don't count since they can be required and have - // positionals with a lower index that aren't required - // Imagine: prog [opt1] -- - // Both of these are valid invocations: - // $ prog r1 -- r2 - // $ prog r1 o1 -- r2 - found = true; - continue; - } - } - } - assert!( - app.get_positionals() - .filter(|p| p.is_set(ArgSettings::Last)) - .count() - < 2, - "Only one positional argument may have last(true) set. Found two." - ); - if app - .get_positionals() - .any(|p| p.is_set(ArgSettings::Last) && p.is_set(ArgSettings::Required)) - && app.has_subcommands() - && !app.is_set(AppSettings::SubcommandsNegateReqs) - { - panic!( - "Having a required positional argument with .last(true) set *and* child \ - subcommands without setting SubcommandsNegateReqs isn't compatible." - ); - } - - true -} diff --git a/third_party/rust/clap/src/build/app/settings.rs b/third_party/rust/clap/src/build/app/settings.rs deleted file mode 100644 index f166795ea082..000000000000 --- a/third_party/rust/clap/src/build/app/settings.rs +++ /dev/null @@ -1,1366 +0,0 @@ -// Std -use std::ops::BitOr; -#[cfg(feature = "yaml")] -use std::str::FromStr; - -// Third party -use bitflags::bitflags; - -#[doc(hidden)] -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct AppFlags(Flags); - -impl Default for AppFlags { - fn default() -> Self { - AppFlags(Flags::COLOR_AUTO) - } -} - -/// Application level settings, which affect how [`App`] operates -/// -/// **NOTE:** When these settings are used, they apply only to current command, and are *not* -/// propagated down or up through child or parent subcommands -/// -/// [`App`]: crate::App -#[derive(Debug, PartialEq, Copy, Clone)] -#[non_exhaustive] -pub enum AppSettings { - /// Try not to fail on parse errors, like missing option values. - /// - /// **Note:** Make sure you apply it as `global_setting` if you want this setting - /// to be propagated to subcommands and sub-subcommands! - /// - /// ```rust - /// # use clap::{App, arg, AppSettings}; - /// let app = App::new("app") - /// .global_setting(AppSettings::IgnoreErrors) - /// .arg(arg!(-c --config "Sets a custom config file").required(false)) - /// .arg(arg!(-x --stuff "Sets a custom stuff file").required(false)) - /// .arg(arg!(f: -f "Flag")); - /// - /// let r = app.try_get_matches_from(vec!["app", "-c", "file", "-f", "-x"]); - /// - /// assert!(r.is_ok(), "unexpected error: {:?}", r); - /// let m = r.unwrap(); - /// assert_eq!(m.value_of("config"), Some("file")); - /// assert!(m.is_present("f")); - /// assert_eq!(m.value_of("stuff"), None); - /// ``` - IgnoreErrors, - - /// Display the message "Press \[ENTER\]/\[RETURN\] to continue..." and wait for user before - /// exiting - /// - /// This is most useful when writing an application which is run from a GUI shortcut, or on - /// Windows where a user tries to open the binary by double-clicking instead of using the - /// command line. - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, Arg, AppSettings}; - /// App::new("myprog") - /// .global_setting(AppSettings::WaitOnError); - /// ``` - WaitOnError, - - /// Specifies that leading hyphens are allowed in all argument *values* (e.g. `-10`). - /// - /// Otherwise they will be parsed as another flag or option. See also - /// [`AppSettings::AllowNegativeNumbers`]. - /// - /// **NOTE:** Use this setting with caution as it silences certain circumstances which would - /// otherwise be an error (such as accidentally forgetting to specify a value for leading - /// option). It is preferred to set this on a per argument basis, via [`Arg::allow_hyphen_values`]. - /// - /// # Examples - /// - /// ```rust - /// # use clap::{Arg, App, AppSettings}; - /// // Imagine you needed to represent negative numbers as well, such as -10 - /// let m = App::new("nums") - /// .setting(AppSettings::AllowHyphenValues) - /// .arg(Arg::new("neg")) - /// .get_matches_from(vec![ - /// "nums", "-20" - /// ]); - /// - /// assert_eq!(m.value_of("neg"), Some("-20")); - /// # ; - /// ``` - /// [`Arg::allow_hyphen_values`]: crate::Arg::allow_hyphen_values() - AllowHyphenValues, - - /// Allows negative numbers to pass as values. - /// - /// This is similar to [`AppSettings::AllowHyphenValues`] except that it only allows numbers, - /// all other undefined leading hyphens will fail to parse. - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, Arg, AppSettings}; - /// let res = App::new("myprog") - /// .global_setting(AppSettings::AllowNegativeNumbers) - /// .arg(Arg::new("num")) - /// .try_get_matches_from(vec![ - /// "myprog", "-20" - /// ]); - /// assert!(res.is_ok()); - /// let m = res.unwrap(); - /// assert_eq!(m.value_of("num").unwrap(), "-20"); - /// ``` - AllowNegativeNumbers, - - /// Specifies that all arguments override themselves. - /// - /// This is the equivalent to saying the `foo` arg using [`Arg::overrides_with("foo")`] for all - /// defined arguments. - /// - /// [`Arg::overrides_with("foo")`]: crate::Arg::overrides_with() - AllArgsOverrideSelf, - - /// Allows one to implement two styles of CLIs where positionals can be used out of order. - /// - /// The first example is a CLI where the second to last positional argument is optional, but - /// the final positional argument is required. Such as `$ prog [optional] ` where one - /// of the two following usages is allowed: - /// - /// * `$ prog [optional] ` - /// * `$ prog ` - /// - /// This would otherwise not be allowed. This is useful when `[optional]` has a default value. - /// - /// **Note:** when using this style of "missing positionals" the final positional *must* be - /// [required] if `--` will not be used to skip to the final positional argument. - /// - /// **Note:** This style also only allows a single positional argument to be "skipped" without - /// the use of `--`. To skip more than one, see the second example. - /// - /// The second example is when one wants to skip multiple optional positional arguments, and use - /// of the `--` operator is OK (but not required if all arguments will be specified anyways). - /// - /// For example, imagine a CLI which has three positional arguments `[foo] [bar] [baz]...` where - /// `baz` accepts multiple values (similar to man `ARGS...` style training arguments). - /// - /// With this setting the following invocations are posisble: - /// - /// * `$ prog foo bar baz1 baz2 baz3` - /// * `$ prog foo -- baz1 baz2 baz3` - /// * `$ prog -- baz1 baz2 baz3` - /// - /// # Examples - /// - /// Style number one from above: - /// - /// ```rust - /// # use clap::{App, Arg, AppSettings}; - /// // Assume there is an external subcommand named "subcmd" - /// let m = App::new("myprog") - /// .setting(AppSettings::AllowMissingPositional) - /// .arg(Arg::new("arg1")) - /// .arg(Arg::new("arg2") - /// .required(true)) - /// .get_matches_from(vec![ - /// "prog", "other" - /// ]); - /// - /// assert_eq!(m.value_of("arg1"), None); - /// assert_eq!(m.value_of("arg2"), Some("other")); - /// ``` - /// - /// Now the same example, but using a default value for the first optional positional argument - /// - /// ```rust - /// # use clap::{App, Arg, AppSettings}; - /// // Assume there is an external subcommand named "subcmd" - /// let m = App::new("myprog") - /// .setting(AppSettings::AllowMissingPositional) - /// .arg(Arg::new("arg1") - /// .default_value("something")) - /// .arg(Arg::new("arg2") - /// .required(true)) - /// .get_matches_from(vec![ - /// "prog", "other" - /// ]); - /// - /// assert_eq!(m.value_of("arg1"), Some("something")); - /// assert_eq!(m.value_of("arg2"), Some("other")); - /// ``` - /// - /// Style number two from above: - /// - /// ```rust - /// # use clap::{App, Arg, AppSettings}; - /// // Assume there is an external subcommand named "subcmd" - /// let m = App::new("myprog") - /// .setting(AppSettings::AllowMissingPositional) - /// .arg(Arg::new("foo")) - /// .arg(Arg::new("bar")) - /// .arg(Arg::new("baz").takes_value(true).multiple_values(true)) - /// .get_matches_from(vec![ - /// "prog", "foo", "bar", "baz1", "baz2", "baz3" - /// ]); - /// - /// assert_eq!(m.value_of("foo"), Some("foo")); - /// assert_eq!(m.value_of("bar"), Some("bar")); - /// assert_eq!(m.values_of("baz").unwrap().collect::>(), &["baz1", "baz2", "baz3"]); - /// ``` - /// - /// Now nofice if we don't specify `foo` or `baz` but use the `--` operator. - /// - /// ```rust - /// # use clap::{App, Arg, AppSettings}; - /// // Assume there is an external subcommand named "subcmd" - /// let m = App::new("myprog") - /// .setting(AppSettings::AllowMissingPositional) - /// .arg(Arg::new("foo")) - /// .arg(Arg::new("bar")) - /// .arg(Arg::new("baz").takes_value(true).multiple_values(true)) - /// .get_matches_from(vec![ - /// "prog", "--", "baz1", "baz2", "baz3" - /// ]); - /// - /// assert_eq!(m.value_of("foo"), None); - /// assert_eq!(m.value_of("bar"), None); - /// assert_eq!(m.values_of("baz").unwrap().collect::>(), &["baz1", "baz2", "baz3"]); - /// ``` - /// - /// [required]: crate::Arg::required() - AllowMissingPositional, - - /// Specifies that the final positional argument is a "VarArg" and that `clap` should not - /// attempt to parse any further args. - /// - /// The values of the trailing positional argument will contain all args from itself on. - /// - /// **NOTE:** The final positional argument **must** have [`Arg::multiple_values(true)`] or the usage - /// string equivalent. - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, arg, AppSettings}; - /// let m = App::new("myprog") - /// .setting(AppSettings::TrailingVarArg) - /// .arg(arg!( ... "commands to run")) - /// .get_matches_from(vec!["myprog", "arg1", "-r", "val1"]); - /// - /// let trail: Vec<&str> = m.values_of("cmd").unwrap().collect(); - /// assert_eq!(trail, ["arg1", "-r", "val1"]); - /// ``` - /// [`Arg::multiple_values(true)`]: crate::Arg::multiple_values() - TrailingVarArg, - - /// Disables the automatic delimiting of values when `--` or [`AppSettings::TrailingVarArg`] - /// was used. - /// - /// **NOTE:** The same thing can be done manually by setting the final positional argument to - /// [`Arg::use_delimiter(false)`]. Using this setting is safer, because it's easier to locate - /// when making changes. - /// - /// # Examples - /// - /// ```no_run - /// # use clap::{App, Arg, AppSettings}; - /// App::new("myprog") - /// .setting(AppSettings::DontDelimitTrailingValues) - /// .get_matches(); - /// ``` - /// - /// [`Arg::use_delimiter(false)`]: crate::Arg::use_delimiter() - DontDelimitTrailingValues, - - /// Allow partial matches of long arguments or their [aliases]. - /// - /// For example, to match an argument named `--test`, one could use `--t`, `--te`, `--tes`, and - /// `--test`. - /// - /// **NOTE:** The match *must not* be ambiguous at all in order to succeed. i.e. to match - /// `--te` to `--test` there could not also be another argument or alias `--temp` because both - /// start with `--te` - /// - /// [aliases]: crate::App::aliases() - InferLongArgs, - - /// Allow partial matches of [subcommand] names and their [aliases]. - /// - /// For example, to match a subcommand named `test`, one could use `t`, `te`, `tes`, and - /// `test`. - /// - /// **NOTE:** The match *must not* be ambiguous at all in order to succeed. i.e. to match `te` - /// to `test` there could not also be a subcommand or alias `temp` because both start with `te` - /// - /// **CAUTION:** This setting can interfere with [positional/free arguments], take care when - /// designing CLIs which allow inferred subcommands and have potential positional/free - /// arguments whose values could start with the same characters as subcommands. If this is the - /// case, it's recommended to use settings such as [`AppSettings::ArgsNegateSubcommands`] in - /// conjunction with this setting. - /// - /// # Examples - /// - /// ```no_run - /// # use clap::{App, Arg, AppSettings}; - /// let m = App::new("prog") - /// .global_setting(AppSettings::InferSubcommands) - /// .subcommand(App::new("test")) - /// .get_matches_from(vec![ - /// "prog", "te" - /// ]); - /// assert_eq!(m.subcommand_name(), Some("test")); - /// ``` - /// - /// [subcommand]: crate::App::subcommand() - /// [positional/free arguments]: crate::Arg::index() - /// [aliases]: crate::App::aliases() - InferSubcommands, - - /// If no [`subcommand`] is present at runtime, error and exit gracefully. - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, AppSettings, ErrorKind}; - /// let err = App::new("myprog") - /// .setting(AppSettings::SubcommandRequired) - /// .subcommand(App::new("test")) - /// .try_get_matches_from(vec![ - /// "myprog", - /// ]); - /// assert!(err.is_err()); - /// assert_eq!(err.unwrap_err().kind, ErrorKind::MissingSubcommand); - /// # ; - /// ``` - /// - /// [`subcommand`]: crate::App::subcommand() - SubcommandRequired, - - /// Display help if no [`subcommands`] are present at runtime and exit gracefully (i.e. an - /// empty run such as `$ myprog`). - /// - /// **NOTE:** This should *not* be used with [`AppSettings::SubcommandRequired`] as they do - /// nearly same thing; this prints the help text, and the other prints an error. - /// - /// **NOTE:** If the user specifies arguments at runtime, but no subcommand the help text will - /// still be displayed and exit. If this is *not* the desired result, consider using - /// [`AppSettings::ArgRequiredElseHelp`] instead. - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, Arg, AppSettings}; - /// App::new("myprog") - /// .setting(AppSettings::SubcommandRequiredElseHelp); - /// ``` - /// - /// [`subcommands`]: crate::App::subcommand() - SubcommandRequiredElseHelp, - - /// Assume unexpected positional arguments are a [`subcommand`]. - /// - /// **NOTE:** Use this setting with caution, - /// as a truly unexpected argument (i.e. one that is *NOT* an external subcommand) - /// will **not** cause an error and instead be treated as a potential subcommand. - /// One should check for such cases manually and inform the user appropriately. - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, AppSettings}; - /// // Assume there is an external subcommand named "subcmd" - /// let m = App::new("myprog") - /// .setting(AppSettings::AllowExternalSubcommands) - /// .get_matches_from(vec![ - /// "myprog", "subcmd", "--option", "value", "-fff", "--flag" - /// ]); - /// - /// // All trailing arguments will be stored under the subcommand's sub-matches using an empty - /// // string argument name - /// match m.subcommand() { - /// Some((external, ext_m)) => { - /// let ext_args: Vec<&str> = ext_m.values_of("").unwrap().collect(); - /// assert_eq!(external, "subcmd"); - /// assert_eq!(ext_args, ["--option", "value", "-fff", "--flag"]); - /// }, - /// _ => {}, - /// } - /// ``` - /// - /// [`subcommand`]: crate::App::subcommand() - /// [`ArgMatches`]: crate::ArgMatches - /// [`ErrorKind::UnknownArgument`]: crate::ErrorKind::UnknownArgument - AllowExternalSubcommands, - - /// Strip directory path from argv\[0\] and use as an argument. - /// - /// A "multicall" executable is a single executable - /// that contains a variety of applets, - /// and decides which applet to run based on the name of the file. - /// The executable can be called from different names by creating hard links - /// or symbolic links to it. - /// - /// This is desirable when it is convenient to store code - /// for many programs in the same file, - /// such as deduplicating code across multiple programs - /// without loading a shared library at runtime. - /// - /// Multicall can't be used with [`NoBinaryName`] since they interpret - /// the command name in incompatible ways. - /// - /// # Examples - /// - /// `hostname` is an example of a multicall executable. - /// Both `hostname` and `dnsdomainname` are provided by the same executable - /// and which behaviour to use is based on the executable file name. - /// - /// This is desirable when the executable has a primary purpose - /// but there is other related functionality that would be convenient to provide - /// and it is convenient for the code to implement it to be in the same executable. - /// - /// The name of the app is essentially unused - /// and may be the same as the name of a subcommand. - /// - /// The names of the immediate subcommands of the App - /// are matched against the basename of the first argument, - /// which is conventionally the path of the executable. - /// - /// This does not allow the subcommand to be passed as the first non-path argument. - /// - /// ```rust - /// # use clap::{App, AppSettings, ErrorKind}; - /// let mut app = App::new("hostname") - /// .setting(AppSettings::Multicall) - /// .subcommand(App::new("hostname")) - /// .subcommand(App::new("dnsdomainname")); - /// let m = app.try_get_matches_from_mut(&["/usr/bin/hostname", "dnsdomainname"]); - /// assert!(m.is_err()); - /// assert_eq!(m.unwrap_err().kind, ErrorKind::UnknownArgument); - /// let m = app.get_matches_from(&["/usr/bin/dnsdomainname"]); - /// assert_eq!(m.subcommand_name(), Some("dnsdomainname")); - /// ``` - /// - /// Busybox is another common example of a multicall executable - /// with a subcommmand for each applet that can be run directly, - /// e.g. with the `cat` applet being run by running `busybox cat`, - /// or with `cat` as a link to the `busybox` binary. - /// - /// This is desirable when the launcher program has additional options - /// or it is useful to run the applet without installing a symlink - /// e.g. to test the applet without installing it - /// or there may already be a command of that name installed. - /// - /// To make an applet usable as both a multicall link and a subcommand - /// the subcommands must be defined both in the top-level App - /// and as subcommands of the "main" applet. - /// - /// ```rust - /// # use clap::{App, AppSettings}; - /// fn applet_commands() -> [App<'static>; 2] { - /// [App::new("true"), App::new("false")] - /// } - /// let mut app = App::new("busybox") - /// .setting(AppSettings::Multicall) - /// .subcommand( - /// App::new("busybox") - /// .subcommand_value_name("APPLET") - /// .subcommand_help_heading("APPLETS") - /// .subcommands(applet_commands()), - /// ) - /// .subcommands(applet_commands()); - /// // When called from the executable's canonical name - /// // its applets can be matched as subcommands. - /// let m = app.try_get_matches_from_mut(&["/usr/bin/busybox", "true"]).unwrap(); - /// assert_eq!(m.subcommand_name(), Some("busybox")); - /// assert_eq!(m.subcommand().unwrap().1.subcommand_name(), Some("true")); - /// // When called from a link named after an applet that applet is matched. - /// let m = app.get_matches_from(&["/usr/bin/true"]); - /// assert_eq!(m.subcommand_name(), Some("true")); - /// ``` - /// - /// **NOTE:** Applets are slightly semantically different from subcommands, - /// so it's recommended to use [`App::subcommand_help_heading`] and - /// [`App::subcommand_value_name`] to change the descriptive text as above. - /// - /// [`NoBinaryName`]: crate::AppSettings::NoBinaryName - /// [`App::subcommand_value_name`]: crate::App::subcommand_value_name - /// [`App::subcommand_help_heading`]: crate::App::subcommand_help_heading - #[cfg(feature = "unstable-multicall")] - Multicall, - - /// Specifies that external subcommands that are invalid UTF-8 should *not* be treated as an error. - /// - /// **NOTE:** Using external subcommand argument values with invalid UTF-8 requires using - /// [`ArgMatches::values_of_os`] or [`ArgMatches::values_of_lossy`] for those particular - /// arguments which may contain invalid UTF-8 values - /// - /// **NOTE:** Setting this requires [`AppSettings::AllowExternalSubcommands`] - /// - /// # Platform Specific - /// - /// Non Windows systems only - /// - /// # Examples - /// - #[cfg_attr(not(unix), doc = " ```ignore")] - #[cfg_attr(unix, doc = " ```")] - /// # use clap::{App, AppSettings}; - /// // Assume there is an external subcommand named "subcmd" - /// let m = App::new("myprog") - /// .setting(AppSettings::AllowInvalidUtf8ForExternalSubcommands) - /// .setting(AppSettings::AllowExternalSubcommands) - /// .get_matches_from(vec![ - /// "myprog", "subcmd", "--option", "value", "-fff", "--flag" - /// ]); - /// - /// // All trailing arguments will be stored under the subcommand's sub-matches using an empty - /// // string argument name - /// match m.subcommand() { - /// Some((external, ext_m)) => { - /// let ext_args: Vec<&std::ffi::OsStr> = ext_m.values_of_os("").unwrap().collect(); - /// assert_eq!(external, "subcmd"); - /// assert_eq!(ext_args, ["--option", "value", "-fff", "--flag"]); - /// }, - /// _ => {}, - /// } - /// ``` - /// - /// [`ArgMatches::values_of_os`]: crate::ArgMatches::values_of_os() - /// [`ArgMatches::values_of_lossy`]: crate::ArgMatches::values_of_lossy() - /// [`subcommands`]: crate::App::subcommand() - AllowInvalidUtf8ForExternalSubcommands, - - /// Specifies that the help subcommand should print the long help message (`--help`). - /// - /// **NOTE:** This setting is useless if [`AppSettings::DisableHelpSubcommand`] or [`AppSettings::NoAutoHelp`] is set, - /// or if the app contains no subcommands at all. - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, Arg, AppSettings}; - /// App::new("myprog") - /// .global_setting(AppSettings::UseLongFormatForHelpSubcommand) - /// .subcommand(App::new("test") - /// .arg(Arg::new("foo") - /// .help("short form about message") - /// .long_help("long form about message") - /// ) - /// ) - /// .get_matches(); - /// ``` - /// [long format]: crate::App::long_about - UseLongFormatForHelpSubcommand, - - /// Allows [`subcommands`] to override all requirements of the parent command. - /// - /// For example, if you had a subcommand or top level application with a required argument - /// that is only required as long as there is no subcommand present, - /// using this setting would allow you to set those arguments to [`Arg::required(true)`] - /// and yet receive no error so long as the user uses a valid subcommand instead. - /// - /// **NOTE:** This defaults to false (using subcommand does *not* negate requirements) - /// - /// # Examples - /// - /// This first example shows that it is an error to not use a required argument - /// - /// ```rust - /// # use clap::{App, Arg, AppSettings, ErrorKind}; - /// let err = App::new("myprog") - /// .setting(AppSettings::SubcommandsNegateReqs) - /// .arg(Arg::new("opt").required(true)) - /// .subcommand(App::new("test")) - /// .try_get_matches_from(vec![ - /// "myprog" - /// ]); - /// assert!(err.is_err()); - /// assert_eq!(err.unwrap_err().kind, ErrorKind::MissingRequiredArgument); - /// # ; - /// ``` - /// - /// This next example shows that it is no longer error to not use a required argument if a - /// valid subcommand is used. - /// - /// ```rust - /// # use clap::{App, Arg, AppSettings, ErrorKind}; - /// let noerr = App::new("myprog") - /// .setting(AppSettings::SubcommandsNegateReqs) - /// .arg(Arg::new("opt").required(true)) - /// .subcommand(App::new("test")) - /// .try_get_matches_from(vec![ - /// "myprog", "test" - /// ]); - /// assert!(noerr.is_ok()); - /// # ; - /// ``` - /// - /// [`Arg::required(true)`]: crate::Arg::required() - /// [`subcommands`]: crate::App::subcommand() - SubcommandsNegateReqs, - - /// Specifies that use of an argument prevents the use of [`subcommands`]. - /// - /// By default `clap` allows arguments between subcommands such - /// as ` [cmd_args] [subcmd_args] [subsubcmd_args]`. - /// - /// This setting disables that functionality and says that arguments can - /// only follow the *final* subcommand. For instance using this setting - /// makes only the following invocations possible: - /// - /// * ` [subsubcmd_args]` - /// * ` [subcmd_args]` - /// * ` [cmd_args]` - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, AppSettings}; - /// App::new("myprog") - /// .setting(AppSettings::ArgsNegateSubcommands); - /// ``` - /// - /// [`subcommands`]: crate::App::subcommand() - ArgsNegateSubcommands, - - /// Prevent subcommands from being consumed as an arguments value. - /// - /// By default, if an option taking multiple values is followed by a subcommand, the - /// subcommand will be parsed as another value. - /// - /// ```text - /// app --foo val1 val2 subcommand - /// --------- ---------- - /// values another value - /// ``` - /// - /// This setting instructs the parser to stop when encountering a subcommand instead of - /// greedily consuming arguments. - /// - /// ```text - /// app --foo val1 val2 subcommand - /// --------- ---------- - /// values subcommand - /// ``` - /// - /// **Note:** Make sure you apply it as `global_setting` if you want this setting - /// to be propagated to subcommands and sub-subcommands! - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, AppSettings, Arg}; - /// let app = App::new("app").subcommand(App::new("sub")).arg( - /// Arg::new("arg") - /// .long("arg") - /// .multiple_values(true) - /// .takes_value(true), - /// ); - /// - /// let matches = app - /// .clone() - /// .try_get_matches_from(&["app", "--arg", "1", "2", "3", "sub"]) - /// .unwrap(); - /// - /// assert_eq!( - /// matches.values_of("arg").unwrap().collect::>(), - /// &["1", "2", "3", "sub"] - /// ); - /// assert!(matches.subcommand_matches("sub").is_none()); - /// - /// let matches = app - /// .setting(AppSettings::SubcommandPrecedenceOverArg) - /// .try_get_matches_from(&["app", "--arg", "1", "2", "3", "sub"]) - /// .unwrap(); - /// - /// assert_eq!( - /// matches.values_of("arg").unwrap().collect::>(), - /// &["1", "2", "3"] - /// ); - /// assert!(matches.subcommand_matches("sub").is_some()); - /// ``` - SubcommandPrecedenceOverArg, - - /// Exit gracefully if no arguments are present (e.g. `$ myprog`). - /// - /// **NOTE:** [`subcommands`] count as arguments - /// - /// **NOTE:** Setting [`Arg::default_value`] effectively disables this option as it will - /// ensure that some argument is always present. - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, AppSettings}; - /// App::new("myprog") - /// .setting(AppSettings::ArgRequiredElseHelp); - /// ``` - /// - /// [`subcommands`]: crate::App::subcommand() - /// [`Arg::default_value`]: crate::Arg::default_value() - ArgRequiredElseHelp, - - /// Displays the arguments and [`subcommands`] in the help message in the order that they were - /// declared in, and not alphabetically which is the default. - /// - /// To override the declaration order, see [`Arg::display_order`] and [`App::display_order`]. - /// - /// # Examples - /// - /// ```no_run - /// # use clap::{App, Arg, AppSettings}; - /// App::new("myprog") - /// .global_setting(AppSettings::DeriveDisplayOrder) - /// .get_matches(); - /// ``` - /// - /// [`subcommands`]: crate::App::subcommand() - /// [`Arg::display_order`]: crate::Arg::display_order - /// [`App::display_order`]: crate::App::display_order - DeriveDisplayOrder, - - /// Disables the automatic collapsing of positional args into `[ARGS]` inside the usage string. - /// - /// # Examples - /// - /// ```no_run - /// # use clap::{App, Arg, AppSettings}; - /// App::new("myprog") - /// .global_setting(AppSettings::DontCollapseArgsInUsage) - /// .get_matches(); - /// ``` - DontCollapseArgsInUsage, - - /// Places the help string for all arguments on the line after the argument. - /// - /// # Examples - /// - /// ```no_run - /// # use clap::{App, Arg, AppSettings}; - /// App::new("myprog") - /// .global_setting(AppSettings::NextLineHelp) - /// .get_matches(); - /// ``` - NextLineHelp, - - /// Disables colorized help messages. - /// - /// # Examples - /// - /// ```no_run - /// # use clap::{App, AppSettings}; - /// App::new("myprog") - /// .setting(AppSettings::DisableColoredHelp) - /// .get_matches(); - /// ``` - DisableColoredHelp, - - /// Disables `-h` and `--help` flag. - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, AppSettings, ErrorKind}; - /// let res = App::new("myprog") - /// .setting(AppSettings::DisableHelpFlag) - /// .try_get_matches_from(vec![ - /// "myprog", "-h" - /// ]); - /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::UnknownArgument); - /// ``` - DisableHelpFlag, - - /// Disables the `help` [`subcommand`]. - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, AppSettings, ErrorKind, }; - /// let res = App::new("myprog") - /// .setting(AppSettings::DisableHelpSubcommand) - /// // Normally, creating a subcommand causes a `help` subcommand to automatically - /// // be generated as well - /// .subcommand(App::new("test")) - /// .try_get_matches_from(vec![ - /// "myprog", "help" - /// ]); - /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::UnknownArgument); - /// ``` - /// - /// [`subcommand`]: crate::App::subcommand() - DisableHelpSubcommand, - - /// Disables `-V` and `--version` flag. - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, AppSettings, ErrorKind}; - /// let res = App::new("myprog") - /// .setting(AppSettings::DisableVersionFlag) - /// .try_get_matches_from(vec![ - /// "myprog", "-V" - /// ]); - /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::UnknownArgument); - /// ``` - DisableVersionFlag, - - /// Specifies to use the version of the current command for all [`subcommands`]. - /// - /// Defaults to `false`; subcommands have independent version strings from their parents. - /// - /// **Note:** Make sure you apply it as `global_setting` if you want this setting - /// to be propagated to subcommands and sub-subcommands! - /// - /// # Examples - /// - /// ```no_run - /// # use clap::{App, Arg, AppSettings}; - /// App::new("myprog") - /// .version("v1.1") - /// .global_setting(AppSettings::PropagateVersion) - /// .subcommand(App::new("test")) - /// .get_matches(); - /// // running `$ myprog test --version` will display - /// // "myprog-test v1.1" - /// ``` - /// - /// [`subcommands`]: crate::App::subcommand() - PropagateVersion, - - /// Specifies that this [`subcommand`] should be hidden from help messages - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, Arg, AppSettings, }; - /// App::new("myprog") - /// .subcommand(App::new("test") - /// .setting(AppSettings::Hidden)) - /// # ; - /// ``` - /// - /// [`subcommand`]: crate::App::subcommand() - Hidden, - - /// Tells `clap` *not* to print possible values when displaying help information. - /// - /// This can be useful if there are many values, or they are explained elsewhere. - /// - /// To set this per argument, see - /// [`Arg::hide_possible_values`][crate::Arg::hide_possible_values]. - HidePossibleValues, - - /// Panic if help descriptions are omitted. - /// - /// **NOTE:** When deriving [`Parser`][crate::Parser], you could instead check this at - /// compile-time with `#![deny(missing_docs)]` - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, Arg, AppSettings}; - /// App::new("myprog") - /// .global_setting(AppSettings::HelpExpected) - /// .arg( - /// Arg::new("foo").help("It does foo stuff") - /// // As required via AppSettings::HelpExpected, a help message was supplied - /// ) - /// # .get_matches(); - /// ``` - /// - /// # Panics - /// - /// ```rust,no_run - /// # use clap::{App, Arg, AppSettings}; - /// App::new("myapp") - /// .global_setting(AppSettings::HelpExpected) - /// .arg( - /// Arg::new("foo") - /// // Someone forgot to put .about("...") here - /// // Since the setting AppSettings::HelpExpected is activated, this will lead to - /// // a panic (if you are in debug mode) - /// ) - /// # .get_matches(); - ///``` - HelpExpected, - - /// Specifies that the parser should not assume the first argument passed is the binary name. - /// - /// This is normally the case when using a "daemon" style mode, or an interactive CLI where - /// one would not normally type the binary or program name for each command. - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, arg, AppSettings}; - /// let m = App::new("myprog") - /// .setting(AppSettings::NoBinaryName) - /// .arg(arg!( ... "commands to run")) - /// .get_matches_from(vec!["command", "set"]); - /// - /// let cmds: Vec<&str> = m.values_of("cmd").unwrap().collect(); - /// assert_eq!(cmds, ["command", "set"]); - /// ``` - /// [`try_get_matches_from_mut`]: crate::App::try_get_matches_from_mut() - NoBinaryName, - - /// Treat the auto-generated `-h, --help` flags like any other flag, and *not* print the help - /// message. - /// - /// This allows one to handle printing of the help message manually. - /// - /// ```rust - /// # use clap::{App, AppSettings}; - /// let result = App::new("myprog") - /// .setting(AppSettings::NoAutoHelp) - /// .try_get_matches_from("myprog --help".split(" ")); - /// - /// // Normally, if `--help` is used clap prints the help message and returns an - /// // ErrorKind::DisplayHelp - /// // - /// // However, `--help` was treated like a normal flag - /// - /// assert!(result.is_ok()); - /// assert!(result.unwrap().is_present("help")); - /// ``` - NoAutoHelp, - - /// Treat the auto-generated `-V, --version` flags like any other flag, and - /// *not* print the version message. - /// - /// This allows one to handle printing of the version message manually. - /// - /// ```rust - /// # use clap::{App, AppSettings}; - /// let result = App::new("myprog") - /// .version("3.0") - /// .setting(AppSettings::NoAutoVersion) - /// .try_get_matches_from("myprog --version".split(" ")); - /// - /// // Normally, if `--version` is used clap prints the version message and returns an - /// // ErrorKind::DisplayVersion - /// // - /// // However, `--version` was treated like a normal flag - /// - /// assert!(result.is_ok()); - /// assert!(result.unwrap().is_present("version")); - /// ``` - NoAutoVersion, - - /// Deprecated, replaced with [`AppSettings::AllowHyphenValues`] - #[deprecated( - since = "3.0.0", - note = "Replaced with `AppSettings::AllowHyphenValues`" - )] - AllowLeadingHyphen, - - /// Deprecated, this is now the default, see [`AppSettings::AllowInvalidUtf8ForExternalSubcommands`] and [`ArgSettings::AllowInvalidUtf8`][crate::ArgSettings::AllowInvalidUtf8] for the opposite. - #[deprecated( - since = "3.0.0", - note = "This is now the default see `AppSettings::AllowInvalidUtf8ForExternalSubcommands` and `ArgSettings::AllowInvalidUtf8` for the opposite." - )] - StrictUtf8, - - /// Deprecated, this is now the default - #[deprecated(since = "3.0.0", note = "This is now the default")] - UnifiedHelpMessage, - - /// Deprecated, this is now the default - #[deprecated(since = "3.0.0", note = "This is now the default")] - ColoredHelp, - - /// Deprecated, see [`App::color`][crate::App::color] - #[deprecated(since = "3.0.0", note = "Replaced with `App::color`")] - ColorAuto, - - /// Deprecated, replaced with [`App::color`][crate::App::color] - #[deprecated(since = "3.0.0", note = "Replaced with `App::color`")] - ColorAlways, - - /// Deprecated, replaced with [`App::color`][crate::App::color] - #[deprecated(since = "3.0.0", note = "Replaced with `App::color`")] - ColorNever, - - /// Deprecated, replaced with [`AppSettings::DisableHelpFlag`] - #[deprecated(since = "3.0.0", note = "Replaced with `AppSettings::DisableHelpFlag`")] - DisableHelpFlags, - - /// Deprecated, replaced with [`AppSettings::DisableVersionFlag`] - #[deprecated( - since = "3.0.0", - note = "Replaced with `AppSettings::DisableVersionFlag`" - )] - DisableVersion, - - /// Deprecated, replaced with [`AppSettings::PropagateVersion`] - #[deprecated( - since = "3.0.0", - note = "Replaced with `AppSettings::PropagateVersion`" - )] - GlobalVersion, - - /// Deprecated, replaced with [`AppSettings::HidePossibleValues`] - #[deprecated( - since = "3.0.0", - note = "Replaced with AppSettings::HidePossibleValues" - )] - HidePossibleValuesInHelp, - - /// Deprecated, this is now the default - #[deprecated(since = "3.0.0", note = "This is now the default")] - UnifiedHelp, - - /// If the app is already built, used for caching. - #[doc(hidden)] - Built, - - /// If the app's bin name is already built, used for caching. - #[doc(hidden)] - BinNameBuilt, -} - -bitflags! { - struct Flags: u64 { - const SC_NEGATE_REQS = 1; - const SC_REQUIRED = 1 << 1; - const ARG_REQUIRED_ELSE_HELP = 1 << 2; - const PROPAGATE_VERSION = 1 << 3; - const DISABLE_VERSION_FOR_SC = 1 << 4; - const WAIT_ON_ERROR = 1 << 6; - const SC_REQUIRED_ELSE_HELP = 1 << 7; - const NO_AUTO_HELP = 1 << 8; - const NO_AUTO_VERSION = 1 << 9; - const DISABLE_VERSION_FLAG = 1 << 10; - const HIDDEN = 1 << 11; - const TRAILING_VARARG = 1 << 12; - const NO_BIN_NAME = 1 << 13; - const ALLOW_UNK_SC = 1 << 14; - const SC_UTF8_NONE = 1 << 15; - const LEADING_HYPHEN = 1 << 16; - const NO_POS_VALUES = 1 << 17; - const NEXT_LINE_HELP = 1 << 18; - const DERIVE_DISP_ORDER = 1 << 19; - const DISABLE_COLORED_HELP = 1 << 20; - const COLOR_ALWAYS = 1 << 21; - const COLOR_AUTO = 1 << 22; - const COLOR_NEVER = 1 << 23; - const DONT_DELIM_TRAIL = 1 << 24; - const ALLOW_NEG_NUMS = 1 << 25; - const DISABLE_HELP_SC = 1 << 27; - const DONT_COLLAPSE_ARGS = 1 << 28; - const ARGS_NEGATE_SCS = 1 << 29; - const PROPAGATE_VALS_DOWN = 1 << 30; - const ALLOW_MISSING_POS = 1 << 31; - const TRAILING_VALUES = 1 << 32; - const BUILT = 1 << 33; - const BIN_NAME_BUILT = 1 << 34; - const VALID_ARG_FOUND = 1 << 35; - const INFER_SUBCOMMANDS = 1 << 36; - const CONTAINS_LAST = 1 << 37; - const ARGS_OVERRIDE_SELF = 1 << 38; - const HELP_REQUIRED = 1 << 39; - const SUBCOMMAND_PRECEDENCE_OVER_ARG = 1 << 40; - const DISABLE_HELP_FLAG = 1 << 41; - const USE_LONG_FORMAT_FOR_HELP_SC = 1 << 42; - const INFER_LONG_ARGS = 1 << 43; - const IGNORE_ERRORS = 1 << 44; - #[cfg(feature = "unstable-multicall")] - const MULTICALL = 1 << 45; - const NO_OP = 0; - } -} - -impl_settings! { AppSettings, AppFlags, - ArgRequiredElseHelp - => Flags::ARG_REQUIRED_ELSE_HELP, - SubcommandPrecedenceOverArg - => Flags::SUBCOMMAND_PRECEDENCE_OVER_ARG, - ArgsNegateSubcommands - => Flags::ARGS_NEGATE_SCS, - AllowExternalSubcommands - => Flags::ALLOW_UNK_SC, - StrictUtf8 - => Flags::NO_OP, - AllowInvalidUtf8ForExternalSubcommands - => Flags::SC_UTF8_NONE, - AllowHyphenValues - => Flags::LEADING_HYPHEN, - AllowLeadingHyphen - => Flags::LEADING_HYPHEN, - AllowNegativeNumbers - => Flags::ALLOW_NEG_NUMS, - AllowMissingPositional - => Flags::ALLOW_MISSING_POS, - UnifiedHelpMessage - => Flags::NO_OP, - ColoredHelp - => Flags::NO_OP, - ColorAlways - => Flags::COLOR_ALWAYS, - ColorAuto - => Flags::COLOR_AUTO, - ColorNever - => Flags::COLOR_NEVER, - DontDelimitTrailingValues - => Flags::DONT_DELIM_TRAIL, - DontCollapseArgsInUsage - => Flags::DONT_COLLAPSE_ARGS, - DeriveDisplayOrder - => Flags::DERIVE_DISP_ORDER, - DisableColoredHelp - => Flags::DISABLE_COLORED_HELP, - DisableHelpSubcommand - => Flags::DISABLE_HELP_SC, - DisableHelpFlag - => Flags::DISABLE_HELP_FLAG, - DisableHelpFlags - => Flags::DISABLE_HELP_FLAG, - DisableVersionFlag - => Flags::DISABLE_VERSION_FLAG, - DisableVersion - => Flags::DISABLE_VERSION_FLAG, - PropagateVersion - => Flags::PROPAGATE_VERSION, - GlobalVersion - => Flags::PROPAGATE_VERSION, - HidePossibleValues - => Flags::NO_POS_VALUES, - HidePossibleValuesInHelp - => Flags::NO_POS_VALUES, - HelpExpected - => Flags::HELP_REQUIRED, - Hidden - => Flags::HIDDEN, - #[cfg(feature = "unstable-multicall")] - Multicall - => Flags::MULTICALL, - NoAutoHelp - => Flags::NO_AUTO_HELP, - NoAutoVersion - => Flags::NO_AUTO_VERSION, - NoBinaryName - => Flags::NO_BIN_NAME, - SubcommandsNegateReqs - => Flags::SC_NEGATE_REQS, - SubcommandRequired - => Flags::SC_REQUIRED, - SubcommandRequiredElseHelp - => Flags::SC_REQUIRED_ELSE_HELP, - UseLongFormatForHelpSubcommand - => Flags::USE_LONG_FORMAT_FOR_HELP_SC, - TrailingVarArg - => Flags::TRAILING_VARARG, - UnifiedHelp => Flags::NO_OP, - NextLineHelp - => Flags::NEXT_LINE_HELP, - IgnoreErrors - => Flags::IGNORE_ERRORS, - WaitOnError - => Flags::WAIT_ON_ERROR, - Built - => Flags::BUILT, - BinNameBuilt - => Flags::BIN_NAME_BUILT, - InferSubcommands - => Flags::INFER_SUBCOMMANDS, - AllArgsOverrideSelf - => Flags::ARGS_OVERRIDE_SELF, - InferLongArgs - => Flags::INFER_LONG_ARGS -} - -/// Deprecated in [Issue #3087](https://github.com/clap-rs/clap/issues/3087), maybe [`clap::Parser`][crate::Parser] would fit your use case? -#[cfg(feature = "yaml")] -impl FromStr for AppSettings { - type Err = String; - fn from_str(s: &str) -> Result::Err> { - #[allow(deprecated)] - #[allow(unreachable_patterns)] - match &*s.to_ascii_lowercase() { - "argrequiredelsehelp" => Ok(AppSettings::ArgRequiredElseHelp), - "subcommandprecedenceoverarg" => Ok(AppSettings::SubcommandPrecedenceOverArg), - "argsnegatesubcommands" => Ok(AppSettings::ArgsNegateSubcommands), - "allowexternalsubcommands" => Ok(AppSettings::AllowExternalSubcommands), - "strictutf8" => Ok(AppSettings::StrictUtf8), - "allowinvalidutf8forexternalsubcommands" => { - Ok(AppSettings::AllowInvalidUtf8ForExternalSubcommands) - } - "allowhyphenvalues" => Ok(AppSettings::AllowHyphenValues), - "allowleadinghyphen" => Ok(AppSettings::AllowLeadingHyphen), - "allownegativenumbers" => Ok(AppSettings::AllowNegativeNumbers), - "allowmissingpositional" => Ok(AppSettings::AllowMissingPositional), - "unifiedhelpmessage" => Ok(AppSettings::UnifiedHelpMessage), - "coloredhelp" => Ok(AppSettings::ColoredHelp), - "coloralways" => Ok(AppSettings::ColorAlways), - "colorauto" => Ok(AppSettings::ColorAuto), - "colornever" => Ok(AppSettings::ColorNever), - "dontdelimittrailingvalues" => Ok(AppSettings::DontDelimitTrailingValues), - "dontcollapseargsinusage" => Ok(AppSettings::DontCollapseArgsInUsage), - "derivedisplayorder" => Ok(AppSettings::DeriveDisplayOrder), - "disablecoloredhelp" => Ok(AppSettings::DisableColoredHelp), - "disablehelpsubcommand" => Ok(AppSettings::DisableHelpSubcommand), - "disablehelpflag" => Ok(AppSettings::DisableHelpFlag), - "disablehelpflags" => Ok(AppSettings::DisableHelpFlags), - "disableversionflag" => Ok(AppSettings::DisableVersionFlag), - "disableversion" => Ok(AppSettings::DisableVersion), - "propagateversion" => Ok(AppSettings::PropagateVersion), - "propagateversion" => Ok(AppSettings::GlobalVersion), - "hidepossiblevalues" => Ok(AppSettings::HidePossibleValues), - "hidepossiblevaluesinhelp" => Ok(AppSettings::HidePossibleValuesInHelp), - "helpexpected" => Ok(AppSettings::HelpExpected), - "hidden" => Ok(AppSettings::Hidden), - "noautohelp" => Ok(AppSettings::NoAutoHelp), - "noautoversion" => Ok(AppSettings::NoAutoVersion), - "nobinaryname" => Ok(AppSettings::NoBinaryName), - "subcommandsnegatereqs" => Ok(AppSettings::SubcommandsNegateReqs), - "subcommandrequired" => Ok(AppSettings::SubcommandRequired), - "subcommandrequiredelsehelp" => Ok(AppSettings::SubcommandRequiredElseHelp), - "uselongformatforhelpsubcommand" => Ok(AppSettings::UseLongFormatForHelpSubcommand), - "trailingvararg" => Ok(AppSettings::TrailingVarArg), - "unifiedhelp" => Ok(AppSettings::UnifiedHelp), - "nextlinehelp" => Ok(AppSettings::NextLineHelp), - "ignoreerrors" => Ok(AppSettings::IgnoreErrors), - "waitonerror" => Ok(AppSettings::WaitOnError), - "built" => Ok(AppSettings::Built), - "binnamebuilt" => Ok(AppSettings::BinNameBuilt), - "infersubcommands" => Ok(AppSettings::InferSubcommands), - "allargsoverrideself" => Ok(AppSettings::AllArgsOverrideSelf), - "inferlongargs" => Ok(AppSettings::InferLongArgs), - _ => Err(format!("unknown AppSetting: `{}`", s)), - } - } -} - -#[cfg(test)] -mod test { - #[allow(clippy::cognitive_complexity)] - #[test] - #[cfg(feature = "yaml")] - fn app_settings_fromstr() { - use super::AppSettings; - - assert_eq!( - "disablehelpflag".parse::().unwrap(), - AppSettings::DisableHelpFlag - ); - assert_eq!( - "argsnegatesubcommands".parse::().unwrap(), - AppSettings::ArgsNegateSubcommands - ); - assert_eq!( - "argrequiredelsehelp".parse::().unwrap(), - AppSettings::ArgRequiredElseHelp - ); - assert_eq!( - "subcommandprecedenceoverarg" - .parse::() - .unwrap(), - AppSettings::SubcommandPrecedenceOverArg - ); - assert_eq!( - "allowexternalsubcommands".parse::().unwrap(), - AppSettings::AllowExternalSubcommands - ); - assert_eq!( - "allowinvalidutf8forexternalsubcommands" - .parse::() - .unwrap(), - AppSettings::AllowInvalidUtf8ForExternalSubcommands - ); - assert_eq!( - "allowhyphenvalues".parse::().unwrap(), - AppSettings::AllowHyphenValues - ); - assert_eq!( - "allownegativenumbers".parse::().unwrap(), - AppSettings::AllowNegativeNumbers - ); - assert_eq!( - "disablehelpsubcommand".parse::().unwrap(), - AppSettings::DisableHelpSubcommand - ); - assert_eq!( - "disableversionflag".parse::().unwrap(), - AppSettings::DisableVersionFlag - ); - assert_eq!( - "dontcollapseargsinusage".parse::().unwrap(), - AppSettings::DontCollapseArgsInUsage - ); - assert_eq!( - "dontdelimittrailingvalues".parse::().unwrap(), - AppSettings::DontDelimitTrailingValues - ); - assert_eq!( - "derivedisplayorder".parse::().unwrap(), - AppSettings::DeriveDisplayOrder - ); - assert_eq!( - "disablecoloredhelp".parse::().unwrap(), - AppSettings::DisableColoredHelp - ); - assert_eq!( - "propagateversion".parse::().unwrap(), - AppSettings::PropagateVersion - ); - assert_eq!( - "hidden".parse::().unwrap(), - AppSettings::Hidden - ); - assert_eq!( - "hidepossiblevalues".parse::().unwrap(), - AppSettings::HidePossibleValues - ); - assert_eq!( - "helpexpected".parse::().unwrap(), - AppSettings::HelpExpected - ); - assert_eq!( - "nobinaryname".parse::().unwrap(), - AppSettings::NoBinaryName - ); - assert_eq!( - "nextlinehelp".parse::().unwrap(), - AppSettings::NextLineHelp - ); - assert_eq!( - "subcommandsnegatereqs".parse::().unwrap(), - AppSettings::SubcommandsNegateReqs - ); - assert_eq!( - "subcommandrequired".parse::().unwrap(), - AppSettings::SubcommandRequired - ); - assert_eq!( - "subcommandrequiredelsehelp".parse::().unwrap(), - AppSettings::SubcommandRequiredElseHelp - ); - assert_eq!( - "uselongformatforhelpsubcommand" - .parse::() - .unwrap(), - AppSettings::UseLongFormatForHelpSubcommand - ); - assert_eq!( - "trailingvararg".parse::().unwrap(), - AppSettings::TrailingVarArg - ); - assert_eq!( - "waitonerror".parse::().unwrap(), - AppSettings::WaitOnError - ); - assert_eq!("built".parse::().unwrap(), AppSettings::Built); - assert_eq!( - "binnamebuilt".parse::().unwrap(), - AppSettings::BinNameBuilt - ); - assert_eq!( - "infersubcommands".parse::().unwrap(), - AppSettings::InferSubcommands - ); - assert!("hahahaha".parse::().is_err()); - } -} diff --git a/third_party/rust/clap/src/build/app/tests.rs b/third_party/rust/clap/src/build/app/tests.rs deleted file mode 100644 index 579243baadb7..000000000000 --- a/third_party/rust/clap/src/build/app/tests.rs +++ /dev/null @@ -1,63 +0,0 @@ -use crate::{App, AppSettings}; - -#[test] -fn propagate_version() { - let mut app = App::new("test") - .setting(AppSettings::PropagateVersion) - .version("1.1") - .subcommand(App::new("sub1")); - app._propagate(); - assert_eq!(app.subcommands[0].version, Some("1.1")); -} - -#[test] -fn global_setting() { - let mut app = App::new("test") - .global_setting(AppSettings::AllowHyphenValues) - .subcommand(App::new("subcmd")); - app._propagate(); - assert!(app - .subcommands - .iter() - .find(|s| s.name == "subcmd") - .unwrap() - .is_set(AppSettings::AllowHyphenValues)); -} - -#[test] -fn global_settings() { - let mut app = App::new("test") - .global_setting(AppSettings::AllowHyphenValues) - .global_setting(AppSettings::TrailingVarArg) - .subcommand(App::new("subcmd")); - app._propagate(); - assert!(app - .subcommands - .iter() - .find(|s| s.name == "subcmd") - .unwrap() - .is_set(AppSettings::AllowHyphenValues)); - assert!(app - .subcommands - .iter() - .find(|s| s.name == "subcmd") - .unwrap() - .is_set(AppSettings::TrailingVarArg)); -} - -// This test will *fail to compile* if App is not Send + Sync -#[test] -fn app_send_sync() { - fn foo(_: T) {} - foo(App::new("test")) -} - -#[test] -fn issue_2090() { - let mut app = App::new("app") - .global_setting(AppSettings::DisableVersionFlag) - .subcommand(App::new("sub")); - app._build(); - - assert!(app.subcommands[0].is_set(AppSettings::DisableVersionFlag)); -} diff --git a/third_party/rust/clap/src/build/app_settings.rs b/third_party/rust/clap/src/build/app_settings.rs new file mode 100644 index 000000000000..65d69ef825bc --- /dev/null +++ b/third_party/rust/clap/src/build/app_settings.rs @@ -0,0 +1,765 @@ +#![allow(deprecated)] + +// Std +use std::ops::BitOr; +#[cfg(feature = "yaml")] +use std::str::FromStr; + +#[allow(unused)] +use crate::Arg; +#[allow(unused)] +use crate::Command; + +// Third party +use bitflags::bitflags; + +#[doc(hidden)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct AppFlags(Flags); + +impl Default for AppFlags { + fn default() -> Self { + AppFlags(Flags::COLOR_AUTO) + } +} + +/// Application level settings, which affect how [`Command`] operates +/// +/// **NOTE:** When these settings are used, they apply only to current command, and are *not* +/// propagated down or up through child or parent subcommands +/// +/// [`Command`]: crate::Command +#[derive(Debug, PartialEq, Copy, Clone)] +#[non_exhaustive] +pub enum AppSettings { + /// Deprecated, replaced with [`Command::ignore_errors`] + #[deprecated(since = "3.1.0", note = "Replaced with `Command::ignore_errors`")] + IgnoreErrors, + + /// Deprecated, replace + /// ```rust,no_run + /// let cmd = clap::Command::new("cmd") + /// .global_setting(clap::AppSettings::WaitOnError) + /// .arg(clap::arg!(--flag)); + /// let m = cmd.get_matches(); + /// ``` + /// with + /// ```rust + /// let cmd = clap::Command::new("cmd") + /// .arg(clap::arg!(--flag)); + /// let m = match cmd.try_get_matches() { + /// Ok(m) => m, + /// Err(err) => { + /// if err.use_stderr() { + /// let _ = err.print(); + /// + /// eprintln!("\nPress [ENTER] / [RETURN] to continue..."); + /// use std::io::BufRead; + /// let mut s = String::new(); + /// let i = std::io::stdin(); + /// i.lock().read_line(&mut s).unwrap(); + /// + /// std::process::exit(2); + /// } else { + /// let _ = err.print(); + /// std::process::exit(0); + /// } + /// } + /// }; + /// ``` + #[deprecated( + since = "3.1.0", + note = "See documentation for how to hand-implement this" + )] + WaitOnError, + + /// Deprecated, replaced with [`Command::allow_hyphen_values`] and + /// [`Arg::is_allow_hyphen_values_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Command::allow_hyphen_values` and `Arg::is_allow_hyphen_values_set`" + )] + AllowHyphenValues, + + /// Deprecated, replaced with [`Command::allow_negative_numbers`] and + /// [`Command::is_allow_negative_numbers_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Command::allow_negative_numbers` and `Command::is_allow_negative_numbers_set`" + )] + AllowNegativeNumbers, + + /// Deprecated, replaced with [`Command::args_override_self`] + #[deprecated(since = "3.1.0", note = "Replaced with `Command::args_override_self`")] + AllArgsOverrideSelf, + + /// Deprecated, replaced with [`Command::allow_missing_positional`] and + /// [`Command::is_allow_missing_positional_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Command::allow_missing_positional` and `Command::is_allow_missing_positional_set`" + )] + AllowMissingPositional, + + /// Deprecated, replaced with [`Command::trailing_var_arg`] and [`Command::is_trailing_var_arg_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Command::trailing_var_arg` and `Command::is_trailing_var_arg_set`" + )] + TrailingVarArg, + + /// Deprecated, replaced with [`Command::dont_delimit_trailing_values`] and + /// [`Command::is_dont_delimit_trailing_values_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Command::dont_delimit_trailing_values` and `Command::is_dont_delimit_trailing_values_set`" + )] + DontDelimitTrailingValues, + + /// Deprecated, replaced with [`Command::infer_long_args`] + #[deprecated(since = "3.1.0", note = "Replaced with `Command::infer_long_args`")] + InferLongArgs, + + /// Deprecated, replaced with [`Command::infer_subcommands`] + #[deprecated(since = "3.1.0", note = "Replaced with `Command::infer_subcommands`")] + InferSubcommands, + + /// Deprecated, replaced with [`Command::subcommand_required`] and + /// [`Command::is_subcommand_required_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Command::subcommand_required` and `Command::is_subcommand_required_set`" + )] + SubcommandRequired, + + /// Deprecated, replaced with [`Command::subcommand_required`] combined with + /// [`Command::arg_required_else_help`]. + #[deprecated( + since = "3.1.0", + note = "Replaced with `Command::subcommand_required` combined with `Command::arg_required_else_help`" + )] + SubcommandRequiredElseHelp, + + /// Deprecated, replaced with [`Command::allow_external_subcommands`] and + /// [`Command::is_allow_external_subcommands_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Command::allow_external_subcommands` and `Command::is_allow_external_subcommands_set`" + )] + AllowExternalSubcommands, + + /// Deprecated, replaced with [`Command::multicall`] and [`Command::is_multicall_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Command::multicall` and `Command::is_multicall_set`" + )] + #[cfg(feature = "unstable-multicall")] + Multicall, + + /// Deprecated, replaced with [`Command::allow_invalid_utf8_for_external_subcommands`] and [`Command::is_allow_invalid_utf8_for_external_subcommands_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Command::allow_invalid_utf8_for_external_subcommands` and `Command::is_allow_invalid_utf8_for_external_subcommands_set`" + )] + AllowInvalidUtf8ForExternalSubcommands, + + /// Deprecated, this is now the default + #[deprecated(since = "3.1.0", note = "This is now the default")] + UseLongFormatForHelpSubcommand, + + /// Deprecated, replaced with [`Command::subcommand_negates_reqs`] and + /// [`Command::is_subcommand_negates_reqs_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Command::subcommand_negates_reqs` and `Command::is_subcommand_negates_reqs_set`" + )] + SubcommandsNegateReqs, + + /// Deprecated, replaced with [`Command::args_conflicts_with_subcommands`] and + /// [`Command::is_args_conflicts_with_subcommands_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Command::args_conflicts_with_subcommands` and `Command::is_args_conflicts_with_subcommands_set`" + )] + ArgsNegateSubcommands, + + /// Deprecated, replaced with [`Command::subcommand_precedence_over_arg`] and + /// [`Command::is_subcommand_precedence_over_arg_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Command::subcommand_precedence_over_arg` and `Command::is_subcommand_precedence_over_arg_set`" + )] + SubcommandPrecedenceOverArg, + + /// Deprecated, replaced with [`Command::arg_required_else_help`] and + /// [`Command::is_arg_required_else_help_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Command::arg_required_else_help` and `Command::is_arg_required_else_help_set`" + )] + ArgRequiredElseHelp, + + /// Displays the arguments and [`subcommands`] in the help message in the order that they were + /// declared in, and not alphabetically which is the default. + /// + /// To override the declaration order, see [`Arg::display_order`] and [`Command::display_order`]. + /// + /// # Examples + /// + /// ```no_run + /// # use clap::{Command, Arg, AppSettings}; + /// Command::new("myprog") + /// .global_setting(AppSettings::DeriveDisplayOrder) + /// .get_matches(); + /// ``` + /// + /// [`subcommands`]: crate::Command::subcommand() + /// [`Arg::display_order`]: crate::Arg::display_order + /// [`Command::display_order`]: crate::Command::display_order + DeriveDisplayOrder, + + /// Deprecated, replaced with [`Command::dont_collapse_args_in_usage`] and + /// [`Command::is_dont_collapse_args_in_usage_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Command::dont_collapse_args_in_usage` and `Command::is_dont_collapse_args_in_usage_set`" + )] + DontCollapseArgsInUsage, + + /// Deprecated, replaced with [`Command::next_line_help`] and [`Command::is_next_line_help_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Command::next_line_help` and `Command::is_next_line_help_set`" + )] + NextLineHelp, + + /// Deprecated, replaced with [`Command::disable_colored_help`] and + /// [`Command::is_disable_colored_help_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Command::disable_colored_help` and `Command::is_disable_colored_help_set`" + )] + DisableColoredHelp, + + /// Deprecated, replaced with [`Command::disable_help_flag`] and [`Command::is_disable_help_flag_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Command::disable_help_flag` and `Command::is_disable_help_flag_set`" + )] + DisableHelpFlag, + + /// Deprecated, replaced with [`Command::disable_help_subcommand`] and + /// [`Command::is_disable_help_subcommand_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Command::disable_help_subcommand` and `Command::is_disable_help_subcommand_set`" + )] + DisableHelpSubcommand, + + /// Deprecated, replaced with [`Command::disable_version_flag`] and + /// [`Command::is_disable_version_flag_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Command::disable_version_flag` and `Command::is_disable_version_flag_set`" + )] + DisableVersionFlag, + + /// Deprecated, replaced with [`Command::propagate_version`] and [`Command::is_propagate_version_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Command::propagate_version` and `Command::is_propagate_version_set`" + )] + PropagateVersion, + + /// Deprecated, replaced with [`Command::hide`] and [`Command::is_hide_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Command::hide` and `Command::is_hide_set`" + )] + Hidden, + + /// Deprecated, replaced with [`Command::hide_possible_values`] and + /// [`Arg::is_hide_possible_values_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Command::hide_possible_values` and `Arg::is_hide_possible_values_set`" + )] + HidePossibleValues, + + /// Deprecated, replaced with [`Command::help_expected`] + #[deprecated(since = "3.1.0", note = "Replaced with `Command::help_expected`")] + HelpExpected, + + /// Deprecated, replaced with [`Command::no_binary_name`] + #[deprecated(since = "3.1.0", note = "Replaced with `Command::no_binary_name`")] + NoBinaryName, + + /// Treat the auto-generated `-h, --help` flags like any other flag, and *not* print the help + /// message. + /// + /// This allows one to handle printing of the help message manually. + /// + /// ```rust + /// # use clap::{Command, AppSettings}; + /// let result = Command::new("myprog") + /// .setting(AppSettings::NoAutoHelp) + /// .try_get_matches_from("myprog --help".split(" ")); + /// + /// // Normally, if `--help` is used clap prints the help message and returns an + /// // ErrorKind::DisplayHelp + /// // + /// // However, `--help` was treated like a normal flag + /// + /// assert!(result.is_ok()); + /// assert!(result.unwrap().is_present("help")); + /// ``` + NoAutoHelp, + + /// Treat the auto-generated `-V, --version` flags like any other flag, and + /// *not* print the version message. + /// + /// This allows one to handle printing of the version message manually. + /// + /// ```rust + /// # use clap::{Command, AppSettings}; + /// let result = Command::new("myprog") + /// .version("3.0") + /// .setting(AppSettings::NoAutoVersion) + /// .try_get_matches_from("myprog --version".split(" ")); + /// + /// // Normally, if `--version` is used clap prints the version message and returns an + /// // ErrorKind::DisplayVersion + /// // + /// // However, `--version` was treated like a normal flag + /// + /// assert!(result.is_ok()); + /// assert!(result.unwrap().is_present("version")); + /// ``` + NoAutoVersion, + + /// Deprecated, replaced with [`AppSettings::AllowHyphenValues`] + #[deprecated( + since = "3.0.0", + note = "Replaced with `AppSettings::AllowHyphenValues`" + )] + #[doc(hidden)] + AllowLeadingHyphen, + + /// Deprecated, this is now the default, see [`AppSettings::AllowInvalidUtf8ForExternalSubcommands`] and [`ArgSettings::AllowInvalidUtf8`][crate::ArgSettings::AllowInvalidUtf8] for the opposite. + #[deprecated( + since = "3.0.0", + note = "This is now the default see `AppSettings::AllowInvalidUtf8ForExternalSubcommands` and `ArgSettings::AllowInvalidUtf8` for the opposite." + )] + #[doc(hidden)] + StrictUtf8, + + /// Deprecated, this is now the default + #[deprecated(since = "3.0.0", note = "This is now the default")] + #[doc(hidden)] + UnifiedHelpMessage, + + /// Deprecated, this is now the default + #[deprecated(since = "3.0.0", note = "This is now the default")] + #[doc(hidden)] + ColoredHelp, + + /// Deprecated, see [`Command::color`][crate::Command::color] + #[deprecated(since = "3.0.0", note = "Replaced with `Command::color`")] + #[doc(hidden)] + ColorAuto, + + /// Deprecated, replaced with [`Command::color`][crate::Command::color] + #[deprecated(since = "3.0.0", note = "Replaced with `Command::color`")] + #[doc(hidden)] + ColorAlways, + + /// Deprecated, replaced with [`Command::color`][crate::Command::color] + #[deprecated(since = "3.0.0", note = "Replaced with `Command::color`")] + #[doc(hidden)] + ColorNever, + + /// Deprecated, replaced with [`AppSettings::DisableHelpFlag`] + #[deprecated(since = "3.0.0", note = "Replaced with `AppSettings::DisableHelpFlag`")] + #[doc(hidden)] + DisableHelpFlags, + + /// Deprecated, replaced with [`AppSettings::DisableVersionFlag`] + #[deprecated( + since = "3.0.0", + note = "Replaced with `AppSettings::DisableVersionFlag`" + )] + #[doc(hidden)] + DisableVersion, + + /// Deprecated, replaced with [`AppSettings::PropagateVersion`] + #[deprecated( + since = "3.0.0", + note = "Replaced with `AppSettings::PropagateVersion`" + )] + #[doc(hidden)] + GlobalVersion, + + /// Deprecated, replaced with [`AppSettings::HidePossibleValues`] + #[deprecated( + since = "3.0.0", + note = "Replaced with AppSettings::HidePossibleValues" + )] + #[doc(hidden)] + HidePossibleValuesInHelp, + + /// Deprecated, this is now the default + #[deprecated(since = "3.0.0", note = "This is now the default")] + #[doc(hidden)] + UnifiedHelp, + + /// If the cmd is already built, used for caching. + #[doc(hidden)] + Built, + + /// If the cmd's bin name is already built, used for caching. + #[doc(hidden)] + BinNameBuilt, +} + +bitflags! { + struct Flags: u64 { + const SC_NEGATE_REQS = 1; + const SC_REQUIRED = 1 << 1; + const ARG_REQUIRED_ELSE_HELP = 1 << 2; + const PROPAGATE_VERSION = 1 << 3; + const DISABLE_VERSION_FOR_SC = 1 << 4; + const WAIT_ON_ERROR = 1 << 6; + const SC_REQUIRED_ELSE_HELP = 1 << 7; + const NO_AUTO_HELP = 1 << 8; + const NO_AUTO_VERSION = 1 << 9; + const DISABLE_VERSION_FLAG = 1 << 10; + const HIDDEN = 1 << 11; + const TRAILING_VARARG = 1 << 12; + const NO_BIN_NAME = 1 << 13; + const ALLOW_UNK_SC = 1 << 14; + const SC_UTF8_NONE = 1 << 15; + const LEADING_HYPHEN = 1 << 16; + const NO_POS_VALUES = 1 << 17; + const NEXT_LINE_HELP = 1 << 18; + const DERIVE_DISP_ORDER = 1 << 19; + const DISABLE_COLORED_HELP = 1 << 20; + const COLOR_ALWAYS = 1 << 21; + const COLOR_AUTO = 1 << 22; + const COLOR_NEVER = 1 << 23; + const DONT_DELIM_TRAIL = 1 << 24; + const ALLOW_NEG_NUMS = 1 << 25; + const DISABLE_HELP_SC = 1 << 27; + const DONT_COLLAPSE_ARGS = 1 << 28; + const ARGS_NEGATE_SCS = 1 << 29; + const PROPAGATE_VALS_DOWN = 1 << 30; + const ALLOW_MISSING_POS = 1 << 31; + const TRAILING_VALUES = 1 << 32; + const BUILT = 1 << 33; + const BIN_NAME_BUILT = 1 << 34; + const VALID_ARG_FOUND = 1 << 35; + const INFER_SUBCOMMANDS = 1 << 36; + const CONTAINS_LAST = 1 << 37; + const ARGS_OVERRIDE_SELF = 1 << 38; + const HELP_REQUIRED = 1 << 39; + const SUBCOMMAND_PRECEDENCE_OVER_ARG = 1 << 40; + const DISABLE_HELP_FLAG = 1 << 41; + const USE_LONG_FORMAT_FOR_HELP_SC = 1 << 42; + const INFER_LONG_ARGS = 1 << 43; + const IGNORE_ERRORS = 1 << 44; + #[cfg(feature = "unstable-multicall")] + const MULTICALL = 1 << 45; + const NO_OP = 0; + } +} + +impl_settings! { AppSettings, AppFlags, + ArgRequiredElseHelp + => Flags::ARG_REQUIRED_ELSE_HELP, + SubcommandPrecedenceOverArg + => Flags::SUBCOMMAND_PRECEDENCE_OVER_ARG, + ArgsNegateSubcommands + => Flags::ARGS_NEGATE_SCS, + AllowExternalSubcommands + => Flags::ALLOW_UNK_SC, + StrictUtf8 + => Flags::NO_OP, + AllowInvalidUtf8ForExternalSubcommands + => Flags::SC_UTF8_NONE, + AllowHyphenValues + => Flags::LEADING_HYPHEN, + AllowLeadingHyphen + => Flags::LEADING_HYPHEN, + AllowNegativeNumbers + => Flags::ALLOW_NEG_NUMS, + AllowMissingPositional + => Flags::ALLOW_MISSING_POS, + UnifiedHelpMessage + => Flags::NO_OP, + ColoredHelp + => Flags::NO_OP, + ColorAlways + => Flags::COLOR_ALWAYS, + ColorAuto + => Flags::COLOR_AUTO, + ColorNever + => Flags::COLOR_NEVER, + DontDelimitTrailingValues + => Flags::DONT_DELIM_TRAIL, + DontCollapseArgsInUsage + => Flags::DONT_COLLAPSE_ARGS, + DeriveDisplayOrder + => Flags::DERIVE_DISP_ORDER, + DisableColoredHelp + => Flags::DISABLE_COLORED_HELP, + DisableHelpSubcommand + => Flags::DISABLE_HELP_SC, + DisableHelpFlag + => Flags::DISABLE_HELP_FLAG, + DisableHelpFlags + => Flags::DISABLE_HELP_FLAG, + DisableVersionFlag + => Flags::DISABLE_VERSION_FLAG, + DisableVersion + => Flags::DISABLE_VERSION_FLAG, + PropagateVersion + => Flags::PROPAGATE_VERSION, + GlobalVersion + => Flags::PROPAGATE_VERSION, + HidePossibleValues + => Flags::NO_POS_VALUES, + HidePossibleValuesInHelp + => Flags::NO_POS_VALUES, + HelpExpected + => Flags::HELP_REQUIRED, + Hidden + => Flags::HIDDEN, + #[cfg(feature = "unstable-multicall")] + Multicall + => Flags::MULTICALL, + NoAutoHelp + => Flags::NO_AUTO_HELP, + NoAutoVersion + => Flags::NO_AUTO_VERSION, + NoBinaryName + => Flags::NO_BIN_NAME, + SubcommandsNegateReqs + => Flags::SC_NEGATE_REQS, + SubcommandRequired + => Flags::SC_REQUIRED, + SubcommandRequiredElseHelp + => Flags::SC_REQUIRED_ELSE_HELP, + UseLongFormatForHelpSubcommand + => Flags::USE_LONG_FORMAT_FOR_HELP_SC, + TrailingVarArg + => Flags::TRAILING_VARARG, + UnifiedHelp => Flags::NO_OP, + NextLineHelp + => Flags::NEXT_LINE_HELP, + IgnoreErrors + => Flags::IGNORE_ERRORS, + WaitOnError + => Flags::WAIT_ON_ERROR, + Built + => Flags::BUILT, + BinNameBuilt + => Flags::BIN_NAME_BUILT, + InferSubcommands + => Flags::INFER_SUBCOMMANDS, + AllArgsOverrideSelf + => Flags::ARGS_OVERRIDE_SELF, + InferLongArgs + => Flags::INFER_LONG_ARGS +} + +/// Deprecated in [Issue #3087](https://github.com/clap-rs/clap/issues/3087), maybe [`clap::Parser`][crate::Parser] would fit your use case? +#[cfg(feature = "yaml")] +impl FromStr for AppSettings { + type Err = String; + fn from_str(s: &str) -> Result::Err> { + #[allow(deprecated)] + #[allow(unreachable_patterns)] + match &*s.to_ascii_lowercase() { + "argrequiredelsehelp" => Ok(AppSettings::ArgRequiredElseHelp), + "subcommandprecedenceoverarg" => Ok(AppSettings::SubcommandPrecedenceOverArg), + "argsnegatesubcommands" => Ok(AppSettings::ArgsNegateSubcommands), + "allowexternalsubcommands" => Ok(AppSettings::AllowExternalSubcommands), + "strictutf8" => Ok(AppSettings::StrictUtf8), + "allowinvalidutf8forexternalsubcommands" => { + Ok(AppSettings::AllowInvalidUtf8ForExternalSubcommands) + } + "allowhyphenvalues" => Ok(AppSettings::AllowHyphenValues), + "allowleadinghyphen" => Ok(AppSettings::AllowLeadingHyphen), + "allownegativenumbers" => Ok(AppSettings::AllowNegativeNumbers), + "allowmissingpositional" => Ok(AppSettings::AllowMissingPositional), + "unifiedhelpmessage" => Ok(AppSettings::UnifiedHelpMessage), + "coloredhelp" => Ok(AppSettings::ColoredHelp), + "coloralways" => Ok(AppSettings::ColorAlways), + "colorauto" => Ok(AppSettings::ColorAuto), + "colornever" => Ok(AppSettings::ColorNever), + "dontdelimittrailingvalues" => Ok(AppSettings::DontDelimitTrailingValues), + "dontcollapseargsinusage" => Ok(AppSettings::DontCollapseArgsInUsage), + "derivedisplayorder" => Ok(AppSettings::DeriveDisplayOrder), + "disablecoloredhelp" => Ok(AppSettings::DisableColoredHelp), + "disablehelpsubcommand" => Ok(AppSettings::DisableHelpSubcommand), + "disablehelpflag" => Ok(AppSettings::DisableHelpFlag), + "disablehelpflags" => Ok(AppSettings::DisableHelpFlags), + "disableversionflag" => Ok(AppSettings::DisableVersionFlag), + "disableversion" => Ok(AppSettings::DisableVersion), + "propagateversion" => Ok(AppSettings::PropagateVersion), + "propagateversion" => Ok(AppSettings::GlobalVersion), + "hidepossiblevalues" => Ok(AppSettings::HidePossibleValues), + "hidepossiblevaluesinhelp" => Ok(AppSettings::HidePossibleValuesInHelp), + "helpexpected" => Ok(AppSettings::HelpExpected), + "hidden" => Ok(AppSettings::Hidden), + "noautohelp" => Ok(AppSettings::NoAutoHelp), + "noautoversion" => Ok(AppSettings::NoAutoVersion), + "nobinaryname" => Ok(AppSettings::NoBinaryName), + "subcommandsnegatereqs" => Ok(AppSettings::SubcommandsNegateReqs), + "subcommandrequired" => Ok(AppSettings::SubcommandRequired), + "subcommandrequiredelsehelp" => Ok(AppSettings::SubcommandRequiredElseHelp), + "uselongformatforhelpsubcommand" => Ok(AppSettings::UseLongFormatForHelpSubcommand), + "trailingvararg" => Ok(AppSettings::TrailingVarArg), + "unifiedhelp" => Ok(AppSettings::UnifiedHelp), + "nextlinehelp" => Ok(AppSettings::NextLineHelp), + "ignoreerrors" => Ok(AppSettings::IgnoreErrors), + "waitonerror" => Ok(AppSettings::WaitOnError), + "built" => Ok(AppSettings::Built), + "binnamebuilt" => Ok(AppSettings::BinNameBuilt), + "infersubcommands" => Ok(AppSettings::InferSubcommands), + "allargsoverrideself" => Ok(AppSettings::AllArgsOverrideSelf), + "inferlongargs" => Ok(AppSettings::InferLongArgs), + _ => Err(format!("unknown AppSetting: `{}`", s)), + } + } +} + +#[cfg(test)] +mod test { + #[allow(clippy::cognitive_complexity)] + #[test] + #[cfg(feature = "yaml")] + fn app_settings_fromstr() { + use super::AppSettings; + + assert_eq!( + "disablehelpflag".parse::().unwrap(), + AppSettings::DisableHelpFlag + ); + assert_eq!( + "argsnegatesubcommands".parse::().unwrap(), + AppSettings::ArgsNegateSubcommands + ); + assert_eq!( + "argrequiredelsehelp".parse::().unwrap(), + AppSettings::ArgRequiredElseHelp + ); + assert_eq!( + "subcommandprecedenceoverarg" + .parse::() + .unwrap(), + AppSettings::SubcommandPrecedenceOverArg + ); + assert_eq!( + "allowexternalsubcommands".parse::().unwrap(), + AppSettings::AllowExternalSubcommands + ); + assert_eq!( + "allowinvalidutf8forexternalsubcommands" + .parse::() + .unwrap(), + AppSettings::AllowInvalidUtf8ForExternalSubcommands + ); + assert_eq!( + "allowhyphenvalues".parse::().unwrap(), + AppSettings::AllowHyphenValues + ); + assert_eq!( + "allownegativenumbers".parse::().unwrap(), + AppSettings::AllowNegativeNumbers + ); + assert_eq!( + "disablehelpsubcommand".parse::().unwrap(), + AppSettings::DisableHelpSubcommand + ); + assert_eq!( + "disableversionflag".parse::().unwrap(), + AppSettings::DisableVersionFlag + ); + assert_eq!( + "dontcollapseargsinusage".parse::().unwrap(), + AppSettings::DontCollapseArgsInUsage + ); + assert_eq!( + "dontdelimittrailingvalues".parse::().unwrap(), + AppSettings::DontDelimitTrailingValues + ); + assert_eq!( + "derivedisplayorder".parse::().unwrap(), + AppSettings::DeriveDisplayOrder + ); + assert_eq!( + "disablecoloredhelp".parse::().unwrap(), + AppSettings::DisableColoredHelp + ); + assert_eq!( + "propagateversion".parse::().unwrap(), + AppSettings::PropagateVersion + ); + assert_eq!( + "hidden".parse::().unwrap(), + AppSettings::Hidden + ); + assert_eq!( + "hidepossiblevalues".parse::().unwrap(), + AppSettings::HidePossibleValues + ); + assert_eq!( + "helpexpected".parse::().unwrap(), + AppSettings::HelpExpected + ); + assert_eq!( + "nobinaryname".parse::().unwrap(), + AppSettings::NoBinaryName + ); + assert_eq!( + "nextlinehelp".parse::().unwrap(), + AppSettings::NextLineHelp + ); + assert_eq!( + "subcommandsnegatereqs".parse::().unwrap(), + AppSettings::SubcommandsNegateReqs + ); + assert_eq!( + "subcommandrequired".parse::().unwrap(), + AppSettings::SubcommandRequired + ); + assert_eq!( + "subcommandrequiredelsehelp".parse::().unwrap(), + AppSettings::SubcommandRequiredElseHelp + ); + assert_eq!( + "uselongformatforhelpsubcommand" + .parse::() + .unwrap(), + AppSettings::UseLongFormatForHelpSubcommand + ); + assert_eq!( + "trailingvararg".parse::().unwrap(), + AppSettings::TrailingVarArg + ); + assert_eq!( + "waitonerror".parse::().unwrap(), + AppSettings::WaitOnError + ); + assert_eq!("built".parse::().unwrap(), AppSettings::Built); + assert_eq!( + "binnamebuilt".parse::().unwrap(), + AppSettings::BinNameBuilt + ); + assert_eq!( + "infersubcommands".parse::().unwrap(), + AppSettings::InferSubcommands + ); + assert!("hahahaha".parse::().is_err()); + } +} diff --git a/third_party/rust/clap/src/build/arg/mod.rs b/third_party/rust/clap/src/build/arg.rs similarity index 86% rename from third_party/rust/clap/src/build/arg/mod.rs rename to third_party/rust/clap/src/build/arg.rs index 053cb2dd4a5d..9fdf2a1cff53 100644 --- a/third_party/rust/clap/src/build/arg/mod.rs +++ b/third_party/rust/clap/src/build/arg.rs @@ -1,14 +1,4 @@ -#[cfg(debug_assertions)] -pub mod debug_asserts; -mod possible_value; -mod settings; -#[cfg(test)] -mod tests; -mod value_hint; - -pub use self::possible_value::PossibleValue; -pub use self::settings::{ArgFlags, ArgSettings}; -pub use self::value_hint::ValueHint; +#![allow(deprecated)] // Std use std::{ @@ -17,7 +7,7 @@ use std::{ error::Error, ffi::OsStr, fmt::{self, Display, Formatter}, - iter, str, + str, sync::{Arc, Mutex}, }; #[cfg(feature = "env")] @@ -27,17 +17,16 @@ use std::{env, ffi::OsString}; use yaml_rust::Yaml; // Internal -use crate::{ - build::usage_parser::UsageParser, - util::{Id, Key}, - INTERNAL_ERROR_MSG, -}; +use crate::build::usage_parser::UsageParser; +use crate::build::ArgPredicate; +use crate::util::{Id, Key}; +use crate::PossibleValue; +use crate::ValueHint; +use crate::INTERNAL_ERROR_MSG; +use crate::{ArgFlags, ArgSettings}; #[cfg(feature = "regex")] -mod regex; - -#[cfg(feature = "regex")] -pub use self::regex::RegexRef; +use crate::build::RegexRef; /// The abstract representation of a command line argument. Used to set all the options and /// relationships that define a valid argument for the program. @@ -46,6 +35,12 @@ pub use self::regex::RegexRef; /// manually, or using a usage string which is far less verbose but has fewer options. You can also /// use a combination of the two methods to achieve the best of both worlds. /// +/// - [Basic API][crate::Arg#basic-api] +/// - [Value Handling][crate::Arg#value-handling] +/// - [Help][crate::Arg#help-1] +/// - [Advanced Argument Relations][crate::Arg#advanced-argument-relations] +/// - [Reflection][crate::Arg#reflection] +/// /// # Examples /// /// ```rust @@ -72,7 +67,7 @@ pub struct Arg<'help> { pub(crate) settings: ArgFlags, pub(crate) overrides: Vec, pub(crate) groups: Vec, - pub(crate) requires: Vec<(Option<&'help str>, Id)>, + pub(crate) requires: Vec<(ArgPredicate<'help>, Id)>, pub(crate) r_ifs: Vec<(Id, &'help str)>, pub(crate) r_ifs_all: Vec<(Id, &'help str)>, pub(crate) r_unless: Vec, @@ -81,7 +76,7 @@ pub struct Arg<'help> { pub(crate) long: Option<&'help str>, pub(crate) aliases: Vec<(&'help str, bool)>, // (name, visible) pub(crate) short_aliases: Vec<(char, bool)>, // (name, visible) - pub(crate) disp_ord: Option, + pub(crate) disp_ord: DisplayOrder, pub(crate) possible_vals: Vec>, pub(crate) val_names: Vec<&'help str>, pub(crate) num_vals: Option, @@ -92,7 +87,7 @@ pub struct Arg<'help> { pub(crate) validator_os: Option>>>, pub(crate) val_delim: Option, pub(crate) default_vals: Vec<&'help OsStr>, - pub(crate) default_vals_ifs: Vec<(Id, Option<&'help OsStr>, Option<&'help OsStr>)>, + pub(crate) default_vals_ifs: Vec<(Id, ArgPredicate<'help>, Option<&'help OsStr>)>, pub(crate) default_missing_vals: Vec<&'help OsStr>, #[cfg(feature = "env")] pub(crate) env: Option<(&'help OsStr, Option)>, @@ -102,6 +97,7 @@ pub struct Arg<'help> { pub(crate) value_hint: ValueHint, } +/// # Basic API impl<'help> Arg<'help> { /// Create a new [`Arg`] with a unique name. /// @@ -115,7 +111,7 @@ impl<'help> Arg<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; + /// # use clap::{Command, Arg}; /// Arg::new("config") /// # ; /// ``` @@ -128,13 +124,19 @@ impl<'help> Arg<'help> { /// /// See [`Arg::new`] for more details. #[must_use] - pub fn name>(mut self, n: S) -> Self { + pub fn id>(mut self, n: S) -> Self { let name = n.into(); self.id = Id::from(&*name); self.name = name; self } + /// Deprecated, replaced with [`Arg::id`] + #[deprecated(since = "3.1.0", note = "Replaced with `Arg::id`")] + pub fn name>(self, n: S) -> Self { + self.id(n) + } + /// Sets the short version of the argument without the preceding `-`. /// /// By default `V` and `h` are used by the auto-generated `version` and `help` arguments, @@ -148,8 +150,8 @@ impl<'help> Arg<'help> { /// argument via a single hyphen (`-`) such as `-c`: /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("config") /// .short('c')) /// .get_matches_from(vec![ @@ -185,8 +187,8 @@ impl<'help> Arg<'help> { /// Setting `long` allows using the argument via a double hyphen (`--`) such as `--config` /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("cfg") /// .long("config")) /// .get_matches_from(vec![ @@ -198,7 +200,14 @@ impl<'help> Arg<'help> { #[inline] #[must_use] pub fn long(mut self, l: &'help str) -> Self { - self.long = Some(l.trim_start_matches(|c| c == '-')); + #[cfg(feature = "unstable-v4")] + { + self.long = Some(l); + } + #[cfg(not(feature = "unstable-v4"))] + { + self.long = Some(l.trim_start_matches(|c| c == '-')); + } self } @@ -210,8 +219,8 @@ impl<'help> Arg<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("test") /// .long("test") /// .alias("alias") @@ -236,8 +245,8 @@ impl<'help> Arg<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("test") /// .short('t') /// .short_alias('e') @@ -264,8 +273,8 @@ impl<'help> Arg<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("test") /// .long("test") /// .aliases(&["do-stuff", "do-tests", "tests"]) @@ -290,8 +299,8 @@ impl<'help> Arg<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("test") /// .short('t') /// .short_aliases(&['e', 's']) @@ -318,8 +327,8 @@ impl<'help> Arg<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("test") /// .visible_alias("something-awesome") /// .long("test") @@ -330,7 +339,7 @@ impl<'help> Arg<'help> { /// assert!(m.is_present("test")); /// assert_eq!(m.value_of("test"), Some("coffee")); /// ``` - /// [`App::alias`]: Arg::alias() + /// [`Command::alias`]: Arg::alias() #[must_use] pub fn visible_alias>(mut self, name: S) -> Self { self.aliases.push((name.into(), true)); @@ -344,8 +353,8 @@ impl<'help> Arg<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("test") /// .long("test") /// .visible_short_alias('t') @@ -371,8 +380,8 @@ impl<'help> Arg<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("test") /// .long("test") /// .visible_aliases(&["something", "awesome", "cool"])) @@ -381,7 +390,7 @@ impl<'help> Arg<'help> { /// ]); /// assert!(m.is_present("test")); /// ``` - /// [`App::aliases`]: Arg::aliases() + /// [`Command::aliases`]: Arg::aliases() #[must_use] pub fn visible_aliases(mut self, names: &[&'help str]) -> Self { self.aliases.extend(names.iter().map(|n| (*n, true))); @@ -395,8 +404,8 @@ impl<'help> Arg<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("test") /// .long("test") /// .visible_short_aliases(&['t', 'e'])) @@ -431,22 +440,22 @@ impl<'help> Arg<'help> { /// /// # Panics /// - /// [`App`] will [`panic!`] if indexes are skipped (such as defining `index(1)` and `index(3)` + /// [`Command`] will [`panic!`] if indexes are skipped (such as defining `index(1)` and `index(3)` /// but not `index(2)`, or a positional argument is defined as multiple and is not the highest /// index /// /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; + /// # use clap::{Command, Arg}; /// Arg::new("config") /// .index(1) /// # ; /// ``` /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("mode") /// .index(1)) /// .arg(Arg::new("debug") @@ -463,7 +472,7 @@ impl<'help> Arg<'help> { /// [`Arg::long`]: Arg::long() /// [`Arg::multiple_values(true)`]: Arg::multiple_values() /// [`panic!`]: https://doc.rust-lang.org/std/macro.panic!.html - /// [`App`]: crate::App + /// [`Command`]: crate::Command #[inline] #[must_use] pub fn index(mut self, idx: usize) -> Self { @@ -483,7 +492,7 @@ impl<'help> Arg<'help> { /// **NOTE:** This will change the usage string to look like `$ prog [OPTIONS] [-- ]` if /// `ARG` is marked as `.last(true)`. /// - /// **NOTE:** This setting will imply [`crate::AppSettings::DontCollapseArgsInUsage`] because failing + /// **NOTE:** This setting will imply [`crate::Command::dont_collapse_args_in_usage`] because failing /// to set this can make the usage string very confusing. /// /// **NOTE**: This setting only applies to positional arguments, and has no effect on OPTIONS @@ -491,8 +500,9 @@ impl<'help> Arg<'help> { /// **NOTE:** Setting this requires [`Arg::takes_value`] /// /// **CAUTION:** Using this setting *and* having child subcommands is not - /// recommended with the exception of *also* using [`crate::AppSettings::ArgsNegateSubcommands`] - /// (or [`crate::AppSettings::SubcommandsNegateReqs`] if the argument marked `Last` is also + /// recommended with the exception of *also* using + /// [`crate::Command::args_conflicts_with_subcommands`] + /// (or [`crate::Command::subcommand_negates_reqs`] if the argument marked `Last` is also /// marked [`Arg::required`]) /// /// # Examples @@ -509,8 +519,8 @@ impl<'help> Arg<'help> { /// and requires that the `--` syntax be used to access it early. /// /// ```rust - /// # use clap::{App, Arg}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg}; + /// let res = Command::new("prog") /// .arg(Arg::new("first")) /// .arg(Arg::new("second")) /// .arg(Arg::new("third") @@ -530,8 +540,8 @@ impl<'help> Arg<'help> { /// failing to use the `--` syntax results in an error. /// /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg, ErrorKind}; + /// let res = Command::new("prog") /// .arg(Arg::new("first")) /// .arg(Arg::new("second")) /// .arg(Arg::new("third") @@ -542,7 +552,7 @@ impl<'help> Arg<'help> { /// ]); /// /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::UnknownArgument); + /// assert_eq!(res.unwrap_err().kind(), ErrorKind::UnknownArgument); /// ``` /// [index]: Arg::index() /// [`UnknownArgument`]: crate::ErrorKind::UnknownArgument @@ -580,8 +590,8 @@ impl<'help> Arg<'help> { /// Setting required requires that the argument be used at runtime. /// /// ```rust - /// # use clap::{App, Arg}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg}; + /// let res = Command::new("prog") /// .arg(Arg::new("cfg") /// .required(true) /// .takes_value(true) @@ -596,8 +606,8 @@ impl<'help> Arg<'help> { /// Setting required and then *not* supplying that argument at runtime is an error. /// /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg, ErrorKind}; + /// let res = Command::new("prog") /// .arg(Arg::new("cfg") /// .required(true) /// .takes_value(true) @@ -607,7 +617,7 @@ impl<'help> Arg<'help> { /// ]); /// /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::MissingRequiredArgument); + /// assert_eq!(res.unwrap_err().kind(), ErrorKind::MissingRequiredArgument); /// ``` #[inline] #[must_use] @@ -625,8 +635,6 @@ impl<'help> Arg<'help> { /// /// **NOTE:** [Conflicting] rules and [override] rules take precedence over being required /// - /// **NOTE:** An argument is considered present when there is a [`Arg::default_value`] - /// /// # Examples /// /// ```rust @@ -641,14 +649,13 @@ impl<'help> Arg<'help> { /// required /// /// ```rust - /// # use clap::{App, Arg}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg}; + /// let res = Command::new("prog") /// .arg(Arg::new("cfg") /// .takes_value(true) /// .requires("input") /// .long("config")) - /// .arg(Arg::new("input") - /// .index(1)) + /// .arg(Arg::new("input")) /// .try_get_matches_from(vec![ /// "prog" /// ]); @@ -659,27 +666,26 @@ impl<'help> Arg<'help> { /// Setting [`Arg::requires(name)`] and *not* supplying that argument is an error. /// /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg, ErrorKind}; + /// let res = Command::new("prog") /// .arg(Arg::new("cfg") /// .takes_value(true) /// .requires("input") /// .long("config")) - /// .arg(Arg::new("input") - /// .index(1)) + /// .arg(Arg::new("input")) /// .try_get_matches_from(vec![ /// "prog", "--config", "file.conf" /// ]); /// /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::MissingRequiredArgument); + /// assert_eq!(res.unwrap_err().kind(), ErrorKind::MissingRequiredArgument); /// ``` /// [`Arg::requires(name)`]: Arg::requires() /// [Conflicting]: Arg::conflicts_with() /// [override]: Arg::overrides_with() #[must_use] pub fn requires(mut self, arg_id: T) -> Self { - self.requires.push((None, arg_id.into())); + self.requires.push((ArgPredicate::IsPresent, arg_id.into())); self } @@ -698,22 +704,21 @@ impl<'help> Arg<'help> { /// is an error. /// /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg, ErrorKind}; + /// let res = Command::new("prog") /// .arg(Arg::new("exclusive") /// .takes_value(true) /// .exclusive(true) /// .long("exclusive")) /// .arg(Arg::new("debug") /// .long("debug")) - /// .arg(Arg::new("input") - /// .index(1)) + /// .arg(Arg::new("input")) /// .try_get_matches_from(vec![ /// "prog", "--exclusive", "file.conf", "file.txt" /// ]); /// /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::ArgumentConflict); + /// assert_eq!(res.unwrap_err().kind(), ErrorKind::ArgumentConflict); /// ``` #[inline] #[must_use] @@ -739,14 +744,14 @@ impl<'help> Arg<'help> { /// want to clutter the source with three duplicate [`Arg`] definitions. /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("verb") /// .long("verbose") /// .short('v') /// .global(true)) - /// .subcommand(App::new("test")) - /// .subcommand(App::new("do-stuff")) + /// .subcommand(Command::new("test")) + /// .subcommand(Command::new("do-stuff")) /// .get_matches_from(vec![ /// "prog", "do-stuff", "--verbose" /// ]); @@ -782,8 +787,8 @@ impl<'help> Arg<'help> { /// An example with flags /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("verbose") /// .multiple_occurrences(true) /// .short('v')) @@ -798,8 +803,8 @@ impl<'help> Arg<'help> { /// An example with options /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("file") /// .multiple_occurrences(true) /// .takes_value(true) @@ -833,7 +838,7 @@ impl<'help> Arg<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; + /// # use clap::{Command, Arg}; /// Arg::new("verbosity") /// .short('v') /// .max_occurrences(3); @@ -842,8 +847,8 @@ impl<'help> Arg<'help> { /// Supplying less than the maximum number of arguments is allowed /// /// ```rust - /// # use clap::{App, Arg}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg}; + /// let res = Command::new("prog") /// .arg(Arg::new("verbosity") /// .max_occurrences(3) /// .short('v')) @@ -859,8 +864,8 @@ impl<'help> Arg<'help> { /// Supplying more than the maximum number of arguments is an error /// /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg, ErrorKind}; + /// let res = Command::new("prog") /// .arg(Arg::new("verbosity") /// .max_occurrences(2) /// .short('v')) @@ -869,7 +874,7 @@ impl<'help> Arg<'help> { /// ]); /// /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::TooManyOccurrences); + /// assert_eq!(res.unwrap_err().kind(), ErrorKind::TooManyOccurrences); /// ``` /// [`Arg::multiple_occurrences(true)`]: Arg::multiple_occurrences() #[inline] @@ -952,7 +957,7 @@ impl<'help> Arg<'help> { } } -/// Value handling +/// # Value Handling impl<'help> Arg<'help> { /// Specifies that the argument takes a value at run time. /// @@ -966,13 +971,13 @@ impl<'help> Arg<'help> { /// `--option=val1,val2,val3` is three values for the `--option` argument. If you wish to /// change the delimiter to another character you can use [`Arg::value_delimiter(char)`], /// alternatively you can turn delimiting values **OFF** by using - /// [`Arg::use_delimiter(false)`][Arg::use_delimiter] + /// [`Arg::use_value_delimiter(false)`][Arg::use_value_delimiter] /// /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("mode") /// .long("mode") /// .takes_value(true)) @@ -1053,8 +1058,8 @@ impl<'help> Arg<'help> { /// An example with options /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("file") /// .takes_value(true) /// .multiple_values(true) @@ -1072,8 +1077,8 @@ impl<'help> Arg<'help> { /// Although `multiple_values` has been specified, we cannot use the argument more than once. /// /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg, ErrorKind}; + /// let res = Command::new("prog") /// .arg(Arg::new("file") /// .takes_value(true) /// .multiple_values(true) @@ -1083,21 +1088,20 @@ impl<'help> Arg<'help> { /// ]); /// /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::UnexpectedMultipleUsage) + /// assert_eq!(res.unwrap_err().kind(), ErrorKind::UnexpectedMultipleUsage) /// ``` /// /// A common mistake is to define an option which allows multiple values, and a positional /// argument. /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("file") /// .takes_value(true) /// .multiple_values(true) /// .short('F')) - /// .arg(Arg::new("word") - /// .index(1)) + /// .arg(Arg::new("word")) /// .get_matches_from(vec![ /// "prog", "-F", "file1", "file2", "file3", "word" /// ]); @@ -1116,14 +1120,13 @@ impl<'help> Arg<'help> { /// number, or to say [`Arg::multiple_occurrences`] is ok, but multiple values is not. /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("file") /// .takes_value(true) /// .multiple_occurrences(true) /// .short('F')) - /// .arg(Arg::new("word") - /// .index(1)) + /// .arg(Arg::new("word")) /// .get_matches_from(vec![ /// "prog", "-F", "file1", "-F", "file2", "-F", "file3", "word" /// ]); @@ -1138,23 +1141,22 @@ impl<'help> Arg<'help> { /// As a final example, let's fix the above error and get a pretty message to the user :) /// /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg, ErrorKind}; + /// let res = Command::new("prog") /// .arg(Arg::new("file") /// .takes_value(true) /// .multiple_occurrences(true) /// .short('F')) - /// .arg(Arg::new("word") - /// .index(1)) + /// .arg(Arg::new("word")) /// .try_get_matches_from(vec![ /// "prog", "-F", "file1", "file2", "file3", "word" /// ]); /// /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::UnknownArgument); + /// assert_eq!(res.unwrap_err().kind(), ErrorKind::UnknownArgument); /// ``` /// - /// [`subcommands`]: crate::App::subcommand() + /// [`subcommands`]: crate::Command::subcommand() /// [`Arg::number_of_values(1)`]: Arg::number_of_values() /// [maximum number of values]: Arg::max_values() /// [specific number of values]: Arg::number_of_values() @@ -1186,7 +1188,7 @@ impl<'help> Arg<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; + /// # use clap::{Command, Arg}; /// Arg::new("file") /// .short('f') /// .number_of_values(3); @@ -1195,8 +1197,8 @@ impl<'help> Arg<'help> { /// Not supplying the correct number of values is an error /// /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg, ErrorKind}; + /// let res = Command::new("prog") /// .arg(Arg::new("file") /// .takes_value(true) /// .number_of_values(2) @@ -1206,7 +1208,7 @@ impl<'help> Arg<'help> { /// ]); /// /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::WrongNumberOfValues); + /// assert_eq!(res.unwrap_err().kind(), ErrorKind::WrongNumberOfValues); /// ``` /// [`Arg::multiple_occurrences(true)`]: Arg::multiple_occurrences() #[inline] @@ -1231,7 +1233,7 @@ impl<'help> Arg<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; + /// # use clap::{Command, Arg}; /// Arg::new("file") /// .short('f') /// .max_values(3); @@ -1240,8 +1242,8 @@ impl<'help> Arg<'help> { /// Supplying less than the maximum number of values is allowed /// /// ```rust - /// # use clap::{App, Arg}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg}; + /// let res = Command::new("prog") /// .arg(Arg::new("file") /// .takes_value(true) /// .max_values(3) @@ -1259,8 +1261,8 @@ impl<'help> Arg<'help> { /// Supplying more than the maximum number of values is an error /// /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg, ErrorKind}; + /// let res = Command::new("prog") /// .arg(Arg::new("file") /// .takes_value(true) /// .max_values(2) @@ -1270,7 +1272,7 @@ impl<'help> Arg<'help> { /// ]); /// /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::UnknownArgument); + /// assert_eq!(res.unwrap_err().kind(), ErrorKind::UnknownArgument); /// ``` /// [`Arg::multiple_occurrences(true)`]: Arg::multiple_occurrences() #[inline] @@ -1296,7 +1298,7 @@ impl<'help> Arg<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; + /// # use clap::{Command, Arg}; /// Arg::new("file") /// .short('f') /// .min_values(3); @@ -1305,8 +1307,8 @@ impl<'help> Arg<'help> { /// Supplying more than the minimum number of values is allowed /// /// ```rust - /// # use clap::{App, Arg}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg}; + /// let res = Command::new("prog") /// .arg(Arg::new("file") /// .takes_value(true) /// .min_values(2) @@ -1324,8 +1326,8 @@ impl<'help> Arg<'help> { /// Supplying less than the minimum number of values is an error /// /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg, ErrorKind}; + /// let res = Command::new("prog") /// .arg(Arg::new("file") /// .takes_value(true) /// .min_values(2) @@ -1335,7 +1337,7 @@ impl<'help> Arg<'help> { /// ]); /// /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::TooFewValues); + /// assert_eq!(res.unwrap_err().kind(), ErrorKind::TooFewValues); /// ``` /// [`Arg::multiple_occurrences(true)`]: Arg::multiple_occurrences() #[inline] @@ -1357,7 +1359,7 @@ impl<'help> Arg<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; + /// # use clap::{Command, Arg}; /// Arg::new("cfg") /// .long("config") /// .value_name("FILE") @@ -1365,8 +1367,8 @@ impl<'help> Arg<'help> { /// ``` /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("config") /// .long("config") /// .value_name("FILE") @@ -1416,15 +1418,15 @@ impl<'help> Arg<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; + /// # use clap::{Command, Arg}; /// Arg::new("speed") /// .short('s') /// .value_names(&["fast", "slow"]); /// ``` /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("io") /// .long("io-files") /// .value_names(&["INFILE", "OUTFILE"])) @@ -1475,9 +1477,9 @@ impl<'help> Arg<'help> { /// To take a full command line and its arguments (for example, when writing a command wrapper): /// /// ``` - /// # use clap::{App, AppSettings, Arg, ValueHint}; - /// App::new("prog") - /// .setting(AppSettings::TrailingVarArg) + /// # use clap::{Command, Arg, ValueHint}; + /// Command::new("prog") + /// .trailing_var_arg(true) /// .arg( /// Arg::new("command") /// .takes_value(true) @@ -1494,7 +1496,7 @@ impl<'help> Arg<'help> { /// Perform a custom validation on the argument value. /// /// You provide a closure - /// which accepts a [`String`] value, and return a [`Result`] where the [`Err(String)`] is a + /// which accepts a [`&str`] value, and return a [`Result`] where the [`Err(String)`] is a /// message displayed to the user. /// /// **NOTE:** The error message does *not* need to contain the `error:` portion, only the @@ -1510,14 +1512,13 @@ impl<'help> Arg<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; + /// # use clap::{Command, Arg}; /// fn has_at(v: &str) -> Result<(), String> { /// if v.contains("@") { return Ok(()); } /// Err(String::from("The value did not contain the required @ sigil")) /// } - /// let res = App::new("prog") + /// let res = Command::new("prog") /// .arg(Arg::new("file") - /// .index(1) /// .validator(has_at)) /// .try_get_matches_from(vec![ /// "prog", "some@file" @@ -1525,7 +1526,6 @@ impl<'help> Arg<'help> { /// assert!(res.is_ok()); /// assert_eq!(res.unwrap().value_of("file"), Some("some@file")); /// ``` - /// [`String`]: std::string::String /// [`Result`]: std::result::Result /// [`Err(String)`]: std::result::Result::Err /// [`Arc`]: std::sync::Arc @@ -1549,16 +1549,15 @@ impl<'help> Arg<'help> { /// #[cfg_attr(not(unix), doc = " ```ignore")] #[cfg_attr(unix, doc = " ```rust")] - /// # use clap::{App, Arg}; + /// # use clap::{Command, Arg}; /// # use std::ffi::{OsStr, OsString}; /// # use std::os::unix::ffi::OsStrExt; /// fn has_ampersand(v: &OsStr) -> Result<(), String> { /// if v.as_bytes().iter().any(|b| *b == b'&') { return Ok(()); } /// Err(String::from("The value did not contain the required & sigil")) /// } - /// let res = App::new("prog") + /// let res = Command::new("prog") /// .arg(Arg::new("file") - /// .index(1) /// .validator_os(has_ampersand)) /// .try_get_matches_from(vec![ /// "prog", "Fish & chips" @@ -1603,14 +1602,13 @@ impl<'help> Arg<'help> { /// You can use the classical `"\d+"` regular expression to match digits only: /// /// ```rust - /// # use clap::{App, Arg}; + /// # use clap::{Command, Arg}; /// use regex::Regex; /// /// let digits = Regex::new(r"\d+").unwrap(); /// - /// let res = App::new("prog") + /// let res = Command::new("prog") /// .arg(Arg::new("digits") - /// .index(1) /// .validator_regex(&digits, "only digits are allowed")) /// .try_get_matches_from(vec![ /// "prog", "12345" @@ -1622,20 +1620,19 @@ impl<'help> Arg<'help> { /// However, any valid `Regex` can be used: /// /// ```rust - /// # use clap::{App, Arg, ErrorKind}; + /// # use clap::{Command, Arg, ErrorKind}; /// use regex::Regex; /// /// let priority = Regex::new(r"[A-C]").unwrap(); /// - /// let res = App::new("prog") + /// let res = Command::new("prog") /// .arg(Arg::new("priority") - /// .index(1) /// .validator_regex(priority, "only priorities A, B or C are allowed")) /// .try_get_matches_from(vec![ /// "prog", "12345" /// ]); /// assert!(res.is_err()); - /// assert_eq!(res.err().unwrap().kind, ErrorKind::ValueValidation) + /// assert_eq!(res.err().unwrap().kind(), ErrorKind::ValueValidation) /// ``` #[cfg(feature = "regex")] #[must_use] @@ -1667,7 +1664,7 @@ impl<'help> Arg<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; + /// # use clap::{Command, Arg}; /// Arg::new("mode") /// .takes_value(true) /// .possible_value("fast") @@ -1678,7 +1675,7 @@ impl<'help> Arg<'help> { /// The same using [`PossibleValue`]: /// /// ```rust - /// # use clap::{App, Arg, PossibleValue}; + /// # use clap::{Command, Arg, PossibleValue}; /// Arg::new("mode").takes_value(true) /// .possible_value(PossibleValue::new("fast")) /// // value with a help text @@ -1689,8 +1686,8 @@ impl<'help> Arg<'help> { /// ``` /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("mode") /// .long("mode") /// .takes_value(true) @@ -1708,8 +1705,8 @@ impl<'help> Arg<'help> { /// possible values. /// /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg, ErrorKind}; + /// let res = Command::new("prog") /// .arg(Arg::new("mode") /// .long("mode") /// .takes_value(true) @@ -1720,7 +1717,7 @@ impl<'help> Arg<'help> { /// "prog", "--mode", "wrong" /// ]); /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::InvalidValue); + /// assert_eq!(res.unwrap_err().kind(), ErrorKind::InvalidValue); /// ``` /// [options]: Arg::takes_value() /// [positional arguments]: Arg::index() @@ -1748,7 +1745,7 @@ impl<'help> Arg<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; + /// # use clap::{Command, Arg}; /// Arg::new("mode") /// .takes_value(true) /// .possible_values(["fast", "slow", "medium"]) @@ -1757,7 +1754,7 @@ impl<'help> Arg<'help> { /// The same using [`PossibleValue`]: /// /// ```rust - /// # use clap::{App, Arg, PossibleValue}; + /// # use clap::{Command, Arg, PossibleValue}; /// Arg::new("mode").takes_value(true).possible_values([ /// PossibleValue::new("fast"), /// // value with a help text @@ -1769,8 +1766,8 @@ impl<'help> Arg<'help> { /// ``` /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("mode") /// .long("mode") /// .takes_value(true) @@ -1786,8 +1783,8 @@ impl<'help> Arg<'help> { /// possible values. /// /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg, ErrorKind}; + /// let res = Command::new("prog") /// .arg(Arg::new("mode") /// .long("mode") /// .takes_value(true) @@ -1796,7 +1793,7 @@ impl<'help> Arg<'help> { /// "prog", "--mode", "wrong" /// ]); /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::InvalidValue); + /// assert_eq!(res.unwrap_err().kind(), ErrorKind::InvalidValue); /// ``` /// [options]: Arg::takes_value() /// [positional arguments]: Arg::index() @@ -1826,10 +1823,10 @@ impl<'help> Arg<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("pv") + /// # use clap::{Command, Arg}; + /// let m = Command::new("pv") /// .arg(Arg::new("option") - /// .long("--option") + /// .long("option") /// .takes_value(true) /// .ignore_case(true) /// .possible_value("test123")) @@ -1843,11 +1840,11 @@ impl<'help> Arg<'help> { /// This setting also works when multiple values can be defined: /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("pv") + /// # use clap::{Command, Arg}; + /// let m = Command::new("pv") /// .arg(Arg::new("option") /// .short('o') - /// .long("--option") + /// .long("option") /// .takes_value(true) /// .ignore_case(true) /// .multiple_values(true) @@ -1889,8 +1886,8 @@ impl<'help> Arg<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("pat") /// .takes_value(true) /// .allow_hyphen_values(true) @@ -1906,8 +1903,8 @@ impl<'help> Arg<'help> { /// hyphen is an error. /// /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg, ErrorKind}; + /// let res = Command::new("prog") /// .arg(Arg::new("pat") /// .takes_value(true) /// .long("pattern")) @@ -1916,7 +1913,7 @@ impl<'help> Arg<'help> { /// ]); /// /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::UnknownArgument); + /// assert_eq!(res.unwrap_err().kind(), ErrorKind::UnknownArgument); /// ``` /// [`Arg::number_of_values(1)`]: Arg::number_of_values() #[inline] @@ -1942,10 +1939,10 @@ impl<'help> Arg<'help> { /// #[cfg_attr(not(unix), doc = " ```ignore")] #[cfg_attr(unix, doc = " ```rust")] - /// # use clap::{App, Arg}; + /// # use clap::{Command, Arg}; /// use std::ffi::OsString; /// use std::os::unix::ffi::{OsStrExt,OsStringExt}; - /// let r = App::new("myprog") + /// let r = Command::new("myprog") /// .arg(Arg::new("arg").allow_invalid_utf8(true)) /// .try_get_matches_from(vec![ /// OsString::from("myprog"), @@ -1984,8 +1981,8 @@ impl<'help> Arg<'help> { /// The default is allowing empty values. /// /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg, ErrorKind}; + /// let res = Command::new("prog") /// .arg(Arg::new("cfg") /// .long("config") /// .short('v') @@ -2001,8 +1998,8 @@ impl<'help> Arg<'help> { /// By adding this setting, we can forbid empty values. /// /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg, ErrorKind}; + /// let res = Command::new("prog") /// .arg(Arg::new("cfg") /// .long("config") /// .short('v') @@ -2013,7 +2010,7 @@ impl<'help> Arg<'help> { /// ]); /// /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::EmptyValue); + /// assert_eq!(res.unwrap_err().kind(), ErrorKind::EmptyValue); /// ``` #[inline] #[must_use] @@ -2037,8 +2034,8 @@ impl<'help> Arg<'help> { /// it and the associated value. /// /// ```rust - /// # use clap::{App, Arg}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg}; + /// let res = Command::new("prog") /// .arg(Arg::new("cfg") /// .takes_value(true) /// .require_equals(true) @@ -2054,8 +2051,8 @@ impl<'help> Arg<'help> { /// error. /// /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg, ErrorKind}; + /// let res = Command::new("prog") /// .arg(Arg::new("cfg") /// .takes_value(true) /// .require_equals(true) @@ -2065,7 +2062,7 @@ impl<'help> Arg<'help> { /// ]); /// /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::NoEquals); + /// assert_eq!(res.unwrap_err().kind(), ErrorKind::NoEquals); /// ``` #[inline] #[must_use] @@ -2094,11 +2091,11 @@ impl<'help> Arg<'help> { /// The following example shows the default behavior. /// /// ```rust - /// # use clap::{App, Arg}; - /// let delims = App::new("prog") + /// # use clap::{Command, Arg}; + /// let delims = Command::new("prog") /// .arg(Arg::new("option") /// .long("option") - /// .use_delimiter(true) + /// .use_value_delimiter(true) /// .takes_value(true)) /// .get_matches_from(vec![ /// "prog", "--option=val1,val2,val3", @@ -2112,8 +2109,8 @@ impl<'help> Arg<'help> { /// behavior /// /// ```rust - /// # use clap::{App, Arg}; - /// let nodelims = App::new("prog") + /// # use clap::{Command, Arg}; + /// let nodelims = Command::new("prog") /// .arg(Arg::new("option") /// .long("option") /// .takes_value(true)) @@ -2128,7 +2125,7 @@ impl<'help> Arg<'help> { /// [`Arg::value_delimiter`]: Arg::value_delimiter() #[inline] #[must_use] - pub fn use_delimiter(mut self, yes: bool) -> Self { + pub fn use_value_delimiter(mut self, yes: bool) -> Self { if yes { if self.val_delim.is_none() { self.val_delim = Some(','); @@ -2141,17 +2138,25 @@ impl<'help> Arg<'help> { } } + /// Deprecated, replaced with [`Arg::use_value_delimiter`] + #[inline] + #[must_use] + #[deprecated(since = "3.1.0", note = "Replaced with `Arg::use_value_delimiter`")] + pub fn use_delimiter(self, yes: bool) -> Self { + self.use_value_delimiter(yes) + } + /// Separator between the arguments values, defaults to `,` (comma). /// - /// **NOTE:** implicitly sets [`Arg::use_delimiter(true)`] + /// **NOTE:** implicitly sets [`Arg::use_value_delimiter(true)`] /// /// **NOTE:** implicitly sets [`Arg::takes_value(true)`] /// /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("config") /// .short('c') /// .long("config") @@ -2162,13 +2167,13 @@ impl<'help> Arg<'help> { /// /// assert_eq!(m.values_of("config").unwrap().collect::>(), ["val1", "val2", "val3"]) /// ``` - /// [`Arg::use_delimiter(true)`]: Arg::use_delimiter() + /// [`Arg::use_value_delimiter(true)`]: Arg::use_value_delimiter() /// [`Arg::takes_value(true)`]: Arg::takes_value() #[inline] #[must_use] pub fn value_delimiter(mut self, d: char) -> Self { self.val_delim = Some(d); - self.takes_value(true).use_delimiter(true) + self.takes_value(true).use_value_delimiter(true) } /// Specifies that *multiple values* may only be set using the delimiter. @@ -2179,7 +2184,7 @@ impl<'help> Arg<'help> { /// /// **NOTE:** The default is `false`. /// - /// **NOTE:** Setting this requires [`Arg::use_delimiter`] and + /// **NOTE:** Setting this requires [`Arg::use_value_delimiter`] and /// [`Arg::takes_value`] /// /// **NOTE:** It's a good idea to inform the user that use of a delimiter is required, either @@ -2191,12 +2196,12 @@ impl<'help> Arg<'help> { /// everything works in this first example, as we use a delimiter, as expected. /// /// ```rust - /// # use clap::{App, Arg}; - /// let delims = App::new("prog") + /// # use clap::{Command, Arg}; + /// let delims = Command::new("prog") /// .arg(Arg::new("opt") /// .short('o') /// .takes_value(true) - /// .use_delimiter(true) + /// .use_value_delimiter(true) /// .require_delimiter(true) /// .multiple_values(true)) /// .get_matches_from(vec![ @@ -2210,12 +2215,12 @@ impl<'help> Arg<'help> { /// In this next example, we will *not* use a delimiter. Notice it's now an error. /// /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg, ErrorKind}; + /// let res = Command::new("prog") /// .arg(Arg::new("opt") /// .short('o') /// .takes_value(true) - /// .use_delimiter(true) + /// .use_value_delimiter(true) /// .require_delimiter(true)) /// .try_get_matches_from(vec![ /// "prog", "-o", "val1", "val2", "val3", @@ -2223,7 +2228,7 @@ impl<'help> Arg<'help> { /// /// assert!(res.is_err()); /// let err = res.unwrap_err(); - /// assert_eq!(err.kind, ErrorKind::UnknownArgument); + /// assert_eq!(err.kind(), ErrorKind::UnknownArgument); /// ``` /// /// What's happening is `-o` is getting `val1`, and because delimiters are required yet none @@ -2234,8 +2239,8 @@ impl<'help> Arg<'help> { /// is *not* an error. /// /// ```rust - /// # use clap::{App, Arg}; - /// let delims = App::new("prog") + /// # use clap::{Command, Arg}; + /// let delims = Command::new("prog") /// .arg(Arg::new("opt") /// .short('o') /// .takes_value(true) @@ -2249,7 +2254,7 @@ impl<'help> Arg<'help> { /// ``` #[inline] #[must_use] - pub fn require_delimiter(self, yes: bool) -> Self { + pub fn require_value_delimiter(self, yes: bool) -> Self { if yes { self.setting(ArgSettings::RequireDelimiter) } else { @@ -2257,6 +2262,14 @@ impl<'help> Arg<'help> { } } + /// Deprecated, replaced with [`Arg::require_value_delimiter`] + #[inline] + #[must_use] + #[deprecated(since = "3.1.0", note = "Replaced with `Arg::require_value_delimiter`")] + pub fn require_delimiter(self, yes: bool) -> Self { + self.require_value_delimiter(yes) + } + /// Sentinel to **stop** parsing multiple values of a give argument. /// /// By default when @@ -2273,7 +2286,7 @@ impl<'help> Arg<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; + /// # use clap::{Command, Arg}; /// Arg::new("vals") /// .takes_value(true) /// .multiple_values(true) @@ -2285,8 +2298,8 @@ impl<'help> Arg<'help> { /// to perform them /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("cmds") /// .takes_value(true) /// .multiple_values(true) @@ -2325,7 +2338,7 @@ impl<'help> Arg<'help> { /// ``` /// /// Will result in everything after `--` to be considered one raw argument. This behavior - /// may not be exactly what you are expecting and using [`crate::AppSettings::TrailingVarArg`] + /// may not be exactly what you are expecting and using [`crate::Command::trailing_var_arg`] /// may be more appropriate. /// /// **NOTE:** Implicitly sets [`Arg::takes_value(true)`] [`Arg::multiple_values(true)`], @@ -2364,16 +2377,13 @@ impl<'help> Arg<'help> { /// /// **NOTE:** This implicitly sets [`Arg::takes_value(true)`]. /// - /// **NOTE:** This setting effectively disables `AppSettings::ArgRequiredElseHelp` if used in - /// conjunction as it ensures that some argument will always be present. - /// /// # Examples /// /// First we use the default value without providing any value at runtime. /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("opt") /// .long("myopt") /// .default_value("myval")) @@ -2389,8 +2399,8 @@ impl<'help> Arg<'help> { /// Next we provide a value at runtime to override the default. /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("opt") /// .long("myopt") /// .default_value("myval")) @@ -2466,11 +2476,11 @@ impl<'help> Arg<'help> { /// Here is an implementation of the common POSIX style `--color` argument. /// /// ```rust - /// # use clap::{App, Arg}; + /// # use clap::{Command, Arg}; /// - /// macro_rules! app { + /// macro_rules! cmd { /// () => {{ - /// App::new("prog") + /// Command::new("prog") /// .arg(Arg::new("color").long("color") /// .value_name("WHEN") /// .possible_values(["always", "auto", "never"]) @@ -2488,7 +2498,7 @@ impl<'help> Arg<'help> { /// /// // first, we'll provide no arguments /// - /// m = app!().get_matches_from(vec![ + /// m = cmd!().get_matches_from(vec![ /// "prog" /// ]); /// @@ -2498,7 +2508,7 @@ impl<'help> Arg<'help> { /// /// // next, we'll provide a runtime value to override the default (as usually done). /// - /// m = app!().get_matches_from(vec![ + /// m = cmd!().get_matches_from(vec![ /// "prog", "--color=never" /// ]); /// @@ -2508,7 +2518,7 @@ impl<'help> Arg<'help> { /// /// // finally, we will use the shortcut and only provide the argument without a value. /// - /// m = app!().get_matches_from(vec![ + /// m = cmd!().get_matches_from(vec![ /// "prog", "--color" /// ]); /// @@ -2585,11 +2595,11 @@ impl<'help> Arg<'help> { /// /// ```rust /// # use std::env; - /// # use clap::{App, Arg}; + /// # use clap::{Command, Arg}; /// /// env::set_var("MY_FLAG", "env"); /// - /// let m = App::new("prog") + /// let m = Command::new("prog") /// .arg(Arg::new("flag") /// .long("flag") /// .env("MY_FLAG") @@ -2609,12 +2619,12 @@ impl<'help> Arg<'help> { /// /// ```rust /// # use std::env; - /// # use clap::{App, Arg}; + /// # use clap::{Command, Arg}; /// /// env::set_var("TRUE_FLAG", "true"); /// env::set_var("FALSE_FLAG", "0"); /// - /// let m = App::new("prog") + /// let m = Command::new("prog") /// .arg(Arg::new("true_flag") /// .long("true_flag") /// .env("TRUE_FLAG")) @@ -2638,11 +2648,11 @@ impl<'help> Arg<'help> { /// /// ```rust /// # use std::env; - /// # use clap::{App, Arg}; + /// # use clap::{Command, Arg}; /// /// env::set_var("MY_FLAG", "env"); /// - /// let m = App::new("prog") + /// let m = Command::new("prog") /// .arg(Arg::new("flag") /// .long("flag") /// .env("MY_FLAG") @@ -2659,11 +2669,11 @@ impl<'help> Arg<'help> { /// /// ```rust /// # use std::env; - /// # use clap::{App, Arg}; + /// # use clap::{Command, Arg}; /// /// env::set_var("MY_FLAG", "env"); /// - /// let m = App::new("prog") + /// let m = Command::new("prog") /// .arg(Arg::new("flag") /// .long("flag") /// .env("MY_FLAG") @@ -2680,17 +2690,17 @@ impl<'help> Arg<'help> { /// /// ```rust /// # use std::env; - /// # use clap::{App, Arg}; + /// # use clap::{Command, Arg}; /// /// env::set_var("MY_FLAG_MULTI", "env1,env2"); /// - /// let m = App::new("prog") + /// let m = Command::new("prog") /// .arg(Arg::new("flag") /// .long("flag") /// .env("MY_FLAG_MULTI") /// .takes_value(true) /// .multiple_values(true) - /// .use_delimiter(true)) + /// .use_value_delimiter(true)) /// .get_matches_from(vec![ /// "prog" /// ]); @@ -2701,7 +2711,7 @@ impl<'help> Arg<'help> { /// [`ArgMatches::value_of`]: crate::ArgMatches::value_of() /// [`ArgMatches::is_present`]: ArgMatches::is_present() /// [`Arg::takes_value(true)`]: Arg::takes_value() - /// [`Arg::use_delimiter(true)`]: Arg::use_delimiter() + /// [`Arg::use_value_delimiter(true)`]: Arg::use_value_delimiter() #[cfg(feature = "env")] #[inline] #[must_use] @@ -2721,7 +2731,7 @@ impl<'help> Arg<'help> { } } -/// Help +/// # Help impl<'help> Arg<'help> { /// Sets the description of the argument for short help (`-h`). /// @@ -2741,8 +2751,8 @@ impl<'help> Arg<'help> { /// `-h` or `--help` (by default). /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("cfg") /// .long("config") /// .help("Some help text describing the --config arg")) @@ -2791,8 +2801,8 @@ impl<'help> Arg<'help> { /// `-h` or `--help` (by default). /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("cfg") /// .long("config") /// .long_help( @@ -2848,8 +2858,8 @@ impl<'help> Arg<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("a") // Typically args are grouped alphabetically by name. /// // Args without a display_order have a value of 999 and are /// // displayed alphabetically with all other 999 valued args. @@ -2890,13 +2900,13 @@ impl<'help> Arg<'help> { #[inline] #[must_use] pub fn display_order(mut self, ord: usize) -> Self { - self.disp_ord = Some(ord); + self.disp_ord.set_explicit(ord); self } /// Override the [current] help section. /// - /// [current]: crate::App::help_heading + /// [current]: crate::Command::help_heading #[inline] #[must_use] pub fn help_heading(mut self, heading: O) -> Self @@ -2912,14 +2922,14 @@ impl<'help> Arg<'help> { /// This can be helpful for arguments with very long or complex help messages. /// This can also be helpful for arguments with very long flag names, or many/long value names. /// - /// **NOTE:** To apply this setting to all arguments consider using - /// [`crate::AppSettings::NextLineHelp`] + /// **NOTE:** To apply this setting to all arguments and subcommands, consider using + /// [`crate::Command::next_line_help`] /// /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("opt") /// .long("long-option-flag") /// .short('o') @@ -2969,8 +2979,8 @@ impl<'help> Arg<'help> { /// Setting `Hidden` will hide the argument when displaying help text /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("cfg") /// .long("config") /// .hide(true) @@ -3010,13 +3020,13 @@ impl<'help> Arg<'help> { /// **NOTE:** Setting this requires [`Arg::takes_value`] /// /// To set this for all arguments, see - /// [`AppSettings::HidePossibleValues`][crate::AppSettings::HidePossibleValues]. + /// [`Command::hide_possible_values`][crate::Command::hide_possible_values]. /// /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("mode") /// .long("mode") /// .possible_values(["fast", "slow"]) @@ -3044,8 +3054,8 @@ impl<'help> Arg<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("connect") + /// # use clap::{Command, Arg}; + /// let m = Command::new("connect") /// .arg(Arg::new("host") /// .long("host") /// .default_value("localhost") @@ -3073,8 +3083,8 @@ impl<'help> Arg<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("mode") /// .long("mode") /// .env("MODE") @@ -3102,8 +3112,8 @@ impl<'help> Arg<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("connect") + /// # use clap::{Command, Arg}; + /// let m = Command::new("connect") /// .arg(Arg::new("host") /// .long("host") /// .env("CONNECT") @@ -3135,7 +3145,7 @@ impl<'help> Arg<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; + /// # use clap::{Command, Arg}; /// Arg::new("debug") /// .hide_short_help(true); /// ``` @@ -3143,8 +3153,8 @@ impl<'help> Arg<'help> { /// Setting `hide_short_help(true)` will hide the argument when displaying short help text /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("cfg") /// .long("config") /// .hide_short_help(true) @@ -3170,8 +3180,8 @@ impl<'help> Arg<'help> { /// However, when --help is called /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("cfg") /// .long("config") /// .hide_short_help(true) @@ -3216,8 +3226,8 @@ impl<'help> Arg<'help> { /// Setting `hide_long_help(true)` will hide the argument when displaying long help text /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("cfg") /// .long("config") /// .hide_long_help(true) @@ -3243,8 +3253,8 @@ impl<'help> Arg<'help> { /// However, when -h is called /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("cfg") /// .long("config") /// .hide_long_help(true) @@ -3278,14 +3288,14 @@ impl<'help> Arg<'help> { } } -/// Advanced argument relations +/// # Advanced Argument Relations impl<'help> Arg<'help> { /// The name of the [`ArgGroup`] the argument belongs to. /// /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; + /// # use clap::{Command, Arg}; /// Arg::new("debug") /// .long("debug") /// .group("mode") @@ -3296,8 +3306,8 @@ impl<'help> Arg<'help> { /// was one of said arguments. /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("debug") /// .long("debug") /// .group("mode")) @@ -3322,7 +3332,7 @@ impl<'help> Arg<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; + /// # use clap::{Command, Arg}; /// Arg::new("debug") /// .long("debug") /// .groups(&["mode", "verbosity"]) @@ -3333,8 +3343,8 @@ impl<'help> Arg<'help> { /// was one of said arguments. /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("debug") /// .long("debug") /// .groups(&["mode", "verbosity"])) @@ -3376,8 +3386,8 @@ impl<'help> Arg<'help> { /// First we use the default value only if another arg is present at runtime. /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("flag") /// .long("flag")) /// .arg(Arg::new("other") @@ -3393,8 +3403,8 @@ impl<'help> Arg<'help> { /// Next we run the same test, but without providing `--flag`. /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("flag") /// .long("flag")) /// .arg(Arg::new("other") @@ -3410,8 +3420,8 @@ impl<'help> Arg<'help> { /// Now lets only use the default value if `--opt` contains the value `special`. /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("opt") /// .takes_value(true) /// .long("opt")) @@ -3429,8 +3439,8 @@ impl<'help> Arg<'help> { /// default value. /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("opt") /// .takes_value(true) /// .long("opt")) @@ -3448,8 +3458,8 @@ impl<'help> Arg<'help> { /// value of some other Arg. /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("flag") /// .long("flag")) /// .arg(Arg::new("other") @@ -3486,7 +3496,8 @@ impl<'help> Arg<'help> { val: Option<&'help OsStr>, default: Option<&'help OsStr>, ) -> Self { - self.default_vals_ifs.push((arg_id.into(), val, default)); + self.default_vals_ifs + .push((arg_id.into(), val.into(), default)); self.takes_value(true) } @@ -3502,8 +3513,8 @@ impl<'help> Arg<'help> { /// First we use the default value only if another arg is present at runtime. /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("flag") /// .long("flag")) /// .arg(Arg::new("opt") @@ -3525,8 +3536,8 @@ impl<'help> Arg<'help> { /// Next we run the same test, but without providing `--flag`. /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("flag") /// .long("flag")) /// .arg(Arg::new("other") @@ -3546,8 +3557,8 @@ impl<'help> Arg<'help> { /// true, only the first evaluated "wins" /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("prog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") /// .arg(Arg::new("flag") /// .long("flag")) /// .arg(Arg::new("opt") @@ -3612,8 +3623,8 @@ impl<'help> Arg<'help> { /// but it's not an error because the `unless` arg has been supplied. /// /// ```rust - /// # use clap::{App, Arg}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg}; + /// let res = Command::new("prog") /// .arg(Arg::new("cfg") /// .required_unless_present("dbg") /// .takes_value(true) @@ -3630,8 +3641,8 @@ impl<'help> Arg<'help> { /// Setting `Arg::required_unless_present(name)` and *not* supplying `name` or this arg is an error. /// /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg, ErrorKind}; + /// let res = Command::new("prog") /// .arg(Arg::new("cfg") /// .required_unless_present("dbg") /// .takes_value(true) @@ -3643,7 +3654,7 @@ impl<'help> Arg<'help> { /// ]); /// /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::MissingRequiredArgument); + /// assert_eq!(res.unwrap_err().kind(), ErrorKind::MissingRequiredArgument); /// ``` /// [required]: Arg::required() #[must_use] @@ -3674,8 +3685,8 @@ impl<'help> Arg<'help> { /// because *all* of the `names` args have been supplied. /// /// ```rust - /// # use clap::{App, Arg}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg}; + /// let res = Command::new("prog") /// .arg(Arg::new("cfg") /// .required_unless_present_all(&["dbg", "infile"]) /// .takes_value(true) @@ -3696,8 +3707,8 @@ impl<'help> Arg<'help> { /// either *all* of `unless` args or the `self` arg is an error. /// /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg, ErrorKind}; + /// let res = Command::new("prog") /// .arg(Arg::new("cfg") /// .required_unless_present_all(&["dbg", "infile"]) /// .takes_value(true) @@ -3712,7 +3723,7 @@ impl<'help> Arg<'help> { /// ]); /// /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::MissingRequiredArgument); + /// assert_eq!(res.unwrap_err().kind(), ErrorKind::MissingRequiredArgument); /// ``` /// [required]: Arg::required() /// [`Arg::required_unless_present_any`]: Arg::required_unless_present_any() @@ -3751,8 +3762,8 @@ impl<'help> Arg<'help> { /// have been supplied. /// /// ```rust - /// # use clap::{App, Arg}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg}; + /// let res = Command::new("prog") /// .arg(Arg::new("cfg") /// .required_unless_present_any(&["dbg", "infile"]) /// .takes_value(true) @@ -3773,8 +3784,8 @@ impl<'help> Arg<'help> { /// or this arg is an error. /// /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg, ErrorKind}; + /// let res = Command::new("prog") /// .arg(Arg::new("cfg") /// .required_unless_present_any(&["dbg", "infile"]) /// .takes_value(true) @@ -3789,7 +3800,7 @@ impl<'help> Arg<'help> { /// ]); /// /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::MissingRequiredArgument); + /// assert_eq!(res.unwrap_err().kind(), ErrorKind::MissingRequiredArgument); /// ``` /// [required]: Arg::required() /// [`Arg::required_unless_present_any(names)`]: Arg::required_unless_present_any() @@ -3807,8 +3818,6 @@ impl<'help> Arg<'help> { /// This argument is [required] only if the specified `arg` is present at runtime and its value /// equals `val`. /// - /// **NOTE:** An argument is considered present when there is a [`Arg::default_value`] - /// /// # Examples /// /// ```rust @@ -3819,8 +3828,8 @@ impl<'help> Arg<'help> { /// ``` /// /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg, ErrorKind}; + /// let res = Command::new("prog") /// .arg(Arg::new("cfg") /// .takes_value(true) /// .required_if_eq("other", "special") @@ -3834,7 +3843,7 @@ impl<'help> Arg<'help> { /// /// assert!(res.is_ok()); // We didn't use --other=special, so "cfg" wasn't required /// - /// let res = App::new("prog") + /// let res = Command::new("prog") /// .arg(Arg::new("cfg") /// .takes_value(true) /// .required_if_eq("other", "special") @@ -3848,9 +3857,9 @@ impl<'help> Arg<'help> { /// /// // We did use --other=special so "cfg" had become required but was missing. /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::MissingRequiredArgument); + /// assert_eq!(res.unwrap_err().kind(), ErrorKind::MissingRequiredArgument); /// - /// let res = App::new("prog") + /// let res = Command::new("prog") /// .arg(Arg::new("cfg") /// .takes_value(true) /// .required_if_eq("other", "special") @@ -3865,7 +3874,7 @@ impl<'help> Arg<'help> { /// // By default, the comparison is case-sensitive, so "cfg" wasn't required /// assert!(res.is_ok()); /// - /// let res = App::new("prog") + /// let res = Command::new("prog") /// .arg(Arg::new("cfg") /// .takes_value(true) /// .required_if_eq("other", "special") @@ -3880,7 +3889,7 @@ impl<'help> Arg<'help> { /// /// // However, case-insensitive comparisons can be enabled. This typically occurs when using Arg::possible_values(). /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::MissingRequiredArgument); + /// assert_eq!(res.unwrap_err().kind(), ErrorKind::MissingRequiredArgument); /// ``` /// [`Arg::requires(name)`]: Arg::requires() /// [Conflicting]: Arg::conflicts_with() @@ -3913,8 +3922,8 @@ impl<'help> Arg<'help> { /// anything other than `val`, this argument isn't required. /// /// ```rust - /// # use clap::{App, Arg}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg}; + /// let res = Command::new("prog") /// .arg(Arg::new("cfg") /// .required_if_eq_any(&[ /// ("extra", "val"), @@ -3939,8 +3948,8 @@ impl<'help> Arg<'help> { /// value of `val` but *not* using this arg is an error. /// /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg, ErrorKind}; + /// let res = Command::new("prog") /// .arg(Arg::new("cfg") /// .required_if_eq_any(&[ /// ("extra", "val"), @@ -3959,7 +3968,7 @@ impl<'help> Arg<'help> { /// ]); /// /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::MissingRequiredArgument); + /// assert_eq!(res.unwrap_err().kind(), ErrorKind::MissingRequiredArgument); /// ``` /// [`Arg::requires(name)`]: Arg::requires() /// [Conflicting]: Arg::conflicts_with() @@ -3993,8 +4002,8 @@ impl<'help> Arg<'help> { /// anything other than `val`, this argument isn't required. /// /// ```rust - /// # use clap::{App, Arg}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg}; + /// let res = Command::new("prog") /// .arg(Arg::new("cfg") /// .required_if_eq_all(&[ /// ("extra", "val"), @@ -4019,8 +4028,8 @@ impl<'help> Arg<'help> { /// value of `val` but *not* using this arg is an error. /// /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg, ErrorKind}; + /// let res = Command::new("prog") /// .arg(Arg::new("cfg") /// .required_if_eq_all(&[ /// ("extra", "val"), @@ -4039,7 +4048,7 @@ impl<'help> Arg<'help> { /// ]); /// /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::MissingRequiredArgument); + /// assert_eq!(res.unwrap_err().kind(), ErrorKind::MissingRequiredArgument); /// ``` /// [required]: Arg::required() #[must_use] @@ -4055,8 +4064,6 @@ impl<'help> Arg<'help> { /// if this arg (`self`) is present and its value equals to `val`. /// If it does, `another_arg` will be marked as required. /// - /// **NOTE:** An argument is considered present when there is a [`Arg::default_value`] - /// /// # Examples /// /// ```rust @@ -4071,8 +4078,8 @@ impl<'help> Arg<'help> { /// `val`, the other argument isn't required. /// /// ```rust - /// # use clap::{App, Arg}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg}; + /// let res = Command::new("prog") /// .arg(Arg::new("cfg") /// .takes_value(true) /// .requires_if("my.cfg", "other") @@ -4089,8 +4096,8 @@ impl<'help> Arg<'help> { /// `arg` is an error. /// /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg, ErrorKind}; + /// let res = Command::new("prog") /// .arg(Arg::new("cfg") /// .takes_value(true) /// .requires_if("my.cfg", "input") @@ -4101,14 +4108,15 @@ impl<'help> Arg<'help> { /// ]); /// /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::MissingRequiredArgument); + /// assert_eq!(res.unwrap_err().kind(), ErrorKind::MissingRequiredArgument); /// ``` /// [`Arg::requires(name)`]: Arg::requires() /// [Conflicting]: Arg::conflicts_with() /// [override]: Arg::overrides_with() #[must_use] pub fn requires_if(mut self, val: &'help str, arg_id: T) -> Self { - self.requires.push((Some(val), arg_id.into())); + self.requires + .push((ArgPredicate::Equals(OsStr::new(val)), arg_id.into())); self } @@ -4116,8 +4124,6 @@ impl<'help> Arg<'help> { /// /// The requirement will only become valid if this arg's value equals `val`. /// - /// **NOTE:** An argument is considered present when there is a [`Arg::default_value`] - /// /// # Examples /// /// ```rust @@ -4135,8 +4141,8 @@ impl<'help> Arg<'help> { /// than `val`, `arg` isn't required. /// /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg, ErrorKind}; + /// let res = Command::new("prog") /// .arg(Arg::new("cfg") /// .takes_value(true) /// .requires_ifs(&[ @@ -4153,15 +4159,17 @@ impl<'help> Arg<'help> { /// ]); /// /// assert!(res.is_err()); // We used --config=special.conf so --option is required - /// assert_eq!(res.unwrap_err().kind, ErrorKind::MissingRequiredArgument); + /// assert_eq!(res.unwrap_err().kind(), ErrorKind::MissingRequiredArgument); /// ``` /// [`Arg::requires(name)`]: Arg::requires() /// [Conflicting]: Arg::conflicts_with() /// [override]: Arg::overrides_with() #[must_use] pub fn requires_ifs(mut self, ifs: &[(&'help str, T)]) -> Self { - self.requires - .extend(ifs.iter().map(|(val, arg)| (Some(*val), Id::from(arg)))); + self.requires.extend( + ifs.iter() + .map(|(val, arg)| (ArgPredicate::Equals(OsStr::new(*val)), Id::from(arg))), + ); self } @@ -4172,8 +4180,6 @@ impl<'help> Arg<'help> { /// **NOTE:** [Conflicting] rules and [override] rules take precedence over being required /// by default. /// - /// **NOTE:** An argument is considered present when there is a [`Arg::default_value`] - /// /// # Examples /// /// ```rust @@ -4188,16 +4194,14 @@ impl<'help> Arg<'help> { /// argument isn't required /// /// ```rust - /// # use clap::{App, Arg}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg}; + /// let res = Command::new("prog") /// .arg(Arg::new("cfg") /// .takes_value(true) /// .requires("input") /// .long("config")) - /// .arg(Arg::new("input") - /// .index(1)) - /// .arg(Arg::new("output") - /// .index(2)) + /// .arg(Arg::new("input")) + /// .arg(Arg::new("output")) /// .try_get_matches_from(vec![ /// "prog" /// ]); @@ -4209,29 +4213,28 @@ impl<'help> Arg<'help> { /// error. /// /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg, ErrorKind}; + /// let res = Command::new("prog") /// .arg(Arg::new("cfg") /// .takes_value(true) /// .requires_all(&["input", "output"]) /// .long("config")) - /// .arg(Arg::new("input") - /// .index(1)) - /// .arg(Arg::new("output") - /// .index(2)) + /// .arg(Arg::new("input")) + /// .arg(Arg::new("output")) /// .try_get_matches_from(vec![ /// "prog", "--config", "file.conf", "in.txt" /// ]); /// /// assert!(res.is_err()); /// // We didn't use output - /// assert_eq!(res.unwrap_err().kind, ErrorKind::MissingRequiredArgument); + /// assert_eq!(res.unwrap_err().kind(), ErrorKind::MissingRequiredArgument); /// ``` /// [Conflicting]: Arg::conflicts_with() /// [override]: Arg::overrides_with() #[must_use] pub fn requires_all(mut self, names: &[T]) -> Self { - self.requires.extend(names.iter().map(|s| (None, s.into()))); + self.requires + .extend(names.iter().map(|s| (ArgPredicate::IsPresent, s.into()))); self } @@ -4248,8 +4251,6 @@ impl<'help> Arg<'help> { /// /// **NOTE** [`Arg::exclusive(true)`] allows specifying an argument which conflicts with every other argument. /// - /// **NOTE:** An argument is considered present when there is a [`Arg::default_value`] - /// /// # Examples /// /// ```rust @@ -4262,8 +4263,8 @@ impl<'help> Arg<'help> { /// Setting conflicting argument, and having both arguments present at runtime is an error. /// /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg, ErrorKind}; + /// let res = Command::new("prog") /// .arg(Arg::new("cfg") /// .takes_value(true) /// .conflicts_with("debug") @@ -4275,7 +4276,7 @@ impl<'help> Arg<'help> { /// ]); /// /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::ArgumentConflict); + /// assert_eq!(res.unwrap_err().kind(), ErrorKind::ArgumentConflict); /// ``` /// /// [`Arg::conflicts_with_all(names)`]: Arg::conflicts_with_all() @@ -4299,8 +4300,6 @@ impl<'help> Arg<'help> { /// /// **NOTE:** [`Arg::exclusive(true)`] allows specifying an argument which conflicts with every other argument. /// - /// **NOTE:** An argument is considered present when there is a [`Arg::default_value`] - /// /// # Examples /// /// ```rust @@ -4314,28 +4313,27 @@ impl<'help> Arg<'help> { /// conflicting argument is an error. /// /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("prog") + /// # use clap::{Command, Arg, ErrorKind}; + /// let res = Command::new("prog") /// .arg(Arg::new("cfg") /// .takes_value(true) /// .conflicts_with_all(&["debug", "input"]) /// .long("config")) /// .arg(Arg::new("debug") /// .long("debug")) - /// .arg(Arg::new("input") - /// .index(1)) + /// .arg(Arg::new("input")) /// .try_get_matches_from(vec![ /// "prog", "--config", "file.conf", "file.txt" /// ]); /// /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::ArgumentConflict); + /// assert_eq!(res.unwrap_err().kind(), ErrorKind::ArgumentConflict); /// ``` /// [`Arg::conflicts_with`]: Arg::conflicts_with() /// [`Arg::exclusive(true)`]: Arg::exclusive() #[must_use] pub fn conflicts_with_all(mut self, names: &[&str]) -> Self { - self.blacklist.extend(names.iter().map(Id::from)); + self.blacklist.extend(names.iter().copied().map(Id::from)); self } @@ -4348,6 +4346,8 @@ impl<'help> Arg<'help> { /// **NOTE:** When an argument is overridden it is essentially as if it never was used, any /// conflicts, requirements, etc. are evaluated **after** all "overrides" have been removed /// + /// **NOTE:** Overriding an argument implies they [conflict][Arg::conflicts_with`]. + /// /// **WARNING:** Positional arguments and options which accept /// [`Arg::multiple_occurrences`] cannot override themselves (or we /// would never be able to advance to the next positional). If a positional @@ -4356,9 +4356,9 @@ impl<'help> Arg<'help> { /// /// # Examples /// - /// ```rust # use clap::{App, Arg}; - /// # use clap::{App, arg}; - /// let m = App::new("prog") + /// ```rust # use clap::{Command, Arg}; + /// # use clap::{Command, arg}; + /// let m = Command::new("prog") /// .arg(arg!(-f --flag "some flag") /// .conflicts_with("debug")) /// .arg(arg!(-d --debug "other flag")) @@ -4382,8 +4382,8 @@ impl<'help> Arg<'help> { /// preventing a "Unexpected multiple usage" error): /// /// ```rust - /// # use clap::{App, arg}; - /// let m = App::new("posix") + /// # use clap::{Command, arg}; + /// let m = Command::new("posix") /// .arg(arg!(--flag "some flag").overrides_with("flag")) /// .get_matches_from(vec!["posix", "--flag", "--flag"]); /// assert!(m.is_present("flag")); @@ -4395,8 +4395,8 @@ impl<'help> Arg<'help> { /// if it's a flag and it already accepts multiple occurrences. /// /// ``` - /// # use clap::{App, arg}; - /// let m = App::new("posix") + /// # use clap::{Command, arg}; + /// let m = Command::new("posix") /// .arg(arg!(--flag ... "some flag").overrides_with("flag")) /// .get_matches_from(vec!["", "--flag", "--flag", "--flag", "--flag"]); /// assert!(m.is_present("flag")); @@ -4408,8 +4408,8 @@ impl<'help> Arg<'help> { /// occurrence happened. /// /// ``` - /// # use clap::{App, arg}; - /// let m = App::new("posix") + /// # use clap::{Command, arg}; + /// let m = Command::new("posix") /// .arg(arg!(--opt "some option").overrides_with("opt")) /// .get_matches_from(vec!["", "--opt=some", "--opt=other"]); /// assert!(m.is_present("opt")); @@ -4420,8 +4420,8 @@ impl<'help> Arg<'help> { /// This will also work when [`Arg::multiple_values`] is enabled: /// /// ``` - /// # use clap::{App, Arg}; - /// let m = App::new("posix") + /// # use clap::{Command, Arg}; + /// let m = Command::new("posix") /// .arg( /// Arg::new("opt") /// .long("opt") @@ -4439,8 +4439,8 @@ impl<'help> Arg<'help> { /// will ignore the "override self" setting. /// /// ``` - /// # use clap::{App, arg}; - /// let m = App::new("posix") + /// # use clap::{Command, arg}; + /// let m = Command::new("posix") /// .arg(arg!(--opt ... "some option") /// .multiple_values(true) /// .overrides_with("opt")) @@ -4463,11 +4463,13 @@ impl<'help> Arg<'help> { /// **NOTE:** When an argument is overridden it is essentially as if it never was used, any /// conflicts, requirements, etc. are evaluated **after** all "overrides" have been removed /// + /// **NOTE:** Overriding an argument implies they [conflict][Arg::conflicts_with_all`]. + /// /// # Examples /// /// ```rust - /// # use clap::{App, arg}; - /// let m = App::new("prog") + /// # use clap::{Command, arg}; + /// let m = Command::new("prog") /// .arg(arg!(-f --flag "some flag") /// .conflicts_with("color")) /// .arg(arg!(-d --debug "other flag")) @@ -4490,14 +4492,20 @@ impl<'help> Arg<'help> { } } -/// Reflection +/// # Reflection impl<'help> Arg<'help> { /// Get the name of the argument #[inline] - pub fn get_name(&self) -> &'help str { + pub fn get_id(&self) -> &'help str { self.name } + /// Deprecated, replaced with [`Arg::get_id`] + #[deprecated(since = "3.1.0", note = "Replaced with `Arg::get_id`")] + pub fn get_name(&self) -> &'help str { + self.get_id() + } + /// Get the help specified for this argument, if any #[inline] pub fn get_help(&self) -> Option<&'help str> { @@ -4621,6 +4629,12 @@ impl<'help> Arg<'help> { self.num_vals } + /// Get the delimiter between multiple values + #[inline] + pub fn get_value_delimiter(&self) -> Option { + self.val_delim + } + /// Get the index of this argument, if any #[inline] pub fn get_index(&self) -> Option { @@ -4632,9 +4646,10 @@ impl<'help> Arg<'help> { self.value_hint } - /// Get information on if this argument is global or not + /// Deprecated, replaced with [`Arg::is_global_set`] + #[deprecated(since = "3.1.0", note = "Replaced with `Arg::is_global_set`")] pub fn get_global(&self) -> bool { - self.is_set(ArgSettings::Global) + self.is_global_set() } /// Get the environment variable name specified for this argument, if any @@ -4680,12 +4695,125 @@ impl<'help> Arg<'help> { pub fn is_positional(&self) -> bool { self.long.is_none() && self.short.is_none() } + + /// Reports whether [`Arg::required`] is set + pub fn is_required_set(&self) -> bool { + self.is_set(ArgSettings::Required) + } + + /// Report whether [`Arg::multiple_values`] is set + pub fn is_multiple_values_set(&self) -> bool { + self.is_set(ArgSettings::MultipleValues) + } + + /// Report whether [`Arg::multiple_occurrences`] is set + pub fn is_multiple_occurrences_set(&self) -> bool { + self.is_set(ArgSettings::MultipleOccurrences) + } + + /// Report whether [`Arg::is_takes_value_set`] is set + pub fn is_takes_value_set(&self) -> bool { + self.is_set(ArgSettings::TakesValue) + } + + /// Report whether [`Arg::allow_hyphen_values`] is set + pub fn is_allow_hyphen_values_set(&self) -> bool { + self.is_set(ArgSettings::AllowHyphenValues) + } + + /// Report whether [`Arg::forbid_empty_values`] is set + pub fn is_forbid_empty_values_set(&self) -> bool { + self.is_set(ArgSettings::ForbidEmptyValues) + } + + /// Report whether [`Arg::is_allow_invalid_utf8_set`] is set + pub fn is_allow_invalid_utf8_set(&self) -> bool { + self.is_set(ArgSettings::AllowInvalidUtf8) + } + + /// Report whether [`Arg::global`] is set + pub fn is_global_set(&self) -> bool { + self.is_set(ArgSettings::Global) + } + + /// Report whether [`Arg::next_line_help`] is set + pub fn is_next_line_help_set(&self) -> bool { + self.is_set(ArgSettings::NextLineHelp) + } + + /// Report whether [`Arg::hide`] is set + pub fn is_hide_set(&self) -> bool { + self.is_set(ArgSettings::Hidden) + } + + /// Report whether [`Arg::hide_default_value`] is set + pub fn is_hide_default_value_set(&self) -> bool { + self.is_set(ArgSettings::HideDefaultValue) + } + + /// Report whether [`Arg::hide_possible_values`] is set + pub fn is_hide_possible_values_set(&self) -> bool { + self.is_set(ArgSettings::HidePossibleValues) + } + + /// Report whether [`Arg::hide_env`] is set + #[cfg(feature = "env")] + pub fn is_hide_env_set(&self) -> bool { + self.is_set(ArgSettings::HideEnv) + } + + /// Report whether [`Arg::hide_env_values`] is set + #[cfg(feature = "env")] + pub fn is_hide_env_values_set(&self) -> bool { + self.is_set(ArgSettings::HideEnvValues) + } + + /// Report whether [`Arg::hide_short_help`] is set + pub fn is_hide_short_help_set(&self) -> bool { + self.is_set(ArgSettings::HiddenShortHelp) + } + + /// Report whether [`Arg::hide_long_help`] is set + pub fn is_hide_long_help_set(&self) -> bool { + self.is_set(ArgSettings::HiddenLongHelp) + } + + /// Report whether [`Arg::use_value_delimiter`] is set + pub fn is_use_value_delimiter_set(&self) -> bool { + self.is_set(ArgSettings::UseValueDelimiter) + } + + /// Report whether [`Arg::require_value_delimiter`] is set + pub fn is_require_value_delimiter_set(&self) -> bool { + self.is_set(ArgSettings::RequireDelimiter) + } + + /// Report whether [`Arg::require_equals`] is set + pub fn is_require_equals_set(&self) -> bool { + self.is_set(ArgSettings::RequireEquals) + } + + /// Reports whether [`Arg::exclusive`] is set + pub fn is_exclusive_set(&self) -> bool { + self.is_set(ArgSettings::Exclusive) + } + + /// Reports whether [`Arg::last`] is set + pub fn is_last_set(&self) -> bool { + self.is_set(ArgSettings::Last) + } + + /// Reports whether [`Arg::ignore_case`] is set + pub fn is_ignore_case_set(&self) -> bool { + self.is_set(ArgSettings::IgnoreCase) + } } -/// Deprecated +/// # Deprecated impl<'help> Arg<'help> { /// Deprecated, replaced with [`Arg::new`] #[deprecated(since = "3.0.0", note = "Replaced with `Arg::new`")] + #[doc(hidden)] pub fn with_name>(n: S) -> Self { Self::new(n) } @@ -4696,6 +4824,7 @@ impl<'help> Arg<'help> { since = "3.0.0", note = "Deprecated in Issue #3087, maybe clap::Parser would fit your use case?" )] + #[doc(hidden)] pub fn from_yaml(y: &'help Yaml) -> Self { #![allow(deprecated)] let yaml_file_hash = y.as_hash().expect("YAML file must be a hash"); @@ -4765,12 +4894,14 @@ impl<'help> Arg<'help> { /// Deprecated in [Issue #3086](https://github.com/clap-rs/clap/issues/3086), see [`arg!`][crate::arg!]. #[deprecated(since = "3.0.0", note = "Deprecated in Issue #3086, see `clap::arg!")] + #[doc(hidden)] pub fn from_usage(u: &'help str) -> Self { UsageParser::from_usage(u).parse() } /// Deprecated, replaced with [`Arg::required_unless_present`] #[deprecated(since = "3.0.0", note = "Replaced with `Arg::required_unless_present`")] + #[doc(hidden)] #[must_use] pub fn required_unless(self, arg_id: T) -> Self { self.required_unless_present(arg_id) @@ -4781,6 +4912,7 @@ impl<'help> Arg<'help> { since = "3.0.0", note = "Replaced with `Arg::required_unless_present_all`" )] + #[doc(hidden)] #[must_use] pub fn required_unless_all(self, names: I) -> Self where @@ -4795,6 +4927,7 @@ impl<'help> Arg<'help> { since = "3.0.0", note = "Replaced with `Arg::required_unless_present_any`" )] + #[doc(hidden)] #[must_use] pub fn required_unless_one(self, names: I) -> Self where @@ -4806,6 +4939,7 @@ impl<'help> Arg<'help> { /// Deprecated, replaced with [`Arg::required_if_eq`] #[deprecated(since = "3.0.0", note = "Replaced with `Arg::required_if_eq`")] + #[doc(hidden)] #[must_use] pub fn required_if(self, arg_id: T, val: &'help str) -> Self { self.required_if_eq(arg_id, val) @@ -4813,6 +4947,7 @@ impl<'help> Arg<'help> { /// Deprecated, replaced with [`Arg::required_if_eq_any`] #[deprecated(since = "3.0.0", note = "Replaced with `Arg::required_if_eq_any`")] + #[doc(hidden)] #[must_use] pub fn required_ifs(self, ifs: &[(T, &'help str)]) -> Self { self.required_if_eq_any(ifs) @@ -4820,6 +4955,7 @@ impl<'help> Arg<'help> { /// Deprecated, replaced with [`Arg::hide`] #[deprecated(since = "3.0.0", note = "Replaced with `Arg::hide`")] + #[doc(hidden)] #[inline] #[must_use] pub fn hidden(self, yes: bool) -> Self { @@ -4828,6 +4964,7 @@ impl<'help> Arg<'help> { /// Deprecated, replaced with [`Arg::ignore_case`] #[deprecated(since = "3.0.0", note = "Replaced with `Arg::ignore_case`")] + #[doc(hidden)] #[inline] #[must_use] pub fn case_insensitive(self, yes: bool) -> Self { @@ -4836,6 +4973,7 @@ impl<'help> Arg<'help> { /// Deprecated, replaced with [`Arg::forbid_empty_values`] #[deprecated(since = "3.0.0", note = "Replaced with `Arg::forbid_empty_values`")] + #[doc(hidden)] #[must_use] pub fn empty_values(self, yes: bool) -> Self { self.forbid_empty_values(!yes) @@ -4847,6 +4985,7 @@ impl<'help> Arg<'help> { since = "3.0.0", note = "Split into `Arg::multiple_occurrences` (most likely what you want) and `Arg::multiple_values`" )] + #[doc(hidden)] #[must_use] pub fn multiple(self, yes: bool) -> Self { self.multiple_occurrences(yes).multiple_values(yes) @@ -4854,6 +4993,7 @@ impl<'help> Arg<'help> { /// Deprecated, replaced with [`Arg::hide_short_help`] #[deprecated(since = "3.0.0", note = "Replaced with `Arg::hide_short_help`")] + #[doc(hidden)] #[inline] #[must_use] pub fn hidden_short_help(self, yes: bool) -> Self { @@ -4862,6 +5002,7 @@ impl<'help> Arg<'help> { /// Deprecated, replaced with [`Arg::hide_long_help`] #[deprecated(since = "3.0.0", note = "Replaced with `Arg::hide_long_help`")] + #[doc(hidden)] #[inline] #[must_use] pub fn hidden_long_help(self, yes: bool) -> Self { @@ -4870,6 +5011,7 @@ impl<'help> Arg<'help> { /// Deprecated, replaced with [`Arg::setting`] #[deprecated(since = "3.0.0", note = "Replaced with `Arg::setting`")] + #[doc(hidden)] #[must_use] pub fn set(self, s: ArgSettings) -> Self { self.setting(s) @@ -4877,21 +5019,21 @@ impl<'help> Arg<'help> { /// Deprecated, replaced with [`Arg::unset_setting`] #[deprecated(since = "3.0.0", note = "Replaced with `Arg::unset_setting`")] + #[doc(hidden)] #[must_use] pub fn unset(self, s: ArgSettings) -> Self { self.unset_setting(s) } } -// Internally used only +/// # Internally used only impl<'help> Arg<'help> { pub(crate) fn _build(&mut self) { if self.is_positional() { self.settings.set(ArgSettings::TakesValue); } - if (self.is_set(ArgSettings::UseValueDelimiter) - || self.is_set(ArgSettings::RequireDelimiter)) + if (self.is_use_value_delimiter_set() || self.is_require_value_delimiter_set()) && self.val_delim.is_none() { self.val_delim = Some(','); @@ -4908,7 +5050,7 @@ impl<'help> Arg<'help> { } let self_id = self.id.clone(); - if self.is_positional() || self.is_set(ArgSettings::MultipleOccurrences) { + if self.is_positional() || self.is_multiple_occurrences_set() { // Remove self-overrides where they don't make sense. // // We can evaluate switching this to a debug assert at a later time (though it will @@ -4924,16 +5066,13 @@ impl<'help> Arg<'help> { } pub(crate) fn longest_filter(&self) -> bool { - self.is_set(ArgSettings::TakesValue) || self.long.is_some() || self.short.is_none() + self.is_takes_value_set() || self.long.is_some() || self.short.is_none() } // Used for positionals when printing pub(crate) fn multiple_str(&self) -> &str { let mult_vals = self.val_names.len() > 1; - if (self.is_set(ArgSettings::MultipleValues) - || self.is_set(ArgSettings::MultipleOccurrences)) - && !mult_vals - { + if (self.is_multiple_values_set() || self.is_multiple_occurrences_set()) && !mult_vals { "..." } else { "" @@ -4943,12 +5082,12 @@ impl<'help> Arg<'help> { // Used for positionals when printing pub(crate) fn name_no_brackets(&self) -> Cow { debug!("Arg::name_no_brackets:{}", self.name); - let mut delim = String::new(); - delim.push(if self.is_set(ArgSettings::RequireDelimiter) { + let delim = if self.is_require_value_delimiter_set() { self.val_delim.expect(INTERNAL_ERROR_MSG) } else { ' ' - }); + } + .to_string(); if !self.val_names.is_empty() { debug!("Arg::name_no_brackets: val_names={:#?}", self.val_names); @@ -4971,11 +5110,11 @@ impl<'help> Arg<'help> { /// Either multiple values or occurrences pub(crate) fn is_multiple(&self) -> bool { - self.is_set(ArgSettings::MultipleValues) | self.is_set(ArgSettings::MultipleOccurrences) + self.is_multiple_values_set() | self.is_multiple_occurrences_set() } pub(crate) fn get_display_order(&self) -> usize { - self.disp_ord.unwrap_or(999) + self.disp_ord.get_explicit() } } @@ -5013,16 +5152,29 @@ impl<'help> Display for Arg<'help> { } else if let Some(s) = self.short { write!(f, "-{}", s)?; } - if !self.is_positional() && self.is_set(ArgSettings::TakesValue) { - let sep = if self.is_set(ArgSettings::RequireEquals) { - "=" + let mut need_closing_bracket = false; + if !self.is_positional() && self.is_takes_value_set() { + let is_optional_val = self.min_vals == Some(0); + let sep = if self.is_require_equals_set() { + if is_optional_val { + need_closing_bracket = true; + "[=" + } else { + "=" + } + } else if is_optional_val { + need_closing_bracket = true; + " [" } else { " " }; - write!(f, "{}", sep)?; + f.write_str(sep)?; } - if self.is_set(ArgSettings::TakesValue) || self.is_positional() { - display_arg_val(self, |s, _| write!(f, "{}", s))?; + if self.is_takes_value_set() || self.is_positional() { + display_arg_val(self, |s, _| f.write_str(s))?; + } + if need_closing_bracket { + f.write_str("]")?; } Ok(()) @@ -5104,13 +5256,14 @@ pub(crate) fn display_arg_val(arg: &Arg, mut write: F) -> Result<(), E> where F: FnMut(&str, bool) -> Result, { - let mult_val = arg.is_set(ArgSettings::MultipleValues); - let mult_occ = arg.is_set(ArgSettings::MultipleOccurrences); - let delim = if arg.is_set(ArgSettings::RequireDelimiter) { + let mult_val = arg.is_multiple_values_set(); + let mult_occ = arg.is_multiple_occurrences_set(); + let delim = if arg.is_require_value_delimiter_set() { arg.val_delim.expect(INTERNAL_ERROR_MSG) } else { ' ' - }; + } + .to_string(); if !arg.val_names.is_empty() { // If have val_name. match (arg.val_names.len(), arg.num_vals) { @@ -5118,11 +5271,10 @@ where // If single value name with multiple num_of_vals, display all // the values with the single value name. let arg_name = format!("<{}>", arg.val_names.get(0).unwrap()); - let mut it = iter::repeat(arg_name).take(num_vals).peekable(); - while let Some(arg_name) = it.next() { + for n in 1..=num_vals { write(&arg_name, true)?; - if it.peek().is_some() { - write(&delim.to_string(), false)?; + if n != num_vals { + write(&delim, false)?; } } } @@ -5132,10 +5284,13 @@ where while let Some(val) = it.next() { write(&format!("<{}>", val), true)?; if it.peek().is_some() { - write(&delim.to_string(), false)?; + write(&delim, false)?; } } - if (num_val_names == 1 && mult_val) || (arg.is_positional() && mult_occ) { + if (num_val_names == 1 && mult_val) + || (arg.is_positional() && mult_occ) + || num_val_names < arg.num_vals.unwrap_or(0) + { write("...", true)?; } } @@ -5143,11 +5298,10 @@ where } else if let Some(num_vals) = arg.num_vals { // If number_of_values is specified, display the value multiple times. let arg_name = format!("<{}>", arg.name); - let mut it = iter::repeat(&arg_name).take(num_vals).peekable(); - while let Some(arg_name) = it.next() { - write(arg_name, true)?; - if it.peek().is_some() { - write(&delim.to_string(), false)?; + for n in 1..=num_vals { + write(&arg_name, true)?; + if n != num_vals { + write(&delim, false)?; } } } else if arg.is_positional() { @@ -5167,6 +5321,43 @@ where Ok(()) } +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub(crate) enum DisplayOrder { + None, + Implicit(usize), + Explicit(usize), +} + +impl DisplayOrder { + pub(crate) fn set_explicit(&mut self, explicit: usize) { + *self = Self::Explicit(explicit) + } + + pub(crate) fn set_implicit(&mut self, implicit: usize) { + *self = (*self).max(Self::Implicit(implicit)) + } + + pub(crate) fn make_explicit(&mut self) { + match *self { + Self::None | Self::Explicit(_) => {} + Self::Implicit(disp) => self.set_explicit(disp), + } + } + + pub(crate) fn get_explicit(self) -> usize { + match self { + Self::None | Self::Implicit(_) => 999, + Self::Explicit(disp) => disp, + } + } +} + +impl Default for DisplayOrder { + fn default() -> Self { + Self::None + } +} + // Flags #[cfg(test)] mod test { @@ -5177,12 +5368,12 @@ mod test { let mut f = Arg::new("flg").multiple_occurrences(true); f.long = Some("flag"); - assert_eq!(&*format!("{}", f), "--flag"); + assert_eq!(f.to_string(), "--flag"); let mut f2 = Arg::new("flg"); f2.short = Some('f'); - assert_eq!(&*format!("{}", f2), "-f"); + assert_eq!(f2.to_string(), "-f"); } #[test] @@ -5191,7 +5382,7 @@ mod test { f.long = Some("flag"); f.aliases = vec![("als", true)]; - assert_eq!(&*format!("{}", f), "--flag") + assert_eq!(f.to_string(), "--flag") } #[test] @@ -5204,7 +5395,7 @@ mod test { ("f3", true), ("f4", true), ]; - assert_eq!(&*format!("{}", f), "-f"); + assert_eq!(f.to_string(), "-f"); } #[test] @@ -5213,7 +5404,7 @@ mod test { f.short = Some('a'); f.short_aliases = vec![('b', true)]; - assert_eq!(&*format!("{}", f), "-a") + assert_eq!(f.to_string(), "-a") } #[test] @@ -5221,7 +5412,7 @@ mod test { let mut f = Arg::new("flg"); f.short = Some('a'); f.short_aliases = vec![('b', false), ('c', true), ('d', true), ('e', true)]; - assert_eq!(&*format!("{}", f), "-a"); + assert_eq!(f.to_string(), "-a"); } // Options @@ -5233,7 +5424,7 @@ mod test { .takes_value(true) .multiple_occurrences(true); - assert_eq!(&*format!("{}", o), "--option "); + assert_eq!(o.to_string(), "--option "); } #[test] @@ -5243,14 +5434,14 @@ mod test { .takes_value(true) .multiple_values(true); - assert_eq!(&*format!("{}", o), "--option ..."); + assert_eq!(o.to_string(), "--option ..."); } #[test] fn option_display2() { let o2 = Arg::new("opt").short('o').value_names(&["file", "name"]); - assert_eq!(&*format!("{}", o2), "-o "); + assert_eq!(o2.to_string(), "-o "); } #[test] @@ -5261,7 +5452,7 @@ mod test { .multiple_values(true) .value_names(&["file", "name"]); - assert_eq!(&*format!("{}", o2), "-o "); + assert_eq!(o2.to_string(), "-o "); } #[test] @@ -5271,7 +5462,7 @@ mod test { .long("option") .visible_alias("als"); - assert_eq!(&*format!("{}", o), "--option "); + assert_eq!(o.to_string(), "--option "); } #[test] @@ -5282,7 +5473,7 @@ mod test { .visible_aliases(&["als2", "als3", "als4"]) .alias("als_not_visible"); - assert_eq!(&*format!("{}", o), "--option "); + assert_eq!(o.to_string(), "--option "); } #[test] @@ -5292,7 +5483,7 @@ mod test { .short('a') .visible_short_alias('b'); - assert_eq!(&*format!("{}", o), "-a "); + assert_eq!(o.to_string(), "-a "); } #[test] @@ -5303,7 +5494,7 @@ mod test { .visible_short_aliases(&['b', 'c', 'd']) .short_alias('e'); - assert_eq!(&*format!("{}", o), "-a "); + assert_eq!(o.to_string(), "-a "); } // Positionals @@ -5315,7 +5506,7 @@ mod test { .takes_value(true) .multiple_values(true); - assert_eq!(&*format!("{}", p), "..."); + assert_eq!(p.to_string(), "..."); } #[test] @@ -5325,21 +5516,21 @@ mod test { .takes_value(true) .multiple_occurrences(true); - assert_eq!(&*format!("{}", p), "..."); + assert_eq!(p.to_string(), "..."); } #[test] fn positional_display_required() { let p2 = Arg::new("pos").index(1).required(true); - assert_eq!(&*format!("{}", p2), ""); + assert_eq!(p2.to_string(), ""); } #[test] fn positional_display_val_names() { let p2 = Arg::new("pos").index(1).value_names(&["file1", "file2"]); - assert_eq!(&*format!("{}", p2), " "); + assert_eq!(p2.to_string(), " "); } #[test] @@ -5349,6 +5540,6 @@ mod test { .required(true) .value_names(&["file1", "file2"]); - assert_eq!(&*format!("{}", p2), " "); + assert_eq!(p2.to_string(), " "); } } diff --git a/third_party/rust/clap/src/build/arg/debug_asserts.rs b/third_party/rust/clap/src/build/arg/debug_asserts.rs deleted file mode 100644 index 8fe77c281994..000000000000 --- a/third_party/rust/clap/src/build/arg/debug_asserts.rs +++ /dev/null @@ -1,80 +0,0 @@ -use crate::{Arg, ArgSettings, ValueHint}; - -pub(crate) fn assert_arg(arg: &Arg) { - debug!("Arg::_debug_asserts:{}", arg.name); - - // Self conflict - // TODO: this check should be recursive - assert!( - !arg.blacklist.iter().any(|x| *x == arg.id), - "Argument '{}' cannot conflict with itself", - arg.name, - ); - - if arg.value_hint != ValueHint::Unknown { - assert!( - arg.is_set(ArgSettings::TakesValue), - "Argument '{}' has value hint but takes no value", - arg.name - ); - - if arg.value_hint == ValueHint::CommandWithArguments { - assert!( - arg.is_set(ArgSettings::MultipleValues), - "Argument '{}' uses hint CommandWithArguments and must accept multiple values", - arg.name - ) - } - } - - if arg.index.is_some() { - assert!( - arg.is_positional(), - "Argument '{}' is a positional argument and can't have short or long name versions", - arg.name - ); - } - - if arg.is_set(ArgSettings::Required) { - assert!( - arg.default_vals.is_empty(), - "Argument '{}' is required and can't have a default value", - arg.name - ); - } - - assert_arg_flags(arg); -} - -fn assert_arg_flags(arg: &Arg) { - use ArgSettings::*; - - macro_rules! checker { - ($a:ident requires $($b:ident)|+) => { - if arg.is_set($a) { - let mut s = String::new(); - - $( - if !arg.is_set($b) { - s.push_str(&format!(" ArgSettings::{} is required when ArgSettings::{} is set.\n", std::stringify!($b), std::stringify!($a))); - } - )+ - - if !s.is_empty() { - panic!("Argument {:?}\n{}", arg.get_name(), s) - } - } - } - } - - checker!(ForbidEmptyValues requires TakesValue); - checker!(RequireDelimiter requires TakesValue | UseValueDelimiter); - checker!(HidePossibleValues requires TakesValue); - checker!(AllowHyphenValues requires TakesValue); - checker!(RequireEquals requires TakesValue); - checker!(Last requires TakesValue); - checker!(HideDefaultValue requires TakesValue); - checker!(MultipleValues requires TakesValue); - checker!(IgnoreCase requires TakesValue); - checker!(AllowInvalidUtf8 requires TakesValue); -} diff --git a/third_party/rust/clap/src/build/arg/tests.rs b/third_party/rust/clap/src/build/arg/tests.rs deleted file mode 100644 index 6a6506e43f9b..000000000000 --- a/third_party/rust/clap/src/build/arg/tests.rs +++ /dev/null @@ -1,8 +0,0 @@ -use crate::Arg; - -// This test will *fail to compile* if Arg is not Send + Sync -#[test] -fn arg_send_sync() { - fn foo(_: T) {} - foo(Arg::new("test")) -} diff --git a/third_party/rust/clap/src/build/arg_group.rs b/third_party/rust/clap/src/build/arg_group.rs index 49d95a869fe2..dbcf9aa41b0f 100644 --- a/third_party/rust/clap/src/build/arg_group.rs +++ b/third_party/rust/clap/src/build/arg_group.rs @@ -16,7 +16,7 @@ use yaml_rust::Yaml; /// /// You can also do things such as name an entire `ArgGroup` as a [conflict] or [requirement] for /// another argument, meaning any of the arguments that belong to that group will cause a failure -/// if present, or must present respectively. +/// if present, or must be present respectively. /// /// Perhaps the most common use of `ArgGroup`s is to require one and *only* one argument to be /// present out of a given set. Imagine that you had multiple arguments, and you want one of them @@ -37,8 +37,8 @@ use yaml_rust::Yaml; /// the arguments from the specified group is present at runtime. /// /// ```rust -/// # use clap::{App, arg, ArgGroup, ErrorKind}; -/// let result = App::new("app") +/// # use clap::{Command, arg, ArgGroup, ErrorKind}; +/// let result = Command::new("cmd") /// .arg(arg!(--"set-ver" "set the version manually").required(false)) /// .arg(arg!(--major "auto increase major")) /// .arg(arg!(--minor "auto increase minor")) @@ -46,17 +46,17 @@ use yaml_rust::Yaml; /// .group(ArgGroup::new("vers") /// .args(&["set-ver", "major", "minor", "patch"]) /// .required(true)) -/// .try_get_matches_from(vec!["app", "--major", "--patch"]); +/// .try_get_matches_from(vec!["cmd", "--major", "--patch"]); /// // Because we used two args in the group it's an error /// assert!(result.is_err()); /// let err = result.unwrap_err(); -/// assert_eq!(err.kind, ErrorKind::ArgumentConflict); +/// assert_eq!(err.kind(), ErrorKind::ArgumentConflict); /// ``` /// This next example shows a passing parse of the same scenario /// /// ```rust -/// # use clap::{App, arg, ArgGroup}; -/// let result = App::new("app") +/// # use clap::{Command, arg, ArgGroup}; +/// let result = Command::new("cmd") /// .arg(arg!(--"set-ver" "set the version manually").required(false)) /// .arg(arg!(--major "auto increase major")) /// .arg(arg!(--minor "auto increase minor")) @@ -64,7 +64,7 @@ use yaml_rust::Yaml; /// .group(ArgGroup::new("vers") /// .args(&["set-ver", "major", "minor","patch"]) /// .required(true)) -/// .try_get_matches_from(vec!["app", "--major"]); +/// .try_get_matches_from(vec!["cmd", "--major"]); /// assert!(result.is_ok()); /// let matches = result.unwrap(); /// // We may not know which of the args was used, so we can test for the group... @@ -104,12 +104,12 @@ impl<'help> ArgGroup<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, ArgGroup}; + /// # use clap::{Command, ArgGroup}; /// ArgGroup::new("config") /// # ; /// ``` pub fn new>(n: S) -> Self { - ArgGroup::default().name(n) + ArgGroup::default().id(n) } /// Sets the group name. @@ -117,24 +117,30 @@ impl<'help> ArgGroup<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, ArgGroup}; + /// # use clap::{Command, ArgGroup}; /// ArgGroup::default().name("config") /// # ; /// ``` #[must_use] - pub fn name>(mut self, n: S) -> Self { + pub fn id>(mut self, n: S) -> Self { self.name = n.into(); - self.id = Id::from(&self.name); + self.id = Id::from(self.name); self } + /// Deprecated, replaced with [`ArgGroup::id`] + #[deprecated(since = "3.1.0", note = "Replaced with `ArgGroup::id`")] + pub fn name>(self, n: S) -> Self { + self.id(n) + } + /// Adds an [argument] to this group by name /// /// # Examples /// /// ```rust - /// # use clap::{App, Arg, ArgGroup}; - /// let m = App::new("myprog") + /// # use clap::{Command, Arg, ArgGroup}; + /// let m = Command::new("myprog") /// .arg(Arg::new("flag") /// .short('f')) /// .arg(Arg::new("color") @@ -160,8 +166,8 @@ impl<'help> ArgGroup<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, Arg, ArgGroup}; - /// let m = App::new("myprog") + /// # use clap::{Command, Arg, ArgGroup}; + /// let m = Command::new("myprog") /// .arg(Arg::new("flag") /// .short('f')) /// .arg(Arg::new("color") @@ -191,8 +197,8 @@ impl<'help> ArgGroup<'help> { /// group /// /// ```rust - /// # use clap::{App, Arg, ArgGroup}; - /// let m = App::new("myprog") + /// # use clap::{Command, Arg, ArgGroup}; + /// let m = Command::new("myprog") /// .arg(Arg::new("flag") /// .short('f')) /// .arg(Arg::new("color") @@ -208,8 +214,8 @@ impl<'help> ArgGroup<'help> { /// an error if more than one of the args in the group was used. /// /// ```rust - /// # use clap::{App, Arg, ArgGroup, ErrorKind}; - /// let result = App::new("myprog") + /// # use clap::{Command, Arg, ArgGroup, ErrorKind}; + /// let result = Command::new("myprog") /// .arg(Arg::new("flag") /// .short('f')) /// .arg(Arg::new("color") @@ -220,7 +226,7 @@ impl<'help> ArgGroup<'help> { /// // Because we used both args in the group it's an error /// assert!(result.is_err()); /// let err = result.unwrap_err(); - /// assert_eq!(err.kind, ErrorKind::ArgumentConflict); + /// assert_eq!(err.kind(), ErrorKind::ArgumentConflict); /// ``` /// /// [`Arg`]: crate::Arg @@ -236,7 +242,7 @@ impl<'help> ArgGroup<'help> { /// This is unless conflicting with another argument. A required group will be displayed in /// the usage string of the application in the format ``. /// - /// **NOTE:** This setting only applies to the current [`App`] / [`Subcommand`]s, and not + /// **NOTE:** This setting only applies to the current [`Command`] / [`Subcommand`]s, and not /// globally. /// /// **NOTE:** By default, [`ArgGroup::multiple`] is set to `false` which when combined with @@ -244,14 +250,11 @@ impl<'help> ArgGroup<'help> { /// Use of more than one arg is an error." Vice setting `ArgGroup::multiple(true)` which /// states, '*At least* one arg from this group must be used. Using multiple is OK." /// - /// **NOTE:** An argument is considered present when there is a - /// [`Arg::default_value`](crate::Arg::default_value) - /// /// # Examples /// /// ```rust - /// # use clap::{App, Arg, ArgGroup, ErrorKind}; - /// let result = App::new("myprog") + /// # use clap::{Command, Arg, ArgGroup, ErrorKind}; + /// let result = Command::new("myprog") /// .arg(Arg::new("flag") /// .short('f')) /// .arg(Arg::new("color") @@ -263,12 +266,12 @@ impl<'help> ArgGroup<'help> { /// // Because we didn't use any of the args in the group, it's an error /// assert!(result.is_err()); /// let err = result.unwrap_err(); - /// assert_eq!(err.kind, ErrorKind::MissingRequiredArgument); + /// assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument); /// ``` /// /// [`Subcommand`]: crate::Subcommand /// [`ArgGroup::multiple`]: ArgGroup::multiple() - /// [`App`]: crate::App + /// [`Command`]: crate::Command #[inline] #[must_use] pub fn required(mut self, yes: bool) -> Self { @@ -282,16 +285,13 @@ impl<'help> ArgGroup<'help> { /// [argument requirement rules], you can name other arguments or groups that must be present /// when any one of the arguments from this group is used. /// - /// **NOTE:** An argument is considered present when there is a - /// [`Arg::default_value`](crate::Arg::default_value) - /// /// **NOTE:** The name provided may be an argument or group name /// /// # Examples /// /// ```rust - /// # use clap::{App, Arg, ArgGroup, ErrorKind}; - /// let result = App::new("myprog") + /// # use clap::{Command, Arg, ArgGroup, ErrorKind}; + /// let result = Command::new("myprog") /// .arg(Arg::new("flag") /// .short('f')) /// .arg(Arg::new("color") @@ -306,7 +306,7 @@ impl<'help> ArgGroup<'help> { /// // error /// assert!(result.is_err()); /// let err = result.unwrap_err(); - /// assert_eq!(err.kind, ErrorKind::MissingRequiredArgument); + /// assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument); /// ``` /// [required group]: ArgGroup::required() /// [argument requirement rules]: crate::Arg::requires() @@ -324,14 +324,11 @@ impl<'help> ArgGroup<'help> { /// /// **NOTE:** The names provided may be an argument or group name /// - /// **NOTE:** An argument is considered present when there is a - /// [`Arg::default_value`](crate::Arg::default_value) - /// /// # Examples /// /// ```rust - /// # use clap::{App, Arg, ArgGroup, ErrorKind}; - /// let result = App::new("myprog") + /// # use clap::{Command, Arg, ArgGroup, ErrorKind}; + /// let result = Command::new("myprog") /// .arg(Arg::new("flag") /// .short('f')) /// .arg(Arg::new("color") @@ -348,7 +345,7 @@ impl<'help> ArgGroup<'help> { /// // yet we only used "-d" it's an error /// assert!(result.is_err()); /// let err = result.unwrap_err(); - /// assert_eq!(err.kind, ErrorKind::MissingRequiredArgument); + /// assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument); /// ``` /// [required group]: ArgGroup::required() /// [argument requirement rules]: crate::Arg::requires_all() @@ -368,14 +365,11 @@ impl<'help> ArgGroup<'help> { /// /// **NOTE:** The name provided may be an argument, or group name /// - /// **NOTE:** An argument is considered present when there is a - /// [`Arg::default_value`](crate::Arg::default_value) - /// /// # Examples /// /// ```rust - /// # use clap::{App, Arg, ArgGroup, ErrorKind}; - /// let result = App::new("myprog") + /// # use clap::{Command, Arg, ArgGroup, ErrorKind}; + /// let result = Command::new("myprog") /// .arg(Arg::new("flag") /// .short('f')) /// .arg(Arg::new("color") @@ -389,7 +383,7 @@ impl<'help> ArgGroup<'help> { /// // because we used an arg from the group, and the group conflicts with "-d", it's an error /// assert!(result.is_err()); /// let err = result.unwrap_err(); - /// assert_eq!(err.kind, ErrorKind::ArgumentConflict); + /// assert_eq!(err.kind(), ErrorKind::ArgumentConflict); /// ``` /// [argument exclusion rules]: crate::Arg::conflicts_with() #[must_use] @@ -405,14 +399,11 @@ impl<'help> ArgGroup<'help> { /// /// **NOTE:** The names provided may be an argument, or group name /// - /// **NOTE:** An argument is considered present when there is a - /// [`Arg::default_value`](crate::Arg::default_value) - /// /// # Examples /// /// ```rust - /// # use clap::{App, Arg, ArgGroup, ErrorKind}; - /// let result = App::new("myprog") + /// # use clap::{Command, Arg, ArgGroup, ErrorKind}; + /// let result = Command::new("myprog") /// .arg(Arg::new("flag") /// .short('f')) /// .arg(Arg::new("color") @@ -429,7 +420,7 @@ impl<'help> ArgGroup<'help> { /// // it's an error /// assert!(result.is_err()); /// let err = result.unwrap_err(); - /// assert_eq!(err.kind, ErrorKind::ArgumentConflict); + /// assert_eq!(err.kind(), ErrorKind::ArgumentConflict); /// ``` /// /// [argument exclusion rules]: crate::Arg::conflicts_with_all() @@ -443,6 +434,7 @@ impl<'help> ArgGroup<'help> { /// Deprecated, replaced with [`ArgGroup::new`] #[deprecated(since = "3.0.0", note = "Replaced with `ArgGroup::new`")] + #[doc(hidden)] pub fn with_name>(n: S) -> Self { Self::new(n) } @@ -453,6 +445,7 @@ impl<'help> ArgGroup<'help> { since = "3.0.0", note = "Maybe clap::Parser would fit your use case? (Issue #3087)" )] + #[doc(hidden)] pub fn from_yaml(yaml: &'help Yaml) -> Self { Self::from(yaml) } @@ -510,7 +503,7 @@ impl<'help> From<&'help Yaml> for ArgGroup<'help> { "conflicts_with" => yaml_vec_or_str!(a, v, conflicts_with), "name" => { if let Some(ys) = v.as_str() { - a = a.name(ys); + a = a.id(ys); } a } diff --git a/third_party/rust/clap/src/build/arg_predicate.rs b/third_party/rust/clap/src/build/arg_predicate.rs new file mode 100644 index 000000000000..58eb5494c035 --- /dev/null +++ b/third_party/rust/clap/src/build/arg_predicate.rs @@ -0,0 +1,14 @@ +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub(crate) enum ArgPredicate<'help> { + IsPresent, + Equals(&'help std::ffi::OsStr), +} + +impl<'help> From> for ArgPredicate<'help> { + fn from(other: Option<&'help std::ffi::OsStr>) -> Self { + match other { + Some(other) => Self::Equals(other), + None => Self::IsPresent, + } + } +} diff --git a/third_party/rust/clap/src/build/arg/settings.rs b/third_party/rust/clap/src/build/arg_settings.rs similarity index 63% rename from third_party/rust/clap/src/build/arg/settings.rs rename to third_party/rust/clap/src/build/arg_settings.rs index 048944bb5144..dee79565b9cb 100644 --- a/third_party/rust/clap/src/build/arg/settings.rs +++ b/third_party/rust/clap/src/build/arg_settings.rs @@ -1,3 +1,5 @@ +#![allow(deprecated)] + // Std use std::ops::BitOr; #[cfg(feature = "yaml")] @@ -6,6 +8,9 @@ use std::str::FromStr; // Third party use bitflags::bitflags; +#[allow(unused)] +use crate::Arg; + #[doc(hidden)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct ArgFlags(Flags); @@ -27,11 +32,24 @@ impl Default for ArgFlags { #[derive(Debug, PartialEq, Copy, Clone)] #[non_exhaustive] pub enum ArgSettings { - /// Specifies that an arg must be used + /// Deprecated, replaced with [`Arg::required`] and [`Arg::is_required_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Arg::required` and `Arg::is_required_set`" + )] Required, - /// Allows an arg to accept multiple values + /// Deprecated, replaced with [`Arg::multiple_values`] and [`Arg::is_multiple_values_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Arg::multiple_values` and `Arg::`is_multiple_values_set`" + )] MultipleValues, - /// Allows an arg to appear multiple times + /// Deprecated, replaced with [`Arg::multiple_occurrences`] and + /// [`Arg::is_multiple_occurrences_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Arg::multiple_occurrences` and `Arg::is_multiple_occurrences_set`" + )] MultipleOccurrences, /// Deprecated, see [`ArgSettings::MultipleOccurrences`] (most likely what you want) and /// [`ArgSettings::MultipleValues`] @@ -39,59 +57,139 @@ pub enum ArgSettings { since = "3.0.0", note = "Split into `ArgSettings::MultipleOccurrences` (most likely what you want) and `ArgSettings::MultipleValues`" )] + #[doc(hidden)] Multiple, - /// Forbids an arg from accepting empty values such as `""` + /// Deprecated, replaced with [`Arg::forbid_empty_values`] and + /// [`Arg::is_forbid_empty_values_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Arg::forbid_empty_values` and `Arg::is_forbid_empty_values_set`" + )] ForbidEmptyValues, - /// Sets an arg to be global (i.e. exist in all subcommands) + /// Deprecated, replaced with [`Arg::global`] and [`Arg::is_global_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Arg::global` and `Arg::is_global_set`" + )] Global, - /// Hides an arg from the help message + /// Deprecated, replaced with [`Arg::hide`] and [`Arg::is_hide_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Arg::hide` and `Arg::is_hide_set`" + )] Hidden, - /// Allows an argument to take a value (such as `--option value`) + /// Deprecated, replaced with [`Arg::takes_value`] and [`Arg::is_takes_value_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Arg::takes_value` and `Arg::is_takes_value_set`" + )] TakesValue, - /// Enables a delimiter to break up arguments `--option val1,val2,val3` becomes three values - /// (`val1`, `val2`, and `val3`) instead of the default one (`val1,val2,val3`) + /// Deprecated, replaced with [`Arg::use_value_delimiter`] and + /// [`Arg::is_use_value_delimiter_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Arg::use_value_delimiter` and `Arg::is_use_value_delimiter_set`" + )] UseValueDelimiter, - /// Tells an arg to display it's help on the line below the arg itself in the help message + /// Deprecated, replaced with [`Arg::next_line_help`] and [`Arg::is_next_line_help_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Arg::next_line_help` and `Arg::is_next_line_help_set`" + )] NextLineHelp, - /// Says that arg *must* use a delimiter to separate values + /// Deprecated, replaced with [`Arg::require_value_delimiter`] and + /// [`Arg::is_require_value_delimiter_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Arg::require_value_delimiter` and `Arg::is_require_value_delimiter_set`" + )] RequireDelimiter, - /// Hides the possible values from the help message + /// Deprecated, replaced with [`Arg::hide_possible_values`] and + /// [`Arg::is_hide_possible_values_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Arg::hide_possible_values` and `Arg::is_hide_possible_values_set`" + )] HidePossibleValues, - /// Allows values that start with a hyphen + /// Deprecated, replaced with [`Arg::allow_hyphen_values`] and + /// [`Arg::is_allow_hyphen_values_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Arg::allow_hyphen_values` and `Arg::is_allow_hyphen_values_set`" + )] AllowHyphenValues, /// Deprecated, replaced with [`ArgSettings::AllowHyphenValues`] #[deprecated( since = "3.0.0", note = "Replaced with `ArgSettings::AllowHyphenValues`" )] + #[doc(hidden)] AllowLeadingHyphen, - /// Requires that an equals be used to provide a value to an option such as `--option=value` + /// Deprecated, replaced with [`Arg::require_equals`] and [`Arg::is_require_equals_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Arg::require_equals` and `Arg::is_require_equals_set`" + )] RequireEquals, - /// Says that a positional arg will be the last positional, and requires `--` to be accessed. - /// It can also be accessed early (i.e. before other positionals) by providing `--` + /// Deprecated, replaced with [`Arg::last`] and [`Arg::is_last_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Arg::last` and `Arg::is_last_set`" + )] Last, - /// Hides the default value from the help message + /// Deprecated, replaced with [`Arg::hide_default_value`] and [`Arg::is_hide_default_value_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Arg::hide_default_value` and `Arg::is_hide_default_value_set`" + )] HideDefaultValue, - /// Possible values become case insensitive + /// Deprecated, replaced with [`Arg::ignore_case`] and [`Arg::is_ignore_case_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Arg::ignore_case` and `Arg::is_ignore_case_set`" + )] IgnoreCase, /// Deprecated, replaced with [`ArgSettings::IgnoreCase`] #[deprecated(since = "3.0.0", note = "Replaced with `ArgSettings::IgnoreCase`")] + #[doc(hidden)] CaseInsensitive, - /// Hides environment variable arguments from the help message + /// Deprecated, replaced with [`Arg::hide_env`] and [`Arg::is_hide_env_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Arg::hide_env` and `Arg::is_hide_env_set`" + )] #[cfg(feature = "env")] HideEnv, - /// Hides any values currently assigned to ENV variables in the help message (good for sensitive - /// information) + /// Deprecated, replaced with [`Arg::hide_env_values`] and [`Arg::is_hide_env_values_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Arg::hide_env_values` and `Arg::is_hide_env_values_set`" + )] #[cfg(feature = "env")] HideEnvValues, - /// The argument should **not** be shown in short help text + /// Deprecated, replaced with [`Arg::hide_short_help`] and [`Arg::is_hide_short_help_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Arg::hide_short_help` and `Arg::is_hide_short_help_set`" + )] HiddenShortHelp, - /// The argument should **not** be shown in long help text + /// Deprecated, replaced with [`Arg::hide_long_help`] and [`Arg::is_hide_long_help_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Arg::hide_long_help` and `Arg::is_hide_long_help_set`" + )] HiddenLongHelp, - /// Specifies that option values that are invalid UTF-8 should *not* be treated as an error. + /// Deprecated, replaced with [`Arg::allow_invalid_utf8`] and [`Arg::is_allow_invalid_utf8_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Arg::allow_invalid_utf8` and `Arg::is_allow_invalid_utf8_set`" + )] AllowInvalidUtf8, - /// Specifies that option should exist on its own. - /// Having any other arguments present at runtime is an error. + /// Deprecated, replaced with [`Arg::exclusive`] and [`Arg::is_exclusive_set`] + #[deprecated( + since = "3.1.0", + note = "Replaced with `Arg::exclusive` and `Arg::is_exclusive_set`" + )] Exclusive, } diff --git a/third_party/rust/clap/src/build/app/mod.rs b/third_party/rust/clap/src/build/command.rs similarity index 51% rename from third_party/rust/clap/src/build/app/mod.rs rename to third_party/rust/clap/src/build/command.rs index 366e18307a7d..1ba2629109bc 100644 --- a/third_party/rust/clap/src/build/app/mod.rs +++ b/third_party/rust/clap/src/build/command.rs @@ -1,59 +1,64 @@ -#[cfg(debug_assertions)] -mod debug_asserts; -mod settings; -#[cfg(test)] -mod tests; - -pub use self::settings::{AppFlags, AppSettings}; +#![allow(deprecated)] // Std -use std::{ - collections::HashMap, - env, - ffi::OsString, - fmt, - io::{self, Write}, - ops::Index, - path::Path, -}; +use std::collections::HashMap; +use std::env; +use std::ffi::OsString; +use std::fmt; +use std::io; +use std::ops::Index; +use std::path::Path; // Third Party -use os_str_bytes::RawOsStr; #[cfg(feature = "yaml")] use yaml_rust::Yaml; // Internal -use crate::{ - build::{arg::ArgProvider, Arg, ArgGroup, ArgSettings}, - mkeymap::MKeyMap, - output::{fmt::Colorizer, Help, HelpWriter, Usage}, - parse::{ArgMatcher, ArgMatches, Input, Parser}, - util::{color::ColorChoice, Id, Key}, - Error, ErrorKind, Result as ClapResult, INTERNAL_ERROR_MSG, -}; +use crate::build::app_settings::{AppFlags, AppSettings}; +use crate::build::arg_settings::ArgSettings; +use crate::build::{arg::ArgProvider, Arg, ArgGroup, ArgPredicate}; +use crate::error::ErrorKind; +use crate::error::Result as ClapResult; +use crate::mkeymap::MKeyMap; +use crate::output::fmt::Stream; +use crate::output::{fmt::Colorizer, Help, HelpWriter, Usage}; +use crate::parse::{ArgMatcher, ArgMatches, Parser}; +use crate::util::ChildGraph; +use crate::util::{color::ColorChoice, Id, Key}; +use crate::PossibleValue; +use crate::{Error, INTERNAL_ERROR_MSG}; + +#[cfg(debug_assertions)] +use crate::build::debug_asserts::assert_app; /// Build a command-line interface. /// /// This includes defining arguments, subcommands, parser behavior, and help output. /// Once all configuration is complete, -/// the [`App::get_matches`] family of methods starts the runtime-parsing +/// the [`Command::get_matches`] family of methods starts the runtime-parsing /// process. These methods then return information about the user supplied /// arguments (or lack thereof). /// /// When deriving a [`Parser`][crate::Parser], you can use -/// [`IntoApp::into_app`][crate::IntoApp::into_app] to access the -/// `App`. +/// [`CommandFactory::command`][crate::CommandFactory::command] to access the +/// `Command`. +/// +/// - [Basic API][crate::App#basic-api] +/// - [Application-wide Settings][crate::App#application-wide-settings] +/// - [Command-specific Settings][crate::App#command-specific-settings] +/// - [Subcommand-specific Settings][crate::App#subcommand-specific-settings] +/// - [Reflection][crate::App#reflection] /// /// # Examples /// /// ```no_run -/// # use clap::{App, Arg}; -/// let m = App::new("My Program") +/// # use clap::{Command, Arg}; +/// let m = Command::new("My Program") /// .author("Me, me@mail.com") /// .version("1.0.2") /// .about("Explains in brief what the program does") /// .arg( -/// Arg::new("in_file").index(1) +/// Arg::new("in_file") /// ) /// .after_help("Longer explanation to appear after the options when \ /// displaying the help information from --help or -h") @@ -61,82 +66,95 @@ use crate::{ /// /// // Your program logic starts here... /// ``` -/// [`App::get_matches`]: App::get_matches() -#[derive(Default, Debug, Clone, PartialEq, Eq)] +/// [`App::get_matches`]: Command::get_matches() +pub type Command<'help> = App<'help>; + +/// Deprecated, replaced with [`Command`] +#[deprecated(since = "3.1.0", note = "Replaced with `Command`")] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct App<'help> { - pub(crate) id: Id, - pub(crate) name: String, - pub(crate) long_flag: Option<&'help str>, - pub(crate) short_flag: Option, - pub(crate) bin_name: Option, - pub(crate) author: Option<&'help str>, - pub(crate) version: Option<&'help str>, - pub(crate) long_version: Option<&'help str>, - pub(crate) about: Option<&'help str>, - pub(crate) long_about: Option<&'help str>, - pub(crate) before_help: Option<&'help str>, - pub(crate) before_long_help: Option<&'help str>, - pub(crate) after_help: Option<&'help str>, - pub(crate) after_long_help: Option<&'help str>, - pub(crate) aliases: Vec<(&'help str, bool)>, // (name, visible) - pub(crate) short_flag_aliases: Vec<(char, bool)>, // (name, visible) - pub(crate) long_flag_aliases: Vec<(&'help str, bool)>, // (name, visible) - pub(crate) usage_str: Option<&'help str>, - pub(crate) usage: Option, - pub(crate) help_str: Option<&'help str>, - pub(crate) disp_ord: Option, - pub(crate) term_w: Option, - pub(crate) max_w: Option, - pub(crate) template: Option<&'help str>, - pub(crate) settings: AppFlags, - pub(crate) g_settings: AppFlags, - pub(crate) args: MKeyMap<'help>, - pub(crate) subcommands: Vec>, - pub(crate) replacers: HashMap<&'help str, &'help [&'help str]>, - pub(crate) groups: Vec>, - pub(crate) current_help_heading: Option<&'help str>, - pub(crate) subcommand_value_name: Option<&'help str>, - pub(crate) subcommand_heading: Option<&'help str>, + id: Id, + name: String, + long_flag: Option<&'help str>, + short_flag: Option, + display_name: Option, + bin_name: Option, + author: Option<&'help str>, + version: Option<&'help str>, + long_version: Option<&'help str>, + about: Option<&'help str>, + long_about: Option<&'help str>, + before_help: Option<&'help str>, + before_long_help: Option<&'help str>, + after_help: Option<&'help str>, + after_long_help: Option<&'help str>, + aliases: Vec<(&'help str, bool)>, // (name, visible) + short_flag_aliases: Vec<(char, bool)>, // (name, visible) + long_flag_aliases: Vec<(&'help str, bool)>, // (name, visible) + usage_str: Option<&'help str>, + usage_name: Option, + help_str: Option<&'help str>, + disp_ord: Option, + term_w: Option, + max_w: Option, + template: Option<&'help str>, + settings: AppFlags, + g_settings: AppFlags, + args: MKeyMap<'help>, + subcommands: Vec>, + replacers: HashMap<&'help str, &'help [&'help str]>, + groups: Vec>, + current_help_heading: Option<&'help str>, + current_disp_ord: Option, + subcommand_value_name: Option<&'help str>, + subcommand_heading: Option<&'help str>, } +/// # Basic API impl<'help> App<'help> { - /// Creates a new instance of an `App`. + /// Creates a new instance of an `Command`. /// /// It is common, but not required, to use binary name as the `name`. This /// name will only be displayed to the user when they request to print /// version or help and usage information. /// - /// See also [`app_from_crate!!`](crate::app_from_crate!) and [`crate_name!`](crate::crate_name!). + /// See also [`command!`](crate::command!) and [`crate_name!`](crate::crate_name!). /// /// # Examples /// /// ```no_run - /// # use clap::App; - /// App::new("My Program") + /// # use clap::Command; + /// Command::new("My Program") /// # ; /// ``` pub fn new>(name: S) -> Self { - let name = name.into(); - - App { - id: Id::from(&*name), - name, - ..Default::default() + /// The actual implementation of `new`, non-generic to save code size. + /// + /// If we don't do this rustc will unnecessarily generate multiple versions + /// of this code. + fn new_inner<'help>(name: String) -> App<'help> { + App { + id: Id::from(&*name), + name, + ..Default::default() + } + .arg( + Arg::new("help") + .long("help") + .help("Print help information") + .global(true) + .generated(), + ) + .arg( + Arg::new("version") + .long("version") + .help("Print version information") + .global(true) + .generated(), + ) } - .arg( - Arg::new("help") - .long("help") - .help("Print help information") - .global(true) - .generated(), - ) - .arg( - Arg::new("version") - .long("version") - .help("Print version information") - .global(true) - .generated(), - ) + + new_inner(name.into()) } /// Adds an [argument] to the list of valid possibilities. @@ -144,8 +162,8 @@ impl<'help> App<'help> { /// # Examples /// /// ```no_run - /// # use clap::{App, arg, Arg}; - /// App::new("myprog") + /// # use clap::{Command, arg, Arg}; + /// Command::new("myprog") /// // Adding a single "flag" argument with a short and help text, using Arg::new() /// .arg( /// Arg::new("debug") @@ -163,6 +181,14 @@ impl<'help> App<'help> { #[must_use] pub fn arg>>(mut self, a: A) -> Self { let mut arg = a.into(); + if let Some(current_disp_ord) = self.current_disp_ord.as_mut() { + if !arg.is_positional() && arg.provider != ArgProvider::Generated { + let current = *current_disp_ord; + arg.disp_ord.set_implicit(current); + *current_disp_ord = current + 1; + } + } + arg.help_heading.get_or_insert(self.current_help_heading); self.args.push(arg); self @@ -173,11 +199,11 @@ impl<'help> App<'help> { /// # Examples /// /// ```no_run - /// # use clap::{App, arg, Arg}; - /// App::new("myprog") + /// # use clap::{Command, arg, Arg}; + /// Command::new("myprog") /// .args(&[ /// arg!("[debug] -d 'turns on debugging info'"), - /// Arg::new("input").index(1).help("the input file to use") + /// Arg::new("input").help("the input file to use") /// ]) /// # ; /// ``` @@ -198,28 +224,28 @@ impl<'help> App<'help> { self } - /// Allows one to mutate an [`Arg`] after it's been added to an [`App`]. + /// Allows one to mutate an [`Arg`] after it's been added to a [`Command`]. /// /// This can be useful for modifying the auto-generated help or version arguments. /// /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; + /// # use clap::{Command, Arg}; /// - /// let mut app = App::new("foo") + /// let mut cmd = Command::new("foo") /// .arg(Arg::new("bar") /// .short('b')) /// .mut_arg("bar", |a| a.short('B')); /// - /// let res = app.try_get_matches_from_mut(vec!["foo", "-b"]); + /// let res = cmd.try_get_matches_from_mut(vec!["foo", "-b"]); /// /// // Since we changed `bar`'s short to "B" this should err as there /// // is no `-b` anymore, only `-B` /// /// assert!(res.is_err()); /// - /// let res = app.try_get_matches_from_mut(vec!["foo", "-B"]); + /// let res = cmd.try_get_matches_from_mut(vec!["foo", "-B"]); /// assert!(res.is_ok()); /// ``` #[must_use] @@ -265,8 +291,8 @@ impl<'help> App<'help> { /// of the arguments from the specified group is present at runtime. /// /// ```no_run - /// # use clap::{App, arg, ArgGroup}; - /// App::new("app") + /// # use clap::{Command, arg, ArgGroup}; + /// Command::new("cmd") /// .arg(arg!("--set-ver [ver] 'set the version manually'")) /// .arg(arg!("--major 'auto increase major'")) /// .arg(arg!("--minor 'auto increase minor'")) @@ -283,13 +309,13 @@ impl<'help> App<'help> { self } - /// Adds multiple [`ArgGroup`]s to the [`App`] at once. + /// Adds multiple [`ArgGroup`]s to the [`Command`] at once. /// /// # Examples /// /// ```no_run - /// # use clap::{App, arg, ArgGroup}; - /// App::new("app") + /// # use clap::{Command, arg, ArgGroup}; + /// Command::new("cmd") /// .arg(arg!("--set-ver [ver] 'set the version manually'")) /// .arg(arg!("--major 'auto increase major'")) /// .arg(arg!("--minor 'auto increase minor'")) @@ -319,20 +345,20 @@ impl<'help> App<'help> { /// Adds a subcommand to the list of valid possibilities. /// - /// Subcommands are effectively sub-[`App`]s, because they can contain their own arguments, - /// subcommands, version, usage, etc. They also function just like [`App`]s, in that they get + /// Subcommands are effectively sub-[`Command`]s, because they can contain their own arguments, + /// subcommands, version, usage, etc. They also function just like [`Command`]s, in that they get /// their own auto generated help, version, and usage. /// - /// A subcommand's [`App::name`] will be used for: + /// A subcommand's [`Command::name`] will be used for: /// - The argument the user passes in /// - Programmatically looking up the subcommand /// /// # Examples /// /// ```no_run - /// # use clap::{App, arg}; - /// App::new("myprog") - /// .subcommand(App::new("config") + /// # use clap::{Command, arg}; + /// Command::new("myprog") + /// .subcommand(Command::new("config") /// .about("Controls configuration features") /// .arg(arg!(" 'Required configuration file to use'"))) /// # ; @@ -349,12 +375,12 @@ impl<'help> App<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, Arg, }; - /// # App::new("myprog") + /// # use clap::{Command, Arg, }; + /// # Command::new("myprog") /// .subcommands( vec![ - /// App::new("config").about("Controls configuration functionality") - /// .arg(Arg::new("config_file").index(1)), - /// App::new("debug").about("Controls debug functionality")]) + /// Command::new("config").about("Controls configuration functionality") + /// .arg(Arg::new("config_file")), + /// Command::new("debug").about("Controls debug functionality")]) /// # ; /// ``` /// [`IntoIterator`]: std::iter::IntoIterator @@ -383,20 +409,20 @@ impl<'help> App<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; - /// fn app() -> App<'static> { - /// App::new("foo") + /// # use clap::{Command, Arg}; + /// fn cmd() -> Command<'static> { + /// Command::new("foo") /// .arg(Arg::new("bar").short('b') /// ) /// } /// /// #[test] /// fn verify_app() { - /// app().debug_assert(); + /// cmd().debug_assert(); /// } /// /// fn main() { - /// let m = app().get_matches_from(vec!["foo", "-b"]); + /// let m = cmd().get_matches_from(vec!["foo", "-b"]); /// println!("{}", m.is_present("bar")); /// } /// ``` @@ -409,9 +435,9 @@ impl<'help> App<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, ErrorKind}; - /// let mut app = App::new("myprog"); - /// let err = app.error(ErrorKind::InvalidValue, "Some failure case"); + /// # use clap::{Command, ErrorKind}; + /// let mut cmd = Command::new("myprog"); + /// let err = cmd.error(ErrorKind::InvalidValue, "Some failure case"); /// ``` pub fn error(&mut self, kind: ErrorKind, message: impl std::fmt::Display) -> Error { Error::raw(kind, message).format(self) @@ -426,13 +452,13 @@ impl<'help> App<'help> { /// # Examples /// /// ```no_run - /// # use clap::{App, Arg}; - /// let matches = App::new("myprog") + /// # use clap::{Command, Arg}; + /// let matches = Command::new("myprog") /// // Args and options go here... /// .get_matches(); /// ``` /// [`env::args_os`]: std::env::args_os() - /// [`App::try_get_matches_from_mut`]: App::try_get_matches_from_mut() + /// [`App::try_get_matches_from_mut`]: Command::try_get_matches_from_mut() #[inline] pub fn get_matches(self) -> ArgMatches { self.get_matches_from(&mut env::args_os()) @@ -440,7 +466,7 @@ impl<'help> App<'help> { /// Parse [`env::args_os`], exiting on failure. /// - /// Like [`App::get_matches`] but doesn't consume the `App`. + /// Like [`App::get_matches`] but doesn't consume the `Command`. /// /// # Panics /// @@ -449,14 +475,14 @@ impl<'help> App<'help> { /// # Examples /// /// ```no_run - /// # use clap::{App, Arg}; - /// let mut app = App::new("myprog") + /// # use clap::{Command, Arg}; + /// let mut cmd = Command::new("myprog") /// // Args and options go here... /// ; - /// let matches = app.get_matches_mut(); + /// let matches = cmd.get_matches_mut(); /// ``` /// [`env::args_os`]: std::env::args_os() - /// [`App::get_matches`]: App::get_matches() + /// [`App::get_matches`]: Command::get_matches() pub fn get_matches_mut(&mut self) -> ArgMatches { self.try_get_matches_from_mut(&mut env::args_os()) .unwrap_or_else(|e| e.exit()) @@ -476,8 +502,8 @@ impl<'help> App<'help> { /// # Examples /// /// ```no_run - /// # use clap::{App, Arg}; - /// let matches = App::new("myprog") + /// # use clap::{Command, Arg}; + /// let matches = Command::new("myprog") /// // Args and options go here... /// .try_get_matches() /// .unwrap_or_else(|e| e.exit()); @@ -499,7 +525,7 @@ impl<'help> App<'help> { /// Parse the specified arguments, exiting on failure. /// /// **NOTE:** The first argument will be parsed as the binary name unless - /// [`AppSettings::NoBinaryName`] is used. + /// [`Command::no_binary_name`] is used. /// /// # Panics /// @@ -508,14 +534,14 @@ impl<'help> App<'help> { /// # Examples /// /// ```no_run - /// # use clap::{App, Arg}; + /// # use clap::{Command, Arg}; /// let arg_vec = vec!["my_prog", "some", "args", "to", "parse"]; /// - /// let matches = App::new("myprog") + /// let matches = Command::new("myprog") /// // Args and options go here... /// .get_matches_from(arg_vec); /// ``` - /// [`App::get_matches`]: App::get_matches() + /// [`App::get_matches`]: Command::get_matches() /// [`clap::Result`]: Result /// [`Vec`]: std::vec::Vec pub fn get_matches_from(mut self, itr: I) -> ArgMatches @@ -537,7 +563,7 @@ impl<'help> App<'help> { /// perform a [`std::process::exit`] yourself. /// /// **NOTE:** The first argument will be parsed as the binary name unless - /// [`AppSettings::NoBinaryName`] is used. + /// [`Command::no_binary_name`] is used. /// /// # Panics /// @@ -546,16 +572,16 @@ impl<'help> App<'help> { /// # Examples /// /// ```no_run - /// # use clap::{App, Arg}; + /// # use clap::{Command, Arg}; /// let arg_vec = vec!["my_prog", "some", "args", "to", "parse"]; /// - /// let matches = App::new("myprog") + /// let matches = Command::new("myprog") /// // Args and options go here... /// .try_get_matches_from(arg_vec) /// .unwrap_or_else(|e| e.exit()); /// ``` - /// [`App::get_matches_from`]: App::get_matches_from() - /// [`App::try_get_matches`]: App::try_get_matches() + /// [`App::get_matches_from`]: Command::get_matches_from() + /// [`App::try_get_matches`]: Command::try_get_matches() /// [`Error::exit`]: crate::Error::exit() /// [`std::process::exit`]: std::process::exit() /// [`clap::Error`]: crate::Error @@ -574,7 +600,7 @@ impl<'help> App<'help> { /// Parse the specified arguments, returning a [`clap::Result`] on failure. /// - /// Like [`App::try_get_matches_from`] but doesn't consume the `App`. + /// Like [`App::try_get_matches_from`] but doesn't consume the `Command`. /// /// **NOTE:** This method WILL NOT exit when `--help` or `--version` (or short versions) are /// used. It will return a [`clap::Error`], where the [`kind`] is a [`ErrorKind::DisplayHelp`] @@ -582,7 +608,7 @@ impl<'help> App<'help> { /// perform a [`std::process::exit`] yourself. /// /// **NOTE:** The first argument will be parsed as the binary name unless - /// [`AppSettings::NoBinaryName`] is used. + /// [`Command::no_binary_name`] is used. /// /// # Panics /// @@ -591,15 +617,15 @@ impl<'help> App<'help> { /// # Examples /// /// ```no_run - /// # use clap::{App, Arg}; + /// # use clap::{Command, Arg}; /// let arg_vec = vec!["my_prog", "some", "args", "to", "parse"]; /// - /// let mut app = App::new("myprog"); + /// let mut cmd = Command::new("myprog"); /// // Args and options go here... - /// let matches = app.try_get_matches_from_mut(arg_vec) + /// let matches = cmd.try_get_matches_from_mut(arg_vec) /// .unwrap_or_else(|e| e.exit()); /// ``` - /// [`App::try_get_matches_from`]: App::try_get_matches_from() + /// [`App::try_get_matches_from`]: Command::try_get_matches_from() /// [`clap::Result`]: Result /// [`clap::Error`]: crate::Error /// [`kind`]: crate::Error @@ -608,26 +634,27 @@ impl<'help> App<'help> { I: IntoIterator, T: Into + Clone, { - let mut it = Input::from(itr.into_iter()); + let mut raw_args = clap_lex::RawArgs::new(itr.into_iter()); + let mut cursor = raw_args.cursor(); #[cfg(feature = "unstable-multicall")] if self.settings.is_set(AppSettings::Multicall) { - if let Some((argv0, _)) = it.next() { + if let Some(argv0) = raw_args.next_os(&mut cursor) { let argv0 = Path::new(&argv0); if let Some(command) = argv0.file_stem().and_then(|f| f.to_str()) { // Stop borrowing command so we can get another mut ref to it. let command = command.to_owned(); debug!( - "App::try_get_matches_from_mut: Parsed command {} from argv", + "Command::try_get_matches_from_mut: Parsed command {} from argv", command ); - debug!("App::try_get_matches_from_mut: Reinserting command into arguments so subcommand parser matches it"); - it.insert(&[&command]); - debug!("App::try_get_matches_from_mut: Clearing name and bin_name so that displayed command name starts with applet name"); + debug!("Command::try_get_matches_from_mut: Reinserting command into arguments so subcommand parser matches it"); + raw_args.insert(&cursor, &[&command]); + debug!("Command::try_get_matches_from_mut: Clearing name and bin_name so that displayed command name starts with applet name"); self.name.clear(); self.bin_name = None; - return self._do_parse(&mut it); + return self._do_parse(&mut raw_args, cursor); } } }; @@ -640,7 +667,7 @@ impl<'help> App<'help> { // to display // the full path when displaying help messages and such if !self.settings.is_set(AppSettings::NoBinaryName) { - if let Some((name, _)) = it.next() { + if let Some(name) = raw_args.next_os(&mut cursor) { let p = Path::new(name); if let Some(f) = p.file_name() { @@ -653,107 +680,107 @@ impl<'help> App<'help> { } } - self._do_parse(&mut it) + self._do_parse(&mut raw_args, cursor) } /// Prints the short help message (`-h`) to [`io::stdout()`]. /// - /// See also [`App::print_long_help`]. + /// See also [`Command::print_long_help`]. /// /// # Examples /// /// ```rust - /// # use clap::App; - /// let mut app = App::new("myprog"); - /// app.print_help(); + /// # use clap::Command; + /// let mut cmd = Command::new("myprog"); + /// cmd.print_help(); /// ``` /// [`io::stdout()`]: std::io::stdout() pub fn print_help(&mut self) -> io::Result<()> { - self._build(); + self._build_self(); let color = self.get_color(); - let p = Parser::new(self); - let mut c = Colorizer::new(false, color); - Help::new(HelpWriter::Buffer(&mut c), &p, false).write_help()?; + let mut c = Colorizer::new(Stream::Stdout, color); + let usage = Usage::new(self); + Help::new(HelpWriter::Buffer(&mut c), self, &usage, false).write_help()?; c.print() } /// Prints the long help message (`--help`) to [`io::stdout()`]. /// - /// See also [`App::print_help`]. + /// See also [`Command::print_help`]. /// /// # Examples /// /// ```rust - /// # use clap::App; - /// let mut app = App::new("myprog"); - /// app.print_long_help(); + /// # use clap::Command; + /// let mut cmd = Command::new("myprog"); + /// cmd.print_long_help(); /// ``` /// [`io::stdout()`]: std::io::stdout() /// [`BufWriter`]: std::io::BufWriter /// [`-h` (short)]: Arg::help() /// [`--help` (long)]: Arg::long_help() pub fn print_long_help(&mut self) -> io::Result<()> { - self._build(); + self._build_self(); let color = self.get_color(); - let p = Parser::new(self); - let mut c = Colorizer::new(false, color); - Help::new(HelpWriter::Buffer(&mut c), &p, true).write_help()?; + let mut c = Colorizer::new(Stream::Stdout, color); + let usage = Usage::new(self); + Help::new(HelpWriter::Buffer(&mut c), self, &usage, true).write_help()?; c.print() } /// Writes the short help message (`-h`) to a [`io::Write`] object. /// - /// See also [`App::write_long_help`]. + /// See also [`Command::write_long_help`]. /// /// # Examples /// /// ```rust - /// # use clap::App; + /// # use clap::Command; /// use std::io; - /// let mut app = App::new("myprog"); + /// let mut cmd = Command::new("myprog"); /// let mut out = io::stdout(); - /// app.write_help(&mut out).expect("failed to write to stdout"); + /// cmd.write_help(&mut out).expect("failed to write to stdout"); /// ``` /// [`io::Write`]: std::io::Write /// [`-h` (short)]: Arg::help() /// [`--help` (long)]: Arg::long_help() - pub fn write_help(&mut self, w: &mut W) -> io::Result<()> { - self._build(); + pub fn write_help(&mut self, w: &mut W) -> io::Result<()> { + self._build_self(); - let p = Parser::new(self); - Help::new(HelpWriter::Normal(w), &p, false).write_help()?; + let usage = Usage::new(self); + Help::new(HelpWriter::Normal(w), self, &usage, false).write_help()?; w.flush() } /// Writes the long help message (`--help`) to a [`io::Write`] object. /// - /// See also [`App::write_help`]. + /// See also [`Command::write_help`]. /// /// # Examples /// /// ```rust - /// # use clap::App; + /// # use clap::Command; /// use std::io; - /// let mut app = App::new("myprog"); + /// let mut cmd = Command::new("myprog"); /// let mut out = io::stdout(); - /// app.write_long_help(&mut out).expect("failed to write to stdout"); + /// cmd.write_long_help(&mut out).expect("failed to write to stdout"); /// ``` /// [`io::Write`]: std::io::Write /// [`-h` (short)]: Arg::help() /// [`--help` (long)]: Arg::long_help() - pub fn write_long_help(&mut self, w: &mut W) -> io::Result<()> { - self._build(); + pub fn write_long_help(&mut self, w: &mut W) -> io::Result<()> { + self._build_self(); - let p = Parser::new(self); - Help::new(HelpWriter::Normal(w), &p, true).write_help()?; + let usage = Usage::new(self); + Help::new(HelpWriter::Normal(w), self, &usage, true).write_help()?; w.flush() } /// Version message rendered as if the user ran `-V`. /// - /// See also [`App::render_long_version`]. + /// See also [`Command::render_long_version`]. /// /// ### Coloring /// @@ -762,14 +789,14 @@ impl<'help> App<'help> { /// ### Examples /// /// ```rust - /// # use clap::App; + /// # use clap::Command; /// use std::io; - /// let app = App::new("myprog"); - /// println!("{}", app.render_version()); + /// let cmd = Command::new("myprog"); + /// println!("{}", cmd.render_version()); /// ``` /// [`io::Write`]: std::io::Write - /// [`-V` (short)]: App::version() - /// [`--version` (long)]: App::long_version() + /// [`-V` (short)]: Command::version() + /// [`--version` (long)]: Command::long_version() /// [ANSI escape codes]: https://en.wikipedia.org/wiki/ANSI_escape_code pub fn render_version(&self) -> String { self._render_version(false) @@ -777,7 +804,7 @@ impl<'help> App<'help> { /// Version message rendered as if the user ran `--version`. /// - /// See also [`App::render_version`]. + /// See also [`Command::render_version`]. /// /// ### Coloring /// @@ -786,14 +813,14 @@ impl<'help> App<'help> { /// ### Examples /// /// ```rust - /// # use clap::App; + /// # use clap::Command; /// use std::io; - /// let app = App::new("myprog"); - /// println!("{}", app.render_long_version()); + /// let cmd = Command::new("myprog"); + /// println!("{}", cmd.render_long_version()); /// ``` /// [`io::Write`]: std::io::Write - /// [`-V` (short)]: App::version() - /// [`--version` (long)]: App::long_version() + /// [`-V` (short)]: Command::version() + /// [`--version` (long)]: Command::long_version() /// [ANSI escape codes]: https://en.wikipedia.org/wiki/ANSI_escape_code pub fn render_long_version(&self) -> String { self._render_version(true) @@ -804,37 +831,523 @@ impl<'help> App<'help> { /// ### Examples /// /// ```rust - /// # use clap::App; + /// # use clap::Command; /// use std::io; - /// let mut app = App::new("myprog"); - /// println!("{}", app.render_usage()); + /// let mut cmd = Command::new("myprog"); + /// println!("{}", cmd.render_usage()); /// ``` pub fn render_usage(&mut self) -> String { // If there are global arguments, or settings we need to propagate them down to subcommands // before parsing incase we run into a subcommand - self._build(); + self._build_self(); - let mut parser = Parser::new(self); - parser._build(); - Usage::new(&parser).create_usage_with_title(&[]) + Usage::new(self).create_usage_with_title(&[]) } } -/// App Settings +/// # Application-wide Settings +/// +/// These settings will apply to the top-level command and all subcommands, by default. Some +/// settings can be overridden in subcommands. +impl<'help> App<'help> { + /// Specifies that the parser should not assume the first argument passed is the binary name. + /// + /// This is normally the case when using a "daemon" style mode. For shells / REPLs, see + /// [`Command::multicall`][App::multicall]. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, arg}; + /// let m = Command::new("myprog") + /// .no_binary_name(true) + /// .arg(arg!( ... "commands to run")) + /// .get_matches_from(vec!["command", "set"]); + /// + /// let cmds: Vec<&str> = m.values_of("cmd").unwrap().collect(); + /// assert_eq!(cmds, ["command", "set"]); + /// ``` + /// [`try_get_matches_from_mut`]: crate::Command::try_get_matches_from_mut() + #[inline] + pub fn no_binary_name(self, yes: bool) -> Self { + if yes { + self.global_setting(AppSettings::NoBinaryName) + } else { + self.unset_global_setting(AppSettings::NoBinaryName) + } + } + + /// Try not to fail on parse errors, like missing option values. + /// + /// **Note:** Make sure you apply it as `global_setting` if you want this setting + /// to be propagated to subcommands and sub-subcommands! + /// + /// **NOTE:** This choice is propagated to all child subcommands. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, arg}; + /// let cmd = Command::new("cmd") + /// .ignore_errors(true) + /// .arg(arg!(-c --config "Sets a custom config file").required(false)) + /// .arg(arg!(-x --stuff "Sets a custom stuff file").required(false)) + /// .arg(arg!(f: -f "Flag")); + /// + /// let r = cmd.try_get_matches_from(vec!["cmd", "-c", "file", "-f", "-x"]); + /// + /// assert!(r.is_ok(), "unexpected error: {:?}", r); + /// let m = r.unwrap(); + /// assert_eq!(m.value_of("config"), Some("file")); + /// assert!(m.is_present("f")); + /// assert_eq!(m.value_of("stuff"), None); + /// ``` + #[inline] + pub fn ignore_errors(self, yes: bool) -> Self { + if yes { + self.global_setting(AppSettings::IgnoreErrors) + } else { + self.unset_global_setting(AppSettings::IgnoreErrors) + } + } + + /// Specifies that all arguments override themselves. + /// + /// This is the equivalent to saying the `foo` arg using [`Arg::overrides_with("foo")`] for all + /// defined arguments. + /// + /// **NOTE:** This will not be applied when [`Arg::multiple_occurrences(true)`]. + /// + /// **NOTE:** This choice is propagated to all child subcommands. + /// + /// [`Arg::overrides_with("foo")`]: crate::Arg::overrides_with() + #[inline] + pub fn args_override_self(self, yes: bool) -> Self { + if yes { + self.global_setting(AppSettings::AllArgsOverrideSelf) + } else { + self.unset_global_setting(AppSettings::AllArgsOverrideSelf) + } + } + + /// Disables the automatic delimiting of values after `--` or when [`Command::trailing_var_arg`] + /// was used. + /// + /// **NOTE:** The same thing can be done manually by setting the final positional argument to + /// [`Arg::use_value_delimiter(false)`]. Using this setting is safer, because it's easier to locate + /// when making changes. + /// + /// **NOTE:** This choice is propagated to all child subcommands. + /// + /// # Examples + /// + /// ```no_run + /// # use clap::{Command, Arg}; + /// Command::new("myprog") + /// .dont_delimit_trailing_values(true) + /// .get_matches(); + /// ``` + /// + /// [`Arg::use_value_delimiter(false)`]: crate::Arg::use_value_delimiter() + #[inline] + pub fn dont_delimit_trailing_values(self, yes: bool) -> Self { + if yes { + self.global_setting(AppSettings::DontDelimitTrailingValues) + } else { + self.unset_global_setting(AppSettings::DontDelimitTrailingValues) + } + } + + /// Sets when to color output. + /// + /// **NOTE:** This choice is propagated to all child subcommands. + /// + /// **NOTE:** Default behaviour is [`ColorChoice::Auto`]. + /// + /// # Examples + /// + /// ```no_run + /// # use clap::{Command, ColorChoice}; + /// Command::new("myprog") + /// .color(ColorChoice::Never) + /// .get_matches(); + /// ``` + /// [`ColorChoice::Auto`]: crate::ColorChoice::Auto + #[cfg(feature = "color")] + #[inline] + #[must_use] + pub fn color(self, color: ColorChoice) -> Self { + #![allow(deprecated)] + let cmd = self + .unset_global_setting(AppSettings::ColorAuto) + .unset_global_setting(AppSettings::ColorAlways) + .unset_global_setting(AppSettings::ColorNever); + match color { + ColorChoice::Auto => cmd.global_setting(AppSettings::ColorAuto), + ColorChoice::Always => cmd.global_setting(AppSettings::ColorAlways), + ColorChoice::Never => cmd.global_setting(AppSettings::ColorNever), + } + } + + /// Sets the terminal width at which to wrap help messages. + /// + /// Using `0` will ignore terminal widths and use source formatting. + /// + /// Defaults to current terminal width when `wrap_help` feature flag is enabled. If the flag + /// is disabled or it cannot be determined, the default is 100. + /// + /// **NOTE:** This setting applies globally and *not* on a per-command basis. + /// + /// # Examples + /// + /// ```no_run + /// # use clap::Command; + /// Command::new("myprog") + /// .term_width(80) + /// # ; + /// ``` + #[inline] + #[must_use] + pub fn term_width(mut self, width: usize) -> Self { + self.term_w = Some(width); + self + } + + /// Sets the maximum terminal width at which to wrap help messages. + /// + /// This only applies when setting the current terminal width. See [`Command::term_width`] for + /// more details. + /// + /// Using `0` will ignore terminal widths and use source formatting. + /// + /// **NOTE:** This setting applies globally and *not* on a per-command basis. + /// + /// # Examples + /// + /// ```no_run + /// # use clap::Command; + /// Command::new("myprog") + /// .max_term_width(100) + /// # ; + /// ``` + #[inline] + #[must_use] + pub fn max_term_width(mut self, w: usize) -> Self { + self.max_w = Some(w); + self + } + + /// Disables `-V` and `--version` flag. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, ErrorKind}; + /// let res = Command::new("myprog") + /// .disable_version_flag(true) + /// .try_get_matches_from(vec![ + /// "myprog", "-V" + /// ]); + /// assert!(res.is_err()); + /// assert_eq!(res.unwrap_err().kind(), ErrorKind::UnknownArgument); + /// ``` + #[inline] + pub fn disable_version_flag(self, yes: bool) -> Self { + if yes { + self.global_setting(AppSettings::DisableVersionFlag) + } else { + self.unset_global_setting(AppSettings::DisableVersionFlag) + } + } + + /// Specifies to use the version of the current command for all [`subcommands`]. + /// + /// Defaults to `false`; subcommands have independent version strings from their parents. + /// + /// **Note:** Make sure you apply it as `global_setting` if you want this setting + /// to be propagated to subcommands and sub-subcommands! + /// + /// **NOTE:** This choice is propagated to all child subcommands. + /// + /// # Examples + /// + /// ```no_run + /// # use clap::{Command, Arg}; + /// Command::new("myprog") + /// .version("v1.1") + /// .propagate_version(true) + /// .subcommand(Command::new("test")) + /// .get_matches(); + /// // running `$ myprog test --version` will display + /// // "myprog-test v1.1" + /// ``` + /// + /// [`subcommands`]: crate::Command::subcommand() + #[inline] + pub fn propagate_version(self, yes: bool) -> Self { + if yes { + self.global_setting(AppSettings::PropagateVersion) + } else { + self.unset_global_setting(AppSettings::PropagateVersion) + } + } + + /// Places the help string for all arguments and subcommands on the line after them. + /// + /// **NOTE:** This choice is propagated to all child subcommands. + /// + /// # Examples + /// + /// ```no_run + /// # use clap::{Command, Arg}; + /// Command::new("myprog") + /// .next_line_help(true) + /// .get_matches(); + /// ``` + #[inline] + pub fn next_line_help(self, yes: bool) -> Self { + if yes { + self.global_setting(AppSettings::NextLineHelp) + } else { + self.unset_global_setting(AppSettings::NextLineHelp) + } + } + + /// Disables `-h` and `--help` flag. + /// + /// **NOTE:** This choice is propagated to all child subcommands. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, ErrorKind}; + /// let res = Command::new("myprog") + /// .disable_help_flag(true) + /// .try_get_matches_from(vec![ + /// "myprog", "-h" + /// ]); + /// assert!(res.is_err()); + /// assert_eq!(res.unwrap_err().kind(), ErrorKind::UnknownArgument); + /// ``` + #[inline] + pub fn disable_help_flag(self, yes: bool) -> Self { + if yes { + self.global_setting(AppSettings::DisableHelpFlag) + } else { + self.unset_global_setting(AppSettings::DisableHelpFlag) + } + } + + /// Disables the `help` [`subcommand`]. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, ErrorKind}; + /// let res = Command::new("myprog") + /// .disable_help_subcommand(true) + /// // Normally, creating a subcommand causes a `help` subcommand to automatically + /// // be generated as well + /// .subcommand(Command::new("test")) + /// .try_get_matches_from(vec![ + /// "myprog", "help" + /// ]); + /// assert!(res.is_err()); + /// assert_eq!(res.unwrap_err().kind(), ErrorKind::UnknownArgument); + /// ``` + /// + /// [`subcommand`]: crate::Command::subcommand() + #[inline] + pub fn disable_help_subcommand(self, yes: bool) -> Self { + if yes { + self.global_setting(AppSettings::DisableHelpSubcommand) + } else { + self.unset_global_setting(AppSettings::DisableHelpSubcommand) + } + } + + /// Disables colorized help messages. + /// + /// **NOTE:** This choice is propagated to all child subcommands. + /// + /// # Examples + /// + /// ```no_run + /// # use clap::Command; + /// Command::new("myprog") + /// .disable_colored_help(true) + /// .get_matches(); + /// ``` + #[inline] + pub fn disable_colored_help(self, yes: bool) -> Self { + if yes { + self.global_setting(AppSettings::DisableColoredHelp) + } else { + self.unset_global_setting(AppSettings::DisableColoredHelp) + } + } + + /// Panic if help descriptions are omitted. + /// + /// **NOTE:** When deriving [`Parser`][crate::Parser], you could instead check this at + /// compile-time with `#![deny(missing_docs)]` + /// + /// **NOTE:** This choice is propagated to all child subcommands. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, Arg}; + /// Command::new("myprog") + /// .help_expected(true) + /// .arg( + /// Arg::new("foo").help("It does foo stuff") + /// // As required via `help_expected`, a help message was supplied + /// ) + /// # .get_matches(); + /// ``` + /// + /// # Panics + /// + /// ```rust,no_run + /// # use clap::{Command, Arg}; + /// Command::new("myapp") + /// .help_expected(true) + /// .arg( + /// Arg::new("foo") + /// // Someone forgot to put .about("...") here + /// // Since the setting `help_expected` is activated, this will lead to + /// // a panic (if you are in debug mode) + /// ) + /// # .get_matches(); + ///``` + #[inline] + pub fn help_expected(self, yes: bool) -> Self { + if yes { + self.global_setting(AppSettings::HelpExpected) + } else { + self.unset_global_setting(AppSettings::HelpExpected) + } + } + + /// Disables the automatic collapsing of positional args into `[ARGS]` inside the usage string. + /// + /// **NOTE:** This choice is propagated to all child subcommands. + /// + /// # Examples + /// + /// ```no_run + /// # use clap::{Command, Arg}; + /// Command::new("myprog") + /// .dont_collapse_args_in_usage(true) + /// .get_matches(); + /// ``` + #[inline] + pub fn dont_collapse_args_in_usage(self, yes: bool) -> Self { + if yes { + self.global_setting(AppSettings::DontCollapseArgsInUsage) + } else { + self.unset_global_setting(AppSettings::DontCollapseArgsInUsage) + } + } + + /// Tells `clap` *not* to print possible values when displaying help information. + /// + /// This can be useful if there are many values, or they are explained elsewhere. + /// + /// To set this per argument, see + /// [`Arg::hide_possible_values`][crate::Arg::hide_possible_values]. + /// + /// **NOTE:** This choice is propagated to all child subcommands. + #[inline] + pub fn hide_possible_values(self, yes: bool) -> Self { + if yes { + self.global_setting(AppSettings::HidePossibleValues) + } else { + self.unset_global_setting(AppSettings::HidePossibleValues) + } + } + + /// Allow partial matches of long arguments or their [aliases]. + /// + /// For example, to match an argument named `--test`, one could use `--t`, `--te`, `--tes`, and + /// `--test`. + /// + /// **NOTE:** The match *must not* be ambiguous at all in order to succeed. i.e. to match + /// `--te` to `--test` there could not also be another argument or alias `--temp` because both + /// start with `--te` + /// + /// **NOTE:** This choice is propagated to all child subcommands. + /// + /// [aliases]: crate::Command::aliases() + #[inline] + pub fn infer_long_args(self, yes: bool) -> Self { + if yes { + self.global_setting(AppSettings::InferLongArgs) + } else { + self.unset_global_setting(AppSettings::InferLongArgs) + } + } + + /// Allow partial matches of [subcommand] names and their [aliases]. + /// + /// For example, to match a subcommand named `test`, one could use `t`, `te`, `tes`, and + /// `test`. + /// + /// **NOTE:** The match *must not* be ambiguous at all in order to succeed. i.e. to match `te` + /// to `test` there could not also be a subcommand or alias `temp` because both start with `te` + /// + /// **CAUTION:** This setting can interfere with [positional/free arguments], take care when + /// designing CLIs which allow inferred subcommands and have potential positional/free + /// arguments whose values could start with the same characters as subcommands. If this is the + /// case, it's recommended to use settings such as [`Command::args_conflicts_with_subcommands`] in + /// conjunction with this setting. + /// + /// **NOTE:** This choice is propagated to all child subcommands. + /// + /// # Examples + /// + /// ```no_run + /// # use clap::{Command, Arg}; + /// let m = Command::new("prog") + /// .infer_subcommands(true) + /// .subcommand(Command::new("test")) + /// .get_matches_from(vec![ + /// "prog", "te" + /// ]); + /// assert_eq!(m.subcommand_name(), Some("test")); + /// ``` + /// + /// [subcommand]: crate::Command::subcommand() + /// [positional/free arguments]: crate::Arg::index() + /// [aliases]: crate::Command::aliases() + #[inline] + pub fn infer_subcommands(self, yes: bool) -> Self { + if yes { + self.global_setting(AppSettings::InferSubcommands) + } else { + self.unset_global_setting(AppSettings::InferSubcommands) + } + } +} + +/// # Command-specific Settings +/// +/// These apply only to the current command and are not inherited by subcommands. impl<'help> App<'help> { /// (Re)Sets the program's name. /// - /// See [`App::new`] for more details. + /// See [`Command::new`] for more details. /// /// # Examples /// /// ```ignore - /// # use clap::{App, load_yaml}; - /// let yaml = load_yaml!("app.yaml"); - /// let app = App::from(yaml) + /// # use clap::{Command, load_yaml}; + /// let yaml = load_yaml!("cmd.yaml"); + /// let cmd = Command::from(yaml) /// .name(crate_name!()); /// - /// // continued logic goes here, such as `app.get_matches()` etc. + /// // continued logic goes here, such as `cmd.get_matches()` etc. /// ``` #[must_use] pub fn name>(mut self, name: S) -> Self { @@ -857,8 +1370,8 @@ impl<'help> App<'help> { /// # Examples /// /// ```no_run - /// # use clap::App; - /// App::new("My Program") + /// # use clap::Command; + /// Command::new("My Program") /// .bin_name("my_binary") /// # ; /// ``` @@ -868,6 +1381,22 @@ impl<'help> App<'help> { self } + /// Overrides the runtime-determined display name of the program for help and error messages. + /// + /// # Examples + /// + /// ```no_run + /// # use clap::Command; + /// Command::new("My Program") + /// .display_name("my_program") + /// # ; + /// ``` + #[must_use] + pub fn display_name>(mut self, name: S) -> Self { + self.display_name = Some(name.into()); + self + } + /// Sets the author(s) for the help message. /// /// **Pro-tip:** Use `clap`s convenience macro [`crate_authors!`] to @@ -877,8 +1406,8 @@ impl<'help> App<'help> { /// # Examples /// /// ```no_run - /// # use clap::App; - /// App::new("myprog") + /// # use clap::Command; + /// Command::new("myprog") /// .author("Me, me@mymain.com") /// # ; /// ``` @@ -891,9 +1420,9 @@ impl<'help> App<'help> { /// Sets the program's description for the short help (`-h`). /// - /// If [`App::long_about`] is not specified, this message will be displayed for `--help`. + /// If [`Command::long_about`] is not specified, this message will be displayed for `--help`. /// - /// **NOTE:** Only `App::about` (short format) is used in completion + /// **NOTE:** Only `Command::about` (short format) is used in completion /// script generation in order to be concise. /// /// See also [`crate_description!`](crate::crate_description!). @@ -901,8 +1430,8 @@ impl<'help> App<'help> { /// # Examples /// /// ```no_run - /// # use clap::App; - /// App::new("myprog") + /// # use clap::Command; + /// Command::new("myprog") /// .about("Does really amazing things for great people") /// # ; /// ``` @@ -914,23 +1443,23 @@ impl<'help> App<'help> { /// Sets the program's description for the long help (`--help`). /// - /// If [`App::about`] is not specified, this message will be displayed for `-h`. + /// If [`Command::about`] is not specified, this message will be displayed for `-h`. /// - /// **NOTE:** Only [`App::about`] (short format) is used in completion + /// **NOTE:** Only [`Command::about`] (short format) is used in completion /// script generation in order to be concise. /// /// # Examples /// /// ```no_run - /// # use clap::App; - /// App::new("myprog") + /// # use clap::Command; + /// Command::new("myprog") /// .long_about( /// "Does really amazing things to great people. Now let's talk a little /// more in depth about how this subcommand really works. It may take about /// a few lines of text, but that's ok!") /// # ; /// ``` - /// [`App::about`]: App::about() + /// [`App::about`]: Command::about() #[must_use] pub fn long_about>>(mut self, long_about: O) -> Self { self.long_about = long_about.into(); @@ -942,13 +1471,13 @@ impl<'help> App<'help> { /// This is often used to describe how to use the arguments, caveats to be noted, or license /// and contact information. /// - /// If [`App::after_long_help`] is not specified, this message will be displayed for `--help`. + /// If [`Command::after_long_help`] is not specified, this message will be displayed for `--help`. /// /// # Examples /// /// ```no_run - /// # use clap::App; - /// App::new("myprog") + /// # use clap::Command; + /// Command::new("myprog") /// .after_help("Does really amazing things for great people... but be careful with -R!") /// # ; /// ``` @@ -964,13 +1493,13 @@ impl<'help> App<'help> { /// This is often used to describe how to use the arguments, caveats to be noted, or license /// and contact information. /// - /// If [`App::after_help`] is not specified, this message will be displayed for `-h`. + /// If [`Command::after_help`] is not specified, this message will be displayed for `-h`. /// /// # Examples /// /// ```no_run - /// # use clap::App; - /// App::new("myprog") + /// # use clap::Command; + /// Command::new("myprog") /// .after_long_help("Does really amazing things to great people... but be careful with -R, \ /// like, for real, be careful with this!") /// # ; @@ -985,13 +1514,13 @@ impl<'help> App<'help> { /// /// This is often used for header, copyright, or license information. /// - /// If [`App::before_long_help`] is not specified, this message will be displayed for `--help`. + /// If [`Command::before_long_help`] is not specified, this message will be displayed for `--help`. /// /// # Examples /// /// ```no_run - /// # use clap::App; - /// App::new("myprog") + /// # use clap::Command; + /// Command::new("myprog") /// .before_help("Some info I'd like to appear before the help info") /// # ; /// ``` @@ -1005,13 +1534,13 @@ impl<'help> App<'help> { /// /// This is often used for header, copyright, or license information. /// - /// If [`App::before_help`] is not specified, this message will be displayed for `-h`. + /// If [`Command::before_help`] is not specified, this message will be displayed for `-h`. /// /// # Examples /// /// ```no_run - /// # use clap::App; - /// App::new("myprog") + /// # use clap::Command; + /// Command::new("myprog") /// .before_long_help("Some verbose and long info I'd like to appear before the help info") /// # ; /// ``` @@ -1023,7 +1552,7 @@ impl<'help> App<'help> { /// Sets the version for the short version (`-V`) and help messages. /// - /// If [`App::long_version`] is not specified, this message will be displayed for `--version`. + /// If [`Command::long_version`] is not specified, this message will be displayed for `--version`. /// /// **Pro-tip:** Use `clap`s convenience macro [`crate_version!`] to /// automatically set your application's version to the same thing as your @@ -1032,8 +1561,8 @@ impl<'help> App<'help> { /// # Examples /// /// ```no_run - /// # use clap::App; - /// App::new("myprog") + /// # use clap::Command; + /// Command::new("myprog") /// .version("v0.1.24") /// # ; /// ``` @@ -1046,7 +1575,7 @@ impl<'help> App<'help> { /// Sets the version for the long version (`--version`) and help messages. /// - /// If [`App::version`] is not specified, this message will be displayed for `-V`. + /// If [`Command::version`] is not specified, this message will be displayed for `-V`. /// /// **Pro-tip:** Use `clap`s convenience macro [`crate_version!`] to /// automatically set your application's version to the same thing as your @@ -1055,8 +1584,8 @@ impl<'help> App<'help> { /// # Examples /// /// ```no_run - /// # use clap::App; - /// App::new("myprog") + /// # use clap::Command; + /// Command::new("myprog") /// .long_version( /// "v0.1.24 /// commit: abcdef89726d @@ -1081,8 +1610,8 @@ impl<'help> App<'help> { /// # Examples /// /// ```no_run - /// # use clap::{App, Arg}; - /// App::new("myprog") + /// # use clap::{Command, Arg}; + /// Command::new("myprog") /// .override_usage("myapp [-clDas] ") /// # ; /// ``` @@ -1099,14 +1628,14 @@ impl<'help> App<'help> { /// /// **NOTE:** This **only** replaces the help message for the current /// command, meaning if you are using subcommands, those help messages will - /// still be auto-generated unless you specify a [`App::override_help`] for + /// still be auto-generated unless you specify a [`Command::override_help`] for /// them as well. /// /// # Examples /// /// ```no_run - /// # use clap::{App, Arg}; - /// App::new("myapp") + /// # use clap::{Command, Arg}; + /// Command::new("myapp") /// .override_help("myapp v1.0\n\ /// Does awesome things\n\ /// (C) me@mail.com\n\n\ @@ -1139,13 +1668,14 @@ impl<'help> App<'help> { /// /// Valid tags are: /// + /// * `{name}` - Display name for the (sub-)command. /// * `{bin}` - Binary name. /// * `{version}` - Version number. /// * `{author}` - Author information. /// * `{author-with-newline}` - Author followed by `\n`. /// * `{author-section}` - Author preceded and followed by `\n`. - /// * `{about}` - General description (from [`App::about`] or - /// [`App::long_about`]). + /// * `{about}` - General description (from [`Command::about`] or + /// [`Command::long_about`]). /// * `{about-with-newline}` - About followed by `\n`. /// * `{about-section}` - About preceded and followed by '\n'. /// * `{usage-heading}` - Automatically generated usage heading. @@ -1155,24 +1685,24 @@ impl<'help> App<'help> { /// * `{options}` - Help for options. /// * `{positionals}` - Help for positional arguments. /// * `{subcommands}` - Help for subcommands. - /// * `{after-help}` - Help from [`App::after_help`] or [`App::after_long_help`]. - /// * `{before-help}` - Help from [`App::before_help`] or [`App::before_long_help`]. + /// * `{after-help}` - Help from [`App::after_help`] or [`Command::after_long_help`]. + /// * `{before-help}` - Help from [`App::before_help`] or [`Command::before_long_help`]. /// /// # Examples /// /// ```no_run - /// # use clap::App; - /// App::new("myprog") + /// # use clap::Command; + /// Command::new("myprog") /// .version("1.0") /// .help_template("{bin} ({version}) - {usage}") /// # ; /// ``` - /// [`App::about`]: App::about() - /// [`App::long_about`]: App::long_about() - /// [`App::after_help`]: App::after_help() - /// [`App::after_long_help`]: App::after_long_help() - /// [`App::before_help`]: App::before_help() - /// [`App::before_long_help`]: App::before_long_help() + /// [`App::about`]: Command::about() + /// [`App::long_about`]: Command::long_about() + /// [`App::after_help`]: Command::after_help() + /// [`App::after_long_help`]: Command::after_long_help() + /// [`App::before_help`]: Command::before_help() + /// [`App::before_long_help`]: Command::before_long_help() #[must_use] pub fn help_template>(mut self, s: S) -> Self { self.template = Some(s.into()); @@ -1181,23 +1711,23 @@ impl<'help> App<'help> { /// Apply a setting for the current command or subcommand. /// - /// See [`App::global_setting`] to apply a setting to this command and all subcommands. + /// See [`Command::global_setting`] to apply a setting to this command and all subcommands. /// /// See [`AppSettings`] for a full list of possibilities and examples. /// /// # Examples /// /// ```no_run - /// # use clap::{App, AppSettings}; - /// App::new("myprog") + /// # use clap::{Command, AppSettings}; + /// Command::new("myprog") /// .setting(AppSettings::SubcommandRequired) /// .setting(AppSettings::AllowLeadingHyphen) /// # ; /// ``` /// or /// ```no_run - /// # use clap::{App, AppSettings}; - /// App::new("myprog") + /// # use clap::{Command, AppSettings}; + /// Command::new("myprog") /// .setting(AppSettings::SubcommandRequired | AppSettings::AllowLeadingHyphen) /// # ; /// ``` @@ -1218,16 +1748,16 @@ impl<'help> App<'help> { /// # Examples /// /// ```no_run - /// # use clap::{App, AppSettings}; - /// App::new("myprog") + /// # use clap::{Command, AppSettings}; + /// Command::new("myprog") /// .unset_setting(AppSettings::SubcommandRequired) /// .setting(AppSettings::AllowLeadingHyphen) /// # ; /// ``` /// or /// ```no_run - /// # use clap::{App, AppSettings}; - /// App::new("myprog") + /// # use clap::{Command, AppSettings}; + /// Command::new("myprog") /// .unset_setting(AppSettings::SubcommandRequired | AppSettings::AllowLeadingHyphen) /// # ; /// ``` @@ -1243,15 +1773,15 @@ impl<'help> App<'help> { /// Apply a setting for the current command and all subcommands. /// - /// See [`App::setting`] to apply a setting only to this command. + /// See [`Command::setting`] to apply a setting only to this command. /// /// See [`AppSettings`] for a full list of possibilities and examples. /// /// # Examples /// /// ```no_run - /// # use clap::{App, AppSettings}; - /// App::new("myprog") + /// # use clap::{Command, AppSettings}; + /// Command::new("myprog") /// .global_setting(AppSettings::AllowNegativeNumbers) /// # ; /// ``` @@ -1270,12 +1800,12 @@ impl<'help> App<'help> { /// # Examples /// /// ```no_run - /// # use clap::{App, AppSettings}; - /// App::new("myprog") + /// # use clap::{Command, AppSettings}; + /// Command::new("myprog") /// .unset_global_setting(AppSettings::AllowNegativeNumbers) /// # ; /// ``` - /// [global]: App::global_setting() + /// [global]: Command::global_setting() #[inline] #[must_use] pub fn unset_global_setting(mut self, setting: AppSettings) -> Self { @@ -1284,31 +1814,15 @@ impl<'help> App<'help> { self } - /// Sets when to color output. - /// - /// **NOTE:** This choice is propagated to all child subcommands. - /// - /// **NOTE:** Default behaviour is [`ColorChoice::Auto`]. - /// - /// # Examples - /// - /// ```no_run - /// # use clap::{App, ColorChoice}; - /// App::new("myprog") - /// .color(ColorChoice::Never) - /// .get_matches(); - /// ``` - /// [`ColorChoice::Auto`]: crate::ColorChoice::Auto - #[cfg(feature = "color")] + /// Deprecated, replaced with [`Command::next_help_heading`] #[inline] #[must_use] - pub fn color(self, color: ColorChoice) -> Self { - #[allow(deprecated)] - match color { - ColorChoice::Auto => self.global_setting(AppSettings::ColorAuto), - ColorChoice::Always => self.global_setting(AppSettings::ColorAlways), - ColorChoice::Never => self.global_setting(AppSettings::ColorNever), - } + #[deprecated(since = "3.1.0", note = "Replaced with `App::next_help_heading`")] + pub fn help_heading(self, heading: O) -> Self + where + O: Into>, + { + self.next_help_heading(heading) } /// Set the default section heading for future args. @@ -1318,13 +1832,13 @@ impl<'help> App<'help> { /// This is useful if the default `OPTIONS` or `ARGS` headings are /// not specific enough for one's use case. /// - /// For subcommands, see [`App::subcommand_help_heading`] + /// For subcommands, see [`Command::subcommand_help_heading`] /// - /// [`App::arg`]: App::arg() + /// [`App::arg`]: Command::arg() /// [`Arg::help_heading`]: crate::Arg::help_heading() #[inline] #[must_use] - pub fn help_heading(mut self, heading: O) -> Self + pub fn next_help_heading(mut self, heading: O) -> Self where O: Into>, { @@ -1332,51 +1846,13 @@ impl<'help> App<'help> { self } - /// Sets the terminal width at which to wrap help messages. + /// Change the starting value for assigning future display orders for ags. /// - /// Using `0` will ignore terminal widths and use source formatting. - /// - /// Defaults to current terminal width when `wrap_help` feature flag is enabled. If the flag - /// is disabled or it cannot be determined, the default is 100. - /// - /// **NOTE:** This setting applies globally and *not* on a per-command basis. - /// - /// # Examples - /// - /// ```no_run - /// # use clap::App; - /// App::new("myprog") - /// .term_width(80) - /// # ; - /// ``` + /// This will be used for any arg that hasn't had [`Arg::display_order`] called. #[inline] #[must_use] - pub fn term_width(mut self, width: usize) -> Self { - self.term_w = Some(width); - self - } - - /// Sets the maximum terminal width at which to wrap help messages. - /// - /// This only applies when setting the current terminal width. See [`App::term_width`] for - /// more details. - /// - /// Using `0` will ignore terminal widths and use source formatting. - /// - /// **NOTE:** This setting applies globally and *not* on a per-command basis. - /// - /// # Examples - /// - /// ```no_run - /// # use clap::App; - /// App::new("myprog") - /// .max_term_width(100) - /// # ; - /// ``` - #[inline] - #[must_use] - pub fn max_term_width(mut self, w: usize) -> Self { - self.max_w = Some(w); + pub fn next_display_order(mut self, disp_ord: impl Into>) -> Self { + self.current_disp_ord = disp_ord.into(); self } @@ -1396,27 +1872,27 @@ impl<'help> App<'help> { /// /// We'll start with the "subcommand short" example. In this example, let's /// assume we have a program with a subcommand `module` which can be invoked - /// via `app module`. Now let's also assume `module` also has a subcommand - /// called `install` which can be invoked `app module install`. If for some - /// reason users needed to be able to reach `app module install` via the - /// short-hand `app install`, we'd have several options. + /// via `cmd module`. Now let's also assume `module` also has a subcommand + /// called `install` which can be invoked `cmd module install`. If for some + /// reason users needed to be able to reach `cmd module install` via the + /// short-hand `cmd install`, we'd have several options. /// /// We *could* create another sibling subcommand to `module` called /// `install`, but then we would need to manage another subcommand and manually - /// dispatch to `app module install` handling code. This is error prone and + /// dispatch to `cmd module install` handling code. This is error prone and /// tedious. /// - /// We could instead use [`App::replace`] so that, when the user types `app + /// We could instead use [`Command::replace`] so that, when the user types `cmd /// install`, `clap` will replace `install` with `module install` which will /// end up getting parsed as if the user typed the entire incantation. /// /// ```rust - /// # use clap::App; - /// let m = App::new("app") - /// .subcommand(App::new("module") - /// .subcommand(App::new("install"))) + /// # use clap::Command; + /// let m = Command::new("cmd") + /// .subcommand(Command::new("module") + /// .subcommand(Command::new("install"))) /// .replace("install", &["module", "install"]) - /// .get_matches_from(vec!["app", "install"]); + /// .get_matches_from(vec!["cmd", "install"]); /// /// assert!(m.subcommand_matches("module").is_some()); /// assert!(m.subcommand_matches("module").unwrap().subcommand_matches("install").is_some()); @@ -1427,7 +1903,7 @@ impl<'help> App<'help> { /// Let's assume we have an application with two flags `--save-context` and /// `--save-runtime`. But often users end up needing to do *both* at the /// same time. We can add a third flag `--save-all` which semantically means - /// the same thing as `app --save-context --save-runtime`. To implement that, + /// the same thing as `cmd --save-context --save-runtime`. To implement that, /// we have several options. /// /// We could create this third argument and manually check if that argument @@ -1437,21 +1913,21 @@ impl<'help> App<'help> { /// and we forgot to update that code to *also* check `--save-all` it'd mean /// an error! /// - /// Luckily we can use [`App::replace`] so that when the user types + /// Luckily we can use [`Command::replace`] so that when the user types /// `--save-all`, `clap` will replace that argument with `--save-context /// --save-runtime`, and parsing will continue like normal. Now all our code /// that was originally checking for things like `--save-context` doesn't /// need to change! /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("app") + /// # use clap::{Command, Arg}; + /// let m = Command::new("cmd") /// .arg(Arg::new("save-context") /// .long("save-context")) /// .arg(Arg::new("save-runtime") /// .long("save-runtime")) /// .replace("--save-all", &["--save-context", "--save-runtime"]) - /// .get_matches_from(vec!["app", "--save-all"]); + /// .get_matches_from(vec!["cmd", "--save-all"]); /// /// assert!(m.is_present("save-context")); /// assert!(m.is_present("save-runtime")); @@ -1464,8 +1940,8 @@ impl<'help> App<'help> { /// above to enforce this: /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("app") + /// # use clap::{Command, Arg}; + /// let m = Command::new("cmd") /// .arg(Arg::new("save-context") /// .long("save-context")) /// .arg(Arg::new("save-runtime") @@ -1475,14 +1951,14 @@ impl<'help> App<'help> { /// .takes_value(true) /// .possible_values(["txt", "json"])) /// .replace("--save-all", &["--save-context", "--save-runtime", "--format=json"]) - /// .get_matches_from(vec!["app", "--save-all"]); + /// .get_matches_from(vec!["cmd", "--save-all"]); /// /// assert!(m.is_present("save-context")); /// assert!(m.is_present("save-runtime")); /// assert_eq!(m.value_of("format"), Some("json")); /// ``` /// - /// [`App::replace`]: App::replace() + /// [`App::replace`]: Command::replace() #[inline] #[cfg(feature = "unstable-replace")] #[must_use] @@ -1490,9 +1966,239 @@ impl<'help> App<'help> { self.replacers.insert(name, target); self } + + /// Exit gracefully if no arguments are present (e.g. `$ myprog`). + /// + /// **NOTE:** [`subcommands`] count as arguments + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command}; + /// Command::new("myprog") + /// .arg_required_else_help(true); + /// ``` + /// + /// [`subcommands`]: crate::Command::subcommand() + /// [`Arg::default_value`]: crate::Arg::default_value() + #[inline] + pub fn arg_required_else_help(self, yes: bool) -> Self { + if yes { + self.setting(AppSettings::ArgRequiredElseHelp) + } else { + self.unset_setting(AppSettings::ArgRequiredElseHelp) + } + } + + /// Specifies that leading hyphens are allowed in all argument *values* (e.g. `-10`). + /// + /// Otherwise they will be parsed as another flag or option. See also + /// [`Command::allow_negative_numbers`]. + /// + /// **NOTE:** Use this setting with caution as it silences certain circumstances which would + /// otherwise be an error (such as accidentally forgetting to specify a value for leading + /// option). It is preferred to set this on a per argument basis, via [`Arg::allow_hyphen_values`]. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Arg, Command}; + /// // Imagine you needed to represent negative numbers as well, such as -10 + /// let m = Command::new("nums") + /// .allow_hyphen_values(true) + /// .arg(Arg::new("neg")) + /// .get_matches_from(vec![ + /// "nums", "-20" + /// ]); + /// + /// assert_eq!(m.value_of("neg"), Some("-20")); + /// # ; + /// ``` + /// [`Arg::allow_hyphen_values`]: crate::Arg::allow_hyphen_values() + #[inline] + pub fn allow_hyphen_values(self, yes: bool) -> Self { + if yes { + self.setting(AppSettings::AllowHyphenValues) + } else { + self.unset_setting(AppSettings::AllowHyphenValues) + } + } + + /// Allows negative numbers to pass as values. + /// + /// This is similar to [`Command::allow_hyphen_values`] except that it only allows numbers, + /// all other undefined leading hyphens will fail to parse. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, Arg}; + /// let res = Command::new("myprog") + /// .allow_negative_numbers(true) + /// .arg(Arg::new("num")) + /// .try_get_matches_from(vec![ + /// "myprog", "-20" + /// ]); + /// assert!(res.is_ok()); + /// let m = res.unwrap(); + /// assert_eq!(m.value_of("num").unwrap(), "-20"); + /// ``` + #[inline] + pub fn allow_negative_numbers(self, yes: bool) -> Self { + if yes { + self.setting(AppSettings::AllowNegativeNumbers) + } else { + self.unset_setting(AppSettings::AllowNegativeNumbers) + } + } + + /// Specifies that the final positional argument is a "VarArg" and that `clap` should not + /// attempt to parse any further args. + /// + /// The values of the trailing positional argument will contain all args from itself on. + /// + /// **NOTE:** The final positional argument **must** have [`Arg::multiple_values(true)`] or the usage + /// string equivalent. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, arg}; + /// let m = Command::new("myprog") + /// .trailing_var_arg(true) + /// .arg(arg!( ... "commands to run")) + /// .get_matches_from(vec!["myprog", "arg1", "-r", "val1"]); + /// + /// let trail: Vec<&str> = m.values_of("cmd").unwrap().collect(); + /// assert_eq!(trail, ["arg1", "-r", "val1"]); + /// ``` + /// [`Arg::multiple_values(true)`]: crate::Arg::multiple_values() + pub fn trailing_var_arg(self, yes: bool) -> Self { + if yes { + self.setting(AppSettings::TrailingVarArg) + } else { + self.unset_setting(AppSettings::TrailingVarArg) + } + } + + /// Allows one to implement two styles of CLIs where positionals can be used out of order. + /// + /// The first example is a CLI where the second to last positional argument is optional, but + /// the final positional argument is required. Such as `$ prog [optional] ` where one + /// of the two following usages is allowed: + /// + /// * `$ prog [optional] ` + /// * `$ prog ` + /// + /// This would otherwise not be allowed. This is useful when `[optional]` has a default value. + /// + /// **Note:** when using this style of "missing positionals" the final positional *must* be + /// [required] if `--` will not be used to skip to the final positional argument. + /// + /// **Note:** This style also only allows a single positional argument to be "skipped" without + /// the use of `--`. To skip more than one, see the second example. + /// + /// The second example is when one wants to skip multiple optional positional arguments, and use + /// of the `--` operator is OK (but not required if all arguments will be specified anyways). + /// + /// For example, imagine a CLI which has three positional arguments `[foo] [bar] [baz]...` where + /// `baz` accepts multiple values (similar to man `ARGS...` style training arguments). + /// + /// With this setting the following invocations are posisble: + /// + /// * `$ prog foo bar baz1 baz2 baz3` + /// * `$ prog foo -- baz1 baz2 baz3` + /// * `$ prog -- baz1 baz2 baz3` + /// + /// # Examples + /// + /// Style number one from above: + /// + /// ```rust + /// # use clap::{Command, Arg}; + /// // Assume there is an external subcommand named "subcmd" + /// let m = Command::new("myprog") + /// .allow_missing_positional(true) + /// .arg(Arg::new("arg1")) + /// .arg(Arg::new("arg2") + /// .required(true)) + /// .get_matches_from(vec![ + /// "prog", "other" + /// ]); + /// + /// assert_eq!(m.value_of("arg1"), None); + /// assert_eq!(m.value_of("arg2"), Some("other")); + /// ``` + /// + /// Now the same example, but using a default value for the first optional positional argument + /// + /// ```rust + /// # use clap::{Command, Arg}; + /// // Assume there is an external subcommand named "subcmd" + /// let m = Command::new("myprog") + /// .allow_missing_positional(true) + /// .arg(Arg::new("arg1") + /// .default_value("something")) + /// .arg(Arg::new("arg2") + /// .required(true)) + /// .get_matches_from(vec![ + /// "prog", "other" + /// ]); + /// + /// assert_eq!(m.value_of("arg1"), Some("something")); + /// assert_eq!(m.value_of("arg2"), Some("other")); + /// ``` + /// + /// Style number two from above: + /// + /// ```rust + /// # use clap::{Command, Arg}; + /// // Assume there is an external subcommand named "subcmd" + /// let m = Command::new("myprog") + /// .allow_missing_positional(true) + /// .arg(Arg::new("foo")) + /// .arg(Arg::new("bar")) + /// .arg(Arg::new("baz").takes_value(true).multiple_values(true)) + /// .get_matches_from(vec![ + /// "prog", "foo", "bar", "baz1", "baz2", "baz3" + /// ]); + /// + /// assert_eq!(m.value_of("foo"), Some("foo")); + /// assert_eq!(m.value_of("bar"), Some("bar")); + /// assert_eq!(m.values_of("baz").unwrap().collect::>(), &["baz1", "baz2", "baz3"]); + /// ``` + /// + /// Now nofice if we don't specify `foo` or `baz` but use the `--` operator. + /// + /// ```rust + /// # use clap::{Command, Arg}; + /// // Assume there is an external subcommand named "subcmd" + /// let m = Command::new("myprog") + /// .allow_missing_positional(true) + /// .arg(Arg::new("foo")) + /// .arg(Arg::new("bar")) + /// .arg(Arg::new("baz").takes_value(true).multiple_values(true)) + /// .get_matches_from(vec![ + /// "prog", "--", "baz1", "baz2", "baz3" + /// ]); + /// + /// assert_eq!(m.value_of("foo"), None); + /// assert_eq!(m.value_of("bar"), None); + /// assert_eq!(m.values_of("baz").unwrap().collect::>(), &["baz1", "baz2", "baz3"]); + /// ``` + /// + /// [required]: crate::Arg::required() + #[inline] + pub fn allow_missing_positional(self, yes: bool) -> Self { + if yes { + self.setting(AppSettings::AllowMissingPositional) + } else { + self.unset_setting(AppSettings::AllowMissingPositional) + } + } } -/// Subcommand-specific Settings +/// # Subcommand-specific Settings impl<'help> App<'help> { /// Sets the short version of the subcommand flag without the preceding `-`. /// @@ -1501,10 +2207,10 @@ impl<'help> App<'help> { /// # Examples /// /// ``` - /// # use clap::{App, Arg}; - /// let matches = App::new("pacman") + /// # use clap::{Command, Arg}; + /// let matches = Command::new("pacman") /// .subcommand( - /// App::new("sync").short_flag('S').arg( + /// Command::new("sync").short_flag('S').arg( /// Arg::new("search") /// .short('s') /// .long("search") @@ -1537,10 +2243,10 @@ impl<'help> App<'help> { /// will *not* be stripped (i.e. `sync-file` is allowed). /// /// ``` - /// # use clap::{App, Arg}; - /// let matches = App::new("pacman") + /// # use clap::{Command, Arg}; + /// let matches = Command::new("pacman") /// .subcommand( - /// App::new("sync").long_flag("sync").arg( + /// Command::new("sync").long_flag("sync").arg( /// Arg::new("search") /// .short('s') /// .long("search") @@ -1557,7 +2263,14 @@ impl<'help> App<'help> { /// [`Arg::long`]: Arg::long() #[must_use] pub fn long_flag(mut self, long: &'help str) -> Self { - self.long_flag = Some(long.trim_start_matches(|c| c == '-')); + #[cfg(feature = "unstable-v4")] + { + self.long_flag = Some(long); + } + #[cfg(not(feature = "unstable-v4"))] + { + self.long_flag = Some(long.trim_start_matches(|c| c == '-')); + } self } @@ -1569,7 +2282,7 @@ impl<'help> App<'help> { /// /// **NOTE:** Aliases defined with this method are *hidden* from the help /// message. If you're looking for aliases that will be displayed in the help - /// message, see [`App::visible_alias`]. + /// message, see [`Command::visible_alias`]. /// /// **NOTE:** When using aliases and checking for the existence of a /// particular subcommand within an [`ArgMatches`] struct, one only needs to @@ -1578,14 +2291,14 @@ impl<'help> App<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, Arg, }; - /// let m = App::new("myprog") - /// .subcommand(App::new("test") + /// # use clap::{Command, Arg, }; + /// let m = Command::new("myprog") + /// .subcommand(Command::new("test") /// .alias("do-stuff")) /// .get_matches_from(vec!["myprog", "do-stuff"]); /// assert_eq!(m.subcommand_name(), Some("test")); /// ``` - /// [`App::visible_alias`]: App::visible_alias() + /// [`App::visible_alias`]: Command::visible_alias() #[must_use] pub fn alias>(mut self, name: S) -> Self { self.aliases.push((name.into(), false)); @@ -1601,9 +2314,9 @@ impl<'help> App<'help> { /// # Examples /// /// ```no_run - /// # use clap::{App, Arg, }; - /// let m = App::new("myprog") - /// .subcommand(App::new("test").short_flag('t') + /// # use clap::{Command, Arg, }; + /// let m = Command::new("myprog") + /// .subcommand(Command::new("test").short_flag('t') /// .short_flag_alias('d')) /// .get_matches_from(vec!["myprog", "-d"]); /// assert_eq!(m.subcommand_name(), Some("test")); @@ -1624,9 +2337,9 @@ impl<'help> App<'help> { /// # Examples /// /// ```no_run - /// # use clap::{App, Arg, }; - /// let m = App::new("myprog") - /// .subcommand(App::new("test").long_flag("test") + /// # use clap::{Command, Arg, }; + /// let m = Command::new("myprog") + /// .subcommand(Command::new("test").long_flag("test") /// .long_flag_alias("testing")) /// .get_matches_from(vec!["myprog", "--testing"]); /// assert_eq!(m.subcommand_name(), Some("test")); @@ -1645,7 +2358,7 @@ impl<'help> App<'help> { /// /// **NOTE:** Aliases defined with this method are *hidden* from the help /// message. If looking for aliases that will be displayed in the help - /// message, see [`App::visible_aliases`]. + /// message, see [`Command::visible_aliases`]. /// /// **NOTE:** When using aliases and checking for the existence of a /// particular subcommand within an [`ArgMatches`] struct, one only needs to @@ -1654,18 +2367,17 @@ impl<'help> App<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("myprog") - /// .subcommand(App::new("test") + /// # use clap::{Command, Arg}; + /// let m = Command::new("myprog") + /// .subcommand(Command::new("test") /// .aliases(&["do-stuff", "do-tests", "tests"])) /// .arg(Arg::new("input") /// .help("the file to add") - /// .index(1) /// .required(false)) /// .get_matches_from(vec!["myprog", "do-tests"]); /// assert_eq!(m.subcommand_name(), Some("test")); /// ``` - /// [`App::visible_aliases`]: App::visible_aliases() + /// [`App::visible_aliases`]: Command::visible_aliases() #[must_use] pub fn aliases(mut self, names: &[&'help str]) -> Self { self.aliases.extend(names.iter().map(|n| (*n, false))); @@ -1681,13 +2393,12 @@ impl<'help> App<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, Arg, }; - /// let m = App::new("myprog") - /// .subcommand(App::new("test").short_flag('t') + /// # use clap::{Command, Arg, }; + /// let m = Command::new("myprog") + /// .subcommand(Command::new("test").short_flag('t') /// .short_flag_aliases(&['a', 'b', 'c'])) /// .arg(Arg::new("input") /// .help("the file to add") - /// .index(1) /// .required(false)) /// .get_matches_from(vec!["myprog", "-a"]); /// assert_eq!(m.subcommand_name(), Some("test")); @@ -1710,13 +2421,12 @@ impl<'help> App<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, Arg, }; - /// let m = App::new("myprog") - /// .subcommand(App::new("test").long_flag("test") + /// # use clap::{Command, Arg, }; + /// let m = Command::new("myprog") + /// .subcommand(Command::new("test").long_flag("test") /// .long_flag_aliases(&["testing", "testall", "test_all"])) /// .arg(Arg::new("input") /// .help("the file to add") - /// .index(1) /// .required(false)) /// .get_matches_from(vec!["myprog", "--testing"]); /// assert_eq!(m.subcommand_name(), Some("test")); @@ -1739,7 +2449,7 @@ impl<'help> App<'help> { /// **NOTE:** The alias defined with this method is *visible* from the help /// message and displayed as if it were just another regular subcommand. If /// looking for an alias that will not be displayed in the help message, see - /// [`App::alias`]. + /// [`Command::alias`]. /// /// **NOTE:** When using aliases and checking for the existence of a /// particular subcommand within an [`ArgMatches`] struct, one only needs to @@ -1748,14 +2458,14 @@ impl<'help> App<'help> { /// # Examples /// /// ```no_run - /// # use clap::{App, Arg}; - /// let m = App::new("myprog") - /// .subcommand(App::new("test") + /// # use clap::{Command, Arg}; + /// let m = Command::new("myprog") + /// .subcommand(Command::new("test") /// .visible_alias("do-stuff")) /// .get_matches_from(vec!["myprog", "do-stuff"]); /// assert_eq!(m.subcommand_name(), Some("test")); /// ``` - /// [`App::alias`]: App::alias() + /// [`App::alias`]: Command::alias() #[must_use] pub fn visible_alias>(mut self, name: S) -> Self { self.aliases.push((name.into(), true)); @@ -1768,19 +2478,19 @@ impl<'help> App<'help> { /// and easier than creating multiple hidden subcommands as one only needs to check for the /// existence of this command, and not all variants. /// - /// See also [`App::short_flag_alias`]. + /// See also [`Command::short_flag_alias`]. /// /// # Examples /// /// ```no_run - /// # use clap::{App, Arg, }; - /// let m = App::new("myprog") - /// .subcommand(App::new("test").short_flag('t') + /// # use clap::{Command, Arg, }; + /// let m = Command::new("myprog") + /// .subcommand(Command::new("test").short_flag('t') /// .visible_short_flag_alias('d')) /// .get_matches_from(vec!["myprog", "-d"]); /// assert_eq!(m.subcommand_name(), Some("test")); /// ``` - /// [`App::short_flag_alias`]: App::short_flag_alias() + /// [`App::short_flag_alias`]: Command::short_flag_alias() #[must_use] pub fn visible_short_flag_alias(mut self, name: char) -> Self { assert!(name != '-', "short alias name cannot be `-`"); @@ -1794,19 +2504,19 @@ impl<'help> App<'help> { /// and easier than creating multiple hidden subcommands as one only needs to check for the /// existence of this command, and not all variants. /// - /// See also [`App::long_flag_alias`]. + /// See also [`Command::long_flag_alias`]. /// /// # Examples /// /// ```no_run - /// # use clap::{App, Arg, }; - /// let m = App::new("myprog") - /// .subcommand(App::new("test").long_flag("test") + /// # use clap::{Command, Arg, }; + /// let m = Command::new("myprog") + /// .subcommand(Command::new("test").long_flag("test") /// .visible_long_flag_alias("testing")) /// .get_matches_from(vec!["myprog", "--testing"]); /// assert_eq!(m.subcommand_name(), Some("test")); /// ``` - /// [`App::long_flag_alias`]: App::long_flag_alias() + /// [`App::long_flag_alias`]: Command::long_flag_alias() #[must_use] pub fn visible_long_flag_alias(mut self, name: &'help str) -> Self { self.long_flag_aliases.push((name, true)); @@ -1823,7 +2533,7 @@ impl<'help> App<'help> { /// **NOTE:** The alias defined with this method is *visible* from the help /// message and displayed as if it were just another regular subcommand. If /// looking for an alias that will not be displayed in the help message, see - /// [`App::alias`]. + /// [`Command::alias`]. /// /// **NOTE:** When using aliases, and checking for the existence of a /// particular subcommand within an [`ArgMatches`] struct, one only needs to @@ -1832,14 +2542,14 @@ impl<'help> App<'help> { /// # Examples /// /// ```no_run - /// # use clap::{App, Arg, }; - /// let m = App::new("myprog") - /// .subcommand(App::new("test") + /// # use clap::{Command, Arg, }; + /// let m = Command::new("myprog") + /// .subcommand(Command::new("test") /// .visible_aliases(&["do-stuff", "tests"])) /// .get_matches_from(vec!["myprog", "do-stuff"]); /// assert_eq!(m.subcommand_name(), Some("test")); /// ``` - /// [`App::alias`]: App::alias() + /// [`App::alias`]: Command::alias() #[must_use] pub fn visible_aliases(mut self, names: &[&'help str]) -> Self { self.aliases.extend(names.iter().map(|n| (*n, true))); @@ -1848,19 +2558,19 @@ impl<'help> App<'help> { /// Add aliases, which function as *visible* short flag subcommands. /// - /// See [`App::short_flag_aliases`]. + /// See [`Command::short_flag_aliases`]. /// /// # Examples /// /// ```no_run - /// # use clap::{App, Arg, }; - /// let m = App::new("myprog") - /// .subcommand(App::new("test").short_flag('b') + /// # use clap::{Command, Arg, }; + /// let m = Command::new("myprog") + /// .subcommand(Command::new("test").short_flag('b') /// .visible_short_flag_aliases(&['t'])) /// .get_matches_from(vec!["myprog", "-t"]); /// assert_eq!(m.subcommand_name(), Some("test")); /// ``` - /// [`App::short_flag_aliases`]: App::short_flag_aliases() + /// [`App::short_flag_aliases`]: Command::short_flag_aliases() #[must_use] pub fn visible_short_flag_aliases(mut self, names: &[char]) -> Self { for s in names { @@ -1872,19 +2582,19 @@ impl<'help> App<'help> { /// Add aliases, which function as *visible* long flag subcommands. /// - /// See [`App::long_flag_aliases`]. + /// See [`Command::long_flag_aliases`]. /// /// # Examples /// /// ```no_run - /// # use clap::{App, Arg, }; - /// let m = App::new("myprog") - /// .subcommand(App::new("test").long_flag("test") + /// # use clap::{Command, Arg, }; + /// let m = Command::new("myprog") + /// .subcommand(Command::new("test").long_flag("test") /// .visible_long_flag_aliases(&["testing", "testall", "test_all"])) /// .get_matches_from(vec!["myprog", "--testing"]); /// assert_eq!(m.subcommand_name(), Some("test")); /// ``` - /// [`App::long_flag_aliases`]: App::long_flag_aliases() + /// [`App::long_flag_aliases`]: Command::long_flag_aliases() #[must_use] pub fn visible_long_flag_aliases(mut self, names: &[&'help str]) -> Self { for s in names { @@ -1906,15 +2616,15 @@ impl<'help> App<'help> { /// # Examples /// /// ```rust - /// # use clap::{App, }; - /// let m = App::new("cust-ord") - /// .subcommand(App::new("alpha") // typically subcommands are grouped + /// # use clap::{Command, }; + /// let m = Command::new("cust-ord") + /// .subcommand(Command::new("alpha") // typically subcommands are grouped /// // alphabetically by name. Subcommands /// // without a display_order have a value of /// // 999 and are displayed alphabetically with /// // all other 999 subcommands /// .about("Some help and text")) - /// .subcommand(App::new("beta") + /// .subcommand(Command::new("beta") /// .display_order(1) // In order to force this subcommand to appear *first* /// // all we have to do is give it a value lower than 999. /// // Any other subcommands with a value of 1 will be displayed @@ -1948,18 +2658,431 @@ impl<'help> App<'help> { self } + /// Specifies that this [`subcommand`] should be hidden from help messages + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, Arg}; + /// Command::new("myprog") + /// .subcommand( + /// Command::new("test").hide(true) + /// ) + /// # ; + /// ``` + /// + /// [`subcommand`]: crate::Command::subcommand() + #[inline] + pub fn hide(self, yes: bool) -> Self { + if yes { + self.setting(AppSettings::Hidden) + } else { + self.unset_setting(AppSettings::Hidden) + } + } + + /// If no [`subcommand`] is present at runtime, error and exit gracefully. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, ErrorKind}; + /// let err = Command::new("myprog") + /// .subcommand_required(true) + /// .subcommand(Command::new("test")) + /// .try_get_matches_from(vec![ + /// "myprog", + /// ]); + /// assert!(err.is_err()); + /// assert_eq!(err.unwrap_err().kind(), ErrorKind::MissingSubcommand); + /// # ; + /// ``` + /// + /// [`subcommand`]: crate::Command::subcommand() + pub fn subcommand_required(self, yes: bool) -> Self { + if yes { + self.setting(AppSettings::SubcommandRequired) + } else { + self.unset_setting(AppSettings::SubcommandRequired) + } + } + + /// Assume unexpected positional arguments are a [`subcommand`]. + /// + /// Arguments will be stored in the `""` argument in the [`ArgMatches`] + /// + /// **NOTE:** Use this setting with caution, + /// as a truly unexpected argument (i.e. one that is *NOT* an external subcommand) + /// will **not** cause an error and instead be treated as a potential subcommand. + /// One should check for such cases manually and inform the user appropriately. + /// + /// **NOTE:** A built-in subcommand will be parsed as an external subcommand when escaped with + /// `--`. + /// + /// # Examples + /// + /// ```rust + /// # use clap::Command; + /// // Assume there is an external subcommand named "subcmd" + /// let m = Command::new("myprog") + /// .allow_external_subcommands(true) + /// .get_matches_from(vec![ + /// "myprog", "subcmd", "--option", "value", "-fff", "--flag" + /// ]); + /// + /// // All trailing arguments will be stored under the subcommand's sub-matches using an empty + /// // string argument name + /// match m.subcommand() { + /// Some((external, ext_m)) => { + /// let ext_args: Vec<&str> = ext_m.values_of("").unwrap().collect(); + /// assert_eq!(external, "subcmd"); + /// assert_eq!(ext_args, ["--option", "value", "-fff", "--flag"]); + /// }, + /// _ => {}, + /// } + /// ``` + /// + /// [`subcommand`]: crate::Command::subcommand() + /// [`ArgMatches`]: crate::ArgMatches + /// [`ErrorKind::UnknownArgument`]: crate::ErrorKind::UnknownArgument + pub fn allow_external_subcommands(self, yes: bool) -> Self { + if yes { + self.setting(AppSettings::AllowExternalSubcommands) + } else { + self.unset_setting(AppSettings::AllowExternalSubcommands) + } + } + + /// Specifies that external subcommands that are invalid UTF-8 should *not* be treated as an error. + /// + /// **NOTE:** Using external subcommand argument values with invalid UTF-8 requires using + /// [`ArgMatches::values_of_os`] or [`ArgMatches::values_of_lossy`] for those particular + /// arguments which may contain invalid UTF-8 values + /// + /// **NOTE:** Setting this requires [`Command::allow_external_subcommands`] + /// + /// # Platform Specific + /// + /// Non Windows systems only + /// + /// # Examples + /// + #[cfg_attr(not(unix), doc = " ```ignore")] + #[cfg_attr(unix, doc = " ```")] + /// # use clap::Command; + /// // Assume there is an external subcommand named "subcmd" + /// let m = Command::new("myprog") + /// .allow_invalid_utf8_for_external_subcommands(true) + /// .allow_external_subcommands(true) + /// .get_matches_from(vec![ + /// "myprog", "subcmd", "--option", "value", "-fff", "--flag" + /// ]); + /// + /// // All trailing arguments will be stored under the subcommand's sub-matches using an empty + /// // string argument name + /// match m.subcommand() { + /// Some((external, ext_m)) => { + /// let ext_args: Vec<&std::ffi::OsStr> = ext_m.values_of_os("").unwrap().collect(); + /// assert_eq!(external, "subcmd"); + /// assert_eq!(ext_args, ["--option", "value", "-fff", "--flag"]); + /// }, + /// _ => {}, + /// } + /// ``` + /// + /// [`ArgMatches::values_of_os`]: crate::ArgMatches::values_of_os() + /// [`ArgMatches::values_of_lossy`]: crate::ArgMatches::values_of_lossy() + /// [`subcommands`]: crate::Command::subcommand() + pub fn allow_invalid_utf8_for_external_subcommands(self, yes: bool) -> Self { + if yes { + self.setting(AppSettings::AllowInvalidUtf8ForExternalSubcommands) + } else { + self.unset_setting(AppSettings::AllowInvalidUtf8ForExternalSubcommands) + } + } + + /// Specifies that use of an argument prevents the use of [`subcommands`]. + /// + /// By default `clap` allows arguments between subcommands such + /// as ` [cmd_args] [subcmd_args] [subsubcmd_args]`. + /// + /// This setting disables that functionality and says that arguments can + /// only follow the *final* subcommand. For instance using this setting + /// makes only the following invocations possible: + /// + /// * ` [subsubcmd_args]` + /// * ` [subcmd_args]` + /// * ` [cmd_args]` + /// + /// # Examples + /// + /// ```rust + /// # use clap::Command; + /// Command::new("myprog") + /// .args_conflicts_with_subcommands(true); + /// ``` + /// + /// [`subcommands`]: crate::Command::subcommand() + pub fn args_conflicts_with_subcommands(self, yes: bool) -> Self { + if yes { + self.setting(AppSettings::ArgsNegateSubcommands) + } else { + self.unset_setting(AppSettings::ArgsNegateSubcommands) + } + } + + /// Prevent subcommands from being consumed as an arguments value. + /// + /// By default, if an option taking multiple values is followed by a subcommand, the + /// subcommand will be parsed as another value. + /// + /// ```text + /// cmd --foo val1 val2 subcommand + /// --------- ---------- + /// values another value + /// ``` + /// + /// This setting instructs the parser to stop when encountering a subcommand instead of + /// greedily consuming arguments. + /// + /// ```text + /// cmd --foo val1 val2 subcommand + /// --------- ---------- + /// values subcommand + /// ``` + /// + /// **Note:** Make sure you apply it as `global_setting` if you want this setting + /// to be propagated to subcommands and sub-subcommands! + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, Arg}; + /// let cmd = Command::new("cmd").subcommand(Command::new("sub")).arg( + /// Arg::new("arg") + /// .long("arg") + /// .multiple_values(true) + /// .takes_value(true), + /// ); + /// + /// let matches = cmd + /// .clone() + /// .try_get_matches_from(&["cmd", "--arg", "1", "2", "3", "sub"]) + /// .unwrap(); + /// assert_eq!( + /// matches.values_of("arg").unwrap().collect::>(), + /// &["1", "2", "3", "sub"] + /// ); + /// assert!(matches.subcommand_matches("sub").is_none()); + /// + /// let matches = cmd + /// .subcommand_precedence_over_arg(true) + /// .try_get_matches_from(&["cmd", "--arg", "1", "2", "3", "sub"]) + /// .unwrap(); + /// assert_eq!( + /// matches.values_of("arg").unwrap().collect::>(), + /// &["1", "2", "3"] + /// ); + /// assert!(matches.subcommand_matches("sub").is_some()); + /// ``` + pub fn subcommand_precedence_over_arg(self, yes: bool) -> Self { + if yes { + self.setting(AppSettings::SubcommandPrecedenceOverArg) + } else { + self.unset_setting(AppSettings::SubcommandPrecedenceOverArg) + } + } + + /// Allows [`subcommands`] to override all requirements of the parent command. + /// + /// For example, if you had a subcommand or top level application with a required argument + /// that is only required as long as there is no subcommand present, + /// using this setting would allow you to set those arguments to [`Arg::required(true)`] + /// and yet receive no error so long as the user uses a valid subcommand instead. + /// + /// **NOTE:** This defaults to false (using subcommand does *not* negate requirements) + /// + /// # Examples + /// + /// This first example shows that it is an error to not use a required argument + /// + /// ```rust + /// # use clap::{Command, Arg, ErrorKind}; + /// let err = Command::new("myprog") + /// .subcommand_negates_reqs(true) + /// .arg(Arg::new("opt").required(true)) + /// .subcommand(Command::new("test")) + /// .try_get_matches_from(vec![ + /// "myprog" + /// ]); + /// assert!(err.is_err()); + /// assert_eq!(err.unwrap_err().kind(), ErrorKind::MissingRequiredArgument); + /// # ; + /// ``` + /// + /// This next example shows that it is no longer error to not use a required argument if a + /// valid subcommand is used. + /// + /// ```rust + /// # use clap::{Command, Arg, ErrorKind}; + /// let noerr = Command::new("myprog") + /// .subcommand_negates_reqs(true) + /// .arg(Arg::new("opt").required(true)) + /// .subcommand(Command::new("test")) + /// .try_get_matches_from(vec![ + /// "myprog", "test" + /// ]); + /// assert!(noerr.is_ok()); + /// # ; + /// ``` + /// + /// [`Arg::required(true)`]: crate::Arg::required() + /// [`subcommands`]: crate::Command::subcommand() + pub fn subcommand_negates_reqs(self, yes: bool) -> Self { + if yes { + self.setting(AppSettings::SubcommandsNegateReqs) + } else { + self.unset_setting(AppSettings::SubcommandsNegateReqs) + } + } + + /// Multiple-personality program dispatched on the binary name (`argv[0]`) + /// + /// A "multicall" executable is a single executable + /// that contains a variety of applets, + /// and decides which applet to run based on the name of the file. + /// The executable can be called from different names by creating hard links + /// or symbolic links to it. + /// + /// This is desirable for: + /// - Easy distribution, a single binary that can install hardlinks to access the different + /// personalities. + /// - Minimal binary size by sharing common code (e.g. standard library, clap) + /// - Custom shells or REPLs where there isn't a single top-level command + /// + /// Setting `multicall` will cause + /// - `argv[0]` to be stripped to the base name and parsed as the first argument, as if + /// [`Command::no_binary_name`][App::no_binary_name] was set. + /// - Help and errors to report subcommands as if they were the top-level command + /// + /// When the subcommand is not present, there are several strategies you may employ, depending + /// on your needs: + /// - Let the error percolate up normally + /// - Print a specialized error message using the + /// [`Error::context`][crate::Error::context] + /// - Print the [help][App::write_help] but this might be ambiguous + /// - Disable `multicall` and re-parse it + /// - Disable `multicall` and re-parse it with a specific subcommand + /// + /// When detecting the error condition, the [`ErrorKind`] isn't sufficient as a sub-subcommand + /// might report the same error. Enable + /// [`allow_external_subcommands`][App::allow_external_subcommands] if you want to specifically + /// get the unrecognized binary name. + /// + /// **NOTE:** Multicall can't be used with [`no_binary_name`] since they interpret + /// the command name in incompatible ways. + /// + /// **NOTE:** The multicall command cannot have arguments. + /// + /// **NOTE:** Applets are slightly semantically different from subcommands, + /// so it's recommended to use [`Command::subcommand_help_heading`] and + /// [`Command::subcommand_value_name`] to change the descriptive text as above. + /// + /// # Examples + /// + /// `hostname` is an example of a multicall executable. + /// Both `hostname` and `dnsdomainname` are provided by the same executable + /// and which behaviour to use is based on the executable file name. + /// + /// This is desirable when the executable has a primary purpose + /// but there is related functionality that would be convenient to provide + /// and implement it to be in the same executable. + /// + /// The name of the cmd is essentially unused + /// and may be the same as the name of a subcommand. + /// + /// The names of the immediate subcommands of the Command + /// are matched against the basename of the first argument, + /// which is conventionally the path of the executable. + /// + /// This does not allow the subcommand to be passed as the first non-path argument. + /// + /// ```rust + /// # use clap::{Command, ErrorKind}; + /// let mut cmd = Command::new("hostname") + /// .multicall(true) + /// .subcommand(Command::new("hostname")) + /// .subcommand(Command::new("dnsdomainname")); + /// let m = cmd.try_get_matches_from_mut(&["/usr/bin/hostname", "dnsdomainname"]); + /// assert!(m.is_err()); + /// assert_eq!(m.unwrap_err().kind(), ErrorKind::UnknownArgument); + /// let m = cmd.get_matches_from(&["/usr/bin/dnsdomainname"]); + /// assert_eq!(m.subcommand_name(), Some("dnsdomainname")); + /// ``` + /// + /// Busybox is another common example of a multicall executable + /// with a subcommmand for each applet that can be run directly, + /// e.g. with the `cat` applet being run by running `busybox cat`, + /// or with `cat` as a link to the `busybox` binary. + /// + /// This is desirable when the launcher program has additional options + /// or it is useful to run the applet without installing a symlink + /// e.g. to test the applet without installing it + /// or there may already be a command of that name installed. + /// + /// To make an applet usable as both a multicall link and a subcommand + /// the subcommands must be defined both in the top-level Command + /// and as subcommands of the "main" applet. + /// + /// ```rust + /// # use clap::Command; + /// fn applet_commands() -> [Command<'static>; 2] { + /// [Command::new("true"), Command::new("false")] + /// } + /// let mut cmd = Command::new("busybox") + /// .multicall(true) + /// .subcommand( + /// Command::new("busybox") + /// .subcommand_value_name("APPLET") + /// .subcommand_help_heading("APPLETS") + /// .subcommands(applet_commands()), + /// ) + /// .subcommands(applet_commands()); + /// // When called from the executable's canonical name + /// // its applets can be matched as subcommands. + /// let m = cmd.try_get_matches_from_mut(&["/usr/bin/busybox", "true"]).unwrap(); + /// assert_eq!(m.subcommand_name(), Some("busybox")); + /// assert_eq!(m.subcommand().unwrap().1.subcommand_name(), Some("true")); + /// // When called from a link named after an applet that applet is matched. + /// let m = cmd.get_matches_from(&["/usr/bin/true"]); + /// assert_eq!(m.subcommand_name(), Some("true")); + /// ``` + /// + /// [`no_binary_name`]: crate::Command::no_binary_name + /// [`App::subcommand_value_name`]: crate::Command::subcommand_value_name + /// [`App::subcommand_help_heading`]: crate::Command::subcommand_help_heading + #[inline] + #[cfg(feature = "unstable-multicall")] + pub fn multicall(self, yes: bool) -> Self { + if yes { + self.setting(AppSettings::Multicall) + } else { + self.unset_setting(AppSettings::Multicall) + } + } + /// Sets the value name used for subcommands when printing usage and help. /// /// By default, this is "SUBCOMMAND". /// - /// See also [`App::subcommand_help_heading`] + /// See also [`Command::subcommand_help_heading`] /// /// # Examples /// /// ```no_run - /// # use clap::{App, Arg}; - /// App::new("myprog") - /// .subcommand(App::new("sub1")) + /// # use clap::{Command, Arg}; + /// Command::new("myprog") + /// .subcommand(Command::new("sub1")) /// .print_help() /// # ; /// ``` @@ -1984,9 +3107,9 @@ impl<'help> App<'help> { /// but usage of `subcommand_value_name` /// /// ```no_run - /// # use clap::{App, Arg}; - /// App::new("myprog") - /// .subcommand(App::new("sub1")) + /// # use clap::{Command, Arg}; + /// Command::new("myprog") + /// .subcommand(Command::new("sub1")) /// .subcommand_value_name("THING") /// .print_help() /// # ; @@ -2021,14 +3144,14 @@ impl<'help> App<'help> { /// /// By default, this is "SUBCOMMANDS". /// - /// See also [`App::subcommand_value_name`] + /// See also [`Command::subcommand_value_name`] /// /// # Examples /// /// ```no_run - /// # use clap::{App, Arg}; - /// App::new("myprog") - /// .subcommand(App::new("sub1")) + /// # use clap::{Command, Arg}; + /// Command::new("myprog") + /// .subcommand(Command::new("sub1")) /// .print_help() /// # ; /// ``` @@ -2053,9 +3176,9 @@ impl<'help> App<'help> { /// but usage of `subcommand_help_heading` /// /// ```no_run - /// # use clap::{App, Arg}; - /// App::new("myprog") - /// .subcommand(App::new("sub1")) + /// # use clap::{Command, Arg}; + /// Command::new("myprog") + /// .subcommand(Command::new("sub1")) /// .subcommand_help_heading("THINGS") /// .print_help() /// # ; @@ -2087,8 +3210,19 @@ impl<'help> App<'help> { } } -/// Reflection +/// # Reflection impl<'help> App<'help> { + #[inline] + pub(crate) fn get_usage_name(&self) -> Option<&str> { + self.usage_name.as_deref() + } + + /// Get the name of the binary. + #[inline] + pub fn get_display_name(&self) -> Option<&str> { + self.display_name.as_deref() + } + /// Get the name of the binary. #[inline] pub fn get_bin_name(&self) -> Option<&str> { @@ -2100,12 +3234,30 @@ impl<'help> App<'help> { self.bin_name = Some(name.into()); } - /// Get the name of the app. + /// Get the name of the cmd. #[inline] pub fn get_name(&self) -> &str { &self.name } + /// Get the version of the cmd. + #[inline] + pub fn get_version(&self) -> Option<&'help str> { + self.version + } + + /// Get the long version of the cmd. + #[inline] + pub fn get_long_version(&self) -> Option<&'help str> { + self.long_version + } + + /// Get the authors of the cmd. + #[inline] + pub fn get_author(&self) -> Option<&'help str> { + self.author + } + /// Get the short flag of the subcommand. #[inline] pub fn get_short_flag(&self) -> Option { @@ -2118,27 +3270,34 @@ impl<'help> App<'help> { self.long_flag } - /// Get the help message specified via [`App::about`]. + /// Get the help message specified via [`Command::about`]. /// - /// [`App::about`]: App::about() + /// [`App::about`]: Command::about() #[inline] pub fn get_about(&self) -> Option<&'help str> { self.about } - /// Get the help message specified via [`App::long_about`]. + /// Get the help message specified via [`Command::long_about`]. /// - /// [`App::long_about`]: App::long_about() + /// [`App::long_about`]: Command::long_about() #[inline] pub fn get_long_about(&self) -> Option<&'help str> { self.long_about } - /// Get the custom section heading specified via [`App::help_heading`]. - /// - /// [`App::help_heading`]: App::help_heading() + /// Deprecated, replaced with [`Command::get_next_help_heading`] #[inline] + #[deprecated(since = "3.1.0", note = "Replaced with `App::get_next_help_heading`")] pub fn get_help_heading(&self) -> Option<&'help str> { + self.get_next_help_heading() + } + + /// Get the custom section heading specified via [`Command::help_heading`]. + /// + /// [`App::help_heading`]: Command::help_heading() + #[inline] + pub fn get_next_help_heading(&self) -> Option<&'help str> { self.current_help_heading } @@ -2184,21 +3343,21 @@ impl<'help> App<'help> { self.long_flag_aliases.iter().map(|a| a.0) } - /// Check if the given [`AppSettings`] variant is currently set on the `App`. + /// Check if the given [`AppSettings`] variant is currently set on the `Command`. /// /// This checks both [local] and [global settings]. /// - /// [local]: App::setting() - /// [global settings]: App::global_setting() + /// [local]: Command::setting() + /// [global settings]: Command::global_setting() #[inline] pub fn is_set(&self, s: AppSettings) -> bool { self.settings.is_set(s) || self.g_settings.is_set(s) } /// Should we color the output? - #[inline] + #[inline(never)] pub fn get_color(&self) -> ColorChoice { - debug!("App::color: Color setting..."); + debug!("Command::color: Color setting..."); if cfg!(feature = "color") { #[allow(deprecated)] @@ -2229,12 +3388,58 @@ impl<'help> App<'help> { self.subcommands.iter_mut() } - /// Returns `true` if this `App` has subcommands. + /// Returns `true` if this `Command` has subcommands. #[inline] pub fn has_subcommands(&self) -> bool { !self.subcommands.is_empty() } + /// Returns the help heading for listing subcommands. + #[inline] + pub fn get_subcommand_help_heading(&self) -> Option<&str> { + self.subcommand_heading + } + + /// Deprecated, replaced with [`App::get_subcommand_help_heading`] + #[inline] + #[deprecated( + since = "3.1.0", + note = "Replaced with `App::get_subcommand_help_heading`" + )] + pub fn get_subommand_help_heading(&self) -> Option<&str> { + self.get_subcommand_help_heading() + } + + /// Returns the subcommand value name. + #[inline] + pub fn get_subcommand_value_name(&self) -> Option<&str> { + self.subcommand_value_name + } + + /// Returns the help heading for listing subcommands. + #[inline] + pub fn get_before_help(&self) -> Option<&str> { + self.before_help + } + + /// Returns the help heading for listing subcommands. + #[inline] + pub fn get_before_long_help(&self) -> Option<&str> { + self.before_long_help + } + + /// Returns the help heading for listing subcommands. + #[inline] + pub fn get_after_help(&self) -> Option<&str> { + self.after_help + } + + /// Returns the help heading for listing subcommands. + #[inline] + pub fn get_after_long_help(&self) -> Option<&str> { + self.after_long_help + } + /// Find subcommand such that its name or one of aliases equals `name`. /// /// This does not recurse through subcommands of subcommands. @@ -2258,6 +3463,12 @@ impl<'help> App<'help> { self.get_subcommands_mut().find(|s| s.aliases_to(name)) } + /// Iterate through the set of groups. + #[inline] + pub fn get_groups(&self) -> impl Iterator> { + self.groups.iter() + } + /// Iterate through the set of arguments. #[inline] pub fn get_arguments(&self) -> impl Iterator> { @@ -2273,7 +3484,7 @@ impl<'help> App<'help> { /// Iterate through the *options*. pub fn get_opts(&self) -> impl Iterator> { self.get_arguments() - .filter(|a| a.is_set(ArgSettings::TakesValue) && !a.is_positional()) + .filter(|a| a.is_takes_value_set() && !a.is_positional()) } /// Get a list of all arguments the given argument conflicts with. @@ -2284,18 +3495,18 @@ impl<'help> App<'help> { /// ### Panics /// /// If the given arg contains a conflict with an argument that is unknown to - /// this `App`. + /// this `Command`. pub fn get_arg_conflicts_with(&self, arg: &Arg) -> Vec<&Arg<'help>> // FIXME: This could probably have been an iterator { - if arg.get_global() { + if arg.is_global_set() { self.get_global_arg_conflicts_with(arg) } else { arg.blacklist .iter() .map(|id| { self.args.args().find(|arg| arg.id == *id).expect( - "App::get_arg_conflicts_with: \ - The passed arg conflicts with an arg unknown to the app", + "Command::get_arg_conflicts_with: \ + The passed arg conflicts with an arg unknown to the cmd", ) }) .collect() @@ -2325,8 +3536,8 @@ impl<'help> App<'help> { ) .find(|arg| arg.id == *id) .expect( - "App::get_arg_conflicts_with: \ - The passed arg conflicts with an arg unknown to the app", + "Command::get_arg_conflicts_with: \ + The passed arg conflicts with an arg unknown to the cmd", ) }) .collect() @@ -2355,6 +3566,143 @@ impl<'help> App<'help> { } vec } + + /// Report whether [`Command::no_binary_name`] is set + pub fn is_no_binary_name_set(&self) -> bool { + self.is_set(AppSettings::NoBinaryName) + } + + /// Report whether [`Command::ignore_errors`] is set + pub(crate) fn is_ignore_errors_set(&self) -> bool { + self.is_set(AppSettings::IgnoreErrors) + } + + /// Report whether [`Command::dont_delimit_trailing_values`] is set + pub fn is_dont_delimit_trailing_values_set(&self) -> bool { + self.is_set(AppSettings::DontDelimitTrailingValues) + } + + /// Report whether [`Command::disable_version_flag`] is set + pub fn is_disable_version_flag_set(&self) -> bool { + self.is_set(AppSettings::DisableVersionFlag) + } + + /// Report whether [`Command::propagate_version`] is set + pub fn is_propagate_version_set(&self) -> bool { + self.is_set(AppSettings::PropagateVersion) + } + + /// Report whether [`Command::next_line_help`] is set + pub fn is_next_line_help_set(&self) -> bool { + self.is_set(AppSettings::NextLineHelp) + } + + /// Report whether [`Command::disable_help_flag`] is set + pub fn is_disable_help_flag_set(&self) -> bool { + self.is_set(AppSettings::DisableHelpFlag) + } + + /// Report whether [`Command::disable_help_subcommand`] is set + pub fn is_disable_help_subcommand_set(&self) -> bool { + self.is_set(AppSettings::DisableHelpSubcommand) + } + + /// Report whether [`Command::disable_colored_help`] is set + pub fn is_disable_colored_help_set(&self) -> bool { + self.is_set(AppSettings::DisableColoredHelp) + } + + /// Report whether [`Command::help_expected`] is set + #[cfg(debug_assertions)] + pub(crate) fn is_help_expected_set(&self) -> bool { + self.is_set(AppSettings::HelpExpected) + } + + /// Report whether [`Command::dont_collapse_args_in_usage`] is set + pub fn is_dont_collapse_args_in_usage_set(&self) -> bool { + self.is_set(AppSettings::DontCollapseArgsInUsage) + } + + /// Report whether [`Command::infer_long_args`] is set + pub(crate) fn is_infer_long_args_set(&self) -> bool { + self.is_set(AppSettings::InferLongArgs) + } + + /// Report whether [`Command::infer_subcommands`] is set + pub(crate) fn is_infer_subcommands_set(&self) -> bool { + self.is_set(AppSettings::InferSubcommands) + } + + /// Report whether [`Command::arg_required_else_help`] is set + pub fn is_arg_required_else_help_set(&self) -> bool { + self.is_set(AppSettings::ArgRequiredElseHelp) + } + + /// Report whether [`Command::allow_hyphen_values`] is set + pub(crate) fn is_allow_hyphen_values_set(&self) -> bool { + self.is_set(AppSettings::AllowHyphenValues) + } + + /// Report whether [`Command::allow_negative_numbers`] is set + pub fn is_allow_negative_numbers_set(&self) -> bool { + self.is_set(AppSettings::AllowNegativeNumbers) + } + + /// Report whether [`Command::trailing_var_arg`] is set + pub fn is_trailing_var_arg_set(&self) -> bool { + self.is_set(AppSettings::TrailingVarArg) + } + + /// Report whether [`Command::allow_missing_positional`] is set + pub fn is_allow_missing_positional_set(&self) -> bool { + self.is_set(AppSettings::AllowMissingPositional) + } + + /// Report whether [`Command::hide`] is set + pub fn is_hide_set(&self) -> bool { + self.is_set(AppSettings::Hidden) + } + + /// Report whether [`Command::subcommand_required`] is set + pub fn is_subcommand_required_set(&self) -> bool { + self.is_set(AppSettings::SubcommandRequired) + } + + /// Report whether [`Command::allow_external_subcommands`] is set + pub fn is_allow_external_subcommands_set(&self) -> bool { + self.is_set(AppSettings::AllowExternalSubcommands) + } + + /// Report whether [`Command::allow_invalid_utf8_for_external_subcommands`] is set + pub fn is_allow_invalid_utf8_for_external_subcommands_set(&self) -> bool { + self.is_set(AppSettings::AllowInvalidUtf8ForExternalSubcommands) + } + + /// Report whether [`Command::args_conflicts_with_subcommands`] is set + pub fn is_args_conflicts_with_subcommands_set(&self) -> bool { + self.is_set(AppSettings::ArgsNegateSubcommands) + } + + /// Report whether [`Command::subcommand_precedence_over_arg`] is set + pub fn is_subcommand_precedence_over_arg_set(&self) -> bool { + self.is_set(AppSettings::SubcommandPrecedenceOverArg) + } + + /// Report whether [`Command::subcommand_negates_reqs`] is set + pub fn is_subcommand_negates_reqs_set(&self) -> bool { + self.is_set(AppSettings::SubcommandsNegateReqs) + } + + /// Report whether [`Command::multicall`] is set + #[cfg(feature = "unstable-multicall")] + pub fn is_multicall_set(&self) -> bool { + self.is_set(AppSettings::Multicall) + } + + #[cfg(not(feature = "unstable-multicall"))] + fn is_multicall_set(&self) -> bool { + false + } } /// Deprecated @@ -2365,12 +3713,13 @@ impl<'help> App<'help> { since = "3.0.0", note = "Deprecated in Issue #3087, maybe clap::Parser would fit your use case?" )] + #[doc(hidden)] pub fn from_yaml(y: &'help Yaml) -> Self { #![allow(deprecated)] let yaml_file_hash = y.as_hash().expect("YAML file must be a hash"); // We WANT this to panic on error...so expect() is good. let (mut a, yaml, err) = if let Some(name) = y["name"].as_str() { - (App::new(name), yaml_file_hash, "app".into()) + (App::new(name), yaml_file_hash, "cmd".into()) } else { let (name_yaml, value_yaml) = yaml_file_hash .iter() @@ -2450,57 +3799,65 @@ impl<'help> App<'help> { a } - /// Deprecated, replaced with [`App::override_usage`] + /// Deprecated, replaced with [`Command::override_usage`] #[deprecated(since = "3.0.0", note = "Replaced with `App::override_usage`")] + #[doc(hidden)] #[must_use] pub fn usage>(self, usage: S) -> Self { self.override_usage(usage) } - /// Deprecated, replaced with [`App::override_help`] + /// Deprecated, replaced with [`Command::override_help`] #[deprecated(since = "3.0.0", note = "Replaced with `App::override_help`")] + #[doc(hidden)] #[must_use] pub fn help>(self, help: S) -> Self { self.override_help(help) } - /// Deprecated, replaced with [`App::mut_arg`] + /// Deprecated, replaced with [`Command::mut_arg`] #[deprecated(since = "3.0.0", note = "Replaced with `App::mut_arg`")] + #[doc(hidden)] #[must_use] pub fn help_short(self, c: char) -> Self { self.mut_arg("help", |a| a.short(c)) } - /// Deprecated, replaced with [`App::mut_arg`] + /// Deprecated, replaced with [`Command::mut_arg`] #[deprecated(since = "3.0.0", note = "Replaced with `App::mut_arg`")] + #[doc(hidden)] #[must_use] pub fn version_short(self, c: char) -> Self { self.mut_arg("version", |a| a.short(c)) } - /// Deprecated, replaced with [`App::mut_arg`] + /// Deprecated, replaced with [`Command::mut_arg`] #[deprecated(since = "3.0.0", note = "Replaced with `App::mut_arg`")] + #[doc(hidden)] #[must_use] pub fn help_message(self, s: impl Into<&'help str>) -> Self { self.mut_arg("help", |a| a.help(s.into())) } - /// Deprecated, replaced with [`App::mut_arg`] + /// Deprecated, replaced with [`Command::mut_arg`] #[deprecated(since = "3.0.0", note = "Replaced with `App::mut_arg`")] + #[doc(hidden)] #[must_use] pub fn version_message(self, s: impl Into<&'help str>) -> Self { self.mut_arg("version", |a| a.help(s.into())) } - /// Deprecated, replaced with [`App::help_template`] + /// Deprecated, replaced with [`Command::help_template`] #[deprecated(since = "3.0.0", note = "Replaced with `App::help_template`")] + #[doc(hidden)] #[must_use] pub fn template>(self, s: S) -> Self { self.help_template(s) } - /// Deprecated, replaced with [`App::setting(a| b)`] + /// Deprecated, replaced with [`Command::setting(a| b)`] #[deprecated(since = "3.0.0", note = "Replaced with `App::setting(a | b)`")] + #[doc(hidden)] #[must_use] pub fn settings(mut self, settings: &[AppSettings]) -> Self { for s in settings { @@ -2509,8 +3866,9 @@ impl<'help> App<'help> { self } - /// Deprecated, replaced with [`App::unset_setting(a| b)`] + /// Deprecated, replaced with [`Command::unset_setting(a| b)`] #[deprecated(since = "3.0.0", note = "Replaced with `App::unset_setting(a | b)`")] + #[doc(hidden)] #[must_use] pub fn unset_settings(mut self, settings: &[AppSettings]) -> Self { for s in settings { @@ -2519,8 +3877,9 @@ impl<'help> App<'help> { self } - /// Deprecated, replaced with [`App::global_setting(a| b)`] + /// Deprecated, replaced with [`Command::global_setting(a| b)`] #[deprecated(since = "3.0.0", note = "Replaced with `App::global_setting(a | b)`")] + #[doc(hidden)] #[must_use] pub fn global_settings(mut self, settings: &[AppSettings]) -> Self { for s in settings { @@ -2530,8 +3889,9 @@ impl<'help> App<'help> { self } - /// Deprecated, replaced with [`App::term_width`] + /// Deprecated, replaced with [`Command::term_width`] #[deprecated(since = "3.0.0", note = "Replaced with `App::term_width`")] + #[doc(hidden)] #[must_use] pub fn set_term_width(self, width: usize) -> Self { self.term_width(width) @@ -2539,6 +3899,7 @@ impl<'help> App<'help> { /// Deprecated in [Issue #3086](https://github.com/clap-rs/clap/issues/3086), see [`arg!`][crate::arg!]. #[deprecated(since = "3.0.0", note = "Deprecated in Issue #3086, see `clap::arg!")] + #[doc(hidden)] #[must_use] pub fn arg_from_usage(self, usage: &'help str) -> Self { #![allow(deprecated)] @@ -2547,6 +3908,7 @@ impl<'help> App<'help> { /// Deprecated in [Issue #3086](https://github.com/clap-rs/clap/issues/3086), see [`arg!`][crate::arg!]. #[deprecated(since = "3.0.0", note = "Deprecated in Issue #3086, see `clap::arg!")] + #[doc(hidden)] #[must_use] pub fn args_from_usage(mut self, usage: &'help str) -> Self { #![allow(deprecated)] @@ -2560,26 +3922,30 @@ impl<'help> App<'help> { self } - /// Deprecated, replaced with [`App::render_version`] + /// Deprecated, replaced with [`Command::render_version`] #[deprecated(since = "3.0.0", note = "Replaced with `App::render_version`")] - pub fn write_version(&self, w: &mut W) -> ClapResult<()> { + #[doc(hidden)] + pub fn write_version(&self, w: &mut W) -> ClapResult<()> { write!(w, "{}", self.render_version()).map_err(From::from) } - /// Deprecated, replaced with [`App::render_long_version`] + /// Deprecated, replaced with [`Command::render_long_version`] #[deprecated(since = "3.0.0", note = "Replaced with `App::render_long_version`")] - pub fn write_long_version(&self, w: &mut W) -> ClapResult<()> { + #[doc(hidden)] + pub fn write_long_version(&self, w: &mut W) -> ClapResult<()> { write!(w, "{}", self.render_long_version()).map_err(From::from) } - /// Deprecated, replaced with [`App::try_get_matches`] + /// Deprecated, replaced with [`Command::try_get_matches`] #[deprecated(since = "3.0.0", note = "Replaced with `App::try_get_matches`")] + #[doc(hidden)] pub fn get_matches_safe(self) -> ClapResult { self.try_get_matches() } - /// Deprecated, replaced with [`App::try_get_matches_from`] + /// Deprecated, replaced with [`Command::try_get_matches_from`] #[deprecated(since = "3.0.0", note = "Replaced with `App::try_get_matches_from`")] + #[doc(hidden)] pub fn get_matches_from_safe(self, itr: I) -> ClapResult where I: IntoIterator, @@ -2588,11 +3954,12 @@ impl<'help> App<'help> { self.try_get_matches_from(itr) } - /// Deprecated, replaced with [`App::try_get_matches_from_mut`] + /// Deprecated, replaced with [`Command::try_get_matches_from_mut`] #[deprecated( since = "3.0.0", note = "Replaced with `App::try_get_matches_from_mut`" )] + #[doc(hidden)] pub fn get_matches_from_safe_borrow(&mut self, itr: I) -> ClapResult where I: IntoIterator, @@ -2604,69 +3971,132 @@ impl<'help> App<'help> { // Internally used only impl<'help> App<'help> { - fn get_used_global_args(&self, matcher: &ArgMatcher) -> Vec { - let global_args: Vec<_> = self - .args - .args() - .filter(|a| a.get_global()) - .map(|ga| ga.id.clone()) - .collect(); - if let Some(used_subcommand) = matcher.0.subcommand.as_ref() { - if let Some(used_subcommand) = self - .subcommands - .iter() - .find(|subcommand| subcommand.id == used_subcommand.id) - { - return [global_args, used_subcommand.get_used_global_args(matcher)].concat(); - } - } - global_args + pub(crate) fn get_id(&self) -> Id { + self.id.clone() } - fn _do_parse(&mut self, it: &mut Input) -> ClapResult { - debug!("App::_do_parse"); + pub(crate) fn get_override_usage(&self) -> Option<&str> { + self.usage_str + } + + pub(crate) fn get_override_help(&self) -> Option<&str> { + self.help_str + } + + pub(crate) fn get_help_template(&self) -> Option<&str> { + self.template + } + + pub(crate) fn get_term_width(&self) -> Option { + self.term_w + } + + pub(crate) fn get_max_term_width(&self) -> Option { + self.max_w + } + + pub(crate) fn get_replacement(&self, key: &str) -> Option<&[&str]> { + self.replacers.get(key).copied() + } + + pub(crate) fn get_keymap(&self) -> &MKeyMap<'help> { + &self.args + } + + fn get_used_global_args(&self, matches: &ArgMatches, global_arg_vec: &mut Vec) { + global_arg_vec.extend( + self.args + .args() + .filter(|a| a.is_global_set()) + .map(|ga| ga.id.clone()), + ); + if let Some((id, matches)) = matches.subcommand() { + if let Some(used_sub) = self.find_subcommand(id) { + used_sub.get_used_global_args(matches, global_arg_vec); + } + } + } + + fn _do_parse( + &mut self, + raw_args: &mut clap_lex::RawArgs, + args_cursor: clap_lex::ArgCursor, + ) -> ClapResult { + debug!("Command::_do_parse"); // If there are global arguments, or settings we need to propagate them down to subcommands // before parsing in case we run into a subcommand - self._build(); + self._build_self(); let mut matcher = ArgMatcher::new(self); // do the real parsing let mut parser = Parser::new(self); - if let Err(error) = parser.get_matches_with(&mut matcher, it) { + if let Err(error) = parser.get_matches_with(&mut matcher, raw_args, args_cursor) { if self.is_set(AppSettings::IgnoreErrors) { - debug!("App::_do_parse: ignoring error: {}", error); + debug!("Command::_do_parse: ignoring error: {}", error); } else { return Err(error); } } - let global_arg_vec: Vec = self.get_used_global_args(&matcher); + let mut global_arg_vec = Default::default(); + self.get_used_global_args(&matcher, &mut global_arg_vec); matcher.propagate_globals(&global_arg_vec); Ok(matcher.into_inner()) } - // used in clap_complete (https://github.com/clap-rs/clap_complete) #[doc(hidden)] + #[deprecated(since = "3.1.10", note = "Replaced with `Command::build`")] pub fn _build_all(&mut self) { - self._build(); - for subcmd in self.get_subcommands_mut() { - subcmd._build(); - } - self._build_bin_names(); + self.build(); } - // used in clap_complete (https://github.com/clap-rs/clap_complete) #[doc(hidden)] + #[deprecated(since = "3.1.10", note = "Replaced with `Command::build`")] pub fn _build(&mut self) { - debug!("App::_build"); + self._build_self() + } + + #[doc(hidden)] + #[deprecated(since = "3.1.13", note = "Replaced with `Command::build`")] + pub fn _build_bin_names(&mut self) { + self._build_bin_names_internal(); + } + + /// Prepare for introspecting on all included [`Command`]s + /// + /// Call this on the top-level [`Command`] when done building and before reading state for + /// cases like completions, custom help output, etc. + pub fn build(&mut self) { + self._build_recursive(); + self._build_bin_names_internal(); + } + + pub(crate) fn _build_recursive(&mut self) { + self._build_self(); + for subcmd in self.get_subcommands_mut() { + subcmd._build_recursive(); + } + } + + pub(crate) fn _build_self(&mut self) { + debug!("Command::_build: name={:?}", self.get_name()); if !self.settings.is_set(AppSettings::Built) { // Make sure all the globally set flags apply to us as well self.settings = self.settings | self.g_settings; + #[cfg(feature = "unstable-multicall")] + { + if self.is_multicall_set() { + self.settings.insert(AppSettings::SubcommandRequired.into()); + self.settings.insert(AppSettings::DisableHelpFlag.into()); + self.settings.insert(AppSettings::DisableVersionFlag.into()); + } + } + self._propagate(); self._check_help_and_version(); self._propagate_global_args(); @@ -2674,6 +4104,7 @@ impl<'help> App<'help> { let mut pos_counter = 1; let self_override = self.is_set(AppSettings::AllArgsOverrideSelf); + let hide_pv = self.is_set(AppSettings::HidePossibleValues); for a in self.args.args_mut() { // Fill in the groups for g in &a.groups { @@ -2687,11 +4118,14 @@ impl<'help> App<'help> { } // Figure out implied settings - if a.is_set(ArgSettings::Last) { + if a.is_last_set() { // if an arg has `Last` set, we need to imply DontCollapseArgsInUsage so that args // in the usage string don't get confused or left out. self.settings.set(AppSettings::DontCollapseArgsInUsage); } + if hide_pv && a.is_takes_value_set() { + a.settings.set(ArgSettings::HidePossibleValues); + } if self_override { let self_id = a.id.clone(); a.overrides.push(self_id); @@ -2706,14 +4140,207 @@ impl<'help> App<'help> { self.args._build(); #[cfg(debug_assertions)] - self::debug_asserts::assert_app(self); + assert_app(self); self.settings.set(AppSettings::Built); } else { - debug!("App::_build: already built"); + debug!("Command::_build: already built"); } } - fn _panic_on_missing_help(&self, help_required_globally: bool) { + pub(crate) fn _build_subcommand(&mut self, name: &str) -> Option<&mut Self> { + use std::fmt::Write; + + let mut mid_string = String::from(" "); + if !self.is_subcommand_negates_reqs_set() { + let reqs = Usage::new(self).get_required_usage_from(&[], None, true); // maybe Some(m) + + for s in &reqs { + mid_string.push_str(s); + mid_string.push(' '); + } + } + let is_multicall_set = cfg!(feature = "unstable-multicall") && self.is_multicall_set(); + + let sc = self.subcommands.iter_mut().find(|s| s.name == name)?; + + // Display subcommand name, short and long in usage + let mut sc_names = sc.name.clone(); + let mut flag_subcmd = false; + if let Some(l) = sc.long_flag { + write!(sc_names, "|--{}", l).unwrap(); + flag_subcmd = true; + } + if let Some(s) = sc.short_flag { + write!(sc_names, "|-{}", s).unwrap(); + flag_subcmd = true; + } + + if flag_subcmd { + sc_names = format!("{{{}}}", sc_names); + } + + let usage_name = self + .bin_name + .as_ref() + .map(|bin_name| format!("{}{}{}", bin_name, mid_string, sc_names)) + .unwrap_or(sc_names); + sc.usage_name = Some(usage_name); + + // bin_name should be parent's bin_name + [] + the sc's name separated by + // a space + let bin_name = format!( + "{}{}{}", + self.bin_name.as_ref().unwrap_or(&String::new()), + if self.bin_name.is_some() { " " } else { "" }, + &*sc.name + ); + debug!( + "Command::_build_subcommand Setting bin_name of {} to {:?}", + sc.name, bin_name + ); + sc.bin_name = Some(bin_name); + + if sc.display_name.is_none() { + let self_display_name = if is_multicall_set { + self.display_name.as_deref().unwrap_or("") + } else { + self.display_name.as_deref().unwrap_or(&self.name) + }; + let display_name = format!( + "{}{}{}", + self_display_name, + if !self_display_name.is_empty() { + "-" + } else { + "" + }, + &*sc.name + ); + debug!( + "Command::_build_subcommand Setting display_name of {} to {:?}", + sc.name, display_name + ); + sc.display_name = Some(display_name); + } + + // Ensure all args are built and ready to parse + sc._build_self(); + + Some(sc) + } + + fn _build_bin_names_internal(&mut self) { + debug!("Command::_build_bin_names"); + + if !self.is_set(AppSettings::BinNameBuilt) { + let mut mid_string = String::from(" "); + if !self.is_subcommand_negates_reqs_set() { + let reqs = Usage::new(self).get_required_usage_from(&[], None, true); // maybe Some(m) + + for s in &reqs { + mid_string.push_str(s); + mid_string.push(' '); + } + } + let is_multicall_set = cfg!(feature = "unstable-multicall") && self.is_multicall_set(); + + let self_bin_name = if is_multicall_set { + self.bin_name.as_deref().unwrap_or("") + } else { + self.bin_name.as_deref().unwrap_or(&self.name) + } + .to_owned(); + + for mut sc in &mut self.subcommands { + debug!("Command::_build_bin_names:iter: bin_name set..."); + + if sc.usage_name.is_none() { + use std::fmt::Write; + // Display subcommand name, short and long in usage + let mut sc_names = sc.name.clone(); + let mut flag_subcmd = false; + if let Some(l) = sc.long_flag { + write!(sc_names, "|--{}", l).unwrap(); + flag_subcmd = true; + } + if let Some(s) = sc.short_flag { + write!(sc_names, "|-{}", s).unwrap(); + flag_subcmd = true; + } + + if flag_subcmd { + sc_names = format!("{{{}}}", sc_names); + } + + let usage_name = format!("{}{}{}", self_bin_name, mid_string, sc_names); + debug!( + "Command::_build_bin_names:iter: Setting usage_name of {} to {:?}", + sc.name, usage_name + ); + sc.usage_name = Some(usage_name); + } else { + debug!( + "Command::_build_bin_names::iter: Using existing usage_name of {} ({:?})", + sc.name, sc.usage_name + ); + } + + if sc.bin_name.is_none() { + let bin_name = format!( + "{}{}{}", + self_bin_name, + if !self_bin_name.is_empty() { " " } else { "" }, + &*sc.name + ); + debug!( + "Command::_build_bin_names:iter: Setting bin_name of {} to {:?}", + sc.name, bin_name + ); + sc.bin_name = Some(bin_name); + } else { + debug!( + "Command::_build_bin_names::iter: Using existing bin_name of {} ({:?})", + sc.name, sc.bin_name + ); + } + + if sc.display_name.is_none() { + let self_display_name = if is_multicall_set { + self.display_name.as_deref().unwrap_or("") + } else { + self.display_name.as_deref().unwrap_or(&self.name) + }; + let display_name = format!( + "{}{}{}", + self_display_name, + if !self_display_name.is_empty() { + "-" + } else { + "" + }, + &*sc.name + ); + debug!( + "Command::_build_bin_names:iter: Setting display_name of {} to {:?}", + sc.name, display_name + ); + sc.display_name = Some(display_name); + } else { + debug!( + "Command::_build_bin_names::iter: Using existing display_name of {} ({:?})", + sc.name, sc.display_name + ); + } + + sc._build_bin_names_internal(); + } + self.set(AppSettings::BinNameBuilt); + } else { + debug!("Command::_build_bin_names: already built"); + } + } + + pub(crate) fn _panic_on_missing_help(&self, help_required_globally: bool) { if self.is_set(AppSettings::HelpExpected) || help_required_globally { let args_missing_help: Vec = self .args @@ -2723,7 +4350,7 @@ impl<'help> App<'help> { .collect(); assert!(args_missing_help.is_empty(), - "AppSettings::HelpExpected is enabled for the App {}, but at least one of its arguments does not have either `help` or `long_help` set. List of such arguments: {}", + "AppSettings::HelpExpected is enabled for the Command {}, but at least one of its arguments does not have either `help` or `long_help` set. List of such arguments: {}", self.name, args_missing_help.join(", ") ); @@ -2735,7 +4362,7 @@ impl<'help> App<'help> { } #[cfg(debug_assertions)] - fn two_args_of(&self, condition: F) -> Option<(&Arg<'help>, &Arg<'help>)> + pub(crate) fn two_args_of(&self, condition: F) -> Option<(&Arg<'help>, &Arg<'help>)> where F: Fn(&Arg) -> bool, { @@ -2753,10 +4380,10 @@ impl<'help> App<'help> { /// Propagate global args pub(crate) fn _propagate_global_args(&mut self) { - debug!("App::_propagate_global_args:{}", self.name); + debug!("Command::_propagate_global_args:{}", self.name); for sc in &mut self.subcommands { - for a in self.args.args().filter(|a| a.get_global()) { + for a in self.args.args().filter(|a| a.is_global_set()) { let mut propagate = false; let is_generated = matches!( a.provider, @@ -2787,7 +4414,7 @@ impl<'help> App<'help> { /// Propagate settings pub(crate) fn _propagate(&mut self) { - debug!("App::_propagate:{}", self.name); + debug!("Command::_propagate:{}", self.name); let mut subcommands = std::mem::take(&mut self.subcommands); for sc in &mut subcommands { self._propagate_subcommand(sc); @@ -2817,7 +4444,7 @@ impl<'help> App<'help> { #[allow(clippy::blocks_in_if_conditions)] pub(crate) fn _check_help_and_version(&mut self) { - debug!("App::_check_help_and_version"); + debug!("Command::_check_help_and_version: {}", self.name); if self.is_set(AppSettings::DisableHelpFlag) || self.args.args().any(|x| { @@ -2829,7 +4456,7 @@ impl<'help> App<'help> { .iter() .any(|sc| sc.long_flag == Some("help")) { - debug!("App::_check_help_and_version: Removing generated help"); + debug!("Command::_check_help_and_version: Removing generated help"); let generated_help_pos = self .args @@ -2840,18 +4467,39 @@ impl<'help> App<'help> { self.args.remove(index); } } else { - let other_arg_has_short = self.args.args().any(|x| x.short == Some('h')); let help = self .args - .args_mut() + .args() .find(|x| x.id == Id::help_hash()) .expect(INTERNAL_ERROR_MSG); + assert_ne!(help.provider, ArgProvider::User); - if !(help.short.is_some() - || other_arg_has_short + if help.short.is_some() { + if help.short == Some('h') { + if let Some(other_arg) = self + .args + .args() + .find(|x| x.id != Id::help_hash() && x.short == Some('h')) + { + panic!( + "`help`s `-h` conflicts with `{}`. + +To change `help`s short, call `cmd.arg(Arg::new(\"help\")...)`.", + other_arg.name + ); + } + } + } else if !(self.args.args().any(|x| x.short == Some('h')) || self.subcommands.iter().any(|sc| sc.short_flag == Some('h'))) { + let help = self + .args + .args_mut() + .find(|x| x.id == Id::help_hash()) + .expect(INTERNAL_ERROR_MSG); help.short = Some('h'); + } else { + debug!("Command::_check_help_and_version: Removing `-h` from help"); } } @@ -2872,10 +4520,10 @@ impl<'help> App<'help> { .iter() .any(|sc| sc.long_flag == Some("version")) { - debug!("App::_check_help_and_version: Removing generated version"); + debug!("Command::_check_help_and_version: Removing generated version"); // This is the check mentioned above that only checks for Generated, not - // GeneratedMuated args by design. + // GeneratedMutated args by design. let generated_version_pos = self .args .args() @@ -2913,7 +4561,7 @@ impl<'help> App<'help> { && self.has_subcommands() && !self.subcommands.iter().any(|s| s.id == Id::help_hash()) { - debug!("App::_check_help_and_version: Building help subcommand"); + debug!("Command::_check_help_and_version: Building help subcommand"); let mut help_subcmd = App::new("help") .about("Print this message or the help of the given subcommand(s)") .arg( @@ -2939,17 +4587,16 @@ impl<'help> App<'help> { } pub(crate) fn _derive_display_order(&mut self) { - debug!("App::_derive_display_order:{}", self.name); + debug!("Command::_derive_display_order:{}", self.name); if self.settings.is_set(AppSettings::DeriveDisplayOrder) { - for (i, a) in self + for a in self .args .args_mut() .filter(|a| !a.is_positional()) .filter(|a| a.provider != ArgProvider::Generated) - .enumerate() { - a.disp_ord.get_or_insert(i); + a.disp_ord.make_explicit(); } for (i, sc) in &mut self.subcommands.iter_mut().enumerate() { sc.disp_ord.get_or_insert(i); @@ -2960,52 +4607,13 @@ impl<'help> App<'help> { } } - // used in clap_complete (https://github.com/clap-rs/clap_complete) - #[doc(hidden)] - pub fn _build_bin_names(&mut self) { - debug!("App::_build_bin_names"); - - if !self.is_set(AppSettings::BinNameBuilt) { - for mut sc in &mut self.subcommands { - debug!("App::_build_bin_names:iter: bin_name set..."); - - if sc.bin_name.is_none() { - debug!("No"); - let bin_name = format!( - "{}{}{}", - self.bin_name.as_ref().unwrap_or(&self.name.clone()), - if self.bin_name.is_some() { " " } else { "" }, - &*sc.name - ); - debug!( - "App::_build_bin_names:iter: Setting bin_name of {} to {}", - self.name, bin_name - ); - sc.bin_name = Some(bin_name); - } else { - debug!("yes ({:?})", sc.bin_name); - } - debug!( - "App::_build_bin_names:iter: Calling build_bin_names from...{}", - sc.name - ); - sc._build_bin_names(); - } - self.set(AppSettings::BinNameBuilt); - } else { - debug!("App::_build_bin_names: already built"); - } - } - pub(crate) fn _render_version(&self, use_long: bool) -> String { - debug!("App::_render_version"); + debug!("Command::_render_version"); let ver = if use_long { - self.long_version - .unwrap_or_else(|| self.version.unwrap_or("")) + self.long_version.or(self.version).unwrap_or("") } else { - self.version - .unwrap_or_else(|| self.long_version.unwrap_or("")) + self.version.or(self.long_version).unwrap_or("") }; if let Some(bn) = self.bin_name.as_ref() { if bn.contains(' ') { @@ -3137,7 +4745,7 @@ impl<'help> App<'help> { /// Iterate through the groups this arg is member of. pub(crate) fn groups_for_arg<'a>(&'a self, arg: &Id) -> impl Iterator + 'a { - debug!("App::groups_for_arg: id={:?}", arg); + debug!("Command::groups_for_arg: id={:?}", arg); let arg = arg.clone(); self.groups .iter() @@ -3159,8 +4767,25 @@ impl<'help> App<'help> { }) } + pub(crate) fn required_graph(&self) -> ChildGraph { + let mut reqs = ChildGraph::with_capacity(5); + for a in self.args.args().filter(|a| a.is_required_set()) { + reqs.insert(a.id.clone()); + } + for group in &self.groups { + if group.required { + let idx = reqs.insert(group.id.clone()); + for a in &group.requires { + reqs.insert_child(idx, a.clone()); + } + } + } + + reqs + } + pub(crate) fn unroll_args_in_group(&self, group: &Id) -> Vec { - debug!("App::unroll_args_in_group: group={:?}", group); + debug!("Command::unroll_args_in_group: group={:?}", group); let mut g_vec = vec![group]; let mut args = vec![]; @@ -3173,13 +4798,13 @@ impl<'help> App<'help> { .args .iter() { - debug!("App::unroll_args_in_group:iter: entity={:?}", n); + debug!("Command::unroll_args_in_group:iter: entity={:?}", n); if !args.contains(n) { if self.find(n).is_some() { - debug!("App::unroll_args_in_group:iter: this is an arg"); + debug!("Command::unroll_args_in_group:iter: this is an arg"); args.push(n.clone()) } else { - debug!("App::unroll_args_in_group:iter: this is a group"); + debug!("Command::unroll_args_in_group:iter: this is a group"); g_vec.push(n); } } @@ -3189,23 +4814,10 @@ impl<'help> App<'help> { args } - pub(crate) fn unroll_requirements_for_arg(&self, arg: &Id, matcher: &ArgMatcher) -> Vec { - let requires_if_or_not = |(val, req_arg): &(Option<&str>, Id)| -> Option { - if let Some(v) = val { - if matcher - .get(arg) - .map(|ma| ma.contains_val(v)) - .unwrap_or(false) - { - Some(req_arg.clone()) - } else { - None - } - } else { - Some(req_arg.clone()) - } - }; - + pub(crate) fn unroll_arg_requires(&self, func: F, arg: &Id) -> Vec + where + F: Fn(&(ArgPredicate<'_>, Id)) -> Option, + { let mut processed = vec![]; let mut r_vec = vec![arg]; let mut args = vec![]; @@ -3218,7 +4830,7 @@ impl<'help> App<'help> { processed.push(a); if let Some(arg) = self.find(a) { - for r in arg.requires.iter().filter_map(requires_if_or_not) { + for r in arg.requires.iter().filter_map(&func) { if let Some(req) = self.find(&r) { if !req.requires.is_empty() { r_vec.push(&req.id) @@ -3240,7 +4852,7 @@ impl<'help> App<'help> { } /// Find a flag subcommand name by long flag or an alias - pub(crate) fn find_long_subcmd(&self, long: &RawOsStr) -> Option<&str> { + pub(crate) fn find_long_subcmd(&self, long: &str) -> Option<&str> { self.get_subcommands() .find(|sc| sc.long_flag_aliases_to(long)) .map(|sc| sc.get_name()) @@ -3249,6 +4861,99 @@ impl<'help> App<'help> { pub(crate) fn get_display_order(&self) -> usize { self.disp_ord.unwrap_or(999) } + + pub(crate) fn write_help_err( + &self, + mut use_long: bool, + stream: Stream, + ) -> ClapResult { + debug!( + "Parser::write_help_err: use_long={:?}, stream={:?}", + use_long && self.use_long_help(), + stream + ); + + use_long = use_long && self.use_long_help(); + let usage = Usage::new(self); + + let mut c = Colorizer::new(stream, self.color_help()); + Help::new(HelpWriter::Buffer(&mut c), self, &usage, use_long).write_help()?; + Ok(c) + } + + pub(crate) fn use_long_help(&self) -> bool { + debug!("Command::use_long_help"); + // In this case, both must be checked. This allows the retention of + // original formatting, but also ensures that the actual -h or --help + // specified by the user is sent through. If hide_short_help is not included, + // then items specified with hidden_short_help will also be hidden. + let should_long = |v: &Arg| { + v.long_help.is_some() + || v.is_hide_long_help_set() + || v.is_hide_short_help_set() + || cfg!(feature = "unstable-v4") + && v.possible_vals.iter().any(PossibleValue::should_show_help) + }; + + // Subcommands aren't checked because we prefer short help for them, deferring to + // `cmd subcmd --help` for more. + self.get_long_about().is_some() + || self.get_before_long_help().is_some() + || self.get_after_long_help().is_some() + || self.get_arguments().any(should_long) + } + + // Should we color the help? + pub(crate) fn color_help(&self) -> ColorChoice { + #[cfg(feature = "color")] + if self.is_disable_colored_help_set() { + return ColorChoice::Never; + } + + self.get_color() + } +} + +impl<'help> Default for App<'help> { + fn default() -> Self { + Self { + id: Default::default(), + name: Default::default(), + long_flag: Default::default(), + short_flag: Default::default(), + display_name: Default::default(), + bin_name: Default::default(), + author: Default::default(), + version: Default::default(), + long_version: Default::default(), + about: Default::default(), + long_about: Default::default(), + before_help: Default::default(), + before_long_help: Default::default(), + after_help: Default::default(), + after_long_help: Default::default(), + aliases: Default::default(), + short_flag_aliases: Default::default(), + long_flag_aliases: Default::default(), + usage_str: Default::default(), + usage_name: Default::default(), + help_str: Default::default(), + disp_ord: Default::default(), + term_w: Default::default(), + max_w: Default::default(), + template: Default::default(), + settings: Default::default(), + g_settings: Default::default(), + args: Default::default(), + subcommands: Default::default(), + replacers: Default::default(), + groups: Default::default(), + current_help_heading: Default::default(), + current_disp_ord: Some(0), + subcommand_value_name: Default::default(), + subcommand_heading: Default::default(), + } + } } impl<'help> Index<&'_ Id> for App<'help> { diff --git a/third_party/rust/clap/src/build/debug_asserts.rs b/third_party/rust/clap/src/build/debug_asserts.rs new file mode 100644 index 000000000000..7b470d843163 --- /dev/null +++ b/third_party/rust/clap/src/build/debug_asserts.rs @@ -0,0 +1,815 @@ +use std::cmp::Ordering; + +use clap_lex::RawOsStr; + +use crate::build::arg::ArgProvider; +use crate::mkeymap::KeyType; +use crate::util::Id; +use crate::{AppSettings, Arg, Command, ValueHint}; + +pub(crate) fn assert_app(cmd: &Command) { + debug!("Command::_debug_asserts"); + + let mut short_flags = vec![]; + let mut long_flags = vec![]; + + // Invalid version flag settings + if cmd.get_version().is_none() && cmd.get_long_version().is_none() { + // PropagateVersion is meaningless if there is no version + assert!( + !cmd.is_propagate_version_set(), + "Command {}: No version information via Command::version or Command::long_version to propagate", + cmd.get_name(), + ); + + // Used `Command::mut_arg("version", ..) but did not provide any version information to display + let has_mutated_version = cmd + .get_arguments() + .any(|x| x.id == Id::version_hash() && x.provider == ArgProvider::GeneratedMutated); + + if has_mutated_version { + assert!(cmd.is_set(AppSettings::NoAutoVersion), + "Command {}: Used Command::mut_arg(\"version\", ..) without providing Command::version, Command::long_version or using AppSettings::NoAutoVersion" + ,cmd.get_name() + ); + } + } + + for sc in cmd.get_subcommands() { + if let Some(s) = sc.get_short_flag().as_ref() { + short_flags.push(Flag::Command(format!("-{}", s), sc.get_name())); + } + + for short_alias in sc.get_all_short_flag_aliases() { + short_flags.push(Flag::Command(format!("-{}", short_alias), sc.get_name())); + } + + if let Some(l) = sc.get_long_flag().as_ref() { + #[cfg(feature = "unstable-v4")] + { + assert!(!l.starts_with('-'), "Command {}: long_flag {:?} must not start with a `-`, that will be handled by the parser", sc.get_name(), l); + } + long_flags.push(Flag::Command(format!("--{}", l), sc.get_name())); + } + + for long_alias in sc.get_all_long_flag_aliases() { + long_flags.push(Flag::Command(format!("--{}", long_alias), sc.get_name())); + } + } + + for arg in cmd.get_arguments() { + assert_arg(arg); + + #[cfg(feature = "unstable-multicall")] + { + assert!( + !cmd.is_multicall_set(), + "Command {}: Arguments like {} cannot be set on a multicall command", + cmd.get_name(), + arg.name + ); + } + + if let Some(s) = arg.short.as_ref() { + short_flags.push(Flag::Arg(format!("-{}", s), &*arg.name)); + } + + for (short_alias, _) in &arg.short_aliases { + short_flags.push(Flag::Arg(format!("-{}", short_alias), arg.name)); + } + + if let Some(l) = arg.long.as_ref() { + #[cfg(feature = "unstable-v4")] + { + assert!(!l.starts_with('-'), "Argument {}: long {:?} must not start with a `-`, that will be handled by the parser", arg.name, l); + } + long_flags.push(Flag::Arg(format!("--{}", l), &*arg.name)); + } + + for (long_alias, _) in &arg.aliases { + long_flags.push(Flag::Arg(format!("--{}", long_alias), arg.name)); + } + + // Name conflicts + assert!( + cmd.two_args_of(|x| x.id == arg.id).is_none(), + "Command {}: Argument names must be unique, but '{}' is in use by more than one argument or group", + cmd.get_name(), + arg.name, + ); + + // Long conflicts + if let Some(l) = arg.long { + if let Some((first, second)) = cmd.two_args_of(|x| x.long == Some(l)) { + panic!( + "Command {}: Long option names must be unique for each argument, \ + but '--{}' is in use by both '{}' and '{}'", + cmd.get_name(), + l, + first.name, + second.name + ) + } + } + + // Short conflicts + if let Some(s) = arg.short { + if let Some((first, second)) = cmd.two_args_of(|x| x.short == Some(s)) { + panic!( + "Command {}: Short option names must be unique for each argument, \ + but '-{}' is in use by both '{}' and '{}'", + cmd.get_name(), + s, + first.name, + second.name + ) + } + } + + // Index conflicts + if let Some(idx) = arg.index { + if let Some((first, second)) = + cmd.two_args_of(|x| x.is_positional() && x.index == Some(idx)) + { + panic!( + "Command {}: Argument '{}' has the same index as '{}' \ + and they are both positional arguments\n\n\t \ + Use Arg::multiple_values(true) to allow one \ + positional argument to take multiple values", + cmd.get_name(), + first.name, + second.name + ) + } + } + + // requires, r_if, r_unless + for req in &arg.requires { + assert!( + cmd.id_exists(&req.1), + "Command {}: Argument or group '{:?}' specified in 'requires*' for '{}' does not exist", + cmd.get_name(), + req.1, + arg.name, + ); + } + + for req in &arg.r_ifs { + #[cfg(feature = "unstable-v4")] + { + assert!( + !arg.is_required_set(), + "Argument {}: `required` conflicts with `required_if_eq*`", + arg.name + ); + } + assert!( + cmd.id_exists(&req.0), + "Command {}: Argument or group '{:?}' specified in 'required_if_eq*' for '{}' does not exist", + cmd.get_name(), + req.0, + arg.name + ); + } + + for req in &arg.r_ifs_all { + #[cfg(feature = "unstable-v4")] + { + assert!( + !arg.is_required_set(), + "Argument {}: `required` conflicts with `required_if_eq_all`", + arg.name + ); + } + assert!( + cmd.id_exists(&req.0), + "Command {}: Argument or group '{:?}' specified in 'required_if_eq_all' for '{}' does not exist", + cmd.get_name(), + req.0, + arg.name + ); + } + + for req in &arg.r_unless { + #[cfg(feature = "unstable-v4")] + { + assert!( + !arg.is_required_set(), + "Argument {}: `required` conflicts with `required_unless*`", + arg.name + ); + } + assert!( + cmd.id_exists(req), + "Command {}: Argument or group '{:?}' specified in 'required_unless*' for '{}' does not exist", + cmd.get_name(), + req, + arg.name, + ); + } + + for req in &arg.r_unless_all { + #[cfg(feature = "unstable-v4")] + { + assert!( + !arg.is_required_set(), + "Argument {}: `required` conflicts with `required_unless*`", + arg.name + ); + } + assert!( + cmd.id_exists(req), + "Command {}: Argument or group '{:?}' specified in 'required_unless*' for '{}' does not exist", + cmd.get_name(), + req, + arg.name, + ); + } + + // blacklist + for req in &arg.blacklist { + assert!( + cmd.id_exists(req), + "Command {}: Argument or group '{:?}' specified in 'conflicts_with*' for '{}' does not exist", + cmd.get_name(), + req, + arg.name, + ); + } + + if arg.is_last_set() { + assert!( + arg.long.is_none(), + "Command {}: Flags or Options cannot have last(true) set. '{}' has both a long and last(true) set.", + cmd.get_name(), + arg.name + ); + assert!( + arg.short.is_none(), + "Command {}: Flags or Options cannot have last(true) set. '{}' has both a short and last(true) set.", + cmd.get_name(), + arg.name + ); + } + + assert!( + !(arg.is_required_set() && arg.is_global_set()), + "Command {}: Global arguments cannot be required.\n\n\t'{}' is marked as both global and required", + cmd.get_name(), + arg.name + ); + + // validators + assert!( + arg.validator.is_none() || arg.validator_os.is_none(), + "Command {}: Argument '{}' has both `validator` and `validator_os` set which is not allowed", + cmd.get_name(), + arg.name + ); + + if arg.value_hint == ValueHint::CommandWithArguments { + assert!( + arg.is_positional(), + "Command {}: Argument '{}' has hint CommandWithArguments and must be positional.", + cmd.get_name(), + arg.name + ); + + assert!( + cmd.is_trailing_var_arg_set(), + "Command {}: Positional argument '{}' has hint CommandWithArguments, so Command must have TrailingVarArg set.", + cmd.get_name(), + arg.name + ); + } + } + + for group in cmd.get_groups() { + // Name conflicts + assert!( + cmd.get_groups().filter(|x| x.id == group.id).count() < 2, + "Command {}: Argument group name must be unique\n\n\t'{}' is already in use", + cmd.get_name(), + group.name, + ); + + // Groups should not have naming conflicts with Args + assert!( + !cmd.get_arguments().any(|x| x.id == group.id), + "Command {}: Argument group name '{}' must not conflict with argument name", + cmd.get_name(), + group.name, + ); + + for arg in &group.args { + // Args listed inside groups should exist + assert!( + cmd.get_arguments().any(|x| x.id == *arg), + "Command {}: Argument group '{}' contains non-existent argument '{:?}'", + cmd.get_name(), + group.name, + arg + ); + } + + // Required groups should have at least one arg without default values + if group.required && !group.args.is_empty() { + assert!( + group.args.iter().any(|arg| { + cmd.get_arguments() + .any(|x| x.id == *arg && x.default_vals.is_empty()) + }), + "Command {}: Argument group '{}' is required but all of it's arguments have a default value.", + cmd.get_name(), + group.name + ) + } + } + + // Conflicts between flags and subcommands + + long_flags.sort_unstable(); + short_flags.sort_unstable(); + + detect_duplicate_flags(&long_flags, "long"); + detect_duplicate_flags(&short_flags, "short"); + + _verify_positionals(cmd); + + if let Some(help_template) = cmd.get_help_template() { + assert!( + !help_template.contains("{flags}"), + "Command {}: {}", + cmd.get_name(), + "`{flags}` template variable was removed in clap3, they are now included in `{options}`", + ); + assert!( + !help_template.contains("{unified}"), + "Command {}: {}", + cmd.get_name(), + "`{unified}` template variable was removed in clap3, use `{options}` instead" + ); + } + + cmd._panic_on_missing_help(cmd.is_help_expected_set()); + assert_app_flags(cmd); +} + +#[derive(Eq)] +enum Flag<'a> { + Command(String, &'a str), + Arg(String, &'a str), +} + +impl PartialEq for Flag<'_> { + fn eq(&self, other: &Flag) -> bool { + self.cmp(other) == Ordering::Equal + } +} + +impl PartialOrd for Flag<'_> { + fn partial_cmp(&self, other: &Flag) -> Option { + use Flag::*; + + match (self, other) { + (Command(s1, _), Command(s2, _)) + | (Arg(s1, _), Arg(s2, _)) + | (Command(s1, _), Arg(s2, _)) + | (Arg(s1, _), Command(s2, _)) => { + if s1 == s2 { + Some(Ordering::Equal) + } else { + s1.partial_cmp(s2) + } + } + } + } +} + +impl Ord for Flag<'_> { + fn cmp(&self, other: &Self) -> Ordering { + self.partial_cmp(other).unwrap() + } +} + +fn detect_duplicate_flags(flags: &[Flag], short_or_long: &str) { + use Flag::*; + + for (one, two) in find_duplicates(flags) { + match (one, two) { + (Command(flag, one), Command(_, another)) if one != another => panic!( + "the '{}' {} flag is specified for both '{}' and '{}' subcommands", + flag, short_or_long, one, another + ), + + (Arg(flag, one), Arg(_, another)) if one != another => panic!( + "{} option names must be unique, but '{}' is in use by both '{}' and '{}'", + short_or_long, flag, one, another + ), + + (Arg(flag, arg), Command(_, sub)) | (Command(flag, sub), Arg(_, arg)) => panic!( + "the '{}' {} flag for the '{}' argument conflicts with the short flag \ + for '{}' subcommand", + flag, short_or_long, arg, sub + ), + + _ => {} + } + } +} + +/// Find duplicates in a sorted array. +/// +/// The algorithm is simple: the array is sorted, duplicates +/// must be placed next to each other, we can check only adjacent elements. +fn find_duplicates(slice: &[T]) -> impl Iterator { + slice.windows(2).filter_map(|w| { + if w[0] == w[1] { + Some((&w[0], &w[1])) + } else { + None + } + }) +} + +fn assert_app_flags(cmd: &Command) { + macro_rules! checker { + ($a:ident requires $($b:ident)|+) => { + if cmd.$a() { + let mut s = String::new(); + + $( + if !cmd.$b() { + s.push_str(&format!(" AppSettings::{} is required when AppSettings::{} is set.\n", std::stringify!($b), std::stringify!($a))); + } + )+ + + if !s.is_empty() { + panic!("{}", s) + } + } + }; + ($a:ident conflicts $($b:ident)|+) => { + if cmd.$a() { + let mut s = String::new(); + + $( + if cmd.$b() { + s.push_str(&format!(" AppSettings::{} conflicts with AppSettings::{}.\n", std::stringify!($b), std::stringify!($a))); + } + )+ + + if !s.is_empty() { + panic!("{}\n{}", cmd.get_name(), s) + } + } + }; + } + + checker!(is_allow_invalid_utf8_for_external_subcommands_set requires is_allow_external_subcommands_set); + #[cfg(feature = "unstable-multicall")] + checker!(is_multicall_set conflicts is_no_binary_name_set); +} + +#[cfg(debug_assertions)] +fn _verify_positionals(cmd: &Command) -> bool { + debug!("Command::_verify_positionals"); + // Because you must wait until all arguments have been supplied, this is the first chance + // to make assertions on positional argument indexes + // + // First we verify that the index highest supplied index, is equal to the number of + // positional arguments to verify there are no gaps (i.e. supplying an index of 1 and 3 + // but no 2) + + let highest_idx = cmd + .get_keymap() + .keys() + .filter_map(|x| { + if let KeyType::Position(n) = x { + Some(*n) + } else { + None + } + }) + .max() + .unwrap_or(0); + + let num_p = cmd.get_keymap().keys().filter(|x| x.is_position()).count(); + + assert!( + highest_idx == num_p, + "Found positional argument whose index is {} but there \ + are only {} positional arguments defined", + highest_idx, + num_p + ); + + // Next we verify that only the highest index has takes multiple arguments (if any) + let only_highest = |a: &Arg| a.is_multiple() && (a.index.unwrap_or(0) != highest_idx); + if cmd.get_positionals().any(only_highest) { + // First we make sure if there is a positional that allows multiple values + // the one before it (second to last) has one of these: + // * a value terminator + // * ArgSettings::Last + // * The last arg is Required + + // We can't pass the closure (it.next()) to the macro directly because each call to + // find() (iterator, not macro) gets called repeatedly. + let last = &cmd.get_keymap()[&KeyType::Position(highest_idx)]; + let second_to_last = &cmd.get_keymap()[&KeyType::Position(highest_idx - 1)]; + + // Either the final positional is required + // Or the second to last has a terminator or .last(true) set + let ok = last.is_required_set() + || (second_to_last.terminator.is_some() || second_to_last.is_last_set()) + || last.is_last_set(); + assert!( + ok, + "When using a positional argument with .multiple_values(true) that is *not the \ + last* positional argument, the last positional argument (i.e. the one \ + with the highest index) *must* have .required(true) or .last(true) set." + ); + + // We make sure if the second to last is Multiple the last is ArgSettings::Last + let ok = second_to_last.is_multiple() || last.is_last_set(); + assert!( + ok, + "Only the last positional argument, or second to last positional \ + argument may be set to .multiple_values(true)" + ); + + // Next we check how many have both Multiple and not a specific number of values set + let count = cmd + .get_positionals() + .filter(|p| { + p.is_multiple_occurrences_set() + || (p.is_multiple_values_set() && p.num_vals.is_none()) + }) + .count(); + let ok = count <= 1 + || (last.is_last_set() + && last.is_multiple() + && second_to_last.is_multiple() + && count == 2); + assert!( + ok, + "Only one positional argument with .multiple_values(true) set is allowed per \ + command, unless the second one also has .last(true) set" + ); + } + + let mut found = false; + + if cmd.is_allow_missing_positional_set() { + // Check that if a required positional argument is found, all positions with a lower + // index are also required. + let mut foundx2 = false; + + for p in cmd.get_positionals() { + if foundx2 && !p.is_required_set() { + assert!( + p.is_required_set(), + "Found non-required positional argument with a lower \ + index than a required positional argument by two or more: {:?} \ + index {:?}", + p.name, + p.index + ); + } else if p.is_required_set() && !p.is_last_set() { + // Args that .last(true) don't count since they can be required and have + // positionals with a lower index that aren't required + // Imagine: prog [opt1] -- + // Both of these are valid invocations: + // $ prog r1 -- r2 + // $ prog r1 o1 -- r2 + if found { + foundx2 = true; + continue; + } + found = true; + continue; + } else { + found = false; + } + } + } else { + // Check that if a required positional argument is found, all positions with a lower + // index are also required + for p in (1..=num_p).rev().filter_map(|n| cmd.get_keymap().get(&n)) { + if found { + assert!( + p.is_required_set(), + "Found non-required positional argument with a lower \ + index than a required positional argument: {:?} index {:?}", + p.name, + p.index + ); + } else if p.is_required_set() && !p.is_last_set() { + // Args that .last(true) don't count since they can be required and have + // positionals with a lower index that aren't required + // Imagine: prog [opt1] -- + // Both of these are valid invocations: + // $ prog r1 -- r2 + // $ prog r1 o1 -- r2 + found = true; + continue; + } + } + } + assert!( + cmd.get_positionals().filter(|p| p.is_last_set()).count() < 2, + "Only one positional argument may have last(true) set. Found two." + ); + if cmd + .get_positionals() + .any(|p| p.is_last_set() && p.is_required_set()) + && cmd.has_subcommands() + && !cmd.is_subcommand_negates_reqs_set() + { + panic!( + "Having a required positional argument with .last(true) set *and* child \ + subcommands without setting SubcommandsNegateReqs isn't compatible." + ); + } + + true +} + +fn assert_arg(arg: &Arg) { + debug!("Arg::_debug_asserts:{}", arg.name); + + // Self conflict + // TODO: this check should be recursive + assert!( + !arg.blacklist.iter().any(|x| *x == arg.id), + "Argument '{}' cannot conflict with itself", + arg.name, + ); + + if arg.value_hint != ValueHint::Unknown { + assert!( + arg.is_takes_value_set(), + "Argument '{}' has value hint but takes no value", + arg.name + ); + + if arg.value_hint == ValueHint::CommandWithArguments { + assert!( + arg.is_multiple_values_set(), + "Argument '{}' uses hint CommandWithArguments and must accept multiple values", + arg.name + ) + } + } + + if arg.index.is_some() { + assert!( + arg.is_positional(), + "Argument '{}' is a positional argument and can't have short or long name versions", + arg.name + ); + } + + if arg.is_required_set() { + assert!( + arg.default_vals.is_empty(), + "Argument '{}' is required and can't have a default value", + arg.name + ); + } + + #[cfg(feature = "unstable-v4")] + { + let num_vals = arg.get_num_vals().unwrap_or(usize::MAX); + let num_val_names = arg.get_value_names().unwrap_or(&[]).len(); + if num_vals < num_val_names { + panic!( + "Argument {}: Too many value names ({}) compared to number_of_values ({})", + arg.name, num_val_names, num_vals + ); + } + } + + assert_arg_flags(arg); + + assert_defaults(arg, "default_value", arg.default_vals.iter().copied()); + assert_defaults( + arg, + "default_missing_value", + arg.default_missing_vals.iter().copied(), + ); + assert_defaults( + arg, + "default_value_if", + arg.default_vals_ifs + .iter() + .filter_map(|(_, _, default)| *default), + ); +} + +fn assert_arg_flags(arg: &Arg) { + macro_rules! checker { + ($a:ident requires $($b:ident)|+) => { + if arg.$a() { + let mut s = String::new(); + + $( + if !arg.$b() { + s.push_str(&format!(" Arg::{} is required when Arg::{} is set.\n", std::stringify!($b), std::stringify!($a))); + } + )+ + + if !s.is_empty() { + panic!("Argument {:?}\n{}", arg.get_id(), s) + } + } + } + } + + checker!(is_forbid_empty_values_set requires is_takes_value_set); + checker!(is_require_value_delimiter_set requires is_takes_value_set); + checker!(is_require_value_delimiter_set requires is_use_value_delimiter_set); + checker!(is_hide_possible_values_set requires is_takes_value_set); + checker!(is_allow_hyphen_values_set requires is_takes_value_set); + checker!(is_require_equals_set requires is_takes_value_set); + checker!(is_last_set requires is_takes_value_set); + checker!(is_hide_default_value_set requires is_takes_value_set); + checker!(is_multiple_values_set requires is_takes_value_set); + checker!(is_ignore_case_set requires is_takes_value_set); + checker!(is_allow_invalid_utf8_set requires is_takes_value_set); +} + +fn assert_defaults<'d>( + arg: &Arg, + field: &'static str, + defaults: impl IntoIterator, +) { + for default_os in defaults { + if let Some(default_s) = default_os.to_str() { + if !arg.possible_vals.is_empty() { + if let Some(delim) = arg.get_value_delimiter() { + for part in default_s.split(delim) { + assert!( + arg.possible_vals.iter().any(|possible_val| { + possible_val.matches(part, arg.is_ignore_case_set()) + }), + "Argument `{}`'s {}={} doesn't match possible values", + arg.name, + field, + part + ) + } + } else { + assert!( + arg.possible_vals.iter().any(|possible_val| { + possible_val.matches(default_s, arg.is_ignore_case_set()) + }), + "Argument `{}`'s {}={} doesn't match possible values", + arg.name, + field, + default_s + ); + } + } + + if let Some(validator) = arg.validator.as_ref() { + let mut validator = validator.lock().unwrap(); + if let Some(delim) = arg.get_value_delimiter() { + for part in default_s.split(delim) { + if let Err(err) = validator(part) { + panic!( + "Argument `{}`'s {}={} failed validation: {}", + arg.name, field, part, err + ); + } + } + } else if let Err(err) = validator(default_s) { + panic!( + "Argument `{}`'s {}={} failed validation: {}", + arg.name, field, default_s, err + ); + } + } + } + + if let Some(validator) = arg.validator_os.as_ref() { + let mut validator = validator.lock().unwrap(); + if let Some(delim) = arg.get_value_delimiter() { + let default_os = RawOsStr::new(default_os); + for part in default_os.split(delim) { + if let Err(err) = validator(&part.to_os_str()) { + panic!( + "Argument `{}`'s {}={:?} failed validation: {}", + arg.name, field, part, err + ); + } + } + } else if let Err(err) = validator(default_os) { + panic!( + "Argument `{}`'s {}={:?} failed validation: {}", + arg.name, field, default_os, err + ); + } + } + } +} diff --git a/third_party/rust/clap/src/build/mod.rs b/third_party/rust/clap/src/build/mod.rs index d23694dd1cab..ae8c46b6363e 100644 --- a/third_party/rust/clap/src/build/mod.rs +++ b/third_party/rust/clap/src/build/mod.rs @@ -1,14 +1,38 @@ #[macro_use] mod macros; -pub mod app; -pub mod arg; - +mod app_settings; +mod arg; mod arg_group; +mod arg_predicate; +mod arg_settings; +mod command; +mod possible_value; mod usage_parser; +mod value_hint; -pub use self::{ - app::{App, AppFlags, AppSettings}, - arg::{Arg, ArgFlags, ArgSettings, PossibleValue, ValueHint}, - arg_group::ArgGroup, -}; +#[cfg(feature = "regex")] +mod regex; + +#[cfg(debug_assertions)] +mod debug_asserts; + +#[cfg(test)] +mod tests; + +pub use app_settings::{AppFlags, AppSettings}; +pub use arg::Arg; +pub use arg_group::ArgGroup; +pub use arg_settings::{ArgFlags, ArgSettings}; +pub use command::Command; +pub use possible_value::PossibleValue; +pub use value_hint::ValueHint; + +#[allow(deprecated)] +pub use command::App; + +#[cfg(feature = "regex")] +pub use self::regex::RegexRef; + +pub(crate) use arg::display_arg_val; +pub(crate) use arg_predicate::ArgPredicate; diff --git a/third_party/rust/clap/src/build/arg/possible_value.rs b/third_party/rust/clap/src/build/possible_value.rs similarity index 78% rename from third_party/rust/clap/src/build/arg/possible_value.rs rename to third_party/rust/clap/src/build/possible_value.rs index 0bd795c3f3a7..e56fb858ce66 100644 --- a/third_party/rust/clap/src/build/arg/possible_value.rs +++ b/third_party/rust/clap/src/build/possible_value.rs @@ -1,4 +1,4 @@ -use std::iter; +use std::{borrow::Cow, iter}; use crate::util::eq_ignore_case; @@ -26,10 +26,10 @@ use crate::util::eq_ignore_case; /// [help]: PossibleValue::help() #[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct PossibleValue<'help> { - pub(crate) name: &'help str, - pub(crate) help: Option<&'help str>, - pub(crate) aliases: Vec<&'help str>, // (name, visible) - pub(crate) hide: bool, + name: &'help str, + help: Option<&'help str>, + aliases: Vec<&'help str>, // (name, visible) + hide: bool, } impl<'help> PossibleValue<'help> { @@ -148,13 +148,41 @@ impl<'help> PossibleValue<'help> { self.help } - /// Should the value be hidden from help messages and completion + /// Get the help specified for this argument, if any and the argument + /// value is not hidden #[inline] + #[cfg(feature = "unstable-v4")] + pub(crate) fn get_visible_help(&self) -> Option<&'help str> { + if !self.hide { + self.help + } else { + None + } + } + + /// Deprecated, replaced with [`PossibleValue::is_hide_set`] + #[inline] + #[deprecated(since = "3.1.0", note = "Replaced with `PossibleValue::is_hide_set`")] pub fn is_hidden(&self) -> bool { + self.is_hide_set() + } + + /// Report if [`PossibleValue::hide`] is set + #[inline] + pub fn is_hide_set(&self) -> bool { self.hide } + /// Report if PossibleValue is not hidden and has a help message + pub(crate) fn should_show_help(&self) -> bool { + !self.hide && self.help.is_some() + } + /// Get the name if argument value is not hidden, `None` otherwise + #[deprecated( + since = "3.1.4", + note = "Use `PossibleValue::is_hide_set` and `PossibleValue::get_name`" + )] pub fn get_visible_name(&self) -> Option<&'help str> { if self.hide { None @@ -163,6 +191,20 @@ impl<'help> PossibleValue<'help> { } } + /// Get the name if argument value is not hidden, `None` otherwise, + /// but wrapped in quotes if it contains whitespace + pub(crate) fn get_visible_quoted_name(&self) -> Option> { + if !self.hide { + Some(if self.name.contains(char::is_whitespace) { + format!("{:?}", self.name).into() + } else { + self.name.into() + }) + } else { + None + } + } + /// Returns all valid values of the argument value. /// /// Namely the name and all aliases. diff --git a/third_party/rust/clap/src/build/arg/regex.rs b/third_party/rust/clap/src/build/regex.rs similarity index 100% rename from third_party/rust/clap/src/build/arg/regex.rs rename to third_party/rust/clap/src/build/regex.rs diff --git a/third_party/rust/clap/src/build/tests.rs b/third_party/rust/clap/src/build/tests.rs new file mode 100644 index 000000000000..76c8b8785508 --- /dev/null +++ b/third_party/rust/clap/src/build/tests.rs @@ -0,0 +1,56 @@ +use crate::Arg; +use crate::Command; + +#[test] +fn propagate_version() { + let mut cmd = Command::new("test") + .propagate_version(true) + .version("1.1") + .subcommand(Command::new("sub1")); + cmd._propagate(); + assert_eq!( + cmd.get_subcommands().next().unwrap().get_version(), + Some("1.1") + ); +} + +#[test] +fn global_setting() { + let mut cmd = Command::new("test") + .disable_version_flag(true) + .subcommand(Command::new("subcmd")); + cmd._propagate(); + assert!(cmd + .get_subcommands() + .find(|s| s.get_name() == "subcmd") + .unwrap() + .is_disable_version_flag_set()); +} + +// This test will *fail to compile* if Command is not Send + Sync +#[test] +fn app_send_sync() { + fn foo(_: T) {} + foo(Command::new("test")) +} + +#[test] +fn issue_2090() { + let mut cmd = Command::new("cmd") + .disable_version_flag(true) + .subcommand(Command::new("sub")); + cmd._build_self(); + + assert!(cmd + .get_subcommands() + .next() + .unwrap() + .is_disable_version_flag_set()); +} + +// This test will *fail to compile* if Arg is not Send + Sync +#[test] +fn arg_send_sync() { + fn foo(_: T) {} + foo(Arg::new("test")) +} diff --git a/third_party/rust/clap/src/build/usage_parser.rs b/third_party/rust/clap/src/build/usage_parser.rs index 1da76e24f0e0..ccfad66bfab6 100644 --- a/third_party/rust/clap/src/build/usage_parser.rs +++ b/third_party/rust/clap/src/build/usage_parser.rs @@ -1,3 +1,5 @@ +#![allow(deprecated)] + // Internal use crate::{ build::{Arg, ArgSettings}, @@ -171,7 +173,7 @@ impl<'help> UsageParser<'help> { if dot_counter == 3 { debug!("UsageParser::multiple: setting multiple"); arg.settings.set(ArgSettings::MultipleOccurrences); - if arg.is_set(ArgSettings::TakesValue) { + if arg.is_takes_value_set() { arg.settings.set(ArgSettings::MultipleValues); arg.settings.set(ArgSettings::UseValueDelimiter); arg.val_delim.get_or_insert(','); @@ -253,7 +255,7 @@ mod test { assert_eq!(a.short.unwrap(), 'f'); assert!(a.long.is_none()); assert_eq!(a.help.unwrap(), "some help info"); - assert!(!a.is_set(ArgSettings::MultipleOccurrences)); + assert!(!a.is_multiple_occurrences_set()); assert!(a.val_names.is_empty()); let a = Arg::from_usage("[flag] --flag 'some help info'"); @@ -261,7 +263,7 @@ mod test { assert_eq!(a.long.unwrap(), "flag"); assert!(a.short.is_none()); assert_eq!(a.help.unwrap(), "some help info"); - assert!(!a.is_set(ArgSettings::MultipleOccurrences)); + assert!(!a.is_multiple_occurrences_set()); assert!(a.val_names.is_empty()); let a = Arg::from_usage("--flag 'some help info'"); @@ -269,7 +271,7 @@ mod test { assert_eq!(a.long.unwrap(), "flag"); assert!(a.short.is_none()); assert_eq!(a.help.unwrap(), "some help info"); - assert!(!a.is_set(ArgSettings::MultipleOccurrences)); + assert!(!a.is_multiple_occurrences_set()); assert!(a.val_names.is_empty()); let a = Arg::from_usage("[flag] -f --flag 'some help info'"); @@ -277,7 +279,7 @@ mod test { assert_eq!(a.short.unwrap(), 'f'); assert_eq!(a.long.unwrap(), "flag"); assert_eq!(a.help.unwrap(), "some help info"); - assert!(!a.is_set(ArgSettings::MultipleOccurrences)); + assert!(!a.is_multiple_occurrences_set()); assert!(a.val_names.is_empty()); let a = Arg::from_usage("[flag] -f... 'some help info'"); @@ -285,7 +287,7 @@ mod test { assert_eq!(a.short.unwrap(), 'f'); assert!(a.long.is_none()); assert_eq!(a.help.unwrap(), "some help info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); + assert!(a.is_multiple_occurrences_set()); assert!(a.val_names.is_empty()); let a = Arg::from_usage("[flag] -f --flag... 'some help info'"); @@ -293,7 +295,7 @@ mod test { assert_eq!(a.long.unwrap(), "flag"); assert_eq!(a.short.unwrap(), 'f'); assert_eq!(a.help.unwrap(), "some help info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); + assert!(a.is_multiple_occurrences_set()); assert!(a.val_names.is_empty()); let a = Arg::from_usage("-f --flag... 'some help info'"); @@ -301,7 +303,7 @@ mod test { assert_eq!(a.long.unwrap(), "flag"); assert_eq!(a.short.unwrap(), 'f'); assert_eq!(a.help.unwrap(), "some help info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); + assert!(a.is_multiple_occurrences_set()); assert!(a.val_names.is_empty()); let a = Arg::from_usage("--flags"); @@ -312,7 +314,7 @@ mod test { let a = Arg::from_usage("--flags..."); assert_eq!(a.name, "flags"); assert_eq!(a.long.unwrap(), "flags"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); + assert!(a.is_multiple_occurrences_set()); assert!(a.val_names.is_empty()); let a = Arg::from_usage("[flags] -f"); @@ -323,7 +325,7 @@ mod test { let a = Arg::from_usage("[flags] -f..."); assert_eq!(a.name, "flags"); assert_eq!(a.short.unwrap(), 'f'); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); + assert!(a.is_multiple_occurrences_set()); assert!(a.val_names.is_empty()); let a = Arg::from_usage("-f 'some help info'"); @@ -331,7 +333,7 @@ mod test { assert_eq!(a.short.unwrap(), 'f'); assert!(a.long.is_none()); assert_eq!(a.help.unwrap(), "some help info"); - assert!(!a.is_set(ArgSettings::MultipleOccurrences)); + assert!(!a.is_multiple_occurrences_set()); assert!(a.val_names.is_empty()); let a = Arg::from_usage("-f"); @@ -342,7 +344,7 @@ mod test { let a = Arg::from_usage("-f..."); assert_eq!(a.name, "f"); assert_eq!(a.short.unwrap(), 'f'); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); + assert!(a.is_multiple_occurrences_set()); assert!(a.val_names.is_empty()); } @@ -354,10 +356,10 @@ mod test { assert_eq!(a.short.unwrap(), 'o'); assert!(a.long.is_none()); assert_eq!(a.help.unwrap(), "some help info"); - assert!(!a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(!a.is_set(ArgSettings::Required)); + assert!(!a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(!a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"opt"]); } @@ -368,10 +370,10 @@ mod test { assert_eq!(a.short.unwrap(), 'o'); assert!(a.long.is_none()); assert_eq!(a.help.unwrap(), "some help info"); - assert!(!a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(!a.is_set(ArgSettings::Required)); + assert!(!a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(!a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"opt"]); } @@ -382,10 +384,10 @@ mod test { assert_eq!(a.short.unwrap(), 'o'); assert!(a.long.is_none()); assert_eq!(a.help.unwrap(), "some help info"); - assert!(!a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(a.is_set(ArgSettings::Required)); + assert!(!a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"opt"]); } @@ -396,10 +398,10 @@ mod test { assert_eq!(a.short.unwrap(), 'o'); assert!(a.long.is_none()); assert_eq!(a.help.unwrap(), "some help info"); - assert!(!a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(a.is_set(ArgSettings::Required)); + assert!(!a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"opt"]); } @@ -410,10 +412,10 @@ mod test { assert_eq!(a.short.unwrap(), 'o'); assert!(a.long.is_none()); assert_eq!(a.help.unwrap(), "some help info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); - assert!(a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(!a.is_set(ArgSettings::Required)); + assert!(a.is_multiple_occurrences_set()); + assert!(a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(!a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"opt"]); } @@ -424,10 +426,10 @@ mod test { assert_eq!(a.short.unwrap(), 'o'); assert!(a.long.is_none()); assert_eq!(a.help.unwrap(), "some help info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(!a.is_set(ArgSettings::Required)); + assert!(a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(!a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"opt"]); } @@ -438,10 +440,10 @@ mod test { assert_eq!(a.short.unwrap(), 'o'); assert!(a.long.is_none()); assert_eq!(a.help.unwrap(), "some help info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); - assert!(a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(!a.is_set(ArgSettings::Required)); + assert!(a.is_multiple_occurrences_set()); + assert!(a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(!a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"opt"]); } @@ -452,10 +454,10 @@ mod test { assert_eq!(a.short.unwrap(), 'o'); assert!(a.long.is_none()); assert_eq!(a.help.unwrap(), "some help info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); - assert!(a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(a.is_set(ArgSettings::Required)); + assert!(a.is_multiple_occurrences_set()); + assert!(a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"opt"]); } @@ -466,10 +468,10 @@ mod test { assert_eq!(a.short.unwrap(), 'o'); assert!(a.long.is_none()); assert_eq!(a.help.unwrap(), "some help info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(a.is_set(ArgSettings::Required)); + assert!(a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"opt"]); } @@ -480,10 +482,10 @@ mod test { assert_eq!(a.short.unwrap(), 'o'); assert!(a.long.is_none()); assert_eq!(a.help.unwrap(), "some help info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); - assert!(a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(a.is_set(ArgSettings::Required)); + assert!(a.is_multiple_occurrences_set()); + assert!(a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"opt"]); } @@ -494,10 +496,10 @@ mod test { assert_eq!(a.long.unwrap(), "opt"); assert!(a.short.is_none()); assert_eq!(a.help.unwrap(), "some help info"); - assert!(!a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(!a.is_set(ArgSettings::Required)); + assert!(!a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(!a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"opt"]); } @@ -508,10 +510,10 @@ mod test { assert_eq!(a.long.unwrap(), "opt"); assert!(a.short.is_none()); assert_eq!(a.help.unwrap(), "some help info"); - assert!(!a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(!a.is_set(ArgSettings::Required)); + assert!(!a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(!a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"option"]); } @@ -522,10 +524,10 @@ mod test { assert_eq!(a.long.unwrap(), "opt"); assert!(a.short.is_none()); assert_eq!(a.help.unwrap(), "some help info"); - assert!(!a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(a.is_set(ArgSettings::Required)); + assert!(!a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"opt"]); } @@ -536,10 +538,10 @@ mod test { assert_eq!(a.long.unwrap(), "opt"); assert!(a.short.is_none()); assert_eq!(a.help.unwrap(), "some help info"); - assert!(!a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(a.is_set(ArgSettings::Required)); + assert!(!a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"option"]); } @@ -550,10 +552,10 @@ mod test { assert_eq!(a.long.unwrap(), "opt"); assert!(a.short.is_none()); assert_eq!(a.help.unwrap(), "some help info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); - assert!(a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(!a.is_set(ArgSettings::Required)); + assert!(a.is_multiple_occurrences_set()); + assert!(a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(!a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"opt"]); } @@ -564,10 +566,10 @@ mod test { assert_eq!(a.long.unwrap(), "opt"); assert!(a.short.is_none()); assert_eq!(a.help.unwrap(), "some help info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(!a.is_set(ArgSettings::Required)); + assert!(a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(!a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"opt"]); } @@ -578,10 +580,10 @@ mod test { assert_eq!(a.long.unwrap(), "opt"); assert!(a.short.is_none()); assert_eq!(a.help.unwrap(), "some help info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); - assert!(a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(!a.is_set(ArgSettings::Required)); + assert!(a.is_multiple_occurrences_set()); + assert!(a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(!a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"option"]); } @@ -592,10 +594,10 @@ mod test { assert_eq!(a.long.unwrap(), "opt"); assert!(a.short.is_none()); assert_eq!(a.help.unwrap(), "some help info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); - assert!(a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(a.is_set(ArgSettings::Required)); + assert!(a.is_multiple_occurrences_set()); + assert!(a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"opt"]); } @@ -606,10 +608,10 @@ mod test { assert_eq!(a.long.unwrap(), "opt"); assert!(a.short.is_none()); assert_eq!(a.help.unwrap(), "some help info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(a.is_set(ArgSettings::Required)); + assert!(a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"opt"]); } @@ -620,10 +622,10 @@ mod test { assert_eq!(a.long.unwrap(), "opt"); assert!(a.short.is_none()); assert_eq!(a.help.unwrap(), "some help info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); - assert!(a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(a.is_set(ArgSettings::Required)); + assert!(a.is_multiple_occurrences_set()); + assert!(a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"option"]); } @@ -634,10 +636,10 @@ mod test { assert_eq!(a.long.unwrap(), "opt"); assert!(a.short.is_none()); assert_eq!(a.help.unwrap(), "some help info"); - assert!(!a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(!a.is_set(ArgSettings::Required)); + assert!(!a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(!a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"opt"]); } @@ -648,10 +650,10 @@ mod test { assert_eq!(a.long.unwrap(), "opt"); assert!(a.short.is_none()); assert_eq!(a.help.unwrap(), "some help info"); - assert!(!a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(!a.is_set(ArgSettings::Required)); + assert!(!a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(!a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"option"]); } @@ -662,10 +664,10 @@ mod test { assert_eq!(a.long.unwrap(), "opt"); assert!(a.short.is_none()); assert_eq!(a.help.unwrap(), "some help info"); - assert!(!a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(a.is_set(ArgSettings::Required)); + assert!(!a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"opt"]); } @@ -676,10 +678,10 @@ mod test { assert_eq!(a.long.unwrap(), "opt"); assert!(a.short.is_none()); assert_eq!(a.help.unwrap(), "some help info"); - assert!(!a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(a.is_set(ArgSettings::Required)); + assert!(!a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"option"]); } @@ -690,10 +692,10 @@ mod test { assert_eq!(a.long.unwrap(), "opt"); assert!(a.short.is_none()); assert_eq!(a.help.unwrap(), "some help info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); - assert!(a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(!a.is_set(ArgSettings::Required)); + assert!(a.is_multiple_occurrences_set()); + assert!(a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(!a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"opt"]); } @@ -704,10 +706,10 @@ mod test { assert_eq!(a.long.unwrap(), "opt"); assert!(a.short.is_none()); assert_eq!(a.help.unwrap(), "some help info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(!a.is_set(ArgSettings::Required)); + assert!(a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(!a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"opt"]); } @@ -718,10 +720,10 @@ mod test { assert_eq!(a.long.unwrap(), "opt"); assert!(a.short.is_none()); assert_eq!(a.help.unwrap(), "some help info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); - assert!(a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(!a.is_set(ArgSettings::Required)); + assert!(a.is_multiple_occurrences_set()); + assert!(a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(!a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"option"]); } @@ -732,10 +734,10 @@ mod test { assert_eq!(a.long.unwrap(), "opt"); assert!(a.short.is_none()); assert_eq!(a.help.unwrap(), "some help info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); - assert!(a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(a.is_set(ArgSettings::Required)); + assert!(a.is_multiple_occurrences_set()); + assert!(a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"opt"]); } @@ -746,10 +748,10 @@ mod test { assert_eq!(a.long.unwrap(), "opt"); assert!(a.short.is_none()); assert_eq!(a.help.unwrap(), "some help info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(a.is_set(ArgSettings::Required)); + assert!(a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"opt"]); } @@ -760,10 +762,10 @@ mod test { assert_eq!(a.long.unwrap(), "opt"); assert!(a.short.is_none()); assert_eq!(a.help.unwrap(), "some help info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); - assert!(a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(a.is_set(ArgSettings::Required)); + assert!(a.is_multiple_occurrences_set()); + assert!(a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"option"]); } @@ -774,10 +776,10 @@ mod test { assert_eq!(a.long.unwrap(), "opt"); assert_eq!(a.short.unwrap(), 'o'); assert_eq!(a.help.unwrap(), "some help info"); - assert!(!a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(!a.is_set(ArgSettings::Required)); + assert!(!a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(!a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"option"]); } @@ -788,10 +790,10 @@ mod test { assert_eq!(a.long.unwrap(), "opt"); assert_eq!(a.short.unwrap(), 'o'); assert_eq!(a.help.unwrap(), "some help info"); - assert!(!a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(!a.is_set(ArgSettings::Required)); + assert!(!a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(!a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"option"]); } @@ -802,10 +804,10 @@ mod test { assert_eq!(a.long.unwrap(), "opt"); assert_eq!(a.short.unwrap(), 'o'); assert_eq!(a.help.unwrap(), "some help info"); - assert!(!a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(a.is_set(ArgSettings::Required)); + assert!(!a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"opt"]); } @@ -816,10 +818,10 @@ mod test { assert_eq!(a.long.unwrap(), "opt"); assert_eq!(a.short.unwrap(), 'o'); assert_eq!(a.help.unwrap(), "some help info"); - assert!(!a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(a.is_set(ArgSettings::Required)); + assert!(!a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"option"]); } @@ -830,10 +832,10 @@ mod test { assert_eq!(a.long.unwrap(), "opt"); assert_eq!(a.short.unwrap(), 'o'); assert_eq!(a.help.unwrap(), "some help info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(!a.is_set(ArgSettings::Required)); + assert!(a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(!a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"option"]); } @@ -844,10 +846,10 @@ mod test { assert_eq!(a.long.unwrap(), "opt"); assert_eq!(a.short.unwrap(), 'o'); assert_eq!(a.help.unwrap(), "some help info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); - assert!(a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(!a.is_set(ArgSettings::Required)); + assert!(a.is_multiple_occurrences_set()); + assert!(a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(!a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"option"]); } @@ -858,10 +860,10 @@ mod test { assert_eq!(a.long.unwrap(), "opt"); assert_eq!(a.short.unwrap(), 'o'); assert_eq!(a.help.unwrap(), "some help info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(a.is_set(ArgSettings::Required)); + assert!(a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"opt"]); } @@ -872,10 +874,10 @@ mod test { assert_eq!(a.long.unwrap(), "opt"); assert_eq!(a.short.unwrap(), 'o'); assert_eq!(a.help.unwrap(), "some help info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); - assert!(a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(a.is_set(ArgSettings::Required)); + assert!(a.is_multiple_occurrences_set()); + assert!(a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"option"]); } @@ -886,10 +888,10 @@ mod test { assert_eq!(a.long.unwrap(), "opt"); assert_eq!(a.short.unwrap(), 'o'); assert_eq!(a.help.unwrap(), "some help info"); - assert!(!a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(!a.is_set(ArgSettings::Required)); + assert!(!a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(!a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"option"]); } @@ -900,10 +902,10 @@ mod test { assert_eq!(a.long.unwrap(), "opt"); assert_eq!(a.short.unwrap(), 'o'); assert_eq!(a.help.unwrap(), "some help info"); - assert!(!a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(!a.is_set(ArgSettings::Required)); + assert!(!a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(!a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"option"]); } @@ -914,10 +916,10 @@ mod test { assert_eq!(a.long.unwrap(), "opt"); assert_eq!(a.short.unwrap(), 'o'); assert_eq!(a.help.unwrap(), "some help info"); - assert!(!a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(a.is_set(ArgSettings::Required)); + assert!(!a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"opt"]); } @@ -928,10 +930,10 @@ mod test { assert_eq!(a.long.unwrap(), "opt"); assert_eq!(a.short.unwrap(), 'o'); assert_eq!(a.help.unwrap(), "some help info"); - assert!(!a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(a.is_set(ArgSettings::Required)); + assert!(!a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"option"]); } @@ -942,10 +944,10 @@ mod test { assert_eq!(a.long.unwrap(), "opt"); assert_eq!(a.short.unwrap(), 'o'); assert_eq!(a.help.unwrap(), "some help info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(!a.is_set(ArgSettings::Required)); + assert!(a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(!a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"option"]); } @@ -956,10 +958,10 @@ mod test { assert_eq!(a.long.unwrap(), "opt"); assert_eq!(a.short.unwrap(), 'o'); assert_eq!(a.help.unwrap(), "some help info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); - assert!(a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(!a.is_set(ArgSettings::Required)); + assert!(a.is_multiple_occurrences_set()); + assert!(a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(!a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"option"]); } @@ -970,10 +972,10 @@ mod test { assert_eq!(a.long.unwrap(), "opt"); assert_eq!(a.short.unwrap(), 'o'); assert_eq!(a.help.unwrap(), "some help info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(a.is_set(ArgSettings::Required)); + assert!(a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"opt"]); } @@ -984,10 +986,10 @@ mod test { assert_eq!(a.long.unwrap(), "opt"); assert_eq!(a.short.unwrap(), 'o'); assert_eq!(a.help.unwrap(), "some help info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); - assert!(a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(a.is_set(ArgSettings::Required)); + assert!(a.is_multiple_occurrences_set()); + assert!(a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"option"]); } @@ -998,10 +1000,10 @@ mod test { assert!(a.long.is_none()); assert_eq!(a.short.unwrap(), 'o'); assert_eq!(a.help.unwrap(), "some help info"); - assert!(!a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(a.is_set(ArgSettings::Required)); + assert!(!a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"file", &"mode"]); } @@ -1012,10 +1014,10 @@ mod test { assert!(a.long.is_none()); assert_eq!(a.short.unwrap(), 'o'); assert_eq!(a.help.unwrap(), "some help info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); - assert!(a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(a.is_set(ArgSettings::Required)); + assert!(a.is_multiple_occurrences_set()); + assert!(a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"file", &"mode"]); } @@ -1026,10 +1028,10 @@ mod test { assert!(a.short.is_none()); assert_eq!(a.long.unwrap(), "opt"); assert_eq!(a.help.unwrap(), "some help info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); - assert!(a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(a.is_set(ArgSettings::Required)); + assert!(a.is_multiple_occurrences_set()); + assert!(a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"file", &"mode"]); } @@ -1040,10 +1042,10 @@ mod test { assert!(a.short.is_none()); assert_eq!(a.long.unwrap(), "opt"); assert_eq!(a.help.unwrap(), "some help info"); - assert!(!a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(!a.is_set(ArgSettings::Required)); + assert!(!a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(!a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"file", &"mode"]); } @@ -1054,10 +1056,10 @@ mod test { assert!(a.short.is_none()); assert_eq!(a.long.unwrap(), "opt"); assert_eq!(a.help.unwrap(), "some help info"); - assert!(!a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(a.is_set(ArgSettings::Required)); + assert!(!a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(a.is_required_set()); } #[test] @@ -1065,9 +1067,9 @@ mod test { let a = Arg::from_usage("[pos] 'some help info'"); assert_eq!(a.name, "pos"); assert_eq!(a.help.unwrap(), "some help info"); - assert!(!a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(!a.is_set(ArgSettings::Required)); + assert!(!a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(!a.is_required_set()); assert!(a.val_names.is_empty()); } @@ -1076,9 +1078,9 @@ mod test { let a = Arg::from_usage(" 'some help info'"); assert_eq!(a.name, "pos"); assert_eq!(a.help.unwrap(), "some help info"); - assert!(!a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::Required)); + assert!(!a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_required_set()); assert!(a.val_names.is_empty()); } @@ -1087,9 +1089,9 @@ mod test { let a = Arg::from_usage("[pos]... 'some help info'"); assert_eq!(a.name, "pos"); assert_eq!(a.help.unwrap(), "some help info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(!a.is_set(ArgSettings::Required)); + assert!(a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(!a.is_required_set()); assert!(a.val_names.is_empty()); } @@ -1098,9 +1100,9 @@ mod test { let a = Arg::from_usage("[pos]... 'some help\' info'"); assert_eq!(a.name, "pos"); assert_eq!(a.help.unwrap(), "some help' info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(!a.is_set(ArgSettings::Required)); + assert!(a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(!a.is_required_set()); assert!(a.val_names.is_empty()); } @@ -1109,9 +1111,9 @@ mod test { let a = Arg::from_usage("[pos]... 'some \'help\' info'"); assert_eq!(a.name, "pos"); assert_eq!(a.help.unwrap(), "some 'help' info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(!a.is_set(ArgSettings::Required)); + assert!(a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(!a.is_required_set()); assert!(a.val_names.is_empty()); } @@ -1123,9 +1125,9 @@ mod test { ); assert_eq!(a.name, "pos"); assert_eq!(a.help.unwrap(), "some help{n}info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(!a.is_set(ArgSettings::Required)); + assert!(a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(!a.is_required_set()); assert!(a.val_names.is_empty()); } @@ -1137,9 +1139,9 @@ mod test { ); assert_eq!(a.name, "pos"); assert_eq!(a.help.unwrap(), "some help' stuff{n}info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(!a.is_set(ArgSettings::Required)); + assert!(a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(!a.is_required_set()); assert!(a.val_names.is_empty()); } @@ -1148,9 +1150,9 @@ mod test { let a = Arg::from_usage("... 'some help info'"); assert_eq!(a.name, "pos"); assert_eq!(a.help.unwrap(), "some help info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::Required)); + assert!(a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_required_set()); assert!(a.val_names.is_empty()); } @@ -1158,9 +1160,9 @@ mod test { fn pos_req() { let a = Arg::from_usage(""); assert_eq!(a.name, "pos"); - assert!(!a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::Required)); + assert!(!a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_required_set()); assert!(a.val_names.is_empty()); } @@ -1168,9 +1170,9 @@ mod test { fn pos_mult() { let a = Arg::from_usage("[pos]..."); assert_eq!(a.name, "pos"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(!a.is_set(ArgSettings::Required)); + assert!(a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(!a.is_required_set()); assert!(a.val_names.is_empty()); } @@ -1179,9 +1181,9 @@ mod test { let a = Arg::from_usage("... @a 'some help info'"); assert_eq!(a.name, "pos"); assert_eq!(a.help.unwrap(), "some help info"); - assert!(a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::Required)); + assert!(a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_required_set()); assert!(a.val_names.is_empty()); assert_eq!(a.default_vals, vec![std::ffi::OsStr::new("a")]); } @@ -1193,10 +1195,10 @@ mod test { assert!(a.long.is_none()); assert_eq!(a.short.unwrap(), 'o'); assert_eq!(a.help.unwrap(), "some help info"); - assert!(!a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(a.is_set(ArgSettings::Required)); + assert!(!a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"file", &"mode"]); assert_eq!(a.default_vals, vec![std::ffi::OsStr::new("a")]); } @@ -1208,10 +1210,10 @@ mod test { assert!(a.short.is_none()); assert_eq!(a.long.unwrap(), "opt"); assert_eq!(a.help.unwrap(), "some help info"); - assert!(!a.is_set(ArgSettings::MultipleOccurrences)); - assert!(!a.is_set(ArgSettings::MultipleValues)); - assert!(a.is_set(ArgSettings::TakesValue)); - assert!(!a.is_set(ArgSettings::Required)); + assert!(!a.is_multiple_occurrences_set()); + assert!(!a.is_multiple_values_set()); + assert!(a.is_takes_value_set()); + assert!(!a.is_required_set()); assert_eq!(a.val_names.iter().collect::>(), [&"file", &"mode"]); assert_eq!(a.default_vals, vec![std::ffi::OsStr::new("a")]); } @@ -1244,12 +1246,12 @@ mod test { #[test] fn value_names_building_num_vals_from_usage() { - use crate::App; - let m = App::new("test") + use crate::Command; + let m = Command::new("test") .arg(Arg::from_usage("--pos ")) .try_get_matches_from(vec!["myprog", "--pos", "val1", "val2", "val3"]); - assert!(m.is_ok(), "{:?}", m.unwrap_err().kind); + assert!(m.is_ok(), "{:?}", m.unwrap_err().kind()); let m = m.unwrap(); assert_eq!( @@ -1260,9 +1262,9 @@ mod test { #[test] fn issue_665() { - use crate::{App, ErrorKind}; + use crate::{error::ErrorKind, Command}; // Verify fix for "arg_from_usage(): required values not being enforced when followed by another option" - let res = App::new("tester") + let res = Command::new("tester") .arg(Arg::from_usage("-v, --reroll-count=[N] 'Mark the patch series as PATCH vN'")) .arg( Arg::from_usage("--subject-prefix [Subject-Prefix] 'Use [Subject-Prefix] instead of the standard [PATCH] prefix'") @@ -1271,6 +1273,6 @@ mod test { .try_get_matches_from(vec!["test", "--subject-prefix", "-v", "2"]); assert!(res.is_err()); - assert_eq!(res.unwrap_err().kind, ErrorKind::EmptyValue); + assert_eq!(res.unwrap_err().kind(), ErrorKind::EmptyValue); } } diff --git a/third_party/rust/clap/src/build/arg/value_hint.rs b/third_party/rust/clap/src/build/value_hint.rs similarity index 93% rename from third_party/rust/clap/src/build/arg/value_hint.rs rename to third_party/rust/clap/src/build/value_hint.rs index b61a3e778e05..7c35d1eb3e55 100644 --- a/third_party/rust/clap/src/build/arg/value_hint.rs +++ b/third_party/rust/clap/src/build/value_hint.rs @@ -24,7 +24,7 @@ use std::str::FromStr; /// /// [^1]: fish completions currently only support named arguments (e.g. -o or --opt), not /// positional arguments. -#[derive(Debug, PartialEq, Copy, Clone)] +#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] #[non_exhaustive] pub enum ValueHint { /// Default value if hint is not specified. Follows shell default behavior, which is usually @@ -48,11 +48,11 @@ pub enum ValueHint { /// common when writing shell wrappers that execute anther command, for example `sudo` or `env`. /// /// This hint is special, the argument must be a positional argument and have - /// [`.multiple_values(true)`] and App must use [`AppSettings::TrailingVarArg`]. The result is that the + /// [`.multiple_values(true)`] and Command must use [`Command::trailing_var_arg(true)`]. The result is that the /// command line `my_app ls -la /` will be parsed as `["ls", "-la", "/"]` and clap won't try to /// parse the `-la` argument itself. /// - /// [`AppSettings::TrailingVarArg`]: crate::AppSettings::TrailingVarArg + /// [`Command::trailing_var_arg(true)`]: crate::Command::trailing_var_arg /// [`.multiple_values(true)`]: crate::Arg::multiple_values() CommandWithArguments, /// Name of a local operating system user. diff --git a/third_party/rust/clap/src/derive.rs b/third_party/rust/clap/src/derive.rs index c629c01b7627..110af93727d0 100644 --- a/third_party/rust/clap/src/derive.rs +++ b/third_party/rust/clap/src/derive.rs @@ -1,18 +1,18 @@ //! This module contains traits that are usable with the `#[derive(...)].` //! macros in [`clap_derive`]. -use crate::{App, ArgMatches, Error, PossibleValue}; +use crate::{ArgMatches, Command, Error, PossibleValue}; use std::ffi::OsString; /// Parse command-line arguments into `Self`. /// /// The primary one-stop-shop trait used to create an instance of a `clap` -/// [`App`], conduct the parsing, and turn the resulting [`ArgMatches`] back +/// [`Command`], conduct the parsing, and turn the resulting [`ArgMatches`] back /// into concrete instance of the user struct. /// /// This trait is primarily a convenience on top of [`FromArgMatches`] + -/// [`IntoApp`] which uses those two underlying traits to build the two +/// [`CommandFactory`] which uses those two underlying traits to build the two /// fundamental functions `parse` which uses the `std::env::args_os` iterator, /// and `parse_from` which allows the consumer to supply the iterator (along /// with fallible options for each). @@ -20,7 +20,7 @@ use std::ffi::OsString; /// See also [`Subcommand`] and [`Args`]. /// /// See the -/// [derive reference](https://github.com/clap-rs/clap/blob/v3.0.10/examples/derive_ref/README.md) +/// [derive reference](https://github.com/clap-rs/clap/blob/v3.1.18/examples/derive_ref/README.md) /// for attributes and best practices. /// /// **NOTE:** Deriving requires the `derive` feature flag @@ -46,11 +46,11 @@ use std::ffi::OsString; /// } /// ``` /// -/// The equivalent [`App`] struct + `From` implementation: +/// The equivalent [`Command`] struct + `From` implementation: /// /// ```rust -/// # use clap::{App, Arg, ArgMatches}; -/// App::new("demo") +/// # use clap::{Command, Arg, ArgMatches}; +/// Command::new("demo") /// .about("My super CLI") /// .arg(Arg::new("verbose") /// .long("verbose") @@ -76,10 +76,10 @@ use std::ffi::OsString; /// } /// ``` /// -pub trait Parser: FromArgMatches + IntoApp + Sized { +pub trait Parser: FromArgMatches + CommandFactory + Sized { /// Parse from `std::env::args_os()`, exit on error fn parse() -> Self { - let matches = ::into_app().get_matches(); + let matches = ::command().get_matches(); let res = ::from_arg_matches(&matches).map_err(format_error::); match res { @@ -94,7 +94,7 @@ pub trait Parser: FromArgMatches + IntoApp + Sized { /// Parse from `std::env::args_os()`, return Err on error. fn try_parse() -> Result { - let matches = ::into_app().try_get_matches()?; + let matches = ::command().try_get_matches()?; ::from_arg_matches(&matches).map_err(format_error::) } @@ -104,7 +104,7 @@ pub trait Parser: FromArgMatches + IntoApp + Sized { I: IntoIterator, T: Into + Clone, { - let matches = ::into_app().get_matches_from(itr); + let matches = ::command().get_matches_from(itr); let res = ::from_arg_matches(&matches).map_err(format_error::); match res { @@ -123,7 +123,7 @@ pub trait Parser: FromArgMatches + IntoApp + Sized { I: IntoIterator, T: Into + Clone, { - let matches = ::into_app().try_get_matches_from(itr)?; + let matches = ::command().try_get_matches_from(itr)?; ::from_arg_matches(&matches).map_err(format_error::) } @@ -133,7 +133,7 @@ pub trait Parser: FromArgMatches + IntoApp + Sized { I: IntoIterator, T: Into + Clone, { - let matches = ::into_app_for_update().get_matches_from(itr); + let matches = ::command_for_update().get_matches_from(itr); let res = ::update_from_arg_matches(self, &matches) .map_err(format_error::); if let Err(e) = res { @@ -149,19 +149,20 @@ pub trait Parser: FromArgMatches + IntoApp + Sized { I: IntoIterator, T: Into + Clone, { - let matches = ::into_app_for_update().try_get_matches_from(itr)?; + let matches = ::command_for_update().try_get_matches_from(itr)?; ::update_from_arg_matches(self, &matches) .map_err(format_error::) } - /// Deprecated, `StructOpt::clap` replaced with [`IntoApp::into_app`] (derive as part of + /// Deprecated, `StructOpt::clap` replaced with [`IntoCommand::command`] (derive as part of /// [`Parser`]) #[deprecated( since = "3.0.0", - note = "`StructOpt::clap` is replaced with `IntoApp::into_app` (derived as part of `Parser`)" + note = "`StructOpt::clap` is replaced with `IntoCommand::command` (derived as part of `Parser`)" )] - fn clap<'help>() -> App<'help> { - ::into_app() + #[doc(hidden)] + fn clap<'help>() -> Command<'help> { + ::command() } /// Deprecated, `StructOpt::from_clap` replaced with [`FromArgMatches::from_arg_matches`] (derive as part of @@ -170,6 +171,7 @@ pub trait Parser: FromArgMatches + IntoApp + Sized { since = "3.0.0", note = "`StructOpt::from_clap` is replaced with `FromArgMatches::from_arg_matches` (derived as part of `Parser`)" )] + #[doc(hidden)] fn from_clap(matches: &ArgMatches) -> Self { ::from_arg_matches(matches).unwrap() } @@ -179,6 +181,7 @@ pub trait Parser: FromArgMatches + IntoApp + Sized { since = "3.0.0", note = "`StructOpt::from_args` is replaced with `Parser::parse` (note the change in derives)" )] + #[doc(hidden)] fn from_args() -> Self { Self::parse() } @@ -188,6 +191,7 @@ pub trait Parser: FromArgMatches + IntoApp + Sized { since = "3.0.0", note = "`StructOpt::from_args_safe` is replaced with `Parser::try_parse` (note the change in derives)" )] + #[doc(hidden)] fn from_args_safe() -> Result { Self::try_parse() } @@ -197,6 +201,7 @@ pub trait Parser: FromArgMatches + IntoApp + Sized { since = "3.0.0", note = "`StructOpt::from_iter` is replaced with `Parser::parse_from` (note the change in derives)" )] + #[doc(hidden)] fn from_iter(itr: I) -> Self where I: IntoIterator, @@ -211,6 +216,7 @@ pub trait Parser: FromArgMatches + IntoApp + Sized { since = "3.0.0", note = "`StructOpt::from_iter_safe` is replaced with `Parser::try_parse_from` (note the change in derives)" )] + #[doc(hidden)] fn from_iter_safe(itr: I) -> Result where I: IntoIterator, @@ -220,18 +226,33 @@ pub trait Parser: FromArgMatches + IntoApp + Sized { } } -/// Create an [`App`] relevant for a user-defined container. +/// Create a [`Command`] relevant for a user-defined container. /// /// Derived as part of [`Parser`]. -pub trait IntoApp: Sized { - /// Build an [`App`] that can instantiate `Self`. +pub trait CommandFactory: Sized { + /// Build a [`Command`] that can instantiate `Self`. /// /// See [`FromArgMatches::from_arg_matches`] for instantiating `Self`. - fn into_app<'help>() -> App<'help>; - /// Build an [`App`] that can update `self`. + fn command<'help>() -> Command<'help> { + #[allow(deprecated)] + Self::into_app() + } + /// Deprecated, replaced with `CommandFactory::command` + #[deprecated(since = "3.1.0", note = "Replaced with `CommandFactory::command")] + fn into_app<'help>() -> Command<'help>; + /// Build a [`Command`] that can update `self`. /// /// See [`FromArgMatches::update_from_arg_matches`] for updating `self`. - fn into_app_for_update<'help>() -> App<'help>; + fn command_for_update<'help>() -> Command<'help> { + #[allow(deprecated)] + Self::into_app_for_update() + } + /// Deprecated, replaced with `CommandFactory::command_for_update` + #[deprecated( + since = "3.1.0", + note = "Replaced with `CommandFactory::command_for_update" + )] + fn into_app_for_update<'help>() -> Command<'help>; } /// Converts an instance of [`ArgMatches`] to a user-defined container. @@ -285,7 +306,7 @@ pub trait FromArgMatches: Sized { /// - `Variant(ChildArgs)`: No attribute is used with enum variants that impl `Args`. /// /// See the -/// [derive reference](https://github.com/clap-rs/clap/blob/v3.0.10/examples/derive_ref/README.md) +/// [derive reference](https://github.com/clap-rs/clap/blob/v3.1.18/examples/derive_ref/README.md) /// for attributes and best practices. /// /// **NOTE:** Deriving requires the `derive` feature flag @@ -307,16 +328,16 @@ pub trait FromArgMatches: Sized { /// } /// ``` pub trait Args: FromArgMatches + Sized { - /// Append to [`App`] so it can instantiate `Self`. + /// Append to [`Command`] so it can instantiate `Self`. /// - /// See also [`IntoApp`]. - fn augment_args(app: App<'_>) -> App<'_>; - /// Append to [`App`] so it can update `self`. + /// See also [`CommandFactory`]. + fn augment_args(cmd: Command<'_>) -> Command<'_>; + /// Append to [`Command`] so it can update `self`. /// /// This is used to implement `#[clap(flatten)]` /// - /// See also [`IntoApp`]. - fn augment_args_for_update(app: App<'_>) -> App<'_>; + /// See also [`CommandFactory`]. + fn augment_args_for_update(cmd: Command<'_>) -> Command<'_>; } /// Parse a sub-command into a user-defined enum. @@ -329,7 +350,7 @@ pub trait Args: FromArgMatches + Sized { /// `Subcommand`. /// /// See the -/// [derive reference](https://github.com/clap-rs/clap/blob/v3.0.10/examples/derive_ref/README.md) +/// [derive reference](https://github.com/clap-rs/clap/blob/v3.1.18/examples/derive_ref/README.md) /// for attributes and best practices. /// /// **NOTE:** Deriving requires the `derive` feature flag @@ -351,16 +372,16 @@ pub trait Args: FromArgMatches + Sized { /// } /// ``` pub trait Subcommand: FromArgMatches + Sized { - /// Append to [`App`] so it can instantiate `Self`. + /// Append to [`Command`] so it can instantiate `Self`. /// - /// See also [`IntoApp`]. - fn augment_subcommands(app: App<'_>) -> App<'_>; - /// Append to [`App`] so it can update `self`. + /// See also [`CommandFactory`]. + fn augment_subcommands(cmd: Command<'_>) -> Command<'_>; + /// Append to [`Command`] so it can update `self`. /// /// This is used to implement `#[clap(flatten)]` /// - /// See also [`IntoApp`]. - fn augment_subcommands_for_update(app: App<'_>) -> App<'_>; + /// See also [`CommandFactory`]. + fn augment_subcommands_for_update(cmd: Command<'_>) -> Command<'_>; /// Test whether `Self` can parse a specific subcommand fn has_subcommand(name: &str) -> bool; } @@ -373,7 +394,7 @@ pub trait Subcommand: FromArgMatches + Sized { /// - Allowing using the `#[clap(default_value_t)]` attribute without implementing `Display`. /// /// See the -/// [derive reference](https://github.com/clap-rs/clap/blob/v3.0.10/examples/derive_ref/README.md) +/// [derive reference](https://github.com/clap-rs/clap/blob/v3.1.18/examples/derive_ref/README.md) /// for attributes and best practices. /// /// **NOTE:** Deriving requires the `derive` feature flag @@ -445,12 +466,13 @@ impl Parser for Box { } } -impl IntoApp for Box { - fn into_app<'help>() -> App<'help> { - ::into_app() +#[allow(deprecated)] +impl CommandFactory for Box { + fn into_app<'help>() -> Command<'help> { + ::into_app() } - fn into_app_for_update<'help>() -> App<'help> { - ::into_app_for_update() + fn into_app_for_update<'help>() -> Command<'help> { + ::into_app_for_update() } } @@ -464,27 +486,27 @@ impl FromArgMatches for Box { } impl Args for Box { - fn augment_args(app: App<'_>) -> App<'_> { - ::augment_args(app) + fn augment_args(cmd: Command<'_>) -> Command<'_> { + ::augment_args(cmd) } - fn augment_args_for_update(app: App<'_>) -> App<'_> { - ::augment_args_for_update(app) + fn augment_args_for_update(cmd: Command<'_>) -> Command<'_> { + ::augment_args_for_update(cmd) } } impl Subcommand for Box { - fn augment_subcommands(app: App<'_>) -> App<'_> { - ::augment_subcommands(app) + fn augment_subcommands(cmd: Command<'_>) -> Command<'_> { + ::augment_subcommands(cmd) } - fn augment_subcommands_for_update(app: App<'_>) -> App<'_> { - ::augment_subcommands_for_update(app) + fn augment_subcommands_for_update(cmd: Command<'_>) -> Command<'_> { + ::augment_subcommands_for_update(cmd) } fn has_subcommand(name: &str) -> bool { ::has_subcommand(name) } } -fn format_error(err: crate::Error) -> crate::Error { - let mut app = I::into_app(); - err.format(&mut app) +fn format_error(err: crate::Error) -> crate::Error { + let mut cmd = I::command(); + err.format(&mut cmd) } diff --git a/third_party/rust/clap/src/error/context.rs b/third_party/rust/clap/src/error/context.rs new file mode 100644 index 000000000000..985cd4d70bac --- /dev/null +++ b/third_party/rust/clap/src/error/context.rs @@ -0,0 +1,55 @@ +/// Semantics for a piece of error information +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[non_exhaustive] +pub enum ContextKind { + /// The cause of the error + InvalidSubcommand, + /// The cause of the error + InvalidArg, + /// Existing arguments + PriorArg, + /// Accepted values + ValidValue, + /// Rejected values + InvalidValue, + /// Number of values present + ActualNumValues, + /// Number of allowed values + ExpectedNumValues, + /// Minimum number of allowed values + MinValues, + /// Number of occurrences present + ActualNumOccurrences, + /// Maximum number of allowed occurrences + MaxOccurrences, + /// Potential fix for the user + SuggestedCommand, + /// Potential fix for the user + SuggestedSubcommand, + /// Potential fix for the user + SuggestedArg, + /// Potential fix for the user + SuggestedValue, + /// Trailing argument + TrailingArg, + /// A usage string + Usage, + /// An opaque message to the user + Custom, +} + +/// A piece of error information +#[derive(Clone, Debug, PartialEq, Eq)] +#[non_exhaustive] +pub enum ContextValue { + /// [`ContextKind`] is self-sufficient, no additional information needed + None, + /// A single value + Bool(bool), + /// A single value + String(String), + /// Many values + Strings(Vec), + /// A single value + Number(isize), +} diff --git a/third_party/rust/clap/src/error/kind.rs b/third_party/rust/clap/src/error/kind.rs new file mode 100644 index 000000000000..2c7c422a6348 --- /dev/null +++ b/third_party/rust/clap/src/error/kind.rs @@ -0,0 +1,441 @@ +/// Command line argument parser kind of error +#[derive(Debug, Copy, Clone, PartialEq)] +#[non_exhaustive] +pub enum ErrorKind { + /// Occurs when an [`Arg`][crate::Arg] has a set of possible values, + /// and the user provides a value which isn't in that set. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, Arg, ErrorKind}; + /// let result = Command::new("prog") + /// .arg(Arg::new("speed") + /// .possible_value("fast") + /// .possible_value("slow")) + /// .try_get_matches_from(vec!["prog", "other"]); + /// assert!(result.is_err()); + /// assert_eq!(result.unwrap_err().kind(), ErrorKind::InvalidValue); + /// ``` + InvalidValue, + + /// Occurs when a user provides a flag, option, argument or subcommand which isn't defined. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, arg, ErrorKind}; + /// let result = Command::new("prog") + /// .arg(arg!(--flag "some flag")) + /// .try_get_matches_from(vec!["prog", "--other"]); + /// assert!(result.is_err()); + /// assert_eq!(result.unwrap_err().kind(), ErrorKind::UnknownArgument); + /// ``` + UnknownArgument, + + /// Occurs when the user provides an unrecognized [`Subcommand`] which meets the threshold for + /// being similar enough to an existing subcommand. + /// If it doesn't meet the threshold, or the 'suggestions' feature is disabled, + /// the more general [`UnknownArgument`] error is returned. + /// + /// # Examples + /// + #[cfg_attr(not(feature = "suggestions"), doc = " ```no_run")] + #[cfg_attr(feature = "suggestions", doc = " ```")] + /// # use clap::{Command, Arg, ErrorKind, }; + /// let result = Command::new("prog") + /// .subcommand(Command::new("config") + /// .about("Used for configuration") + /// .arg(Arg::new("config_file") + /// .help("The configuration file to use"))) + /// .try_get_matches_from(vec!["prog", "confi"]); + /// assert!(result.is_err()); + /// assert_eq!(result.unwrap_err().kind(), ErrorKind::InvalidSubcommand); + /// ``` + /// + /// [`Subcommand`]: crate::Subcommand + /// [`UnknownArgument`]: ErrorKind::UnknownArgument + InvalidSubcommand, + + /// Occurs when the user provides an unrecognized [`Subcommand`] which either + /// doesn't meet the threshold for being similar enough to an existing subcommand, + /// or the 'suggestions' feature is disabled. + /// Otherwise the more detailed [`InvalidSubcommand`] error is returned. + /// + /// This error typically happens when passing additional subcommand names to the `help` + /// subcommand. Otherwise, the more general [`UnknownArgument`] error is used. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, Arg, ErrorKind, }; + /// let result = Command::new("prog") + /// .subcommand(Command::new("config") + /// .about("Used for configuration") + /// .arg(Arg::new("config_file") + /// .help("The configuration file to use"))) + /// .try_get_matches_from(vec!["prog", "help", "nothing"]); + /// assert!(result.is_err()); + /// assert_eq!(result.unwrap_err().kind(), ErrorKind::UnrecognizedSubcommand); + /// ``` + /// + /// [`Subcommand`]: crate::Subcommand + /// [`InvalidSubcommand`]: ErrorKind::InvalidSubcommand + /// [`UnknownArgument`]: ErrorKind::UnknownArgument + UnrecognizedSubcommand, + + /// Occurs when the user provides an empty value for an option that does not allow empty + /// values. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, Arg, ErrorKind}; + /// let res = Command::new("prog") + /// .arg(Arg::new("color") + /// .takes_value(true) + /// .forbid_empty_values(true) + /// .long("color")) + /// .try_get_matches_from(vec!["prog", "--color="]); + /// assert!(res.is_err()); + /// assert_eq!(res.unwrap_err().kind(), ErrorKind::EmptyValue); + /// ``` + EmptyValue, + + /// Occurs when the user doesn't use equals for an option that requires equal + /// sign to provide values. + /// + /// ```rust + /// # use clap::{Command, Arg, ErrorKind}; + /// let res = Command::new("prog") + /// .arg(Arg::new("color") + /// .takes_value(true) + /// .require_equals(true) + /// .long("color")) + /// .try_get_matches_from(vec!["prog", "--color", "red"]); + /// assert!(res.is_err()); + /// assert_eq!(res.unwrap_err().kind(), ErrorKind::NoEquals); + /// ``` + NoEquals, + + /// Occurs when the user provides a value for an argument with a custom validation and the + /// value fails that validation. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, Arg, ErrorKind}; + /// fn is_numeric(val: &str) -> Result<(), String> { + /// match val.parse::() { + /// Ok(..) => Ok(()), + /// Err(..) => Err(String::from("Value wasn't a number!")), + /// } + /// } + /// + /// let result = Command::new("prog") + /// .arg(Arg::new("num") + /// .validator(is_numeric)) + /// .try_get_matches_from(vec!["prog", "NotANumber"]); + /// assert!(result.is_err()); + /// assert_eq!(result.unwrap_err().kind(), ErrorKind::ValueValidation); + /// ``` + ValueValidation, + + /// Occurs when a user provides more values for an argument than were defined by setting + /// [`Arg::max_values`]. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, Arg, ErrorKind}; + /// let result = Command::new("prog") + /// .arg(Arg::new("arg") + /// .max_values(2)) + /// .try_get_matches_from(vec!["prog", "too", "many", "values"]); + /// assert!(result.is_err()); + /// assert_eq!(result.unwrap_err().kind(), ErrorKind::TooManyValues); + /// ``` + /// [`Arg::max_values`]: crate::Arg::max_values() + TooManyValues, + + /// Occurs when the user provides fewer values for an argument than were defined by setting + /// [`Arg::min_values`]. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, Arg, ErrorKind}; + /// let result = Command::new("prog") + /// .arg(Arg::new("some_opt") + /// .long("opt") + /// .min_values(3)) + /// .try_get_matches_from(vec!["prog", "--opt", "too", "few"]); + /// assert!(result.is_err()); + /// assert_eq!(result.unwrap_err().kind(), ErrorKind::TooFewValues); + /// ``` + /// [`Arg::min_values`]: crate::Arg::min_values() + TooFewValues, + + /// Occurs when a user provides more occurrences for an argument than were defined by setting + /// [`Arg::max_occurrences`]. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, Arg, ErrorKind}; + /// let result = Command::new("prog") + /// .arg(Arg::new("verbosity") + /// .short('v') + /// .max_occurrences(2)) + /// .try_get_matches_from(vec!["prog", "-vvv"]); + /// assert!(result.is_err()); + /// assert_eq!(result.unwrap_err().kind(), ErrorKind::TooManyOccurrences); + /// ``` + /// [`Arg::max_occurrences`]: crate::Arg::max_occurrences() + TooManyOccurrences, + + /// Occurs when the user provides a different number of values for an argument than what's + /// been defined by setting [`Arg::number_of_values`] or than was implicitly set by + /// [`Arg::value_names`]. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, Arg, ErrorKind}; + /// let result = Command::new("prog") + /// .arg(Arg::new("some_opt") + /// .long("opt") + /// .takes_value(true) + /// .number_of_values(2)) + /// .try_get_matches_from(vec!["prog", "--opt", "wrong"]); + /// assert!(result.is_err()); + /// assert_eq!(result.unwrap_err().kind(), ErrorKind::WrongNumberOfValues); + /// ``` + /// + /// [`Arg::number_of_values`]: crate::Arg::number_of_values() + /// [`Arg::value_names`]: crate::Arg::value_names() + WrongNumberOfValues, + + /// Occurs when the user provides two values which conflict with each other and can't be used + /// together. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, Arg, ErrorKind}; + /// let result = Command::new("prog") + /// .arg(Arg::new("debug") + /// .long("debug") + /// .conflicts_with("color")) + /// .arg(Arg::new("color") + /// .long("color")) + /// .try_get_matches_from(vec!["prog", "--debug", "--color"]); + /// assert!(result.is_err()); + /// assert_eq!(result.unwrap_err().kind(), ErrorKind::ArgumentConflict); + /// ``` + ArgumentConflict, + + /// Occurs when the user does not provide one or more required arguments. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, Arg, ErrorKind}; + /// let result = Command::new("prog") + /// .arg(Arg::new("debug") + /// .required(true)) + /// .try_get_matches_from(vec!["prog"]); + /// assert!(result.is_err()); + /// assert_eq!(result.unwrap_err().kind(), ErrorKind::MissingRequiredArgument); + /// ``` + MissingRequiredArgument, + + /// Occurs when a subcommand is required (as defined by [`Command::subcommand_required`]), + /// but the user does not provide one. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, ErrorKind}; + /// let err = Command::new("prog") + /// .subcommand_required(true) + /// .subcommand(Command::new("test")) + /// .try_get_matches_from(vec![ + /// "myprog", + /// ]); + /// assert!(err.is_err()); + /// assert_eq!(err.unwrap_err().kind(), ErrorKind::MissingSubcommand); + /// # ; + /// ``` + /// + /// [`Command::subcommand_required`]: crate::Command::subcommand_required + MissingSubcommand, + + /// Occurs when the user provides multiple values to an argument which doesn't allow that. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, Arg, ErrorKind}; + /// let result = Command::new("prog") + /// .arg(Arg::new("debug") + /// .long("debug") + /// .multiple_occurrences(false)) + /// .try_get_matches_from(vec!["prog", "--debug", "--debug"]); + /// assert!(result.is_err()); + /// assert_eq!(result.unwrap_err().kind(), ErrorKind::UnexpectedMultipleUsage); + /// ``` + UnexpectedMultipleUsage, + + /// Occurs when the user provides a value containing invalid UTF-8. + /// + /// To allow arbitrary data + /// - Set [`Arg::allow_invalid_utf8`] for argument values + /// - Set [`Command::allow_invalid_utf8_for_external_subcommands`] for external-subcommand + /// values + /// + /// # Platform Specific + /// + /// Non-Windows platforms only (such as Linux, Unix, OSX, etc.) + /// + /// # Examples + /// + #[cfg_attr(not(unix), doc = " ```ignore")] + #[cfg_attr(unix, doc = " ```")] + /// # use clap::{Command, Arg, ErrorKind}; + /// # use std::os::unix::ffi::OsStringExt; + /// # use std::ffi::OsString; + /// let result = Command::new("prog") + /// .arg(Arg::new("utf8") + /// .short('u') + /// .takes_value(true)) + /// .try_get_matches_from(vec![OsString::from("myprog"), + /// OsString::from("-u"), + /// OsString::from_vec(vec![0xE9])]); + /// assert!(result.is_err()); + /// assert_eq!(result.unwrap_err().kind(), ErrorKind::InvalidUtf8); + /// ``` + /// + /// [`Arg::allow_invalid_utf8`]: crate::Arg::allow_invalid_utf8 + /// [`Command::allow_invalid_utf8_for_external_subcommands`]: crate::Command::allow_invalid_utf8_for_external_subcommands + InvalidUtf8, + + /// Not a true "error" as it means `--help` or similar was used. + /// The help message will be sent to `stdout`. + /// + /// **Note**: If the help is displayed due to an error (such as missing subcommands) it will + /// be sent to `stderr` instead of `stdout`. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, Arg, ErrorKind}; + /// let result = Command::new("prog") + /// .try_get_matches_from(vec!["prog", "--help"]); + /// assert!(result.is_err()); + /// assert_eq!(result.unwrap_err().kind(), ErrorKind::DisplayHelp); + /// ``` + DisplayHelp, + + /// Occurs when either an argument or a [`Subcommand`] is required, as defined by + /// [`Command::arg_required_else_help`] , but the user did not provide + /// one. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, Arg, ErrorKind, }; + /// let result = Command::new("prog") + /// .arg_required_else_help(true) + /// .subcommand(Command::new("config") + /// .about("Used for configuration") + /// .arg(Arg::new("config_file") + /// .help("The configuration file to use"))) + /// .try_get_matches_from(vec!["prog"]); + /// assert!(result.is_err()); + /// assert_eq!(result.unwrap_err().kind(), ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand); + /// ``` + /// + /// [`Subcommand`]: crate::Subcommand + /// [`Command::arg_required_else_help`]: crate::Command::arg_required_else_help + DisplayHelpOnMissingArgumentOrSubcommand, + + /// Not a true "error" as it means `--version` or similar was used. + /// The message will be sent to `stdout`. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, Arg, ErrorKind}; + /// let result = Command::new("prog") + /// .version("3.0") + /// .try_get_matches_from(vec!["prog", "--version"]); + /// assert!(result.is_err()); + /// assert_eq!(result.unwrap_err().kind(), ErrorKind::DisplayVersion); + /// ``` + DisplayVersion, + + /// Occurs when using the [`ArgMatches::value_of_t`] and friends to convert an argument value + /// into type `T`, but the argument you requested wasn't used. I.e. you asked for an argument + /// with name `config` to be converted, but `config` wasn't used by the user. + /// + /// [`ArgMatches::value_of_t`]: crate::ArgMatches::value_of_t() + ArgumentNotFound, + + /// Represents an [I/O error]. + /// Can occur when writing to `stderr` or `stdout` or reading a configuration file. + /// + /// [I/O error]: std::io::Error + Io, + + /// Represents a [Format error] (which is a part of [`Display`]). + /// Typically caused by writing to `stderr` or `stdout`. + /// + /// [`Display`]: std::fmt::Display + /// [Format error]: std::fmt::Error + Format, +} + +impl ErrorKind { + /// End-user description of the error case, where relevant + pub fn as_str(self) -> Option<&'static str> { + match self { + Self::InvalidValue => Some("One of the values isn't valid for an argument"), + Self::UnknownArgument => { + Some("Found an argument which wasn't expected or isn't valid in this context") + } + Self::InvalidSubcommand => Some("A subcommand wasn't recognized"), + Self::UnrecognizedSubcommand => Some("A subcommand wasn't recognized"), + Self::EmptyValue => Some("An argument requires a value but none was supplied"), + Self::NoEquals => Some("Equal is needed when assigning values to one of the arguments"), + Self::ValueValidation => Some("Invalid for for one of the arguments"), + Self::TooManyValues => Some("An argument received an unexpected value"), + Self::TooFewValues => Some("An argument requires more values"), + Self::TooManyOccurrences => Some("An argument occurred too many times"), + Self::WrongNumberOfValues => Some("An argument received too many or too few values"), + Self::ArgumentConflict => { + Some("An argument cannot be used with one or more of the other specified arguments") + } + Self::MissingRequiredArgument => { + Some("One or more required arguments were not provided") + } + Self::MissingSubcommand => Some("A subcommand is required but one was not provided"), + Self::UnexpectedMultipleUsage => { + Some("An argument was provided more than once but cannot be used multiple times") + } + Self::InvalidUtf8 => Some("Invalid UTF-8 was detected in one or more arguments"), + Self::DisplayHelp => None, + Self::DisplayHelpOnMissingArgumentOrSubcommand => None, + Self::DisplayVersion => None, + Self::ArgumentNotFound => Some("An argument wasn't found"), + Self::Io => None, + Self::Format => None, + } + } +} + +impl std::fmt::Display for ErrorKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.as_str().unwrap_or_default().fmt(f) + } +} diff --git a/third_party/rust/clap/src/error/mod.rs b/third_party/rust/clap/src/error/mod.rs new file mode 100644 index 000000000000..5c8a18e397ed --- /dev/null +++ b/third_party/rust/clap/src/error/mod.rs @@ -0,0 +1,1181 @@ +//! Error reporting +#![allow(deprecated)] + +// Std +use std::{ + borrow::Cow, + convert::From, + error, + fmt::{self, Debug, Display, Formatter}, + io::{self, BufRead}, + result::Result as StdResult, +}; + +// Internal +use crate::{ + build::Arg, + output::fmt::Colorizer, + output::fmt::Stream, + parse::features::suggestions, + util::{color::ColorChoice, safe_exit, SUCCESS_CODE, USAGE_CODE}, + AppSettings, Command, +}; + +mod context; +mod kind; + +pub use context::ContextKind; +pub use context::ContextValue; +pub use kind::ErrorKind; + +/// Short hand for [`Result`] type +/// +/// [`Result`]: std::result::Result +pub type Result = StdResult; + +/// Command Line Argument Parser Error +/// +/// See [`Command::error`] to create an error. +/// +/// [`Command::error`]: crate::Command::error +#[derive(Debug)] +pub struct Error { + inner: Box, + /// Deprecated, replaced with [`Error::kind()`] + #[deprecated(since = "3.1.0", note = "Replaced with `Error::kind()`")] + pub kind: ErrorKind, + /// Deprecated, replaced with [`Error::context()`] + #[deprecated(since = "3.1.0", note = "Replaced with `Error::context()`")] + pub info: Vec, +} + +#[derive(Debug)] +struct ErrorInner { + kind: ErrorKind, + context: Vec<(ContextKind, ContextValue)>, + message: Option, + source: Option>, + help_flag: Option<&'static str>, + color_when: ColorChoice, + wait_on_exit: bool, + backtrace: Option, +} + +impl Error { + /// Create an unformatted error + /// + /// This is for you need to pass the error up to + /// a place that has access to the `Command` at which point you can call [`Error::format`]. + /// + /// Prefer [`Command::error`] for generating errors. + /// + /// [`Command::error`]: crate::Command::error + pub fn raw(kind: ErrorKind, message: impl std::fmt::Display) -> Self { + Self::new(kind).set_message(message.to_string()) + } + + /// Format the existing message with the Command's context + #[must_use] + pub fn format(mut self, cmd: &mut Command) -> Self { + cmd._build_self(); + let usage = cmd.render_usage(); + if let Some(message) = self.inner.message.as_mut() { + message.format(cmd, usage); + } + self.with_cmd(cmd) + } + + /// Type of error for programmatic processing + pub fn kind(&self) -> ErrorKind { + self.inner.kind + } + + /// Additional information to further qualify the error + pub fn context(&self) -> impl Iterator { + self.inner.context.iter().map(|(k, v)| (*k, v)) + } + + /// Should the message be written to `stdout` or not? + #[inline] + pub fn use_stderr(&self) -> bool { + self.stream() == Stream::Stderr + } + + pub(crate) fn stream(&self) -> Stream { + match self.kind() { + ErrorKind::DisplayHelp | ErrorKind::DisplayVersion => Stream::Stdout, + _ => Stream::Stderr, + } + } + + /// Prints the error and exits. + /// + /// Depending on the error kind, this either prints to `stderr` and exits with a status of `2` + /// or prints to `stdout` and exits with a status of `0`. + pub fn exit(&self) -> ! { + if self.use_stderr() { + // Swallow broken pipe errors + let _ = self.print(); + + if self.inner.wait_on_exit { + wlnerr!("\nPress [ENTER] / [RETURN] to continue..."); + let mut s = String::new(); + let i = io::stdin(); + i.lock().read_line(&mut s).unwrap(); + } + + safe_exit(USAGE_CODE); + } + + // Swallow broken pipe errors + let _ = self.print(); + safe_exit(SUCCESS_CODE) + } + + /// Prints formatted and colored error to `stdout` or `stderr` according to its error kind + /// + /// # Example + /// ```no_run + /// use clap::Command; + /// + /// match Command::new("Command").try_get_matches() { + /// Ok(matches) => { + /// // do_something + /// }, + /// Err(err) => { + /// err.print().expect("Error writing Error"); + /// // do_something + /// }, + /// }; + /// ``` + pub fn print(&self) -> io::Result<()> { + self.formatted().print() + } + + /// Deprecated, replaced with [`Command::error`] + /// + /// [`Command::error`]: crate::Command::error + #[deprecated(since = "3.0.0", note = "Replaced with `Command::error`")] + #[doc(hidden)] + pub fn with_description(description: String, kind: ErrorKind) -> Self { + Error::raw(kind, description) + } + + fn new(kind: ErrorKind) -> Self { + Self { + inner: Box::new(ErrorInner { + kind, + context: Vec::new(), + message: None, + source: None, + help_flag: None, + color_when: ColorChoice::Never, + wait_on_exit: false, + backtrace: Backtrace::new(), + }), + kind, + info: vec![], + } + } + + #[inline(never)] + fn for_app(kind: ErrorKind, cmd: &Command, colorizer: Colorizer, info: Vec) -> Self { + Self::new(kind) + .set_message(colorizer) + .with_cmd(cmd) + .set_info(info) + } + + pub(crate) fn with_cmd(self, cmd: &Command) -> Self { + self.set_wait_on_exit(cmd.is_set(AppSettings::WaitOnError)) + .set_color(cmd.get_color()) + .set_help_flag(get_help_flag(cmd)) + } + + pub(crate) fn set_message(mut self, message: impl Into) -> Self { + self.inner.message = Some(message.into()); + self + } + + pub(crate) fn set_info(mut self, info: Vec) -> Self { + self.info = info; + self + } + + pub(crate) fn set_source(mut self, source: Box) -> Self { + self.inner.source = Some(source); + self + } + + pub(crate) fn set_color(mut self, color_when: ColorChoice) -> Self { + self.inner.color_when = color_when; + self + } + + pub(crate) fn set_help_flag(mut self, help_flag: Option<&'static str>) -> Self { + self.inner.help_flag = help_flag; + self + } + + pub(crate) fn set_wait_on_exit(mut self, yes: bool) -> Self { + self.inner.wait_on_exit = yes; + self + } + + /// Does not verify if `ContextKind` is already present + #[inline(never)] + pub(crate) fn insert_context_unchecked( + mut self, + kind: ContextKind, + value: ContextValue, + ) -> Self { + self.inner.context.push((kind, value)); + self + } + + /// Does not verify if `ContextKind` is already present + #[inline(never)] + pub(crate) fn extend_context_unchecked( + mut self, + context: [(ContextKind, ContextValue); N], + ) -> Self { + self.inner.context.extend(context); + self + } + + #[inline(never)] + fn get_context(&self, kind: ContextKind) -> Option<&ContextValue> { + self.inner + .context + .iter() + .find_map(|(k, v)| (*k == kind).then(|| v)) + } + + pub(crate) fn display_help(cmd: &Command, colorizer: Colorizer) -> Self { + Self::for_app(ErrorKind::DisplayHelp, cmd, colorizer, vec![]) + } + + pub(crate) fn display_help_error(cmd: &Command, colorizer: Colorizer) -> Self { + Self::for_app( + ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand, + cmd, + colorizer, + vec![], + ) + } + + pub(crate) fn display_version(cmd: &Command, colorizer: Colorizer) -> Self { + Self::for_app(ErrorKind::DisplayVersion, cmd, colorizer, vec![]) + } + + pub(crate) fn argument_conflict( + cmd: &Command, + arg: &Arg, + mut others: Vec, + usage: String, + ) -> Self { + let info = others.clone(); + let others = match others.len() { + 0 => ContextValue::None, + 1 => ContextValue::String(others.pop().unwrap()), + _ => ContextValue::Strings(others), + }; + Self::new(ErrorKind::ArgumentConflict) + .with_cmd(cmd) + .set_info(info) + .extend_context_unchecked([ + ( + ContextKind::InvalidArg, + ContextValue::String(arg.to_string()), + ), + (ContextKind::PriorArg, others), + (ContextKind::Usage, ContextValue::String(usage)), + ]) + } + + pub(crate) fn empty_value(cmd: &Command, good_vals: &[&str], arg: &Arg, usage: String) -> Self { + let info = vec![arg.to_string()]; + let mut err = Self::new(ErrorKind::EmptyValue) + .with_cmd(cmd) + .set_info(info) + .extend_context_unchecked([ + ( + ContextKind::InvalidArg, + ContextValue::String(arg.to_string()), + ), + (ContextKind::Usage, ContextValue::String(usage)), + ]); + if !good_vals.is_empty() { + err = err.insert_context_unchecked( + ContextKind::ValidValue, + ContextValue::Strings(good_vals.iter().map(|s| (*s).to_owned()).collect()), + ); + } + err + } + + pub(crate) fn no_equals(cmd: &Command, arg: String, usage: String) -> Self { + let info = vec![arg.to_string()]; + Self::new(ErrorKind::NoEquals) + .with_cmd(cmd) + .set_info(info) + .extend_context_unchecked([ + (ContextKind::InvalidArg, ContextValue::String(arg)), + (ContextKind::Usage, ContextValue::String(usage)), + ]) + } + + pub(crate) fn invalid_value( + cmd: &Command, + bad_val: String, + good_vals: &[&str], + arg: &Arg, + usage: String, + ) -> Self { + let mut info = vec![arg.to_string(), bad_val.clone()]; + info.extend(good_vals.iter().map(|s| (*s).to_owned())); + + let suggestion = suggestions::did_you_mean(&bad_val, good_vals.iter()).pop(); + let mut err = Self::new(ErrorKind::InvalidValue) + .with_cmd(cmd) + .set_info(info) + .extend_context_unchecked([ + ( + ContextKind::InvalidArg, + ContextValue::String(arg.to_string()), + ), + (ContextKind::InvalidValue, ContextValue::String(bad_val)), + ( + ContextKind::ValidValue, + ContextValue::Strings(good_vals.iter().map(|s| (*s).to_owned()).collect()), + ), + (ContextKind::Usage, ContextValue::String(usage)), + ]); + if let Some(suggestion) = suggestion { + err = err.insert_context_unchecked( + ContextKind::SuggestedValue, + ContextValue::String(suggestion), + ); + } + err + } + + pub(crate) fn invalid_subcommand( + cmd: &Command, + subcmd: String, + did_you_mean: String, + name: String, + usage: String, + ) -> Self { + let info = vec![subcmd.clone()]; + let suggestion = format!("{} -- {}", name, subcmd); + Self::new(ErrorKind::InvalidSubcommand) + .with_cmd(cmd) + .set_info(info) + .extend_context_unchecked([ + (ContextKind::InvalidSubcommand, ContextValue::String(subcmd)), + ( + ContextKind::SuggestedSubcommand, + ContextValue::String(did_you_mean), + ), + ( + ContextKind::SuggestedCommand, + ContextValue::String(suggestion), + ), + (ContextKind::Usage, ContextValue::String(usage)), + ]) + } + + pub(crate) fn unrecognized_subcommand(cmd: &Command, subcmd: String, usage: String) -> Self { + let info = vec![subcmd.clone()]; + Self::new(ErrorKind::UnrecognizedSubcommand) + .with_cmd(cmd) + .set_info(info) + .extend_context_unchecked([ + (ContextKind::InvalidSubcommand, ContextValue::String(subcmd)), + (ContextKind::Usage, ContextValue::String(usage)), + ]) + } + + pub(crate) fn missing_required_argument( + cmd: &Command, + required: Vec, + usage: String, + ) -> Self { + let info = required.clone(); + Self::new(ErrorKind::MissingRequiredArgument) + .with_cmd(cmd) + .set_info(info) + .extend_context_unchecked([ + (ContextKind::InvalidArg, ContextValue::Strings(required)), + (ContextKind::Usage, ContextValue::String(usage)), + ]) + } + + pub(crate) fn missing_subcommand(cmd: &Command, name: String, usage: String) -> Self { + let info = vec![]; + Self::new(ErrorKind::MissingSubcommand) + .with_cmd(cmd) + .set_info(info) + .extend_context_unchecked([ + (ContextKind::InvalidSubcommand, ContextValue::String(name)), + (ContextKind::Usage, ContextValue::String(usage)), + ]) + } + + pub(crate) fn invalid_utf8(cmd: &Command, usage: String) -> Self { + let info = vec![]; + Self::new(ErrorKind::InvalidUtf8) + .with_cmd(cmd) + .set_info(info) + .extend_context_unchecked([(ContextKind::Usage, ContextValue::String(usage))]) + } + + pub(crate) fn too_many_occurrences( + cmd: &Command, + arg: &Arg, + max_occurs: usize, + curr_occurs: usize, + usage: String, + ) -> Self { + let info = vec![ + arg.to_string(), + curr_occurs.to_string(), + max_occurs.to_string(), + ]; + Self::new(ErrorKind::TooManyOccurrences) + .with_cmd(cmd) + .set_info(info) + .extend_context_unchecked([ + ( + ContextKind::InvalidArg, + ContextValue::String(arg.to_string()), + ), + ( + ContextKind::MaxOccurrences, + ContextValue::Number(max_occurs as isize), + ), + ( + ContextKind::ActualNumValues, + ContextValue::Number(curr_occurs as isize), + ), + (ContextKind::Usage, ContextValue::String(usage)), + ]) + } + + pub(crate) fn too_many_values(cmd: &Command, val: String, arg: String, usage: String) -> Self { + let info = vec![arg.to_string(), val.clone()]; + Self::new(ErrorKind::TooManyValues) + .with_cmd(cmd) + .set_info(info) + .extend_context_unchecked([ + (ContextKind::InvalidArg, ContextValue::String(arg)), + (ContextKind::InvalidValue, ContextValue::String(val)), + (ContextKind::Usage, ContextValue::String(usage)), + ]) + } + + pub(crate) fn too_few_values( + cmd: &Command, + arg: &Arg, + min_vals: usize, + curr_vals: usize, + usage: String, + ) -> Self { + let info = vec![arg.to_string(), curr_vals.to_string(), min_vals.to_string()]; + Self::new(ErrorKind::TooFewValues) + .with_cmd(cmd) + .set_info(info) + .extend_context_unchecked([ + ( + ContextKind::InvalidArg, + ContextValue::String(arg.to_string()), + ), + ( + ContextKind::MinValues, + ContextValue::Number(min_vals as isize), + ), + ( + ContextKind::ActualNumValues, + ContextValue::Number(curr_vals as isize), + ), + (ContextKind::Usage, ContextValue::String(usage)), + ]) + } + + pub(crate) fn value_validation( + arg: String, + val: String, + err: Box, + ) -> Self { + let info = vec![arg.to_string(), val.to_string(), err.to_string()]; + Self::new(ErrorKind::ValueValidation) + .set_info(info) + .set_source(err) + .extend_context_unchecked([ + (ContextKind::InvalidArg, ContextValue::String(arg)), + (ContextKind::InvalidValue, ContextValue::String(val)), + ]) + } + + pub(crate) fn wrong_number_of_values( + cmd: &Command, + arg: &Arg, + num_vals: usize, + curr_vals: usize, + usage: String, + ) -> Self { + let info = vec![arg.to_string(), curr_vals.to_string(), num_vals.to_string()]; + Self::new(ErrorKind::WrongNumberOfValues) + .with_cmd(cmd) + .set_info(info) + .extend_context_unchecked([ + ( + ContextKind::InvalidArg, + ContextValue::String(arg.to_string()), + ), + ( + ContextKind::ExpectedNumValues, + ContextValue::Number(num_vals as isize), + ), + ( + ContextKind::ActualNumValues, + ContextValue::Number(curr_vals as isize), + ), + (ContextKind::Usage, ContextValue::String(usage)), + ]) + } + + pub(crate) fn unexpected_multiple_usage(cmd: &Command, arg: &Arg, usage: String) -> Self { + let info = vec![arg.to_string()]; + Self::new(ErrorKind::UnexpectedMultipleUsage) + .with_cmd(cmd) + .set_info(info) + .extend_context_unchecked([ + ( + ContextKind::InvalidArg, + ContextValue::String(arg.to_string()), + ), + (ContextKind::Usage, ContextValue::String(usage)), + ]) + } + + pub(crate) fn unknown_argument( + cmd: &Command, + arg: String, + did_you_mean: Option<(String, Option)>, + usage: String, + ) -> Self { + let info = vec![arg.to_string()]; + let mut err = Self::new(ErrorKind::UnknownArgument) + .with_cmd(cmd) + .set_info(info) + .extend_context_unchecked([ + (ContextKind::InvalidArg, ContextValue::String(arg)), + (ContextKind::Usage, ContextValue::String(usage)), + ]); + if let Some((flag, sub)) = did_you_mean { + err = err.insert_context_unchecked( + ContextKind::SuggestedArg, + ContextValue::String(format!("--{}", flag)), + ); + if let Some(sub) = sub { + err = err.insert_context_unchecked( + ContextKind::SuggestedSubcommand, + ContextValue::String(sub), + ); + } + } + err + } + + pub(crate) fn unnecessary_double_dash(cmd: &Command, arg: String, usage: String) -> Self { + let info = vec![arg.to_string()]; + Self::new(ErrorKind::UnknownArgument) + .with_cmd(cmd) + .set_info(info) + .extend_context_unchecked([ + (ContextKind::InvalidArg, ContextValue::String(arg)), + (ContextKind::TrailingArg, ContextValue::Bool(true)), + (ContextKind::Usage, ContextValue::String(usage)), + ]) + } + + pub(crate) fn argument_not_found_auto(arg: String) -> Self { + let info = vec![arg.to_string()]; + Self::new(ErrorKind::ArgumentNotFound) + .set_info(info) + .extend_context_unchecked([(ContextKind::InvalidArg, ContextValue::String(arg))]) + } + + fn formatted(&self) -> Cow<'_, Colorizer> { + if let Some(message) = self.inner.message.as_ref() { + message.formatted() + } else { + let mut c = Colorizer::new(self.stream(), self.inner.color_when); + + start_error(&mut c); + + if !self.write_dynamic_context(&mut c) { + if let Some(msg) = self.kind().as_str() { + c.none(msg.to_owned()); + } else if let Some(source) = self.inner.source.as_ref() { + c.none(source.to_string()); + } else { + c.none("Unknown cause"); + } + } + + let usage = self.get_context(ContextKind::Usage); + if let Some(ContextValue::String(usage)) = usage { + put_usage(&mut c, usage); + } + + try_help(&mut c, self.inner.help_flag); + + Cow::Owned(c) + } + } + + #[must_use] + fn write_dynamic_context(&self, c: &mut Colorizer) -> bool { + match self.kind() { + ErrorKind::ArgumentConflict => { + let invalid_arg = self.get_context(ContextKind::InvalidArg); + let prior_arg = self.get_context(ContextKind::PriorArg); + if let (Some(ContextValue::String(invalid_arg)), Some(prior_arg)) = + (invalid_arg, prior_arg) + { + c.none("The argument '"); + c.warning(invalid_arg); + c.none("' cannot be used with"); + + match prior_arg { + ContextValue::Strings(values) => { + c.none(":"); + for v in values { + c.none("\n "); + c.warning(&**v); + } + } + ContextValue::String(value) => { + c.none(" '"); + c.warning(value); + c.none("'"); + } + _ => { + c.none(" one or more of the other specified arguments"); + } + } + true + } else { + false + } + } + ErrorKind::EmptyValue => { + let invalid_arg = self.get_context(ContextKind::InvalidArg); + if let Some(ContextValue::String(invalid_arg)) = invalid_arg { + c.none("The argument '"); + c.warning(invalid_arg); + c.none("' requires a value but none was supplied"); + + let possible_values = self.get_context(ContextKind::ValidValue); + if let Some(ContextValue::Strings(possible_values)) = possible_values { + c.none("\n\t[possible values: "); + if let Some((last, elements)) = possible_values.split_last() { + for v in elements { + c.good(escape(v)); + c.none(", "); + } + c.good(escape(last)); + } + c.none("]"); + } + true + } else { + false + } + } + ErrorKind::NoEquals => { + let invalid_arg = self.get_context(ContextKind::InvalidArg); + if let Some(ContextValue::String(invalid_arg)) = invalid_arg { + c.none("Equal sign is needed when assigning values to '"); + c.warning(invalid_arg); + c.none("'."); + true + } else { + false + } + } + ErrorKind::InvalidValue => { + let invalid_arg = self.get_context(ContextKind::InvalidArg); + let invalid_value = self.get_context(ContextKind::InvalidValue); + if let ( + Some(ContextValue::String(invalid_arg)), + Some(ContextValue::String(invalid_value)), + ) = (invalid_arg, invalid_value) + { + c.none(quote(invalid_value)); + c.none(" isn't a valid value for '"); + c.warning(invalid_arg); + c.none("'"); + + let possible_values = self.get_context(ContextKind::ValidValue); + if let Some(ContextValue::Strings(possible_values)) = possible_values { + c.none("\n\t[possible values: "); + if let Some((last, elements)) = possible_values.split_last() { + for v in elements { + c.good(escape(v)); + c.none(", "); + } + c.good(escape(last)); + } + c.none("]"); + } + + let suggestion = self.get_context(ContextKind::SuggestedValue); + if let Some(ContextValue::String(suggestion)) = suggestion { + c.none("\n\n\tDid you mean "); + c.good(quote(suggestion)); + c.none("?"); + } + true + } else { + false + } + } + ErrorKind::InvalidSubcommand => { + let invalid_sub = self.get_context(ContextKind::InvalidSubcommand); + if let Some(ContextValue::String(invalid_sub)) = invalid_sub { + c.none("The subcommand '"); + c.warning(invalid_sub); + c.none("' wasn't recognized"); + + let valid_sub = self.get_context(ContextKind::SuggestedSubcommand); + if let Some(ContextValue::String(valid_sub)) = valid_sub { + c.none("\n\n\tDid you mean "); + c.good(valid_sub); + c.none("?"); + } + + let suggestion = self.get_context(ContextKind::SuggestedCommand); + if let Some(ContextValue::String(suggestion)) = suggestion { + c.none( + "\n\nIf you believe you received this message in error, try re-running with '", + ); + c.good(suggestion); + c.none("'"); + } + true + } else { + false + } + } + ErrorKind::UnrecognizedSubcommand => { + let invalid_sub = self.get_context(ContextKind::InvalidSubcommand); + if let Some(ContextValue::String(invalid_sub)) = invalid_sub { + c.none("The subcommand '"); + c.warning(invalid_sub); + c.none("' wasn't recognized"); + true + } else { + false + } + } + ErrorKind::MissingRequiredArgument => { + let invalid_arg = self.get_context(ContextKind::InvalidArg); + if let Some(ContextValue::Strings(invalid_arg)) = invalid_arg { + c.none("The following required arguments were not provided:"); + for v in invalid_arg { + c.none("\n "); + c.good(&**v); + } + true + } else { + false + } + } + ErrorKind::MissingSubcommand => { + let invalid_sub = self.get_context(ContextKind::InvalidSubcommand); + if let Some(ContextValue::String(invalid_sub)) = invalid_sub { + c.none("'"); + c.warning(invalid_sub); + c.none("' requires a subcommand but one was not provided"); + true + } else { + false + } + } + ErrorKind::InvalidUtf8 => false, + ErrorKind::TooManyOccurrences => { + let invalid_arg = self.get_context(ContextKind::InvalidArg); + let actual_num_occurs = self.get_context(ContextKind::ActualNumOccurrences); + let max_occurs = self.get_context(ContextKind::MaxOccurrences); + if let ( + Some(ContextValue::String(invalid_arg)), + Some(ContextValue::Number(actual_num_occurs)), + Some(ContextValue::Number(max_occurs)), + ) = (invalid_arg, actual_num_occurs, max_occurs) + { + let were_provided = Error::singular_or_plural(*actual_num_occurs as usize); + c.none("The argument '"); + c.warning(invalid_arg); + c.none("' allows at most "); + c.warning(max_occurs.to_string()); + c.none(" occurrences but "); + c.warning(actual_num_occurs.to_string()); + c.none(were_provided); + true + } else { + false + } + } + ErrorKind::TooManyValues => { + let invalid_arg = self.get_context(ContextKind::InvalidArg); + let invalid_value = self.get_context(ContextKind::InvalidValue); + if let ( + Some(ContextValue::String(invalid_arg)), + Some(ContextValue::String(invalid_value)), + ) = (invalid_arg, invalid_value) + { + c.none("The value '"); + c.warning(invalid_value); + c.none("' was provided to '"); + c.warning(invalid_arg); + c.none("' but it wasn't expecting any more values"); + true + } else { + false + } + } + ErrorKind::TooFewValues => { + let invalid_arg = self.get_context(ContextKind::InvalidArg); + let actual_num_values = self.get_context(ContextKind::ActualNumValues); + let min_values = self.get_context(ContextKind::MinValues); + if let ( + Some(ContextValue::String(invalid_arg)), + Some(ContextValue::Number(actual_num_values)), + Some(ContextValue::Number(min_values)), + ) = (invalid_arg, actual_num_values, min_values) + { + let were_provided = Error::singular_or_plural(*actual_num_values as usize); + c.none("The argument '"); + c.warning(invalid_arg); + c.none("' requires at least "); + c.warning(min_values.to_string()); + c.none(" values but only "); + c.warning(actual_num_values.to_string()); + c.none(were_provided); + true + } else { + false + } + } + ErrorKind::ValueValidation => { + let invalid_arg = self.get_context(ContextKind::InvalidArg); + let invalid_value = self.get_context(ContextKind::InvalidValue); + if let ( + Some(ContextValue::String(invalid_arg)), + Some(ContextValue::String(invalid_value)), + ) = (invalid_arg, invalid_value) + { + c.none("Invalid value "); + c.warning(quote(invalid_value)); + c.none(" for '"); + c.warning(invalid_arg); + if let Some(source) = self.inner.source.as_deref() { + c.none("': "); + c.none(source.to_string()); + } else { + c.none("'"); + } + true + } else { + false + } + } + ErrorKind::WrongNumberOfValues => { + let invalid_arg = self.get_context(ContextKind::InvalidArg); + let actual_num_values = self.get_context(ContextKind::ActualNumValues); + let num_values = self.get_context(ContextKind::ExpectedNumValues); + if let ( + Some(ContextValue::String(invalid_arg)), + Some(ContextValue::Number(actual_num_values)), + Some(ContextValue::Number(num_values)), + ) = (invalid_arg, actual_num_values, num_values) + { + let were_provided = Error::singular_or_plural(*actual_num_values as usize); + c.none("The argument '"); + c.warning(invalid_arg); + c.none("' requires "); + c.warning(num_values.to_string()); + c.none(" values, but "); + c.warning(actual_num_values.to_string()); + c.none(were_provided); + true + } else { + false + } + } + ErrorKind::UnexpectedMultipleUsage => { + let invalid_arg = self.get_context(ContextKind::InvalidArg); + if let Some(ContextValue::String(invalid_arg)) = invalid_arg { + c.none("The argument '"); + c.warning(invalid_arg.to_string()); + c.none("' was provided more than once, but cannot be used multiple times"); + true + } else { + false + } + } + ErrorKind::UnknownArgument => { + let invalid_arg = self.get_context(ContextKind::InvalidArg); + if let Some(ContextValue::String(invalid_arg)) = invalid_arg { + c.none("Found argument '"); + c.warning(invalid_arg.to_string()); + c.none("' which wasn't expected, or isn't valid in this context"); + + let valid_sub = self.get_context(ContextKind::SuggestedSubcommand); + let valid_arg = self.get_context(ContextKind::SuggestedArg); + match (valid_sub, valid_arg) { + ( + Some(ContextValue::String(valid_sub)), + Some(ContextValue::String(valid_arg)), + ) => { + c.none("\n\n\tDid you mean "); + c.none("to put '"); + c.good(valid_arg); + c.none("' after the subcommand '"); + c.good(valid_sub); + c.none("'?"); + } + (None, Some(ContextValue::String(valid_arg))) => { + c.none("\n\n\tDid you mean '"); + c.good(valid_arg); + c.none("'?"); + } + (_, _) => {} + } + + let invalid_arg = self.get_context(ContextKind::InvalidArg); + if let Some(ContextValue::String(invalid_arg)) = invalid_arg { + if invalid_arg.starts_with('-') { + c.none(format!( + "\n\n\tIf you tried to supply `{}` as a value rather than a flag, use `-- {}`", + invalid_arg, invalid_arg + )); + } + + let trailing_arg = self.get_context(ContextKind::TrailingArg); + if trailing_arg == Some(&ContextValue::Bool(true)) { + c.none(format!( + "\n\n\tIf you tried to supply `{}` as a subcommand, remove the '--' before it.", + invalid_arg + )); + } + } + true + } else { + false + } + } + ErrorKind::ArgumentNotFound => { + let invalid_arg = self.get_context(ContextKind::InvalidArg); + if let Some(ContextValue::String(invalid_arg)) = invalid_arg { + c.none("The argument '"); + c.warning(invalid_arg.to_string()); + c.none("' wasn't found"); + true + } else { + false + } + } + ErrorKind::DisplayHelp + | ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand + | ErrorKind::DisplayVersion + | ErrorKind::Io + | ErrorKind::Format => false, + } + } + + /// Returns the singular or plural form on the verb to be based on the argument's value. + fn singular_or_plural(n: usize) -> &'static str { + if n > 1 { + " were provided" + } else { + " was provided" + } + } +} + +impl From for Error { + fn from(e: io::Error) -> Self { + Error::raw(ErrorKind::Io, e) + } +} + +impl From for Error { + fn from(e: fmt::Error) -> Self { + Error::raw(ErrorKind::Format, e) + } +} + +impl error::Error for Error { + #[allow(trivial_casts)] + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + self.inner.source.as_ref().map(|e| e.as_ref() as _) + } +} + +impl Display for Error { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + // Assuming `self.message` already has a trailing newline, from `try_help` or similar + write!(f, "{}", self.formatted())?; + if let Some(backtrace) = self.inner.backtrace.as_ref() { + writeln!(f)?; + writeln!(f, "Backtrace:")?; + writeln!(f, "{}", backtrace)?; + } + Ok(()) + } +} + +fn start_error(c: &mut Colorizer) { + c.error("error:"); + c.none(" "); +} + +fn put_usage(c: &mut Colorizer, usage: impl Into) { + c.none("\n\n"); + c.none(usage); +} + +fn get_help_flag(cmd: &Command) -> Option<&'static str> { + if !cmd.is_disable_help_flag_set() { + Some("--help") + } else if cmd.has_subcommands() && !cmd.is_disable_help_subcommand_set() { + Some("help") + } else { + None + } +} + +fn try_help(c: &mut Colorizer, help: Option<&str>) { + if let Some(help) = help { + c.none("\n\nFor more information try "); + c.good(help); + c.none("\n"); + } else { + c.none("\n"); + } +} + +fn quote(s: impl AsRef) -> String { + let s = s.as_ref(); + format!("{:?}", s) +} + +fn escape(s: impl AsRef) -> String { + let s = s.as_ref(); + if s.contains(char::is_whitespace) { + quote(s) + } else { + s.to_owned() + } +} + +#[derive(Clone, Debug)] +pub(crate) enum Message { + Raw(String), + Formatted(Colorizer), +} + +impl Message { + fn format(&mut self, cmd: &Command, usage: String) { + match self { + Message::Raw(s) => { + let mut c = Colorizer::new(Stream::Stderr, cmd.get_color()); + + let mut message = String::new(); + std::mem::swap(s, &mut message); + start_error(&mut c); + c.none(message); + put_usage(&mut c, usage); + try_help(&mut c, get_help_flag(cmd)); + *self = Self::Formatted(c); + } + Message::Formatted(_) => {} + } + } + + fn formatted(&self) -> Cow { + match self { + Message::Raw(s) => { + let mut c = Colorizer::new(Stream::Stderr, ColorChoice::Never); + start_error(&mut c); + c.none(s); + Cow::Owned(c) + } + Message::Formatted(c) => Cow::Borrowed(c), + } + } +} + +impl From for Message { + fn from(inner: String) -> Self { + Self::Raw(inner) + } +} + +impl From for Message { + fn from(inner: Colorizer) -> Self { + Self::Formatted(inner) + } +} + +#[cfg(feature = "debug")] +#[derive(Debug)] +struct Backtrace(backtrace::Backtrace); + +#[cfg(feature = "debug")] +impl Backtrace { + fn new() -> Option { + Some(Self(backtrace::Backtrace::new())) + } +} + +#[cfg(feature = "debug")] +impl Display for Backtrace { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + // `backtrace::Backtrace` uses `Debug` instead of `Display` + write!(f, "{:?}", self.0) + } +} + +#[cfg(not(feature = "debug"))] +#[derive(Debug)] +struct Backtrace; + +#[cfg(not(feature = "debug"))] +impl Backtrace { + fn new() -> Option { + None + } +} + +#[cfg(not(feature = "debug"))] +impl Display for Backtrace { + fn fmt(&self, _: &mut Formatter) -> fmt::Result { + Ok(()) + } +} + +#[cfg(test)] +mod tests { + /// Check `clap::Error` impls Send and Sync. + mod clap_error_impl_send_sync { + use crate::Error; + trait Foo: std::error::Error + Send + Sync + 'static {} + impl Foo for Error {} + } +} diff --git a/third_party/rust/clap/src/lib.rs b/third_party/rust/clap/src/lib.rs index 87dd42a66717..dc92f8739513 100644 --- a/third_party/rust/clap/src/lib.rs +++ b/third_party/rust/clap/src/lib.rs @@ -26,17 +26,24 @@ #[cfg(not(feature = "std"))] compile_error!("`std` feature is currently required to build `clap`"); +pub use crate::build::Command; +pub use crate::build::{ + AppFlags, AppSettings, Arg, ArgFlags, ArgGroup, ArgSettings, PossibleValue, ValueHint, +}; +pub use crate::error::Error; +pub use crate::parse::{ArgMatches, Indices, OsValues, ValueSource, Values}; #[cfg(feature = "color")] pub use crate::util::color::ColorChoice; -pub use crate::{ - build::{ - App, AppFlags, AppSettings, Arg, ArgFlags, ArgGroup, ArgSettings, PossibleValue, ValueHint, - }, - parse::errors::{Error, ErrorKind, Result}, - parse::{ArgMatches, Indices, OsValues, Values}, -}; +#[cfg(not(feature = "color"))] +#[allow(unused_imports)] +pub(crate) use crate::util::color::ColorChoice; -pub use crate::derive::{ArgEnum, Args, FromArgMatches, IntoApp, Parser, Subcommand}; +pub use crate::derive::{ArgEnum, Args, CommandFactory, FromArgMatches, Parser, Subcommand}; + +pub use crate::error::{ErrorKind, Result}; + +#[allow(deprecated)] +pub use crate::build::App; #[cfg(feature = "yaml")] #[doc(hidden)] @@ -44,14 +51,19 @@ pub use crate::derive::{ArgEnum, Args, FromArgMatches, IntoApp, Parser, Subcomma since = "3.0.0", note = "Deprecated in Issue #3087, maybe clap::Parser would fit your use case?" )] +#[doc(hidden)] pub use yaml_rust::YamlLoader; #[cfg(feature = "derive")] #[doc(hidden)] pub use clap_derive::{self, *}; +/// Deprecated, replaced with [`CommandFactory`] +#[deprecated(since = "3.0.0", note = "Replaced with `CommandFactory`")] +pub use CommandFactory as IntoApp; /// Deprecated, replaced with [`Parser`] #[deprecated(since = "3.0.0", note = "Replaced with `Parser`")] +#[doc(hidden)] pub use Parser as StructOpt; #[cfg(any(feature = "derive", feature = "cargo"))] @@ -65,7 +77,9 @@ mod macros; mod derive; #[cfg(feature = "regex")] -pub use crate::build::arg::RegexRef; +pub use crate::build::RegexRef; + +pub mod error; mod build; mod mkeymap; @@ -77,21 +91,23 @@ const INTERNAL_ERROR_MSG: &str = "Fatal internal error. Please consider filing a report at https://github.com/clap-rs/clap/issues"; const INVALID_UTF8: &str = "unexpected invalid UTF-8 code point"; -/// Deprecated, replaced with [`App::new`], unless you were looking for [Subcommand] +/// Deprecated, replaced with [`Command::new`], unless you were looking for [Subcommand] #[deprecated( since = "3.0.0", - note = "Replaced with `App::new` unless you intended the `Subcommand` trait" + note = "Replaced with `Command::new` unless you intended the `Subcommand` trait" )] +#[doc(hidden)] #[derive(Debug, Copy, Clone)] pub struct SubCommand {} #[allow(deprecated)] impl SubCommand { - /// Deprecated, replaced with [`App::new`]. + /// Deprecated, replaced with [`Command::new`]. /// Did you mean Subcommand (lower-case c)? - #[deprecated(since = "3.0.0", note = "Replaced with `App::new`")] + #[deprecated(since = "3.0.0", note = "Replaced with `Command::new`")] + #[doc(hidden)] pub fn with_name<'help>(name: &str) -> App<'help> { - App::new(name) + Command::new(name) } /// Deprecated in [Issue #3087](https://github.com/clap-rs/clap/issues/3087), maybe [`clap::Parser`][crate::Parser] would fit your use case? @@ -100,8 +116,9 @@ impl SubCommand { since = "3.0.0", note = "Deprecated in Issue #3087, maybe clap::Parser would fit your use case?" )] + #[doc(hidden)] pub fn from_yaml(yaml: &yaml_rust::Yaml) -> App { #![allow(deprecated)] - App::from_yaml(yaml) + Command::from_yaml(yaml) } } diff --git a/third_party/rust/clap/src/macros.rs b/third_party/rust/clap/src/macros.rs index a706c28cb026..f50b854959e6 100644 --- a/third_party/rust/clap/src/macros.rs +++ b/third_party/rust/clap/src/macros.rs @@ -4,6 +4,7 @@ since = "3.0.0", note = "Deprecated in Issue #3087, maybe clap::Parser would fit your use case?" )] +#[doc(hidden)] #[macro_export] macro_rules! load_yaml { ($yaml:expr) => { @@ -15,6 +16,7 @@ macro_rules! load_yaml { /// Deprecated, replaced with [`ArgMatches::value_of_t`][crate::ArgMatches::value_of_t] #[macro_export] #[deprecated(since = "3.0.0", note = "Replaced with `ArgMatches::value_of_t`")] +#[doc(hidden)] macro_rules! value_t { ($m:ident, $v:expr, $t:ty) => { $crate::value_t!($m.value_of($v), $t) @@ -30,6 +32,7 @@ macro_rules! value_t { since = "3.0.0", note = "Replaced with `ArgMatches::value_of_t_or_exit`" )] +#[doc(hidden)] macro_rules! value_t_or_exit { ($m:ident, $v:expr, $t:ty) => { value_t_or_exit!($m.value_of($v), $t) @@ -42,6 +45,7 @@ macro_rules! value_t_or_exit { /// Deprecated, replaced with [`ArgMatches::values_of_t`][crate::ArgMatches::value_of_t] #[macro_export] #[deprecated(since = "3.0.0", note = "Replaced with `ArgMatches::values_of_t`")] +#[doc(hidden)] macro_rules! values_t { ($m:ident, $v:expr, $t:ty) => { values_t!($m.values_of($v), $t) @@ -57,6 +61,7 @@ macro_rules! values_t { since = "3.0.0", note = "Replaced with `ArgMatches::values_of_t_or_exit`" )] +#[doc(hidden)] macro_rules! values_t_or_exit { ($m:ident, $v:expr, $t:ty) => { values_t_or_exit!($m.values_of($v), $t) @@ -66,8 +71,18 @@ macro_rules! values_t_or_exit { }; } +#[deprecated(since = "3.0.0", note = "Replaced with `ArgEnum`")] +#[doc(hidden)] +#[macro_export] +macro_rules! _clap_count_exprs { + () => { 0 }; + ($e:expr) => { 1 }; + ($e:expr, $($es:expr),+) => { 1 + $crate::_clap_count_exprs!($($es),*) }; +} + /// Deprecated, replaced with [`ArgEnum`][crate::ArgEnum] #[deprecated(since = "3.0.0", note = "Replaced with `ArgEnum`")] +#[doc(hidden)] #[macro_export] macro_rules! arg_enum { (@as_item $($i:item)*) => ($($i)*); @@ -180,9 +195,9 @@ macro_rules! arg_enum { /// ```no_run /// # #[macro_use] /// # extern crate clap; -/// # use clap::App; +/// # use clap::Command; /// # fn main() { -/// let m = App::new("app") +/// let m = Command::new("cmd") /// .version(crate_version!()) /// .get_matches(); /// # } @@ -195,7 +210,7 @@ macro_rules! crate_version { }; } -/// Allows you to pull the authors for the app from your Cargo.toml at +/// Allows you to pull the authors for the command from your Cargo.toml at /// compile time in the form: /// `"author1 lastname :author2 lastname "` /// @@ -209,9 +224,9 @@ macro_rules! crate_version { /// ```no_run /// # #[macro_use] /// # extern crate clap; -/// # use clap::App; +/// # use clap::Command; /// # fn main() { -/// let m = App::new("app") +/// let m = Command::new("cmd") /// .author(crate_authors!("\n")) /// .get_matches(); /// # } @@ -239,9 +254,9 @@ macro_rules! crate_authors { /// ```no_run /// # #[macro_use] /// # extern crate clap; -/// # use clap::App; +/// # use clap::Command; /// # fn main() { -/// let m = App::new("app") +/// let m = Command::new("cmd") /// .about(crate_description!()) /// .get_matches(); /// # } @@ -261,9 +276,9 @@ macro_rules! crate_description { /// ```no_run /// # #[macro_use] /// # extern crate clap; -/// # use clap::App; +/// # use clap::Command; /// # fn main() { -/// let m = App::new(crate_name!()) +/// let m = Command::new(crate_name!()) /// .get_matches(); /// # } /// ``` @@ -275,17 +290,12 @@ macro_rules! crate_name { }; } -/// Allows you to build the `App` instance from your Cargo.toml at compile time. -/// -/// Equivalent to using the `crate_*!` macros with their respective fields. -/// -/// Provided separator is for the [`crate_authors!`] macro, -/// refer to the documentation therefor. +/// Allows you to build the `Command` instance from your Cargo.toml at compile time. /// /// **NOTE:** Changing the values in your `Cargo.toml` does not trigger a re-build automatically, /// and therefore won't change the generated output until you recompile. /// -/// **Pro Tip:** In some cases you can "trick" the compiler into triggering a rebuild when your +/// In some cases you can "trick" the compiler into triggering a rebuild when your /// `Cargo.toml` is changed by including this in your `src/main.rs` file /// `include_str!("../Cargo.toml");` /// @@ -295,41 +305,78 @@ macro_rules! crate_name { /// # #[macro_use] /// # extern crate clap; /// # fn main() { -/// let m = app_from_crate!().get_matches(); +/// let m = command!().get_matches(); /// # } /// ``` #[cfg(feature = "cargo")] #[macro_export] +macro_rules! command { + () => {{ + $crate::command!($crate::crate_name!()) + }}; + ($name:expr) => {{ + let mut cmd = $crate::Command::new($name).version($crate::crate_version!()); + + let author = $crate::crate_authors!(); + if !author.is_empty() { + cmd = cmd.author(author) + } + + let about = $crate::crate_description!(); + if !about.is_empty() { + cmd = cmd.about(about) + } + + cmd + }}; +} + +/// Requires `cargo` feature flag to be enabled. +#[cfg(not(feature = "cargo"))] +#[macro_export] +macro_rules! command { + () => {{ + compile_error!("`cargo` feature flag is required"); + }}; + ($name:expr) => {{ + compile_error!("`cargo` feature flag is required"); + }}; +} + +/// Deprecated, replaced with [`clap::command!`][crate::command] +#[cfg(feature = "cargo")] +#[deprecated(since = "3.1.0", note = "Replaced with `clap::command!")] +#[macro_export] macro_rules! app_from_crate { () => {{ - let mut app = $crate::App::new($crate::crate_name!()).version($crate::crate_version!()); + let mut cmd = $crate::Command::new($crate::crate_name!()).version($crate::crate_version!()); let author = $crate::crate_authors!(", "); if !author.is_empty() { - app = app.author(author) + cmd = cmd.author(author) } let about = $crate::crate_description!(); if !about.is_empty() { - app = app.about(about) + cmd = cmd.about(about) } - app + cmd }}; ($sep:expr) => {{ - let mut app = $crate::App::new($crate::crate_name!()).version($crate::crate_version!()); + let mut cmd = $crate::Command::new($crate::crate_name!()).version($crate::crate_version!()); let author = $crate::crate_authors!($sep); if !author.is_empty() { - app = app.author(author) + cmd = cmd.author(author) } let about = $crate::crate_description!(); if !about.is_empty() { - app = app.about(about) + cmd = cmd.about(about) } - app + cmd }}; } @@ -378,12 +425,12 @@ macro_rules! arg_impl { @arg ({ debug_assert_eq!($arg.get_value_names(), None, "Flags should precede values"); - debug_assert!(!$arg.is_set($crate::ArgSettings::MultipleOccurrences), "Flags should precede `...`"); + debug_assert!(!$arg.is_multiple_occurrences_set(), "Flags should precede `...`"); let mut arg = $arg; let long = $crate::arg_impl! { @string $long }; - if arg.get_name().is_empty() { - arg = arg.name(long); + if arg.get_id().is_empty() { + arg = arg.id(long); } arg.long(long) }) @@ -400,12 +447,12 @@ macro_rules! arg_impl { @arg ({ debug_assert_eq!($arg.get_value_names(), None, "Flags should precede values"); - debug_assert!(!$arg.is_set($crate::ArgSettings::MultipleOccurrences), "Flags should precede `...`"); + debug_assert!(!$arg.is_multiple_occurrences_set(), "Flags should precede `...`"); let mut arg = $arg; let long = $crate::arg_impl! { @string $long }; - if arg.get_name().is_empty() { - arg = arg.name(long); + if arg.get_id().is_empty() { + arg = arg.id(long); } arg.long(long) }) @@ -423,7 +470,7 @@ macro_rules! arg_impl { ({ debug_assert_eq!($arg.get_long(), None, "Short flags should precede long flags"); debug_assert_eq!($arg.get_value_names(), None, "Flags should precede values"); - debug_assert!(!$arg.is_set($crate::ArgSettings::MultipleOccurrences), "Flags should precede `...`"); + debug_assert!(!$arg.is_multiple_occurrences_set(), "Flags should precede `...`"); $arg.short($crate::arg_impl! { @char $short }) }) @@ -441,7 +488,7 @@ macro_rules! arg_impl { ({ debug_assert_eq!($arg.get_long(), None, "Short flags should precede long flags"); debug_assert_eq!($arg.get_value_names(), None, "Flags should precede values"); - debug_assert!(!$arg.is_set($crate::ArgSettings::MultipleOccurrences), "Flags should precede `...`"); + debug_assert!(!$arg.is_multiple_occurrences_set(), "Flags should precede `...`"); $arg.short($crate::arg_impl! { @char $short }) }) @@ -457,7 +504,7 @@ macro_rules! arg_impl { $crate::arg_impl! { @arg ({ - debug_assert!(!$arg.is_set($crate::ArgSettings::MultipleOccurrences), "Values should precede `...`"); + debug_assert!(!$arg.is_multiple_occurrences_set(), "Values should precede `...`"); debug_assert_eq!($arg.get_value_names(), None, "Multiple values not yet supported"); let mut arg = $arg; @@ -466,8 +513,34 @@ macro_rules! arg_impl { arg = arg.takes_value(true); let value_name = $crate::arg_impl! { @string $value_name }; - if arg.get_name().is_empty() { - arg = arg.name(value_name); + if arg.get_id().is_empty() { + arg = arg.id(value_name); + } + arg.value_name(value_name) + }) + $($tail)* + } + }; + ( + @arg + ($arg:expr) + <$value_name:literal> + $($tail:tt)* + ) => { + $crate::arg_impl! { + @arg + ({ + debug_assert!(!$arg.is_multiple_occurrences_set(), "Values should precede `...`"); + debug_assert_eq!($arg.get_value_names(), None, "Multiple values not yet supported"); + + let mut arg = $arg; + + arg = arg.required(true); + arg = arg.takes_value(true); + + let value_name = $crate::arg_impl! { @string $value_name }; + if arg.get_id().is_empty() { + arg = arg.id(value_name); } arg.value_name(value_name) }) @@ -483,7 +556,7 @@ macro_rules! arg_impl { $crate::arg_impl! { @arg ({ - debug_assert!(!$arg.is_set($crate::ArgSettings::MultipleOccurrences), "Values should precede `...`"); + debug_assert!(!$arg.is_multiple_occurrences_set(), "Values should precede `...`"); debug_assert_eq!($arg.get_value_names(), None, "Multiple values not yet supported"); let mut arg = $arg; @@ -496,8 +569,38 @@ macro_rules! arg_impl { arg = arg.takes_value(true); let value_name = $crate::arg_impl! { @string $value_name }; - if arg.get_name().is_empty() { - arg = arg.name(value_name); + if arg.get_id().is_empty() { + arg = arg.id(value_name); + } + arg.value_name(value_name) + }) + $($tail)* + } + }; + ( + @arg + ($arg:expr) + [$value_name:literal] + $($tail:tt)* + ) => { + $crate::arg_impl! { + @arg + ({ + debug_assert!(!$arg.is_multiple_occurrences_set(), "Values should precede `...`"); + debug_assert_eq!($arg.get_value_names(), None, "Multiple values not yet supported"); + + let mut arg = $arg; + + if arg.get_long().is_none() && arg.get_short().is_none() { + arg = arg.required(false); + } else { + arg = arg.min_values(0).max_values(1); + } + arg = arg.takes_value(true); + + let value_name = $crate::arg_impl! { @string $value_name }; + if arg.get_id().is_empty() { + arg = arg.id(value_name); } arg.value_name(value_name) }) @@ -595,14 +698,14 @@ macro_rules! arg_impl { /// /// ### Help String /// -/// The help string is denoted between a pair of single quotes `''` and may contain any +/// The help string is denoted between a pair of double quotes `""` and may contain any /// characters. /// /// # Examples /// /// ```rust -/// # use clap::{App, Arg, arg}; -/// App::new("prog") +/// # use clap::{Command, Arg, arg}; +/// Command::new("prog") /// .args(&[ /// arg!(--config "a required file for the configuration and no short"), /// arg!(-d --debug ... "turns on debugging information and allows multiples"), @@ -622,7 +725,7 @@ macro_rules! arg { let arg = $crate::arg_impl! { @arg ($crate::Arg::default()) $($tail)+ }; - debug_assert!(!arg.get_name().is_empty(), "Without a value or long flag, the `name:` prefix is required"); + debug_assert!(!arg.get_id().is_empty(), "Without a value or long flag, the `name:` prefix is required"); arg }}; } @@ -632,6 +735,7 @@ macro_rules! arg { since = "3.0.0", note = "Replaced with `clap::Parser` for a declarative API (Issue clap-rs/clap#2835)" )] +#[doc(hidden)] #[macro_export] macro_rules! clap_app { (@app ($builder:expr)) => { $builder }; @@ -681,7 +785,7 @@ macro_rules! clap_app { (@app ($builder:expr) (@subcommand $name:ident => $($tail:tt)*) $($tt:tt)*) => { $crate::clap_app!{ @app ($builder.subcommand( - $crate::clap_app!{ @app ($crate::App::new(stringify!($name))) $($tail)* } + $crate::clap_app!{ @app ($crate::Command::new(stringify!($name))) $($tail)* } )) $($tt)* } @@ -773,15 +877,15 @@ macro_rules! clap_app { // Build a subcommand outside of an app. (@subcommand $name:ident => $($tail:tt)*) => { - $crate::clap_app!{ @app ($crate::App::new(stringify!($name))) $($tail)* } + $crate::clap_app!{ @app ($crate::Command::new(stringify!($name))) $($tail)* } }; // Start the magic (($name:expr) => $($tail:tt)*) => {{ - $crate::clap_app!{ @app ($crate::App::new($name)) $($tail)*} + $crate::clap_app!{ @app ($crate::Command::new($name)) $($tail)*} }}; ($name:ident => $($tail:tt)*) => {{ - $crate::clap_app!{ @app ($crate::App::new(stringify!($name))) $($tail)*} + $crate::clap_app!{ @app ($crate::Command::new(stringify!($name))) $($tail)*} }}; } @@ -894,7 +998,7 @@ macro_rules! debug { ($($arg:tt)*) => ({ let prefix = format!("[{:>w$}] \t", module_path!(), w = 28); let body = format!($($arg)*); - let mut color = $crate::output::fmt::Colorizer::new(true, $crate::ColorChoice::Auto); + let mut color = $crate::output::fmt::Colorizer::new($crate::output::fmt::Stream::Stderr, $crate::ColorChoice::Auto); color.hint(prefix); color.hint(body); color.none("\n"); diff --git a/third_party/rust/clap/src/mkeymap.rs b/third_party/rust/clap/src/mkeymap.rs index 54d29e814c75..8ba3dcb3061d 100644 --- a/third_party/rust/clap/src/mkeymap.rs +++ b/third_party/rust/clap/src/mkeymap.rs @@ -49,6 +49,15 @@ impl PartialEq<&str> for KeyType { } } +impl PartialEq for KeyType { + fn eq(&self, rhs: &str) -> bool { + match self { + KeyType::Long(l) => l == rhs, + _ => false, + } + } +} + impl PartialEq for KeyType { fn eq(&self, rhs: &OsStr) -> bool { match self { diff --git a/third_party/rust/clap/src/output/fmt.rs b/third_party/rust/clap/src/output/fmt.rs index a416935bb8a9..dc1f46e08f07 100644 --- a/third_party/rust/clap/src/output/fmt.rs +++ b/third_party/rust/clap/src/output/fmt.rs @@ -5,46 +5,52 @@ use std::{ io::{self, Write}, }; +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub(crate) enum Stream { + Stdout, + Stderr, +} + #[derive(Clone, Debug)] pub(crate) struct Colorizer { - use_stderr: bool, + stream: Stream, #[allow(unused)] color_when: ColorChoice, pieces: Vec<(String, Style)>, } impl Colorizer { - #[inline] - pub(crate) fn new(use_stderr: bool, color_when: ColorChoice) -> Self { + #[inline(never)] + pub(crate) fn new(stream: Stream, color_when: ColorChoice) -> Self { Colorizer { - use_stderr, + stream, color_when, pieces: vec![], } } - #[inline] + #[inline(never)] pub(crate) fn good(&mut self, msg: impl Into) { self.pieces.push((msg.into(), Style::Good)); } - #[inline] + #[inline(never)] pub(crate) fn warning(&mut self, msg: impl Into) { self.pieces.push((msg.into(), Style::Warning)); } - #[inline] + #[inline(never)] pub(crate) fn error(&mut self, msg: impl Into) { self.pieces.push((msg.into(), Style::Error)); } - #[inline] + #[inline(never)] #[allow(dead_code)] pub(crate) fn hint(&mut self, msg: impl Into) { self.pieces.push((msg.into(), Style::Hint)); } - #[inline] + #[inline(never)] pub(crate) fn none(&mut self, msg: impl Into) { self.pieces.push((msg.into(), Style::Default)); } @@ -58,14 +64,13 @@ impl Colorizer { let color_when = match self.color_when { ColorChoice::Always => DepColorChoice::Always, - ColorChoice::Auto if is_a_tty(self.use_stderr) => DepColorChoice::Auto, + ColorChoice::Auto if is_a_tty(self.stream) => DepColorChoice::Auto, _ => DepColorChoice::Never, }; - let writer = if self.use_stderr { - BufferWriter::stderr(color_when) - } else { - BufferWriter::stdout(color_when) + let writer = match self.stream { + Stream::Stderr => BufferWriter::stderr(color_when), + Stream::Stdout => BufferWriter::stdout(color_when), }; let mut buffer = writer.buffer(); @@ -101,14 +106,17 @@ impl Colorizer { pub(crate) fn print(&self) -> io::Result<()> { // [e]println can't be used here because it panics // if something went wrong. We don't want that. - if self.use_stderr { - let stderr = std::io::stderr(); - let mut stderr = stderr.lock(); - write!(stderr, "{}", self) - } else { - let stdout = std::io::stdout(); - let mut stdout = stdout.lock(); - write!(stdout, "{}", self) + match self.stream { + Stream::Stdout => { + let stdout = std::io::stdout(); + let mut stdout = stdout.lock(); + write!(stdout, "{}", self) + } + Stream::Stderr => { + let stderr = std::io::stderr(); + let mut stderr = stderr.lock(); + write!(stderr, "{}", self) + } } } } @@ -140,11 +148,10 @@ impl Default for Style { } #[cfg(feature = "color")] -fn is_a_tty(stderr: bool) -> bool { - let stream = if stderr { - atty::Stream::Stderr - } else { - atty::Stream::Stdout +fn is_a_tty(stream: Stream) -> bool { + let stream = match stream { + Stream::Stdout => atty::Stream::Stdout, + Stream::Stderr => atty::Stream::Stderr, }; atty::is(stream) diff --git a/third_party/rust/clap/src/output/help.rs b/third_party/rust/clap/src/output/help.rs index 7544b0c18df3..85ba7d13a29b 100644 --- a/third_party/rust/clap/src/output/help.rs +++ b/third_party/rust/clap/src/output/help.rs @@ -2,16 +2,16 @@ use std::{ borrow::Cow, cmp, - collections::BTreeMap, + fmt::Write as _, io::{self, Write}, usize, }; // Internal use crate::{ - build::{arg::display_arg_val, App, AppSettings, Arg, ArgSettings}, + build::{display_arg_val, Arg, Command}, output::{fmt::Colorizer, Usage}, - parse::Parser, + PossibleValue, }; // Third party @@ -21,17 +21,26 @@ use textwrap::core::display_width; /// `clap` Help Writer. /// /// Wraps a writer stream providing different methods to generate help for `clap` objects. -pub(crate) struct Help<'help, 'app, 'parser, 'writer> { +pub(crate) struct Help<'help, 'cmd, 'writer> { writer: HelpWriter<'writer>, - parser: &'parser Parser<'help, 'app>, + cmd: &'cmd Command<'help>, + usage: &'cmd Usage<'help, 'cmd>, next_line_help: bool, - hide_pv: bool, term_w: usize, use_long: bool, } // Public Functions -impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { +impl<'help, 'cmd, 'writer> Help<'help, 'cmd, 'writer> { + #[cfg(feature = "unstable-v4")] + const DEFAULT_TEMPLATE: &'static str = "\ + {before-help}{name} {version}\n\ + {author-with-newline}{about-with-newline}\n\ + {usage-heading}\n {usage}\n\ + \n\ + {all-args}{after-help}\ + "; + #[cfg(not(feature = "unstable-v4"))] const DEFAULT_TEMPLATE: &'static str = "\ {before-help}{bin} {version}\n\ {author-with-newline}{about-with-newline}\n\ @@ -40,6 +49,13 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { {all-args}{after-help}\ "; + #[cfg(feature = "unstable-v4")] + const DEFAULT_NO_ARGS_TEMPLATE: &'static str = "\ + {before-help}{name} {version}\n\ + {author-with-newline}{about-with-newline}\n\ + {usage-heading}\n {usage}{after-help}\ + "; + #[cfg(not(feature = "unstable-v4"))] const DEFAULT_NO_ARGS_TEMPLATE: &'static str = "\ {before-help}{bin} {version}\n\ {author-with-newline}{about-with-newline}\n\ @@ -49,29 +65,29 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { /// Create a new `Help` instance. pub(crate) fn new( writer: HelpWriter<'writer>, - parser: &'parser Parser<'help, 'app>, + cmd: &'cmd Command<'help>, + usage: &'cmd Usage<'help, 'cmd>, use_long: bool, ) -> Self { debug!("Help::new"); - let term_w = match parser.app.term_w { + let term_w = match cmd.get_term_width() { Some(0) => usize::MAX, Some(w) => w, None => cmp::min( dimensions().map_or(100, |(w, _)| w), - match parser.app.max_w { + match cmd.get_max_term_width() { None | Some(0) => usize::MAX, Some(mw) => mw, }, ), }; - let next_line_help = parser.is_set(AppSettings::NextLineHelp); - let hide_pv = parser.is_set(AppSettings::HidePossibleValues); + let next_line_help = cmd.is_next_line_help_set(); Help { writer, - parser, + cmd, + usage, next_line_help, - hide_pv, term_w, use_long, } @@ -81,22 +97,20 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { pub(crate) fn write_help(&mut self) -> io::Result<()> { debug!("Help::write_help"); - if let Some(h) = self.parser.app.help_str { + if let Some(h) = self.cmd.get_override_help() { self.none(h)?; - } else if let Some(tmpl) = self.parser.app.template { + } else if let Some(tmpl) = self.cmd.get_help_template() { self.write_templated_help(tmpl)?; } else { let pos = self - .parser - .app + .cmd .get_positionals() .any(|arg| should_show_arg(self.use_long, arg)); let non_pos = self - .parser - .app + .cmd .get_non_positionals() .any(|arg| should_show_arg(self.use_long, arg)); - let subcmds = self.parser.app.has_visible_subcommands(); + let subcmds = self.cmd.has_visible_subcommands(); if non_pos || pos || subcmds { self.write_templated_help(Self::DEFAULT_TEMPLATE)?; @@ -124,19 +138,23 @@ macro_rules! write_method { } // Methods to write Arg help. -impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { +impl<'help, 'cmd, 'writer> Help<'help, 'cmd, 'writer> { + #[inline(never)] fn good + AsRef<[u8]>>(&mut self, msg: T) -> io::Result<()> { write_method!(self, msg, good) } + #[inline(never)] fn warning + AsRef<[u8]>>(&mut self, msg: T) -> io::Result<()> { write_method!(self, msg, warning) } + #[inline(never)] fn none + AsRef<[u8]>>(&mut self, msg: T) -> io::Result<()> { write_method!(self, msg, none) } + #[inline(never)] fn spaces(&mut self, n: usize) -> io::Result<()> { // A string with 64 consecutive spaces. const SHORT_SPACE: &str = @@ -155,7 +173,7 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { let mut longest = 2; let mut arg_v = Vec::with_capacity(10); - for arg in args + for &arg in args .iter() .filter(|arg| should_show_arg(self.use_long, *arg)) { @@ -179,10 +197,10 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { debug!("Help::write_args"); // The shortest an arg can legally be is 2 (i.e. '-x') let mut longest = 2; - let mut ord_m = BTreeMap::new(); + let mut ord_v = Vec::new(); // Determine the longest - for arg in args.iter().filter(|arg| { + for &arg in args.iter().filter(|arg| { // If it's NextLineHelp we don't care to compute how long it is because it may be // NextLineHelp on purpose simply *because* it's so long and would throw off all other // args alignment @@ -193,9 +211,6 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { longest = longest.max(display_width(arg.to_string().as_str())); debug!("Help::write_args: New Longest...{}", longest); } - let btm = ord_m - .entry(arg.get_display_order()) - .or_insert_with(BTreeMap::new); // Formatting key like this to ensure that: // 1. Argument has long flags are printed just after short flags. @@ -216,19 +231,15 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { s.push_str(arg.name); s }; - btm.insert(key, arg); + ord_v.push((arg.get_display_order(), key, arg)); } + ord_v.sort_by(|a, b| (a.0, &a.1).cmp(&(b.0, &b.1))); let next_line_help = self.will_args_wrap(args, longest); - let num_ord_m = ord_m.len(); - for (i, btm) in ord_m.values().enumerate() { - let last_btm = i + 1 == num_ord_m; - let num_args = btm.len(); - for (i, arg) in btm.values().enumerate() { - let last_arg = last_btm && i + 1 == num_args; - self.write_arg(arg, last_arg, next_line_help, longest)?; - } + for (i, (_, _, arg)) in ord_v.iter().enumerate() { + let last_arg = i + 1 == ord_v.len(); + self.write_arg(arg, last_arg, next_line_help, longest)?; } Ok(()) } @@ -261,7 +272,7 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { self.none(TAB)?; if let Some(s) = arg.short { - self.good(&format!("-{}", s)) + self.good(format!("-{}", s)) } else if !arg.is_positional() { self.none(TAB) } else { @@ -272,49 +283,65 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { /// Writes argument's long command to the wrapped stream. fn long(&mut self, arg: &Arg<'help>) -> io::Result<()> { debug!("Help::long"); - if arg.is_positional() { - return Ok(()); - } - if arg.is_set(ArgSettings::TakesValue) { - if let Some(l) = arg.long { - if arg.short.is_some() { - self.none(", ")?; - } - self.good(&format!("--{}", l))? - } - - let sep = if arg.is_set(ArgSettings::RequireEquals) { - "=" - } else { - " " - }; - self.none(sep)?; - } else if let Some(l) = arg.long { + if let Some(long) = arg.long { if arg.short.is_some() { self.none(", ")?; } - self.good(&format!("--{}", l))?; + self.good(format!("--{}", long))?; } Ok(()) } /// Writes argument's possible values to the wrapped stream. - fn val(&mut self, arg: &Arg<'help>, next_line_help: bool, longest: usize) -> io::Result<()> { + fn val(&mut self, arg: &Arg<'help>) -> io::Result<()> { debug!("Help::val: arg={}", arg.name); - if arg.is_set(ArgSettings::TakesValue) || arg.is_positional() { + let mut need_closing_bracket = false; + if arg.is_takes_value_set() && !arg.is_positional() { + let is_optional_val = arg.min_vals == Some(0); + let sep = if arg.is_require_equals_set() { + if is_optional_val { + need_closing_bracket = true; + "[=" + } else { + "=" + } + } else if is_optional_val { + need_closing_bracket = true; + " [" + } else { + " " + }; + self.none(sep)?; + } + + if arg.is_takes_value_set() || arg.is_positional() { display_arg_val( arg, |s, good| if good { self.good(s) } else { self.none(s) }, )?; } - debug!("Help::val: Has switch..."); + if need_closing_bracket { + self.none("]")?; + } + Ok(()) + } + + /// Write alignment padding between arg's switches/values and its about message. + fn align_to_about( + &mut self, + arg: &Arg<'help>, + next_line_help: bool, + longest: usize, + ) -> io::Result<()> { + debug!("Help::align_to_about: arg={}", arg.name); + debug!("Help::align_to_about: Has switch..."); if self.use_long { - // long help prints messages on the next line so it don't need to align text - debug!("Help::val: printing long help so skip alignment"); + // long help prints messages on the next line so it doesn't need to align text + debug!("Help::align_to_about: printing long help so skip alignment"); } else if !arg.is_positional() { debug!("Yes"); - debug!("Help::val: nlh...{:?}", next_line_help); + debug!("Help::align_to_about: nlh...{:?}", next_line_help); if !next_line_help { let self_len = display_width(arg.to_string().as_str()); // subtract ourself @@ -343,12 +370,11 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { fn write_before_help(&mut self) -> io::Result<()> { debug!("Help::write_before_help"); let before_help = if self.use_long { - self.parser - .app - .before_long_help - .or(self.parser.app.before_help) + self.cmd + .get_before_long_help() + .or_else(|| self.cmd.get_before_help()) } else { - self.parser.app.before_help + self.cmd.get_before_help() }; if let Some(output) = before_help { self.none(text_wrapper(&output.replace("{n}", "\n"), self.term_w))?; @@ -360,12 +386,11 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { fn write_after_help(&mut self) -> io::Result<()> { debug!("Help::write_after_help"); let after_help = if self.use_long { - self.parser - .app - .after_long_help - .or(self.parser.app.after_help) + self.cmd + .get_after_long_help() + .or_else(|| self.cmd.get_after_help()) } else { - self.parser.app.after_help + self.cmd.get_after_help() }; if let Some(output) = after_help { self.none("\n\n")?; @@ -377,7 +402,7 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { /// Writes argument's help to the wrapped stream. fn help( &mut self, - is_not_positional: bool, + arg: Option<&Arg<'help>>, about: &str, spec_vals: &str, next_line_help: bool, @@ -393,11 +418,11 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { longest + 12 }; - let too_long = spaces + display_width(about) + display_width(spec_vals) >= self.term_w; + let too_long = spaces + display_width(&help) >= self.term_w; // Is help on next line, if so then indent if next_line_help { - self.none(&format!("\n{}{}{}", TAB, TAB, TAB))?; + self.none(format!("\n{}{}{}", TAB, TAB, TAB))?; } debug!("Help::help: Too long..."); @@ -415,17 +440,100 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { if let Some(part) = help.lines().next() { self.none(part)?; } + + // indent of help + let spaces = if next_line_help { + TAB_WIDTH * 3 + } else if let Some(true) = arg.map(|a| a.is_positional()) { + longest + TAB_WIDTH * 2 + } else { + longest + TAB_WIDTH * 3 + }; + for part in help.lines().skip(1) { self.none("\n")?; - if next_line_help { - self.none(&format!("{}{}{}", TAB, TAB, TAB))?; - } else if is_not_positional { - self.spaces(longest + 12)?; - } else { - self.spaces(longest + 8)?; - } + self.spaces(spaces)?; self.none(part)?; } + + #[cfg(feature = "unstable-v4")] + if let Some(arg) = arg { + const DASH_SPACE: usize = "- ".len(); + const COLON_SPACE: usize = ": ".len(); + if self.use_long + && !arg.is_hide_possible_values_set() + && arg + .possible_vals + .iter() + .any(PossibleValue::should_show_help) + { + debug!("Help::help: Found possible vals...{:?}", arg.possible_vals); + if !help.is_empty() { + self.none("\n\n")?; + self.spaces(spaces)?; + } + self.none("Possible values:")?; + let longest = arg + .possible_vals + .iter() + .filter_map(|f| f.get_visible_quoted_name().map(|name| display_width(&name))) + .max() + .expect("Only called with possible value"); + let help_longest = arg + .possible_vals + .iter() + .filter_map(|f| f.get_visible_help().map(display_width)) + .max() + .expect("Only called with possible value with help"); + // should new line + let taken = longest + spaces + DASH_SPACE; + + let possible_value_new_line = + self.term_w >= taken && self.term_w < taken + COLON_SPACE + help_longest; + + let spaces = spaces + TAB_WIDTH - DASH_SPACE; + let spaces_help = if possible_value_new_line { + spaces + DASH_SPACE + } else { + spaces + longest + DASH_SPACE + COLON_SPACE + }; + + for pv in arg.possible_vals.iter().filter(|pv| !pv.is_hide_set()) { + self.none("\n")?; + self.spaces(spaces)?; + self.none("- ")?; + self.good(pv.get_name())?; + if let Some(help) = pv.get_help() { + debug!("Help::help: Possible Value help"); + + if possible_value_new_line { + self.none(":\n")?; + self.spaces(spaces_help)?; + } else { + self.none(": ")?; + // To align help messages + self.spaces(longest - display_width(pv.get_name()))?; + } + + let avail_chars = if self.term_w > spaces_help { + self.term_w - spaces_help + } else { + usize::MAX + }; + + let help = text_wrapper(help, avail_chars); + let mut help = help.lines(); + + self.none(help.next().unwrap_or_default())?; + for part in help { + self.none("\n")?; + self.spaces(spaces_help)?; + self.none(part)?; + } + } + } + } + } Ok(()) } @@ -439,21 +547,16 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { ) -> io::Result<()> { self.short(arg)?; self.long(arg)?; - self.val(arg, next_line_help, longest)?; + self.val(arg)?; + self.align_to_about(arg, next_line_help, longest)?; let about = if self.use_long { - arg.long_help.unwrap_or_else(|| arg.help.unwrap_or("")) + arg.long_help.or(arg.help).unwrap_or("") } else { - arg.help.unwrap_or_else(|| arg.long_help.unwrap_or("")) + arg.help.or(arg.long_help).unwrap_or("") }; - self.help( - !arg.is_positional(), - about, - spec_vals, - next_line_help, - longest, - )?; + self.help(Some(arg), about, spec_vals, next_line_help, longest)?; Ok(()) } @@ -468,7 +571,7 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { } fn arg_next_line_help(&self, arg: &Arg<'help>, spec_vals: &str, longest: usize) -> bool { - if self.next_line_help || arg.is_set(ArgSettings::NextLineHelp) || self.use_long { + if self.next_line_help || arg.is_next_line_help_set() || self.use_long { // setting_next_line true } else { @@ -487,12 +590,12 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { let mut spec_vals = vec![]; #[cfg(feature = "env")] if let Some(ref env) = a.env { - if !a.is_set(ArgSettings::HideEnv) { + if !a.is_hide_env_set() { debug!( "Help::spec_vals: Found environment variable...[{:?}:{:?}]", env.0, env.1 ); - let env_val = if !a.is_set(ArgSettings::HideEnvValues) { + let env_val = if !a.is_hide_env_values_set() { format!( "={}", env.1 @@ -506,7 +609,7 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { spec_vals.push(env_info); } } - if !a.is_set(ArgSettings::HideDefaultValue) && !a.default_vals.is_empty() { + if !a.is_hide_default_value_set() && !a.default_vals.is_empty() { debug!( "Help::spec_vals: Found default value...[{:?}]", a.default_vals @@ -563,9 +666,11 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { } } - if !self.hide_pv - && !a.is_set(ArgSettings::HidePossibleValues) - && !a.possible_vals.is_empty() + if !(a.is_hide_possible_values_set() + || a.possible_vals.is_empty() + || cfg!(feature = "unstable-v4") + && self.use_long + && a.possible_vals.iter().any(PossibleValue::should_show_help)) { debug!( "Help::spec_vals: Found possible vals...{:?}", @@ -575,15 +680,7 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { let pvs = a .possible_vals .iter() - .filter_map(|value| { - if value.is_hidden() { - None - } else if value.get_name().contains(char::is_whitespace) { - Some(format!("{:?}", value.get_name())) - } else { - Some(value.get_name().to_string()) - } - }) + .filter_map(PossibleValue::get_visible_quoted_name) .collect::>() .join(", "); @@ -604,9 +701,9 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { fn write_about(&mut self, before_new_line: bool, after_new_line: bool) -> io::Result<()> { let about = if self.use_long { - self.parser.app.long_about.or(self.parser.app.about) + self.cmd.get_long_about().or_else(|| self.cmd.get_about()) } else { - self.parser.app.about + self.cmd.get_about() }; if let Some(output) = about { if before_new_line { @@ -621,7 +718,7 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { } fn write_author(&mut self, before_new_line: bool, after_new_line: bool) -> io::Result<()> { - if let Some(author) = self.parser.app.author { + if let Some(author) = self.cmd.get_author() { if before_new_line { self.none("\n")?; } @@ -634,7 +731,10 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { } fn write_version(&mut self) -> io::Result<()> { - let version = self.parser.app.version.or(self.parser.app.long_version); + let version = self + .cmd + .get_version() + .or_else(|| self.cmd.get_long_version()); if let Some(output) = version { self.none(text_wrapper(output, self.term_w))?; } @@ -643,32 +743,38 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { } /// Methods to write a single subcommand -impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { +impl<'help, 'cmd, 'writer> Help<'help, 'cmd, 'writer> { fn write_subcommand( &mut self, sc_str: &str, - app: &App<'help>, + cmd: &Command<'help>, next_line_help: bool, longest: usize, ) -> io::Result<()> { debug!("Help::write_subcommand"); - let spec_vals = &self.sc_spec_vals(app); + let spec_vals = &self.sc_spec_vals(cmd); - let about = app.about.unwrap_or_else(|| app.long_about.unwrap_or("")); + let about = cmd + .get_about() + .or_else(|| cmd.get_long_about()) + .unwrap_or(""); self.subcmd(sc_str, next_line_help, longest)?; - self.help(false, about, spec_vals, next_line_help, longest) + self.help(None, about, spec_vals, next_line_help, longest) } - fn sc_spec_vals(&self, a: &App) -> String { - debug!("Help::sc_spec_vals: a={}", a.name); + fn sc_spec_vals(&self, a: &Command) -> String { + debug!("Help::sc_spec_vals: a={}", a.get_name()); let mut spec_vals = vec![]; - if !a.aliases.is_empty() || !a.short_flag_aliases.is_empty() { - debug!("Help::spec_vals: Found aliases...{:?}", a.aliases); + if 0 < a.get_all_aliases().count() || 0 < a.get_all_short_flag_aliases().count() { + debug!( + "Help::spec_vals: Found aliases...{:?}", + a.get_all_aliases().collect::>() + ); debug!( "Help::spec_vals: Found short flag aliases...{:?}", - a.short_flag_aliases + a.get_all_short_flag_aliases().collect::>() ); let mut short_als = a @@ -689,13 +795,18 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { spec_vals.join(" ") } - fn subcommand_next_line_help(&self, app: &App<'help>, spec_vals: &str, longest: usize) -> bool { + fn subcommand_next_line_help( + &self, + cmd: &Command<'help>, + spec_vals: &str, + longest: usize, + ) -> bool { if self.next_line_help | self.use_long { // setting_next_line true } else { // force_next_line - let h = app.about.unwrap_or(""); + let h = cmd.get_about().unwrap_or(""); let h_w = display_width(h) + display_width(spec_vals); let taken = longest + 12; self.term_w >= taken @@ -717,30 +828,26 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { } // Methods to write Parser help. -impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { +impl<'help, 'cmd, 'writer> Help<'help, 'cmd, 'writer> { /// Writes help for all arguments (options, flags, args, subcommands) /// including titles of a Parser Object to the wrapped stream. pub(crate) fn write_all_args(&mut self) -> io::Result<()> { debug!("Help::write_all_args"); let pos = self - .parser - .app + .cmd .get_positionals_with_no_heading() .filter(|arg| should_show_arg(self.use_long, arg)) .collect::>(); let non_pos = self - .parser - .app + .cmd .get_non_positionals_with_no_heading() .filter(|arg| should_show_arg(self.use_long, arg)) .collect::>(); - let subcmds = self.parser.app.has_visible_subcommands(); + let subcmds = self.cmd.has_visible_subcommands(); let custom_headings = self - .parser - .app - .args - .args() + .cmd + .get_arguments() .filter_map(|arg| arg.get_help_heading()) .collect::>(); @@ -764,10 +871,8 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { if !custom_headings.is_empty() { for heading in custom_headings { let args = self - .parser - .app - .args - .args() + .cmd + .get_arguments() .filter(|a| { if let Some(help_heading) = a.get_help_heading() { return help_heading == heading; @@ -781,8 +886,8 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { if !first { self.none("\n\n")?; } - self.warning(&*format!("{}:\n", heading))?; - self.write_args(&*args)?; + self.warning(format!("{}:\n", heading))?; + self.write_args(&args)?; first = false } } @@ -793,19 +898,30 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { self.none("\n\n")?; } - self.warning(self.parser.app.subcommand_heading.unwrap_or("SUBCOMMANDS"))?; + self.warning( + self.cmd + .get_subcommand_help_heading() + .unwrap_or("SUBCOMMANDS"), + )?; self.warning(":\n")?; - self.write_subcommands(self.parser.app)?; + self.write_subcommands(self.cmd)?; } Ok(()) } /// Will use next line help on writing subcommands. - fn will_subcommands_wrap(&self, subcommands: &[App<'help>], longest: usize) -> bool { + fn will_subcommands_wrap<'a>( + &self, + subcommands: impl IntoIterator>, + longest: usize, + ) -> bool + where + 'help: 'a, + { subcommands - .iter() + .into_iter() .filter(|&subcommand| should_show_subcommand(subcommand)) .any(|subcommand| { let spec_vals = &self.sc_spec_vals(subcommand); @@ -814,66 +930,73 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { } /// Writes help for subcommands of a Parser Object to the wrapped stream. - fn write_subcommands(&mut self, app: &App<'help>) -> io::Result<()> { + fn write_subcommands(&mut self, cmd: &Command<'help>) -> io::Result<()> { debug!("Help::write_subcommands"); // The shortest an arg can legally be is 2 (i.e. '-x') let mut longest = 2; - let mut ord_m = BTreeMap::new(); - for subcommand in app - .subcommands - .iter() + let mut ord_v = Vec::new(); + for subcommand in cmd + .get_subcommands() .filter(|subcommand| should_show_subcommand(subcommand)) { - let btm = ord_m - .entry(subcommand.get_display_order()) - .or_insert_with(BTreeMap::new); let mut sc_str = String::new(); - sc_str.push_str( - &subcommand - .short_flag - .map_or(String::new(), |c| format!("-{}, ", c)), - ); - sc_str.push_str( - &subcommand - .long_flag - .map_or(String::new(), |c| format!("--{}, ", c)), - ); - sc_str.push_str(&subcommand.name); + sc_str.push_str(subcommand.get_name()); + if let Some(short) = subcommand.get_short_flag() { + write!(sc_str, " -{}", short).unwrap(); + } + if let Some(long) = subcommand.get_long_flag() { + write!(sc_str, " --{}", long).unwrap(); + } longest = longest.max(display_width(&sc_str)); - btm.insert(sc_str, subcommand.clone()); + ord_v.push((subcommand.get_display_order(), sc_str, subcommand)); } + ord_v.sort_by(|a, b| (a.0, &a.1).cmp(&(b.0, &b.1))); debug!("Help::write_subcommands longest = {}", longest); - let next_line_help = self.will_subcommands_wrap(&app.subcommands, longest); + let next_line_help = self.will_subcommands_wrap(cmd.get_subcommands(), longest); let mut first = true; - for btm in ord_m.values() { - for (sc_str, sc) in btm { - if first { - first = false; - } else { - self.none("\n")?; - } - self.write_subcommand(sc_str, sc, next_line_help, longest)?; + for (_, sc_str, sc) in &ord_v { + if first { + first = false; + } else { + self.none("\n")?; } + self.write_subcommand(sc_str, sc, next_line_help, longest)?; } Ok(()) } + /// Writes binary name of a Parser Object to the wrapped stream. + fn write_display_name(&mut self) -> io::Result<()> { + debug!("Help::write_display_name"); + + let display_name = text_wrapper( + &self + .cmd + .get_display_name() + .unwrap_or_else(|| self.cmd.get_name()) + .replace("{n}", "\n"), + self.term_w, + ); + self.good(&display_name)?; + Ok(()) + } + /// Writes binary name of a Parser Object to the wrapped stream. fn write_bin_name(&mut self) -> io::Result<()> { debug!("Help::write_bin_name"); - let bin_name = if let Some(bn) = self.parser.app.bin_name.as_ref() { + let bin_name = if let Some(bn) = self.cmd.get_bin_name() { if bn.contains(' ') { // In case we're dealing with subcommands i.e. git mv is translated to git-mv bn.replace(' ', "-") } else { - text_wrapper(&self.parser.app.name.replace("{n}", "\n"), self.term_w) + text_wrapper(&self.cmd.get_name().replace("{n}", "\n"), self.term_w) } } else { - text_wrapper(&self.parser.app.name.replace("{n}", "\n"), self.term_w) + text_wrapper(&self.cmd.get_name().replace("{n}", "\n"), self.term_w) }; self.good(&bin_name)?; Ok(()) @@ -881,12 +1004,12 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { } // Methods to write Parser help using templates. -impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { +impl<'help, 'cmd, 'writer> Help<'help, 'cmd, 'writer> { /// Write help to stream for the parser in the format defined by the template. /// - /// For details about the template language see [`App::help_template`]. + /// For details about the template language see [`Command::help_template`]. /// - /// [`App::help_template`]: App::help_template() + /// [`Command::help_template`]: Command::help_template() fn write_templated_help(&mut self, template: &str) -> io::Result<()> { debug!("Help::write_templated_help"); @@ -928,6 +1051,9 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { for part in parts { tags! { match part { + "name" => { + self.write_display_name()?; + } "bin" => { self.write_bin_name()?; } @@ -956,7 +1082,7 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { self.warning("USAGE:")?; } "usage" => { - self.none(Usage::new(self.parser).create_usage_no_title(&[]))?; + self.none(self.usage.create_usage_no_title(&[]))?; } "all-args" => { self.write_all_args()?; @@ -964,13 +1090,13 @@ impl<'help, 'app, 'parser, 'writer> Help<'help, 'app, 'parser, 'writer> { "options" => { // Include even those with a heading as we don't have a good way of // handling help_heading in the template. - self.write_args(&self.parser.app.get_non_positionals().collect::>())?; + self.write_args(&self.cmd.get_non_positionals().collect::>())?; } "positionals" => { - self.write_args(&self.parser.app.get_positionals().collect::>())?; + self.write_args(&self.cmd.get_positionals().collect::>())?; } "subcommands" => { - self.write_subcommands(self.parser.app)?; + self.write_subcommands(self.cmd)?; } "after-help" => { self.write_after_help()?; @@ -995,6 +1121,7 @@ pub(crate) fn dimensions() -> Option<(usize, usize)> { } const TAB: &str = " "; +const TAB_WIDTH: usize = 4; pub(crate) enum HelpWriter<'writer> { Normal(&'writer mut dyn Write), @@ -1003,20 +1130,22 @@ pub(crate) enum HelpWriter<'writer> { fn should_show_arg(use_long: bool, arg: &Arg) -> bool { debug!("should_show_arg: use_long={:?}, arg={}", use_long, arg.name); - if arg.is_set(ArgSettings::Hidden) { + if arg.is_hide_set() { return false; } - (!arg.is_set(ArgSettings::HiddenLongHelp) && use_long) - || (!arg.is_set(ArgSettings::HiddenShortHelp) && !use_long) - || arg.is_set(ArgSettings::NextLineHelp) + (!arg.is_hide_long_help_set() && use_long) + || (!arg.is_hide_short_help_set() && !use_long) + || arg.is_next_line_help_set() } -fn should_show_subcommand(subcommand: &App) -> bool { - !subcommand.is_set(AppSettings::Hidden) +fn should_show_subcommand(subcommand: &Command) -> bool { + !subcommand.is_hide_set() } fn text_wrapper(help: &str, width: usize) -> String { - let wrapper = textwrap::Options::new(width).break_words(false); + let wrapper = textwrap::Options::new(width) + .break_words(false) + .word_splitter(textwrap::WordSplitter::NoHyphenation); help.lines() .map(|line| textwrap::fill(line, &wrapper)) .collect::>() diff --git a/third_party/rust/clap/src/output/usage.rs b/third_party/rust/clap/src/output/usage.rs index b520b3d99dc3..f223d9c127a8 100644 --- a/third_party/rust/clap/src/output/usage.rs +++ b/third_party/rust/clap/src/output/usage.rs @@ -1,24 +1,29 @@ -// std -use std::collections::BTreeMap; - use indexmap::IndexSet; // Internal -use crate::{ - build::AppSettings as AS, - build::{Arg, ArgSettings}, - parse::{ArgMatcher, Parser}, - util::Id, - INTERNAL_ERROR_MSG, -}; +use crate::build::AppSettings as AS; +use crate::build::{Arg, ArgPredicate, Command}; +use crate::parse::ArgMatcher; +use crate::util::ChildGraph; +use crate::util::Id; +use crate::INTERNAL_ERROR_MSG; -pub(crate) struct Usage<'help, 'app, 'parser> { - p: &'parser Parser<'help, 'app>, +pub(crate) struct Usage<'help, 'cmd> { + cmd: &'cmd Command<'help>, + required: Option<&'cmd ChildGraph>, } -impl<'help, 'app, 'parser> Usage<'help, 'app, 'parser> { - pub(crate) fn new(p: &'parser Parser<'help, 'app>) -> Self { - Usage { p } +impl<'help, 'cmd> Usage<'help, 'cmd> { + pub(crate) fn new(cmd: &'cmd Command<'help>) -> Self { + Usage { + cmd, + required: None, + } + } + + pub(crate) fn required(mut self, required: &'cmd ChildGraph) -> Self { + self.required = Some(required); + self } // Creates a usage string for display. This happens just after all arguments were parsed, but before @@ -34,7 +39,7 @@ impl<'help, 'app, 'parser> Usage<'help, 'app, 'parser> { // Creates a usage string (*without title*) if one was not provided by the user manually. pub(crate) fn create_usage_no_title(&self, used: &[Id]) -> String { debug!("Usage::create_usage_no_title"); - if let Some(u) = self.p.app.usage_str { + if let Some(u) = self.cmd.get_override_usage() { String::from(&*u) } else if used.is_empty() { self.create_help_usage(true) @@ -44,20 +49,19 @@ impl<'help, 'app, 'parser> Usage<'help, 'app, 'parser> { } // Creates a usage string for display in help messages (i.e. not for errors) - pub(crate) fn create_help_usage(&self, incl_reqs: bool) -> String { + fn create_help_usage(&self, incl_reqs: bool) -> String { debug!("Usage::create_help_usage; incl_reqs={:?}", incl_reqs); let mut usage = String::with_capacity(75); let name = self - .p - .app - .usage - .as_ref() - .unwrap_or_else(|| self.p.app.bin_name.as_ref().unwrap_or(&self.p.app.name)); - usage.push_str(&*name); + .cmd + .get_usage_name() + .or_else(|| self.cmd.get_bin_name()) + .unwrap_or_else(|| self.cmd.get_name()); + usage.push_str(name); let req_string = if incl_reqs { self.get_required_usage_from(&[], None, false) .iter() - .fold(String::new(), |a, s| a + &format!(" {}", s)[..]) + .fold(String::new(), |a, s| a + " " + s) } else { String::new() }; @@ -66,39 +70,27 @@ impl<'help, 'app, 'parser> Usage<'help, 'app, 'parser> { usage.push_str(" [OPTIONS]"); } - let allow_missing_positional = self.p.app.is_set(AS::AllowMissingPositional); + let allow_missing_positional = self.cmd.is_allow_missing_positional_set(); if !allow_missing_positional { usage.push_str(&req_string); } - let has_last = self - .p - .app - .get_positionals() - .any(|p| p.is_set(ArgSettings::Last)); + let has_last = self.cmd.get_positionals().any(|p| p.is_last_set()); // places a '--' in the usage string if there are args and options // supporting multiple values if self - .p - .app + .cmd .get_non_positionals() - .any(|o| o.is_set(ArgSettings::MultipleValues)) - && self - .p - .app - .get_positionals() - .any(|p| !p.is_set(ArgSettings::Required)) - && !(self.p.app.has_visible_subcommands() - || self.p.is_set(AS::AllowExternalSubcommands)) + .any(|o| o.is_multiple_values_set()) + && self.cmd.get_positionals().any(|p| !p.is_required_set()) + && !(self.cmd.has_visible_subcommands() || self.cmd.is_allow_external_subcommands_set()) && !has_last { usage.push_str(" [--]"); } - let not_req_or_hidden = |p: &Arg| { - (!p.is_set(ArgSettings::Required) || p.is_set(ArgSettings::Last)) - && !p.is_set(ArgSettings::Hidden) - }; - if self.p.app.get_positionals().any(not_req_or_hidden) { + let not_req_or_hidden = + |p: &Arg| (!p.is_required_set() || p.is_last_set()) && !p.is_hide_set(); + if self.cmd.get_positionals().any(not_req_or_hidden) { if let Some(args_tag) = self.get_args_tag(incl_reqs) { usage.push_str(&*args_tag); } else { @@ -106,20 +98,13 @@ impl<'help, 'app, 'parser> Usage<'help, 'app, 'parser> { } if has_last && incl_reqs { let pos = self - .p - .app + .cmd .get_positionals() - .find(|p| p.is_set(ArgSettings::Last)) + .find(|p| p.is_last_set()) .expect(INTERNAL_ERROR_MSG); debug!("Usage::create_help_usage: '{}' has .last(true)", pos.name); - let req = pos.is_set(ArgSettings::Required); - if req - && self - .p - .app - .get_positionals() - .any(|p| !p.is_set(ArgSettings::Required)) - { + let req = pos.is_required_set(); + if req && self.cmd.get_positionals().any(|p| !p.is_required_set()) { usage.push_str(" -- <"); } else if req { usage.push_str(" [--] <"); @@ -140,14 +125,16 @@ impl<'help, 'app, 'parser> Usage<'help, 'app, 'parser> { } // incl_reqs is only false when this function is called recursively - if self.p.app.has_visible_subcommands() && incl_reqs - || self.p.is_set(AS::AllowExternalSubcommands) + if self.cmd.has_visible_subcommands() && incl_reqs + || self.cmd.is_allow_external_subcommands_set() { - let placeholder = self.p.app.subcommand_value_name.unwrap_or("SUBCOMMAND"); - if self.p.is_set(AS::SubcommandsNegateReqs) || self.p.is_set(AS::ArgsNegateSubcommands) + let placeholder = self.cmd.get_subcommand_value_name().unwrap_or("SUBCOMMAND"); + #[allow(deprecated)] + if self.cmd.is_subcommand_negates_reqs_set() + || self.cmd.is_args_conflicts_with_subcommands_set() { usage.push_str("\n "); - if !self.p.is_set(AS::ArgsNegateSubcommands) { + if !self.cmd.is_args_conflicts_with_subcommands_set() { usage.push_str(&*self.create_help_usage(false)); } else { usage.push_str(&*name); @@ -155,8 +142,8 @@ impl<'help, 'app, 'parser> Usage<'help, 'app, 'parser> { usage.push_str(" <"); usage.push_str(placeholder); usage.push('>'); - } else if self.p.is_set(AS::SubcommandRequired) - || self.p.is_set(AS::SubcommandRequiredElseHelp) + } else if self.cmd.is_subcommand_required_set() + || self.cmd.is_set(AS::SubcommandRequiredElseHelp) { usage.push_str(" <"); usage.push_str(placeholder); @@ -167,7 +154,7 @@ impl<'help, 'app, 'parser> Usage<'help, 'app, 'parser> { usage.push(']'); } } - usage.shrink_to_fit(); + let usage = usage.trim().to_owned(); debug!("Usage::create_help_usage: usage={}", usage); usage } @@ -181,20 +168,18 @@ impl<'help, 'app, 'parser> Usage<'help, 'app, 'parser> { let r_string = self .get_required_usage_from(used, None, true) .iter() - .fold(String::new(), |acc, s| acc + &format!(" {}", s)[..]); + .fold(String::new(), |acc, s| acc + " " + s); usage.push_str( - &self - .p - .app - .usage - .as_ref() - .unwrap_or_else(|| self.p.app.bin_name.as_ref().unwrap_or(&self.p.app.name))[..], + self.cmd + .get_usage_name() + .or_else(|| self.cmd.get_bin_name()) + .unwrap_or_else(|| self.cmd.get_name()), ); usage.push_str(&*r_string); - if self.p.is_set(AS::SubcommandRequired) { + if self.cmd.is_subcommand_required_set() { usage.push_str(" <"); - usage.push_str(self.p.app.subcommand_value_name.unwrap_or("SUBCOMMAND")); + usage.push_str(self.cmd.get_subcommand_value_name().unwrap_or("SUBCOMMAND")); usage.push('>'); } usage.shrink_to_fit(); @@ -206,22 +191,17 @@ impl<'help, 'app, 'parser> Usage<'help, 'app, 'parser> { debug!("Usage::get_args_tag; incl_reqs = {:?}", incl_reqs); let mut count = 0; for pos in self - .p - .app + .cmd .get_positionals() - .filter(|pos| !pos.is_set(ArgSettings::Required)) - .filter(|pos| !pos.is_set(ArgSettings::Hidden)) - .filter(|pos| !pos.is_set(ArgSettings::Last)) + .filter(|pos| !pos.is_required_set()) + .filter(|pos| !pos.is_hide_set()) + .filter(|pos| !pos.is_last_set()) { debug!("Usage::get_args_tag:iter:{}", pos.name); - let required = self.p.app.groups_for_arg(&pos.id).any(|grp_s| { + let required = self.cmd.groups_for_arg(&pos.id).any(|grp_s| { debug!("Usage::get_args_tag:iter:{:?}:iter:{:?}", pos.name, grp_s); // if it's part of a required group we don't want to count it - self.p - .app - .groups - .iter() - .any(|g| g.required && (g.id == grp_s)) + self.cmd.get_groups().any(|g| g.required && (g.id == grp_s)) }); if !required { count += 1; @@ -232,28 +212,23 @@ impl<'help, 'app, 'parser> Usage<'help, 'app, 'parser> { } } - if !self.p.is_set(AS::DontCollapseArgsInUsage) && count > 1 { + if !self.cmd.is_dont_collapse_args_in_usage_set() && count > 1 { debug!("Usage::get_args_tag:iter: More than one, returning [ARGS]"); // [ARGS] None } else if count == 1 && incl_reqs { let pos = self - .p - .app + .cmd .get_positionals() .find(|pos| { - !pos.is_set(ArgSettings::Required) - && !pos.is_set(ArgSettings::Hidden) - && !pos.is_set(ArgSettings::Last) - && !self.p.app.groups_for_arg(&pos.id).any(|grp_s| { + !pos.is_required_set() + && !pos.is_hide_set() + && !pos.is_last_set() + && !self.cmd.groups_for_arg(&pos.id).any(|grp_s| { debug!("Usage::get_args_tag:iter:{:?}:iter:{:?}", pos.name, grp_s); // if it's part of a required group we don't want to count it - self.p - .app - .groups - .iter() - .any(|g| g.required && (g.id == grp_s)) + self.cmd.get_groups().any(|g| g.required && (g.id == grp_s)) }) }) .expect(INTERNAL_ERROR_MSG); @@ -268,18 +243,17 @@ impl<'help, 'app, 'parser> Usage<'help, 'app, 'parser> { pos.name_no_brackets(), pos.multiple_str() )) - } else if self.p.is_set(AS::DontCollapseArgsInUsage) - && self.p.app.has_positionals() + } else if self.cmd.is_dont_collapse_args_in_usage_set() + && self.cmd.has_positionals() && incl_reqs { debug!("Usage::get_args_tag:iter: Don't collapse returning all"); Some( - self.p - .app + self.cmd .get_positionals() - .filter(|pos| !pos.is_set(ArgSettings::Required)) - .filter(|pos| !pos.is_set(ArgSettings::Hidden)) - .filter(|pos| !pos.is_set(ArgSettings::Last)) + .filter(|pos| !pos.is_required_set()) + .filter(|pos| !pos.is_hide_set()) + .filter(|pos| !pos.is_last_set()) .map(|pos| format!(" [{}]{}", pos.name_no_brackets(), pos.multiple_str())) .collect::>() .join(""), @@ -287,26 +261,24 @@ impl<'help, 'app, 'parser> Usage<'help, 'app, 'parser> { } else if !incl_reqs { debug!("Usage::get_args_tag:iter: incl_reqs=false, building secondary usage string"); let highest_req_pos = self - .p - .app + .cmd .get_positionals() .filter_map(|pos| { - if pos.is_set(ArgSettings::Required) && !pos.is_set(ArgSettings::Last) { + if pos.is_required_set() && !pos.is_last_set() { Some(pos.index) } else { None } }) .max() - .unwrap_or_else(|| Some(self.p.app.get_positionals().count())); + .unwrap_or_else(|| Some(self.cmd.get_positionals().count())); Some( - self.p - .app + self.cmd .get_positionals() .filter(|pos| pos.index <= highest_req_pos) - .filter(|pos| !pos.is_set(ArgSettings::Required)) - .filter(|pos| !pos.is_set(ArgSettings::Hidden)) - .filter(|pos| !pos.is_set(ArgSettings::Last)) + .filter(|pos| !pos.is_required_set()) + .filter(|pos| !pos.is_hide_set()) + .filter(|pos| !pos.is_last_set()) .map(|pos| format!(" [{}]{}", pos.name_no_brackets(), pos.multiple_str())) .collect::>() .join(""), @@ -319,7 +291,7 @@ impl<'help, 'app, 'parser> Usage<'help, 'app, 'parser> { // Determines if we need the `[OPTIONS]` tag in the usage string fn needs_options_tag(&self) -> bool { debug!("Usage::needs_options_tag"); - 'outer: for f in self.p.app.get_non_positionals() { + 'outer: for f in self.cmd.get_non_positionals() { debug!("Usage::needs_options_tag:iter: f={}", f.name); // Don't print `[OPTIONS]` just for help or version @@ -328,23 +300,17 @@ impl<'help, 'app, 'parser> Usage<'help, 'app, 'parser> { continue; } - if f.is_set(ArgSettings::Hidden) { + if f.is_hide_set() { debug!("Usage::needs_options_tag:iter Option is hidden"); continue; } - if f.is_set(ArgSettings::Required) { + if f.is_required_set() { debug!("Usage::needs_options_tag:iter Option is required"); continue; } - for grp_s in self.p.app.groups_for_arg(&f.id) { + for grp_s in self.cmd.groups_for_arg(&f.id) { debug!("Usage::needs_options_tag:iter:iter: grp_s={:?}", grp_s); - if self - .p - .app - .groups - .iter() - .any(|g| g.id == grp_s && g.required) - { + if self.cmd.get_groups().any(|g| g.id == grp_s && g.required) { debug!("Usage::needs_options_tag:iter:iter: Group is required"); continue 'outer; } @@ -367,24 +333,44 @@ impl<'help, 'app, 'parser> Usage<'help, 'app, 'parser> { incls: &[Id], matcher: Option<&ArgMatcher>, incl_last: bool, - ) -> Vec { + ) -> IndexSet { debug!( "Usage::get_required_usage_from: incls={:?}, matcher={:?}, incl_last={:?}", incls, matcher.is_some(), incl_last ); - let mut ret_val = Vec::new(); + let mut ret_val = IndexSet::new(); let mut unrolled_reqs = IndexSet::new(); - for a in self.p.required.iter() { - if let Some(m) = matcher { - for aa in self.p.app.unroll_requirements_for_arg(a, m) { - // if we don't check for duplicates here this causes duplicate error messages - // see https://github.com/clap-rs/clap/issues/2770 - unrolled_reqs.insert(aa); - } + let required_owned; + let required = if let Some(required) = self.required { + required + } else { + required_owned = self.cmd.required_graph(); + &required_owned + }; + + for a in required.iter() { + let is_relevant = |(val, req_arg): &(ArgPredicate<'_>, Id)| -> Option { + let required = match val { + ArgPredicate::Equals(_) => { + if let Some(matcher) = matcher { + matcher.check_explicit(a, *val) + } else { + false + } + } + ArgPredicate::IsPresent => true, + }; + required.then(|| req_arg.clone()) + }; + + for aa in self.cmd.unroll_arg_requires(is_relevant, a) { + // if we don't check for duplicates here this causes duplicate error messages + // see https://github.com/clap-rs/clap/issues/2770 + unrolled_reqs.insert(aa); } // always include the required arg itself. it will not be enumerated // by unroll_requirements_for_arg. @@ -397,36 +383,33 @@ impl<'help, 'app, 'parser> Usage<'help, 'app, 'parser> { ); let args_in_groups = self - .p - .app - .groups - .iter() - .filter(|gn| self.p.required.contains(&gn.id)) - .flat_map(|g| self.p.app.unroll_args_in_group(&g.id)) + .cmd + .get_groups() + .filter(|gn| required.contains(&gn.id)) + .flat_map(|g| self.cmd.unroll_args_in_group(&g.id)) .collect::>(); for a in unrolled_reqs .iter() .chain(incls.iter()) - .filter(|name| !self.p.app.get_positionals().any(|p| &&p.id == name)) - .filter(|name| !self.p.app.groups.iter().any(|g| &&g.id == name)) + .filter(|name| !self.cmd.get_positionals().any(|p| &&p.id == name)) + .filter(|name| !self.cmd.get_groups().any(|g| &&g.id == name)) .filter(|name| !args_in_groups.contains(name)) .filter(|name| !(matcher.is_some() && matcher.as_ref().unwrap().contains(name))) { debug!("Usage::get_required_usage_from:iter:{:?}", a); - let arg = self.p.app.find(a).expect(INTERNAL_ERROR_MSG).to_string(); - ret_val.push(arg); + let arg = self.cmd.find(a).expect(INTERNAL_ERROR_MSG).to_string(); + ret_val.insert(arg); } let mut g_vec: Vec = vec![]; for g in unrolled_reqs .iter() - .filter(|n| self.p.app.groups.iter().any(|g| g.id == **n)) + .filter(|n| self.cmd.get_groups().any(|g| g.id == **n)) { // don't print requirement for required groups that have an arg. if let Some(m) = matcher { let have_group_entry = self - .p - .app + .cmd .unroll_args_in_group(g) .iter() .any(|arg| m.contains(arg)); @@ -435,28 +418,29 @@ impl<'help, 'app, 'parser> Usage<'help, 'app, 'parser> { } } - let elem = self.p.app.format_group(g); + let elem = self.cmd.format_group(g); if !g_vec.contains(&elem) { g_vec.push(elem); } } - ret_val.extend_from_slice(&g_vec); + ret_val.extend(g_vec); - let pmap = unrolled_reqs + let mut pvec = unrolled_reqs .iter() .chain(incls.iter()) - .filter(|a| self.p.app.get_positionals().any(|p| &&p.id == a)) + .filter(|a| self.cmd.get_positionals().any(|p| &&p.id == a)) .filter(|&pos| matcher.map_or(true, |m| !m.contains(pos))) - .filter_map(|pos| self.p.app.find(pos)) - .filter(|&pos| incl_last || !pos.is_set(ArgSettings::Last)) + .filter_map(|pos| self.cmd.find(pos)) + .filter(|&pos| incl_last || !pos.is_last_set()) .filter(|pos| !args_in_groups.contains(&pos.id)) .map(|pos| (pos.index.unwrap(), pos)) - .collect::>(); // sort by index + .collect::>(); + pvec.sort_by_key(|(ind, _)| *ind); // sort by index - for p in pmap.values() { - debug!("Usage::get_required_usage_from:iter:{:?}", p.id); + for (_, p) in pvec { + debug!("Usage::get_required_usage_from:push:{:?}", p.id); if !args_in_groups.contains(&p.id) { - ret_val.push(p.to_string()); + ret_val.insert(p.to_string()); } } diff --git a/third_party/rust/clap/src/parse/arg_matcher.rs b/third_party/rust/clap/src/parse/arg_matcher.rs index 9e5a59c03515..31804a46fcc7 100644 --- a/third_party/rust/clap/src/parse/arg_matcher.rs +++ b/third_party/rust/clap/src/parse/arg_matcher.rs @@ -3,8 +3,8 @@ use std::{collections::HashMap, ffi::OsString, mem, ops::Deref}; // Internal use crate::{ - build::{App, Arg, ArgSettings}, - parse::{ArgMatches, MatchedArg, SubCommand, ValueType}, + build::{Arg, ArgPredicate, Command}, + parse::{ArgMatches, MatchedArg, SubCommand, ValueSource}, util::Id, }; @@ -12,25 +12,29 @@ use crate::{ use indexmap::map::Entry; #[derive(Debug, Default)] -pub(crate) struct ArgMatcher(pub(crate) ArgMatches); +pub(crate) struct ArgMatcher(ArgMatches); impl ArgMatcher { - pub(crate) fn new(_app: &App) -> Self { + pub(crate) fn new(_cmd: &Command) -> Self { ArgMatcher(ArgMatches { #[cfg(debug_assertions)] valid_args: { - let args = _app.args.args().map(|a| a.id.clone()); - let groups = _app.groups.iter().map(|g| g.id.clone()); + let args = _cmd.get_arguments().map(|a| a.id.clone()); + let groups = _cmd.get_groups().map(|g| g.id.clone()); args.chain(groups).collect() }, #[cfg(debug_assertions)] - valid_subcommands: _app.subcommands.iter().map(|sc| sc.id.clone()).collect(), + valid_subcommands: _cmd.get_subcommands().map(|sc| sc.get_id()).collect(), // HACK: Allow an external subcommand's ArgMatches be a stand-in for any ArgMatches // since users can't detect it and avoid the asserts. // // See clap-rs/clap#3263 #[cfg(debug_assertions)] - disable_asserts: _app.is_set(crate::AppSettings::AllowExternalSubcommands), + #[cfg(not(feature = "unstable-v4"))] + disable_asserts: _cmd.is_allow_external_subcommands_set(), + #[cfg(debug_assertions)] + #[cfg(feature = "unstable-v4")] + disable_asserts: false, ..Default::default() }) } @@ -62,14 +66,15 @@ impl ArgMatcher { // a default value of `other` myprog would have an existing MatchedArg for // --global-arg where the value is `other`, however the occurs will be 0. let to_update = if let Some(parent_ma) = vals_map.get(global_arg) { - if parent_ma.occurs > 0 && ma.occurs == 0 { - parent_ma.clone() + if parent_ma.get_occurrences() > 0 && ma.get_occurrences() == 0 { + parent_ma } else { - ma.clone() + ma } } else { - ma.clone() - }; + ma + } + .clone(); vals_map.insert(global_arg.clone(), to_update); } } @@ -84,6 +89,7 @@ impl ArgMatcher { } } + #[cfg(not(feature = "unstable-v4"))] pub(crate) fn get_mut(&mut self, arg: &Id) -> Option<&mut MatchedArg> { self.0.args.get_mut(arg) } @@ -100,17 +106,6 @@ impl ArgMatcher { self.0.args.contains_key(arg) } - pub(crate) fn contains_explicit(&self, arg: &Id) -> bool { - self.0 - .args - .get(arg) - .map_or(false, |a| a.ty != ValueType::DefaultValue) - } - - pub(crate) fn is_empty(&self) -> bool { - self.0.args.is_empty() - } - pub(crate) fn arg_names(&self) -> indexmap::map::Keys { self.0.args.keys() } @@ -131,24 +126,41 @@ impl ArgMatcher { self.0.args.iter() } + pub(crate) fn check_explicit<'a>(&self, arg: &Id, predicate: ArgPredicate<'a>) -> bool { + self.get(arg).map_or(false, |a| a.check_explicit(predicate)) + } + pub(crate) fn inc_occurrence_of_arg(&mut self, arg: &Arg) { let id = &arg.id; debug!("ArgMatcher::inc_occurrence_of_arg: id={:?}", id); let ma = self.entry(id).or_insert(MatchedArg::new()); - ma.set_ty(ValueType::CommandLine); - ma.set_ignore_case(arg.is_set(ArgSettings::IgnoreCase)); - ma.invalid_utf8_allowed(arg.is_set(ArgSettings::AllowInvalidUtf8)); - ma.occurs += 1; + ma.update_ty(ValueSource::CommandLine); + ma.set_ignore_case(arg.is_ignore_case_set()); + ma.invalid_utf8_allowed(arg.is_allow_invalid_utf8_set()); + ma.inc_occurrences(); } pub(crate) fn inc_occurrence_of_group(&mut self, id: &Id) { debug!("ArgMatcher::inc_occurrence_of_group: id={:?}", id); let ma = self.entry(id).or_insert(MatchedArg::new()); - ma.set_ty(ValueType::CommandLine); - ma.occurs += 1; + ma.update_ty(ValueSource::CommandLine); + ma.inc_occurrences(); } - pub(crate) fn add_val_to(&mut self, arg: &Id, val: OsString, ty: ValueType, append: bool) { + #[cfg(feature = "unstable-v4")] + pub(crate) fn inc_occurrence_of_external(&mut self, allow_invalid_utf8: bool) { + let id = &Id::empty_hash(); + debug!( + "ArgMatcher::inc_occurrence_of_external: id={:?}, allow_invalid_utf8={}", + id, allow_invalid_utf8 + ); + let ma = self.entry(id).or_insert(MatchedArg::new()); + ma.update_ty(ValueSource::CommandLine); + ma.invalid_utf8_allowed(allow_invalid_utf8); + ma.inc_occurrences(); + } + + pub(crate) fn add_val_to(&mut self, arg: &Id, val: OsString, ty: ValueSource, append: bool) { if append { self.append_val_to(arg, val, ty); } else { @@ -156,18 +168,18 @@ impl ArgMatcher { } } - fn push_val_to(&mut self, arg: &Id, val: OsString, ty: ValueType) { + fn push_val_to(&mut self, arg: &Id, val: OsString, ty: ValueSource) { // We will manually inc occurrences later(for flexibility under // specific circumstances, like only add one occurrence for flag // when we met: `--flag=one,two`). let ma = self.entry(arg).or_default(); - ma.set_ty(ty); + ma.update_ty(ty); ma.push_val(val); } - fn append_val_to(&mut self, arg: &Id, val: OsString, ty: ValueType) { + fn append_val_to(&mut self, arg: &Id, val: OsString, ty: ValueSource) { let ma = self.entry(arg).or_default(); - ma.set_ty(ty); + ma.update_ty(ty); ma.append_val(val); } @@ -176,9 +188,9 @@ impl ArgMatcher { ma.new_val_group(); } - pub(crate) fn add_index_to(&mut self, arg: &Id, idx: usize, ty: ValueType) { + pub(crate) fn add_index_to(&mut self, arg: &Id, idx: usize, ty: ValueSource) { let ma = self.entry(arg).or_default(); - ma.set_ty(ty); + ma.update_ty(ty); ma.push_index(idx); } @@ -195,7 +207,7 @@ impl ArgMatcher { let current_num = ma.num_vals(); if let Some(num) = o.num_vals { debug!("ArgMatcher::needs_more_vals: num_vals...{}", num); - return if o.is_set(ArgSettings::MultipleOccurrences) { + return if o.is_multiple_occurrences_set() { (current_num % num) != 0 } else { num != current_num @@ -207,7 +219,7 @@ impl ArgMatcher { debug!("ArgMatcher::needs_more_vals: min_vals...true"); return true; } - return o.is_set(ArgSettings::MultipleValues); + return o.is_multiple_values_set(); } true } diff --git a/third_party/rust/clap/src/parse/errors.rs b/third_party/rust/clap/src/parse/errors.rs deleted file mode 100644 index 454834b965ab..000000000000 --- a/third_party/rust/clap/src/parse/errors.rs +++ /dev/null @@ -1,1242 +0,0 @@ -// Std -use std::{ - borrow::Cow, - convert::From, - error, - fmt::{self, Debug, Display, Formatter}, - io::{self, BufRead}, - result::Result as StdResult, -}; - -// Internal -use crate::{ - build::Arg, - output::fmt::Colorizer, - parse::features::suggestions, - util::{color::ColorChoice, safe_exit, SUCCESS_CODE, USAGE_CODE}, - App, AppSettings, -}; - -/// Short hand for [`Result`] type -/// -/// [`Result`]: std::result::Result -pub type Result = StdResult; - -/// Command line argument parser kind of error -#[derive(Debug, Copy, Clone, PartialEq)] -#[non_exhaustive] -pub enum ErrorKind { - /// Occurs when an [`Arg`] has a set of possible values, - /// and the user provides a value which isn't in that set. - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let result = App::new("prog") - /// .arg(Arg::new("speed") - /// .possible_value("fast") - /// .possible_value("slow")) - /// .try_get_matches_from(vec!["prog", "other"]); - /// assert!(result.is_err()); - /// assert_eq!(result.unwrap_err().kind, ErrorKind::InvalidValue); - /// ``` - InvalidValue, - - /// Occurs when a user provides a flag, option, argument or subcommand which isn't defined. - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, arg, ErrorKind}; - /// let result = App::new("prog") - /// .arg(arg!(--flag "some flag")) - /// .try_get_matches_from(vec!["prog", "--other"]); - /// assert!(result.is_err()); - /// assert_eq!(result.unwrap_err().kind, ErrorKind::UnknownArgument); - /// ``` - UnknownArgument, - - /// Occurs when the user provides an unrecognized [`Subcommand`] which meets the threshold for - /// being similar enough to an existing subcommand. - /// If it doesn't meet the threshold, or the 'suggestions' feature is disabled, - /// the more general [`UnknownArgument`] error is returned. - /// - /// # Examples - /// - #[cfg_attr(not(feature = "suggestions"), doc = " ```no_run")] - #[cfg_attr(feature = "suggestions", doc = " ```")] - /// # use clap::{App, Arg, ErrorKind, }; - /// let result = App::new("prog") - /// .subcommand(App::new("config") - /// .about("Used for configuration") - /// .arg(Arg::new("config_file") - /// .help("The configuration file to use") - /// .index(1))) - /// .try_get_matches_from(vec!["prog", "confi"]); - /// assert!(result.is_err()); - /// assert_eq!(result.unwrap_err().kind, ErrorKind::InvalidSubcommand); - /// ``` - /// - /// [`Subcommand`]: crate::Subcommand - /// [`UnknownArgument`]: ErrorKind::UnknownArgument - InvalidSubcommand, - - /// Occurs when the user provides an unrecognized [`Subcommand`] which either - /// doesn't meet the threshold for being similar enough to an existing subcommand, - /// or the 'suggestions' feature is disabled. - /// Otherwise the more detailed [`InvalidSubcommand`] error is returned. - /// - /// This error typically happens when passing additional subcommand names to the `help` - /// subcommand. Otherwise, the more general [`UnknownArgument`] error is used. - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, Arg, ErrorKind, }; - /// let result = App::new("prog") - /// .subcommand(App::new("config") - /// .about("Used for configuration") - /// .arg(Arg::new("config_file") - /// .help("The configuration file to use") - /// .index(1))) - /// .try_get_matches_from(vec!["prog", "help", "nothing"]); - /// assert!(result.is_err()); - /// assert_eq!(result.unwrap_err().kind, ErrorKind::UnrecognizedSubcommand); - /// ``` - /// - /// [`Subcommand`]: crate::Subcommand - /// [`InvalidSubcommand`]: ErrorKind::InvalidSubcommand - /// [`UnknownArgument`]: ErrorKind::UnknownArgument - UnrecognizedSubcommand, - - /// Occurs when the user provides an empty value for an option that does not allow empty - /// values. - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("prog") - /// .arg(Arg::new("color") - /// .takes_value(true) - /// .forbid_empty_values(true) - /// .long("color")) - /// .try_get_matches_from(vec!["prog", "--color="]); - /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::EmptyValue); - /// ``` - EmptyValue, - - /// Occurs when the user doesn't use equals for an option that requires equal - /// sign to provide values. - /// - /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let res = App::new("prog") - /// .arg(Arg::new("color") - /// .takes_value(true) - /// .require_equals(true) - /// .long("color")) - /// .try_get_matches_from(vec!["prog", "--color", "red"]); - /// assert!(res.is_err()); - /// assert_eq!(res.unwrap_err().kind, ErrorKind::NoEquals); - /// ``` - NoEquals, - - /// Occurs when the user provides a value for an argument with a custom validation and the - /// value fails that validation. - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// fn is_numeric(val: &str) -> Result<(), String> { - /// match val.parse::() { - /// Ok(..) => Ok(()), - /// Err(..) => Err(String::from("Value wasn't a number!")), - /// } - /// } - /// - /// let result = App::new("prog") - /// .arg(Arg::new("num") - /// .validator(is_numeric)) - /// .try_get_matches_from(vec!["prog", "NotANumber"]); - /// assert!(result.is_err()); - /// assert_eq!(result.unwrap_err().kind, ErrorKind::ValueValidation); - /// ``` - ValueValidation, - - /// Occurs when a user provides more values for an argument than were defined by setting - /// [`Arg::max_values`]. - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let result = App::new("prog") - /// .arg(Arg::new("arg") - /// .max_values(2)) - /// .try_get_matches_from(vec!["prog", "too", "many", "values"]); - /// assert!(result.is_err()); - /// assert_eq!(result.unwrap_err().kind, ErrorKind::TooManyValues); - /// ``` - /// [`Arg::max_values`]: Arg::max_values() - TooManyValues, - - /// Occurs when the user provides fewer values for an argument than were defined by setting - /// [`Arg::min_values`]. - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let result = App::new("prog") - /// .arg(Arg::new("some_opt") - /// .long("opt") - /// .min_values(3)) - /// .try_get_matches_from(vec!["prog", "--opt", "too", "few"]); - /// assert!(result.is_err()); - /// assert_eq!(result.unwrap_err().kind, ErrorKind::TooFewValues); - /// ``` - /// [`Arg::min_values`]: Arg::min_values() - TooFewValues, - - /// Occurs when a user provides more occurrences for an argument than were defined by setting - /// [`Arg::max_occurrences`]. - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let result = App::new("prog") - /// .arg(Arg::new("verbosity") - /// .short('v') - /// .max_occurrences(2)) - /// .try_get_matches_from(vec!["prog", "-vvv"]); - /// assert!(result.is_err()); - /// assert_eq!(result.unwrap_err().kind, ErrorKind::TooManyOccurrences); - /// ``` - /// [`Arg::max_occurrences`]: Arg::max_occurrences() - TooManyOccurrences, - - /// Occurs when the user provides a different number of values for an argument than what's - /// been defined by setting [`Arg::number_of_values`] or than was implicitly set by - /// [`Arg::value_names`]. - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let result = App::new("prog") - /// .arg(Arg::new("some_opt") - /// .long("opt") - /// .takes_value(true) - /// .number_of_values(2)) - /// .try_get_matches_from(vec!["prog", "--opt", "wrong"]); - /// assert!(result.is_err()); - /// assert_eq!(result.unwrap_err().kind, ErrorKind::WrongNumberOfValues); - /// ``` - /// - /// [`Arg::number_of_values`]: Arg::number_of_values() - /// [`Arg::value_names`]: Arg::value_names() - WrongNumberOfValues, - - /// Occurs when the user provides two values which conflict with each other and can't be used - /// together. - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let result = App::new("prog") - /// .arg(Arg::new("debug") - /// .long("debug") - /// .conflicts_with("color")) - /// .arg(Arg::new("color") - /// .long("color")) - /// .try_get_matches_from(vec!["prog", "--debug", "--color"]); - /// assert!(result.is_err()); - /// assert_eq!(result.unwrap_err().kind, ErrorKind::ArgumentConflict); - /// ``` - ArgumentConflict, - - /// Occurs when the user does not provide one or more required arguments. - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let result = App::new("prog") - /// .arg(Arg::new("debug") - /// .required(true)) - /// .try_get_matches_from(vec!["prog"]); - /// assert!(result.is_err()); - /// assert_eq!(result.unwrap_err().kind, ErrorKind::MissingRequiredArgument); - /// ``` - MissingRequiredArgument, - - /// Occurs when a subcommand is required (as defined by [`AppSettings::SubcommandRequired`]), - /// but the user does not provide one. - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, AppSettings, ErrorKind}; - /// let err = App::new("prog") - /// .setting(AppSettings::SubcommandRequired) - /// .subcommand(App::new("test")) - /// .try_get_matches_from(vec![ - /// "myprog", - /// ]); - /// assert!(err.is_err()); - /// assert_eq!(err.unwrap_err().kind, ErrorKind::MissingSubcommand); - /// # ; - /// ``` - /// - /// [`AppSettings::SubcommandRequired`]: crate::AppSettings::SubcommandRequired - MissingSubcommand, - - /// Occurs when the user provides multiple values to an argument which doesn't allow that. - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let result = App::new("prog") - /// .arg(Arg::new("debug") - /// .long("debug") - /// .multiple_occurrences(false)) - /// .try_get_matches_from(vec!["prog", "--debug", "--debug"]); - /// assert!(result.is_err()); - /// assert_eq!(result.unwrap_err().kind, ErrorKind::UnexpectedMultipleUsage); - /// ``` - UnexpectedMultipleUsage, - - /// Occurs when the user provides a value containing invalid UTF-8. - /// - /// To allow arbitrary data - /// - Set [`Arg::allow_invalid_utf8`] for argument values - /// - Set [`AppSettings::AllowInvalidUtf8ForExternalSubcommands`] for external-subcommand - /// values - /// - /// # Platform Specific - /// - /// Non-Windows platforms only (such as Linux, Unix, OSX, etc.) - /// - /// # Examples - /// - #[cfg_attr(not(unix), doc = " ```ignore")] - #[cfg_attr(unix, doc = " ```")] - /// # use clap::{App, Arg, ErrorKind, AppSettings}; - /// # use std::os::unix::ffi::OsStringExt; - /// # use std::ffi::OsString; - /// let result = App::new("prog") - /// .arg(Arg::new("utf8") - /// .short('u') - /// .takes_value(true)) - /// .try_get_matches_from(vec![OsString::from("myprog"), - /// OsString::from("-u"), - /// OsString::from_vec(vec![0xE9])]); - /// assert!(result.is_err()); - /// assert_eq!(result.unwrap_err().kind, ErrorKind::InvalidUtf8); - /// ``` - /// - /// [`Arg::allow_invalid_utf8`]: crate::Arg::allow_invalid_utf8 - /// [`AppSettings::AllowInvalidUtf8ForExternalSubcommands`]: crate::AppSettings::AllowInvalidUtf8ForExternalSubcommands - InvalidUtf8, - - /// Not a true "error" as it means `--help` or similar was used. - /// The help message will be sent to `stdout`. - /// - /// **Note**: If the help is displayed due to an error (such as missing subcommands) it will - /// be sent to `stderr` instead of `stdout`. - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let result = App::new("prog") - /// .try_get_matches_from(vec!["prog", "--help"]); - /// assert!(result.is_err()); - /// assert_eq!(result.unwrap_err().kind, ErrorKind::DisplayHelp); - /// ``` - DisplayHelp, - - /// Occurs when either an argument or a [`Subcommand`] is required, as defined by - /// [`AppSettings::ArgRequiredElseHelp`] and - /// [`AppSettings::SubcommandRequiredElseHelp`], but the user did not provide - /// one. - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, Arg, AppSettings, ErrorKind, }; - /// let result = App::new("prog") - /// .setting(AppSettings::ArgRequiredElseHelp) - /// .subcommand(App::new("config") - /// .about("Used for configuration") - /// .arg(Arg::new("config_file") - /// .help("The configuration file to use"))) - /// .try_get_matches_from(vec!["prog"]); - /// assert!(result.is_err()); - /// assert_eq!(result.unwrap_err().kind, ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand); - /// ``` - /// - /// [`Subcommand`]: crate::Subcommand - /// [`AppSettings::ArgRequiredElseHelp`]: crate::AppSettings::ArgRequiredElseHelp - /// [`AppSettings::SubcommandRequiredElseHelp`]: crate::AppSettings::SubcommandRequiredElseHelp - DisplayHelpOnMissingArgumentOrSubcommand, - - /// Not a true "error" as it means `--version` or similar was used. - /// The message will be sent to `stdout`. - /// - /// # Examples - /// - /// ```rust - /// # use clap::{App, Arg, ErrorKind}; - /// let result = App::new("prog") - /// .version("3.0") - /// .try_get_matches_from(vec!["prog", "--version"]); - /// assert!(result.is_err()); - /// assert_eq!(result.unwrap_err().kind, ErrorKind::DisplayVersion); - /// ``` - DisplayVersion, - - /// Occurs when using the [`ArgMatches::value_of_t`] and friends to convert an argument value - /// into type `T`, but the argument you requested wasn't used. I.e. you asked for an argument - /// with name `config` to be converted, but `config` wasn't used by the user. - /// - /// [`ArgMatches::value_of_t`]: crate::ArgMatches::value_of_t() - ArgumentNotFound, - - /// Represents an [I/O error]. - /// Can occur when writing to `stderr` or `stdout` or reading a configuration file. - /// - /// [I/O error]: std::io::Error - Io, - - /// Represents a [Format error] (which is a part of [`Display`]). - /// Typically caused by writing to `stderr` or `stdout`. - /// - /// [`Display`]: std::fmt::Display - /// [Format error]: std::fmt::Error - Format, -} - -/// Command Line Argument Parser Error -/// -/// See [`App::error`] to create an error. -/// -/// [`App::error`]: crate::App::error -#[derive(Debug)] -pub struct Error { - /// Formatted error message, enhancing the cause message with extra information - pub(crate) message: Message, - /// The type of error - pub kind: ErrorKind, - /// Additional information depending on the error kind, like values and argument names. - /// Useful when you want to render an error of your own. - pub info: Vec, - pub(crate) source: Option>, - wait_on_exit: bool, - backtrace: Option, -} - -impl Error { - /// Create an unformatted error - /// - /// This is for you need to pass the error up to - /// a place that has access to the `App` at which point you can call [`Error::format`]. - /// - /// Prefer [`App::error`] for generating errors. - /// - /// [`App::error`]: crate::App::error - pub fn raw(kind: ErrorKind, message: impl std::fmt::Display) -> Self { - Self::new(message.to_string(), kind, false) - } - - /// Format the existing message with the App's context - #[must_use] - pub fn format(mut self, app: &mut App) -> Self { - app._build(); - let usage = app.render_usage(); - self.message.format(app, usage); - self.wait_on_exit = app.settings.is_set(AppSettings::WaitOnError); - self - } - - /// Should the message be written to `stdout` or not? - #[inline] - pub fn use_stderr(&self) -> bool { - !matches!( - self.kind, - ErrorKind::DisplayHelp | ErrorKind::DisplayVersion - ) - } - - /// Prints the error and exits. - /// - /// Depending on the error kind, this either prints to `stderr` and exits with a status of `1` - /// or prints to `stdout` and exits with a status of `0`. - pub fn exit(&self) -> ! { - if self.use_stderr() { - // Swallow broken pipe errors - let _ = self.print(); - - if self.wait_on_exit { - wlnerr!("\nPress [ENTER] / [RETURN] to continue..."); - let mut s = String::new(); - let i = io::stdin(); - i.lock().read_line(&mut s).unwrap(); - } - - safe_exit(USAGE_CODE); - } - - // Swallow broken pipe errors - let _ = self.print(); - safe_exit(SUCCESS_CODE) - } - - /// Prints formatted and colored error to `stdout` or `stderr` according to its error kind - /// - /// # Example - /// ```no_run - /// use clap::App; - /// - /// match App::new("App").try_get_matches() { - /// Ok(matches) => { - /// // do_something - /// }, - /// Err(err) => { - /// err.print().expect("Error writing Error"); - /// // do_something - /// }, - /// }; - /// ``` - pub fn print(&self) -> io::Result<()> { - self.message.formatted().print() - } - - /// Deprecated, replaced with [`App::error`] - /// - /// [`App::error`]: crate::App::error - #[deprecated(since = "3.0.0", note = "Replaced with `App::error`")] - pub fn with_description(description: String, kind: ErrorKind) -> Self { - Error::raw(kind, description) - } - - pub(crate) fn new(message: impl Into, kind: ErrorKind, wait_on_exit: bool) -> Self { - Self { - message: message.into(), - kind, - info: vec![], - source: None, - wait_on_exit, - backtrace: Backtrace::new(), - } - } - - pub(crate) fn set_info(mut self, info: Vec) -> Self { - self.info = info; - self - } - - pub(crate) fn set_source(mut self, source: Box) -> Self { - self.source = Some(source); - self - } - - pub(crate) fn argument_conflict( - app: &App, - arg: &Arg, - others: Vec, - usage: String, - ) -> Self { - let mut c = Colorizer::new(true, app.get_color()); - let arg = arg.to_string(); - - start_error(&mut c, "The argument '"); - c.warning(arg); - c.none("' cannot be used with"); - - let mut info = vec![]; - match others.len() { - 0 => { - c.none(" one or more of the other specified arguments"); - } - 1 => { - let v = &others[0]; - c.none(" '"); - c.warning(v.clone()); - c.none("'"); - info.push(v.clone()); - } - _ => { - c.none(":"); - for v in others { - c.none("\n "); - c.warning(v.to_string()); - info.push(v.to_string()); - } - } - } - - put_usage(&mut c, usage); - try_help(app, &mut c); - - Self::new( - c, - ErrorKind::ArgumentConflict, - app.settings.is_set(AppSettings::WaitOnError), - ) - .set_info(info) - } - - pub(crate) fn empty_value(app: &App, arg: &Arg, usage: String) -> Self { - let mut c = Colorizer::new(true, app.get_color()); - let arg = arg.to_string(); - - start_error(&mut c, "The argument '"); - c.warning(arg.clone()); - c.none("' requires a value but none was supplied"); - put_usage(&mut c, usage); - try_help(app, &mut c); - - Self::new( - c, - ErrorKind::EmptyValue, - app.settings.is_set(AppSettings::WaitOnError), - ) - .set_info(vec![arg]) - } - - pub(crate) fn no_equals(app: &App, arg: String, usage: String) -> Self { - let mut c = Colorizer::new(true, app.get_color()); - - start_error(&mut c, "Equal sign is needed when assigning values to '"); - c.warning(&arg); - c.none("'."); - - put_usage(&mut c, usage); - try_help(app, &mut c); - - Self::new( - c, - ErrorKind::NoEquals, - app.settings.is_set(AppSettings::WaitOnError), - ) - .set_info(vec![arg]) - } - - pub(crate) fn invalid_value( - app: &App, - bad_val: String, - good_vals: &[G], - arg: &Arg, - usage: String, - ) -> Self - where - G: AsRef + Display, - { - let mut c = Colorizer::new(true, app.get_color()); - let suffix = suggestions::did_you_mean(&bad_val, good_vals.iter()).pop(); - - let mut sorted: Vec = good_vals - .iter() - .map(|v| v.to_string()) - .map(|v| { - if v.contains(char::is_whitespace) { - format!("{:?}", v) - } else { - v - } - }) - .collect(); - sorted.sort(); - - start_error(&mut c, ""); - c.warning(format!("{:?}", bad_val)); - c.none(" isn't a valid value for '"); - c.warning(arg.to_string()); - c.none("'\n\t[possible values: "); - - if let Some((last, elements)) = sorted.split_last() { - for v in elements { - c.good(v); - c.none(", "); - } - - c.good(last); - } - - c.none("]"); - - if let Some(val) = suffix { - c.none("\n\n\tDid you mean "); - c.good(format!("{:?}", val)); - c.none("?"); - } - - put_usage(&mut c, usage); - try_help(app, &mut c); - - let mut info = vec![arg.to_string(), bad_val]; - info.extend(sorted); - - Self::new( - c, - ErrorKind::InvalidValue, - app.settings.is_set(AppSettings::WaitOnError), - ) - .set_info(info) - } - - pub(crate) fn invalid_subcommand( - app: &App, - subcmd: String, - did_you_mean: String, - name: String, - usage: String, - ) -> Self { - let mut c = Colorizer::new(true, app.get_color()); - - start_error(&mut c, "The subcommand '"); - c.warning(subcmd.clone()); - c.none("' wasn't recognized\n\n\tDid you mean "); - c.good(did_you_mean); - c.none(""); - c.none(format!( - "?\n\nIf you believe you received this message in error, try re-running with '{} ", - name - )); - c.good("--"); - c.none(format!(" {}'", subcmd)); - put_usage(&mut c, usage); - try_help(app, &mut c); - - Self::new( - c, - ErrorKind::InvalidSubcommand, - app.settings.is_set(AppSettings::WaitOnError), - ) - .set_info(vec![subcmd]) - } - - pub(crate) fn unrecognized_subcommand(app: &App, subcmd: String, name: String) -> Self { - let mut c = Colorizer::new(true, app.get_color()); - - start_error(&mut c, " The subcommand '"); - c.warning(subcmd.clone()); - c.none("' wasn't recognized\n\n"); - c.warning("USAGE:"); - c.none(format!("\n {} ", name)); - try_help(app, &mut c); - - Self::new( - c, - ErrorKind::UnrecognizedSubcommand, - app.settings.is_set(AppSettings::WaitOnError), - ) - .set_info(vec![subcmd]) - } - - pub(crate) fn missing_required_argument( - app: &App, - required: Vec, - usage: String, - ) -> Self { - let mut c = Colorizer::new(true, app.get_color()); - - start_error( - &mut c, - "The following required arguments were not provided:", - ); - - let mut info = vec![]; - for v in required { - c.none("\n "); - c.good(v.to_string()); - info.push(v.to_string()); - } - - put_usage(&mut c, usage); - try_help(app, &mut c); - - Self::new( - c, - ErrorKind::MissingRequiredArgument, - app.settings.is_set(AppSettings::WaitOnError), - ) - .set_info(info) - } - - pub(crate) fn missing_subcommand(app: &App, name: String, usage: String) -> Self { - let mut c = Colorizer::new(true, app.get_color()); - - start_error(&mut c, "'"); - c.warning(name); - c.none("' requires a subcommand, but one was not provided"); - put_usage(&mut c, usage); - try_help(app, &mut c); - - Self::new( - c, - ErrorKind::MissingSubcommand, - app.settings.is_set(AppSettings::WaitOnError), - ) - } - - pub(crate) fn invalid_utf8(app: &App, usage: String) -> Self { - let mut c = Colorizer::new(true, app.get_color()); - - start_error( - &mut c, - "Invalid UTF-8 was detected in one or more arguments", - ); - put_usage(&mut c, usage); - try_help(app, &mut c); - - Self::new( - c, - ErrorKind::InvalidUtf8, - app.settings.is_set(AppSettings::WaitOnError), - ) - } - - pub(crate) fn too_many_occurrences( - app: &App, - arg: &Arg, - max_occurs: usize, - curr_occurs: usize, - usage: String, - ) -> Self { - let mut c = Colorizer::new(true, app.get_color()); - let verb = Error::singular_or_plural(curr_occurs); - - start_error(&mut c, "The argument '"); - c.warning(arg.to_string()); - c.none("' allows at most "); - c.warning(max_occurs.to_string()); - c.none(" occurrences, but "); - c.warning(curr_occurs.to_string()); - c.none(format!(" {} provided", verb)); - put_usage(&mut c, usage); - try_help(app, &mut c); - - Self::new( - c, - ErrorKind::TooManyOccurrences, - app.settings.is_set(AppSettings::WaitOnError), - ) - .set_info(vec![ - arg.to_string(), - curr_occurs.to_string(), - max_occurs.to_string(), - ]) - } - - pub(crate) fn too_many_values(app: &App, val: String, arg: String, usage: String) -> Self { - let mut c = Colorizer::new(true, app.get_color()); - - start_error(&mut c, "The value '"); - c.warning(val.clone()); - c.none("' was provided to '"); - c.warning(&arg); - c.none("' but it wasn't expecting any more values"); - put_usage(&mut c, usage); - try_help(app, &mut c); - - Self::new( - c, - ErrorKind::TooManyValues, - app.settings.is_set(AppSettings::WaitOnError), - ) - .set_info(vec![arg, val]) - } - - pub(crate) fn too_few_values( - app: &App, - arg: &Arg, - min_vals: usize, - curr_vals: usize, - usage: String, - ) -> Self { - let mut c = Colorizer::new(true, app.get_color()); - let verb = Error::singular_or_plural(curr_vals); - - start_error(&mut c, "The argument '"); - c.warning(arg.to_string()); - c.none("' requires at least "); - c.warning(min_vals.to_string()); - c.none(" values, but only "); - c.warning(curr_vals.to_string()); - c.none(format!(" {} provided", verb)); - put_usage(&mut c, usage); - try_help(app, &mut c); - - Self::new( - c, - ErrorKind::TooFewValues, - app.settings.is_set(AppSettings::WaitOnError), - ) - .set_info(vec![ - arg.to_string(), - curr_vals.to_string(), - min_vals.to_string(), - ]) - } - - pub(crate) fn value_validation( - app: &App, - arg: String, - val: String, - err: Box, - ) -> Self { - let mut err = Self::value_validation_with_color( - arg, - val, - err, - app.get_color(), - app.settings.is_set(AppSettings::WaitOnError), - ); - match &mut err.message { - Message::Raw(_) => { - unreachable!("`value_validation_with_color` only deals in formatted errors") - } - Message::Formatted(c) => try_help(app, c), - } - err - } - - pub(crate) fn value_validation_without_app( - arg: String, - val: String, - err: Box, - ) -> Self { - let mut err = Self::value_validation_with_color(arg, val, err, ColorChoice::Never, false); - match &mut err.message { - Message::Raw(_) => { - unreachable!("`value_validation_with_color` only deals in formatted errors") - } - Message::Formatted(c) => { - c.none("\n"); - } - } - err - } - - fn value_validation_with_color( - arg: String, - val: String, - err: Box, - color: ColorChoice, - wait_on_exit: bool, - ) -> Self { - let mut c = Colorizer::new(true, color); - - start_error(&mut c, "Invalid value"); - - c.none(" for '"); - c.warning(arg.clone()); - c.none("'"); - - c.none(format!(": {}", err)); - - Self::new(c, ErrorKind::ValueValidation, wait_on_exit) - .set_info(vec![arg, val, err.to_string()]) - .set_source(err) - } - - pub(crate) fn wrong_number_of_values( - app: &App, - arg: &Arg, - num_vals: usize, - curr_vals: usize, - usage: String, - ) -> Self { - let mut c = Colorizer::new(true, app.get_color()); - let verb = Error::singular_or_plural(curr_vals); - - start_error(&mut c, "The argument '"); - c.warning(arg.to_string()); - c.none("' requires "); - c.warning(num_vals.to_string()); - c.none(" values, but "); - c.warning(curr_vals.to_string()); - c.none(format!(" {} provided", verb)); - put_usage(&mut c, usage); - try_help(app, &mut c); - - Self::new( - c, - ErrorKind::WrongNumberOfValues, - app.settings.is_set(AppSettings::WaitOnError), - ) - .set_info(vec![ - arg.to_string(), - curr_vals.to_string(), - num_vals.to_string(), - ]) - } - - pub(crate) fn unexpected_multiple_usage(app: &App, arg: &Arg, usage: String) -> Self { - let mut c = Colorizer::new(true, app.get_color()); - let arg = arg.to_string(); - - start_error(&mut c, "The argument '"); - c.warning(arg.clone()); - c.none("' was provided more than once, but cannot be used multiple times"); - put_usage(&mut c, usage); - try_help(app, &mut c); - - Self::new( - c, - ErrorKind::UnexpectedMultipleUsage, - app.settings.is_set(AppSettings::WaitOnError), - ) - .set_info(vec![arg]) - } - - pub(crate) fn unknown_argument( - app: &App, - arg: String, - did_you_mean: Option<(String, Option)>, - usage: String, - ) -> Self { - let mut c = Colorizer::new(true, app.get_color()); - - start_error(&mut c, "Found argument '"); - c.warning(arg.clone()); - c.none("' which wasn't expected, or isn't valid in this context"); - - if let Some((flag, subcmd)) = did_you_mean { - let flag = format!("--{}", flag); - c.none("\n\n\tDid you mean "); - - if let Some(subcmd) = subcmd { - c.none("to put '"); - c.good(flag); - c.none("' after the subcommand '"); - c.good(subcmd); - c.none("'?"); - } else { - c.none("'"); - c.good(flag); - c.none("'?"); - } - } - - // If the user wants to supply things like `--a-flag` or `-b` as a value, - // suggest `--` for disambiguation. - if arg.starts_with('-') { - c.none(format!( - "\n\n\tIf you tried to supply `{}` as a value rather than a flag, use `-- {}`", - arg, arg - )); - } - - put_usage(&mut c, usage); - try_help(app, &mut c); - - Self::new( - c, - ErrorKind::UnknownArgument, - app.settings.is_set(AppSettings::WaitOnError), - ) - .set_info(vec![arg]) - } - - pub(crate) fn unnecessary_double_dash(app: &App, arg: String, usage: String) -> Self { - let mut c = Colorizer::new(true, app.get_color()); - - start_error(&mut c, "Found argument '"); - c.warning(arg.clone()); - c.none("' which wasn't expected, or isn't valid in this context"); - - c.none(format!( - "\n\n\tIf you tried to supply `{}` as a subcommand, remove the '--' before it.", - arg - )); - put_usage(&mut c, usage); - try_help(app, &mut c); - - Self::new( - c, - ErrorKind::UnknownArgument, - app.settings.is_set(AppSettings::WaitOnError), - ) - .set_info(vec![arg]) - } - - pub(crate) fn argument_not_found_auto(arg: String) -> Self { - let mut c = Colorizer::new(true, ColorChoice::Never); - - start_error(&mut c, "The argument '"); - c.warning(arg.clone()); - c.none("' wasn't found\n"); - - Self::new(c, ErrorKind::ArgumentNotFound, false).set_info(vec![arg]) - } - - /// Returns the singular or plural form on the verb to be based on the argument's value. - fn singular_or_plural(n: usize) -> String { - if n > 1 { - String::from("were") - } else { - String::from("was") - } - } -} - -impl From for Error { - fn from(e: io::Error) -> Self { - Error::raw(ErrorKind::Io, e) - } -} - -impl From for Error { - fn from(e: fmt::Error) -> Self { - Error::raw(ErrorKind::Format, e) - } -} - -impl error::Error for Error { - #[allow(trivial_casts)] - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - self.source.as_ref().map(|e| e.as_ref() as _) - } -} - -impl Display for Error { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - // Assuming `self.message` already has a trailing newline, from `try_help` or similar - write!(f, "{}", self.message.formatted())?; - if let Some(backtrace) = self.backtrace.as_ref() { - writeln!(f)?; - writeln!(f, "Backtrace:")?; - writeln!(f, "{}", backtrace)?; - } - Ok(()) - } -} - -fn start_error(c: &mut Colorizer, msg: impl Into) { - c.error("error:"); - c.none(" "); - c.none(msg); -} - -fn put_usage(c: &mut Colorizer, usage: impl Into) { - c.none("\n\n"); - c.none(usage); -} - -fn try_help(app: &App, c: &mut Colorizer) { - if !app.settings.is_set(AppSettings::DisableHelpFlag) { - c.none("\n\nFor more information try "); - c.good("--help"); - c.none("\n"); - } else if app.has_subcommands() && !app.settings.is_set(AppSettings::DisableHelpSubcommand) { - c.none("\n\nFor more information try "); - c.good("help"); - c.none("\n"); - } else { - c.none("\n"); - } -} - -#[derive(Clone, Debug)] -pub(crate) enum Message { - Raw(String), - Formatted(Colorizer), -} - -impl Message { - fn format(&mut self, app: &App, usage: String) { - match self { - Message::Raw(s) => { - let mut c = Colorizer::new(true, app.get_color()); - - let mut message = String::new(); - std::mem::swap(s, &mut message); - start_error(&mut c, message); - put_usage(&mut c, usage); - try_help(app, &mut c); - *self = Self::Formatted(c); - } - Message::Formatted(_) => {} - } - } - - fn formatted(&self) -> Cow { - match self { - Message::Raw(s) => { - let mut c = Colorizer::new(true, ColorChoice::Never); - start_error(&mut c, s); - Cow::Owned(c) - } - Message::Formatted(c) => Cow::Borrowed(c), - } - } -} - -impl From for Message { - fn from(inner: String) -> Self { - Self::Raw(inner) - } -} - -impl From for Message { - fn from(inner: Colorizer) -> Self { - Self::Formatted(inner) - } -} - -#[cfg(feature = "debug")] -#[derive(Debug)] -struct Backtrace(backtrace::Backtrace); - -#[cfg(feature = "debug")] -impl Backtrace { - fn new() -> Option { - Some(Self(backtrace::Backtrace::new())) - } -} - -#[cfg(feature = "debug")] -impl Display for Backtrace { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - // `backtrace::Backtrace` uses `Debug` instead of `Display` - write!(f, "{:?}", self.0) - } -} - -#[cfg(not(feature = "debug"))] -#[derive(Debug)] -struct Backtrace; - -#[cfg(not(feature = "debug"))] -impl Backtrace { - fn new() -> Option { - None - } -} - -#[cfg(not(feature = "debug"))] -impl Display for Backtrace { - fn fmt(&self, _: &mut Formatter) -> fmt::Result { - Ok(()) - } -} - -#[cfg(test)] -mod tests { - /// Check `clap::Error` impls Send and Sync. - mod clap_error_impl_send_sync { - use crate::Error; - trait Foo: std::error::Error + Send + Sync + 'static {} - impl Foo for Error {} - } -} diff --git a/third_party/rust/clap/src/parse/features/suggestions.rs b/third_party/rust/clap/src/parse/features/suggestions.rs index b690ec635746..77ba8c7a250e 100644 --- a/third_party/rust/clap/src/parse/features/suggestions.rs +++ b/third_party/rust/clap/src/parse/features/suggestions.rs @@ -2,7 +2,7 @@ use std::cmp::Ordering; // Internal -use crate::build::App; +use crate::build::Command; /// Produces multiple strings from a given list of possible values which are similar /// to the passed in value `v` within a certain confidence by least confidence. @@ -33,13 +33,14 @@ where } /// Returns a suffix that can be empty, or is the standard 'did you mean' phrase -pub(crate) fn did_you_mean_flag( +pub(crate) fn did_you_mean_flag<'a, 'help, I, T>( arg: &str, remaining_args: &[&str], longs: I, - subcommands: &mut [App], + subcommands: impl IntoIterator>, ) -> Option<(String, Option)> where + 'help: 'a, T: AsRef, I: IntoIterator, { @@ -48,11 +49,11 @@ where match did_you_mean(arg, longs).pop() { Some(candidate) => Some((candidate, None)), None => subcommands - .iter_mut() + .into_iter() .filter_map(|subcommand| { - subcommand._build(); + subcommand._build_self(); - let longs = subcommand.args.keys().filter_map(|a| { + let longs = subcommand.get_keymap().keys().filter_map(|a| { if let KeyType::Long(v) = a { Some(v.to_string_lossy().into_owned()) } else { diff --git a/third_party/rust/clap/src/parse/matches/arg_matches.rs b/third_party/rust/clap/src/parse/matches/arg_matches.rs index a4bb3f5a42e6..a2fa6237c041 100644 --- a/third_party/rust/clap/src/parse/matches/arg_matches.rs +++ b/third_party/rust/clap/src/parse/matches/arg_matches.rs @@ -12,23 +12,22 @@ use std::{ use indexmap::IndexMap; // Internal -use crate::{ - parse::MatchedArg, - util::{Id, Key}, - {Error, INVALID_UTF8}, -}; +use crate::parse::MatchedArg; +use crate::parse::ValueSource; +use crate::util::{Id, Key}; +use crate::{Error, INVALID_UTF8}; /// Container for parse results. /// /// Used to get information about the arguments that were supplied to the program at runtime by -/// the user. New instances of this struct are obtained by using the [`App::get_matches`] family of +/// the user. New instances of this struct are obtained by using the [`Command::get_matches`] family of /// methods. /// /// # Examples /// /// ```no_run -/// # use clap::{App, Arg}; -/// let matches = App::new("MyApp") +/// # use clap::{Command, Arg}; +/// let matches = Command::new("MyApp") /// .arg(Arg::new("out") /// .long("output") /// .required(true) @@ -66,7 +65,7 @@ use crate::{ /// } /// } /// ``` -/// [`App::get_matches`]: crate::App::get_matches() +/// [`Command::get_matches`]: crate::Command::get_matches() #[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct ArgMatches { #[cfg(debug_assertions)] @@ -80,6 +79,29 @@ pub struct ArgMatches { } impl ArgMatches { + /// Check if any args were present on the command line + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, Arg}; + /// let mut cmd = Command::new("myapp") + /// .arg(Arg::new("output") + /// .takes_value(true)); + /// + /// let m = cmd + /// .try_get_matches_from_mut(vec!["myapp", "something"]) + /// .unwrap(); + /// assert!(m.args_present()); + /// + /// let m = cmd + /// .try_get_matches_from_mut(vec!["myapp"]) + /// .unwrap(); + /// assert!(! m.args_present()); + pub fn args_present(&self) -> bool { + !self.args.is_empty() + } + /// Gets the value of a specific option or positional argument. /// /// i.e. an argument that [takes an additional value][crate::Arg::takes_value] at runtime. @@ -103,8 +125,8 @@ impl ArgMatches { /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("myapp") + /// # use clap::{Command, Arg}; + /// let m = Command::new("myapp") /// .arg(Arg::new("output") /// .takes_value(true)) /// .get_matches_from(vec!["myapp", "something"]); @@ -116,6 +138,7 @@ impl ArgMatches { /// [`ArgMatches::values_of`]: ArgMatches::values_of() /// [`default_value`]: crate::Arg::default_value() /// [`occurrences_of`]: crate::ArgMatches::occurrences_of() + #[cfg_attr(debug_assertions, track_caller)] pub fn value_of(&self, id: T) -> Option<&str> { let id = Id::from(id); let arg = self.get_arg(&id)?; @@ -150,11 +173,11 @@ impl ArgMatches { /// #[cfg_attr(not(unix), doc = " ```ignore")] #[cfg_attr(unix, doc = " ```")] - /// # use clap::{App, arg}; + /// # use clap::{Command, arg}; /// use std::ffi::OsString; /// use std::os::unix::ffi::{OsStrExt,OsStringExt}; /// - /// let m = App::new("utf8") + /// let m = Command::new("utf8") /// .arg(arg!( "some arg") /// .allow_invalid_utf8(true)) /// .get_matches_from(vec![OsString::from("myprog"), @@ -201,11 +224,11 @@ impl ArgMatches { /// #[cfg_attr(not(unix), doc = " ```ignore")] #[cfg_attr(unix, doc = " ```")] - /// # use clap::{App, arg}; + /// # use clap::{Command, arg}; /// use std::ffi::OsString; /// use std::os::unix::ffi::{OsStrExt,OsStringExt}; /// - /// let m = App::new("utf8") + /// let m = Command::new("utf8") /// .arg(arg!( "some arg") /// .allow_invalid_utf8(true)) /// .get_matches_from(vec![OsString::from("myprog"), @@ -241,8 +264,8 @@ impl ArgMatches { /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("myprog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("myprog") /// .arg(Arg::new("output") /// .multiple_occurrences(true) /// .short('o') @@ -255,6 +278,7 @@ impl ArgMatches { /// ``` /// [values]: Values /// [`Iterator`]: std::iter::Iterator + #[cfg_attr(debug_assertions, track_caller)] pub fn values_of(&self, id: T) -> Option { let id = Id::from(id); let arg = self.get_arg(&id)?; @@ -269,8 +293,41 @@ impl ArgMatches { Some(v) } - /// Placeholder documentation. + /// Get an [`Iterator`] over groups of values of a specific option. + /// + /// specifically grouped by the occurrences of the options. + /// + /// Each group is a `Vec<&str>` containing the arguments passed to a single occurrence + /// of the option. + /// + /// If the option doesn't support multiple occurrences, or there was only a single occurrence, + /// the iterator will only contain a single item. + /// + /// Returns `None` if the option wasn't present. + /// + /// # Panics + /// + /// If the value is invalid UTF-8. + /// + /// If `id` is not a valid argument or group name. + /// + /// # Examples + /// ```rust + /// # use clap::{Command,Arg}; + /// let m = Command::new("myprog") + /// .arg(Arg::new("exec") + /// .short('x') + /// .min_values(1) + /// .multiple_occurrences(true) + /// .value_terminator(";")) + /// .get_matches_from(vec![ + /// "myprog", "-x", "echo", "hi", ";", "-x", "echo", "bye"]); + /// let vals: Vec> = m.grouped_values_of("exec").unwrap().collect(); + /// assert_eq!(vals, [["echo", "hi"], ["echo", "bye"]]); + /// ``` + /// [`Iterator`]: std::iter::Iterator #[cfg(feature = "unstable-grouped")] + #[cfg_attr(debug_assertions, track_caller)] pub fn grouped_values_of(&self, id: T) -> Option { let id = Id::from(id); let arg = self.get_arg(&id)?; @@ -303,11 +360,11 @@ impl ArgMatches { /// #[cfg_attr(not(unix), doc = " ```ignore")] #[cfg_attr(unix, doc = " ```")] - /// # use clap::{App, arg}; + /// # use clap::{Command, arg}; /// use std::ffi::OsString; /// use std::os::unix::ffi::OsStringExt; /// - /// let m = App::new("utf8") + /// let m = Command::new("utf8") /// .arg(arg!( ... "some arg") /// .allow_invalid_utf8(true)) /// .get_matches_from(vec![OsString::from("myprog"), @@ -352,11 +409,11 @@ impl ArgMatches { /// #[cfg_attr(not(unix), doc = " ```ignore")] #[cfg_attr(unix, doc = " ```")] - /// # use clap::{App, arg}; + /// # use clap::{Command, arg}; /// use std::ffi::{OsStr,OsString}; /// use std::os::unix::ffi::{OsStrExt,OsStringExt}; /// - /// let m = App::new("utf8") + /// let m = Command::new("utf8") /// .arg(arg!( ... "some arg") /// .allow_invalid_utf8(true)) /// .get_matches_from(vec![OsString::from("myprog"), @@ -408,8 +465,8 @@ impl ArgMatches { /// # Examples /// /// ``` - /// # use clap::{App, arg}; - /// let matches = App::new("myapp") + /// # use clap::{Command, arg}; + /// let matches = Command::new("myapp") /// .arg(arg!([length] "Set the length to use as a pos whole num i.e. 20")) /// .get_matches_from(&["test", "12"]); /// @@ -440,7 +497,7 @@ impl ArgMatches { v, name, e ); - Error::value_validation_without_app(name.to_string(), v.to_string(), message.into()) + Error::value_validation(name.to_string(), v.to_string(), message.into()) }) } @@ -458,8 +515,8 @@ impl ArgMatches { /// # Examples /// /// ``` - /// # use clap::{App, arg}; - /// let matches = App::new("myapp") + /// # use clap::{Command, arg}; + /// let matches = Command::new("myapp") /// .arg(arg!([length] "Set the length to use as a pos whole num i.e. 20")) /// .get_matches_from(&["test", "12"]); /// @@ -501,8 +558,8 @@ impl ArgMatches { /// # Examples /// /// ``` - /// # use clap::{App, arg}; - /// let matches = App::new("myapp") + /// # use clap::{Command, arg}; + /// let matches = Command::new("myapp") /// .arg(arg!([length] ... "A sequence of integers because integers are neat!")) /// .get_matches_from(&["test", "12", "77", "40"]); /// @@ -528,7 +585,7 @@ impl ArgMatches { v.parse::().map_err(|e| { let message = format!("The argument '{}' isn't a valid value: {}", v, e); - Error::value_validation_without_app(name.to_string(), v.to_string(), message.into()) + Error::value_validation(name.to_string(), v.to_string(), message.into()) }) }) .collect() @@ -548,8 +605,8 @@ impl ArgMatches { /// # Examples /// /// ``` - /// # use clap::{App, arg}; - /// let matches = App::new("myapp") + /// # use clap::{Command, arg}; + /// let matches = Command::new("myapp") /// .arg(arg!([length] ... "A sequence of integers because integers are neat!")) /// .get_matches_from(&["test", "12", "77", "40"]); /// @@ -582,8 +639,8 @@ impl ArgMatches { /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("myprog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("myprog") /// .arg(Arg::new("debug") /// .short('d')) /// .get_matches_from(vec![ @@ -604,6 +661,36 @@ impl ArgMatches { self.args.contains_key(&id) } + /// Report where argument value came from + /// + /// # Panics + /// + /// If `id` is is not a valid argument or group name. + /// + /// # Examples + /// + /// ```rust + /// # use clap::{Command, Arg, ValueSource}; + /// let m = Command::new("myprog") + /// .arg(Arg::new("debug") + /// .short('d')) + /// .get_matches_from(vec![ + /// "myprog", "-d" + /// ]); + /// + /// assert_eq!(m.value_source("debug"), Some(ValueSource::CommandLine)); + /// ``` + /// + /// [`default_value`]: crate::Arg::default_value() + /// [`occurrences_of`]: ArgMatches::occurrences_of() + pub fn value_source(&self, id: T) -> Option { + let id = Id::from(id); + + let value = self.get_arg(&id); + + value.and_then(MatchedArg::source) + } + /// The number of times an argument was used at runtime. /// /// If an argument isn't present it will return `0`. @@ -619,8 +706,8 @@ impl ArgMatches { /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("myprog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("myprog") /// .arg(Arg::new("debug") /// .short('d') /// .multiple_occurrences(true)) @@ -634,8 +721,8 @@ impl ArgMatches { /// This next example shows that counts actual uses of the argument, not just `-`'s /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("myprog") + /// # use clap::{Command, Arg}; + /// let m = Command::new("myprog") /// .arg(Arg::new("debug") /// .short('d') /// .multiple_occurrences(true)) @@ -649,7 +736,8 @@ impl ArgMatches { /// assert_eq!(m.occurrences_of("flag"), 1); /// ``` pub fn occurrences_of(&self, id: T) -> u64 { - self.get_arg(&Id::from(id)).map_or(0, |a| a.occurs) + self.get_arg(&Id::from(id)) + .map_or(0, |a| a.get_occurrences()) } /// The first index of that an argument showed up. @@ -681,8 +769,8 @@ impl ArgMatches { /// in an `ArgMatches` struct for querying. /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("myapp") + /// # use clap::{Command, Arg}; + /// let m = Command::new("myapp") /// .arg(Arg::new("flag") /// .short('f')) /// .arg(Arg::new("option") @@ -699,8 +787,8 @@ impl ArgMatches { /// Now notice, if we use one of the other styles of options: /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("myapp") + /// # use clap::{Command, Arg}; + /// let m = Command::new("myapp") /// .arg(Arg::new("flag") /// .short('f')) /// .arg(Arg::new("option") @@ -718,8 +806,8 @@ impl ArgMatches { /// flags. Let's also throw in the final option style for good measure. /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("myapp") + /// # use clap::{Command, Arg}; + /// let m = Command::new("myapp") /// .arg(Arg::new("flag") /// .short('f')) /// .arg(Arg::new("flag2") @@ -744,8 +832,8 @@ impl ArgMatches { /// One final combination of flags/options to see how they combine: /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("myapp") + /// # use clap::{Command, Arg}; + /// let m = Command::new("myapp") /// .arg(Arg::new("flag") /// .short('f')) /// .arg(Arg::new("flag2") @@ -770,11 +858,11 @@ impl ArgMatches { /// The last part to mention is when values are sent in multiple groups with a [delimiter]. /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("myapp") + /// # use clap::{Command, Arg}; + /// let m = Command::new("myapp") /// .arg(Arg::new("option") /// .short('o') - /// .use_delimiter(true) + /// .use_value_delimiter(true) /// .multiple_values(true)) /// .get_matches_from(vec!["myapp", "-o=val1,val2,val3"]); /// // ARGV indices: ^0 ^1 @@ -811,11 +899,11 @@ impl ArgMatches { /// # Examples /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("myapp") + /// # use clap::{Command, Arg}; + /// let m = Command::new("myapp") /// .arg(Arg::new("option") /// .short('o') - /// .use_delimiter(true) + /// .use_value_delimiter(true) /// .multiple_values(true)) /// .get_matches_from(vec!["myapp", "-o=val1,val2,val3"]); /// // ARGV indices: ^0 ^1 @@ -829,8 +917,8 @@ impl ArgMatches { /// Another quick example is when flags and options are used together /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("myapp") + /// # use clap::{Command, Arg}; + /// let m = Command::new("myapp") /// .arg(Arg::new("option") /// .short('o') /// .takes_value(true) @@ -852,8 +940,8 @@ impl ArgMatches { /// index. /// /// ```rust - /// # use clap::{App, Arg}; - /// let m = App::new("myapp") + /// # use clap::{Command, Arg}; + /// let m = Command::new("myapp") /// .arg(Arg::new("option") /// .short('o') /// .takes_value(true) @@ -886,11 +974,11 @@ impl ArgMatches { /// # Examples /// /// ```no_run - /// # use clap::{App, Arg, }; - /// let app_m = App::new("git") - /// .subcommand(App::new("clone")) - /// .subcommand(App::new("push")) - /// .subcommand(App::new("commit")) + /// # use clap::{Command, Arg, }; + /// let app_m = Command::new("git") + /// .subcommand(Command::new("clone")) + /// .subcommand(Command::new("push")) + /// .subcommand(Command::new("commit")) /// .get_matches(); /// /// match app_m.subcommand() { @@ -906,10 +994,10 @@ impl ArgMatches { /// with pattern matching! /// /// ```rust - /// # use clap::{App, AppSettings}; + /// # use clap::Command; /// // Assume there is an external subcommand named "subcmd" - /// let app_m = App::new("myprog") - /// .setting(AppSettings::AllowExternalSubcommands) + /// let app_m = Command::new("myprog") + /// .allow_external_subcommands(true) /// .get_matches_from(vec![ /// "myprog", "subcmd", "--option", "value", "-fff", "--flag" /// ]); @@ -925,7 +1013,7 @@ impl ArgMatches { /// _ => {}, /// } /// ``` - /// [subcommand]: crate::App::subcommand + /// [subcommand]: crate::Command::subcommand #[inline] pub fn subcommand(&self) -> Option<(&str, &ArgMatches)> { self.subcommand.as_ref().map(|sc| (&*sc.name, &sc.matches)) @@ -944,11 +1032,11 @@ impl ArgMatches { /// # Examples /// /// ```rust - /// # use clap::{App, Arg, }; - /// let app_m = App::new("myprog") + /// # use clap::{Command, Arg, }; + /// let app_m = Command::new("myprog") /// .arg(Arg::new("debug") /// .short('d')) - /// .subcommand(App::new("test") + /// .subcommand(Command::new("test") /// .arg(Arg::new("opt") /// .long("option") /// .takes_value(true))) @@ -966,8 +1054,8 @@ impl ArgMatches { /// } /// ``` /// - /// [subcommand]: crate::App::subcommand - /// [`App`]: crate::App + /// [subcommand]: crate::Command::subcommand + /// [`Command`]: crate::Command pub fn subcommand_matches(&self, id: T) -> Option<&ArgMatches> { self.get_subcommand(&id.into()).map(|sc| &sc.matches) } @@ -979,11 +1067,11 @@ impl ArgMatches { /// # Examples /// /// ```no_run - /// # use clap::{App, Arg, }; - /// let app_m = App::new("git") - /// .subcommand(App::new("clone")) - /// .subcommand(App::new("push")) - /// .subcommand(App::new("commit")) + /// # use clap::{Command, Arg, }; + /// let app_m = Command::new("git") + /// .subcommand(Command::new("clone")) + /// .subcommand(Command::new("push")) + /// .subcommand(Command::new("commit")) /// .get_matches(); /// /// match app_m.subcommand_name() { @@ -993,8 +1081,8 @@ impl ArgMatches { /// _ => {}, // Either no subcommand or one not tested for... /// } /// ``` - /// [subcommand]: crate::App::subcommand - /// [`App`]: crate::App + /// [subcommand]: crate::Command::subcommand + /// [`Command`]: crate::Command #[inline] pub fn subcommand_name(&self) -> Option<&str> { self.subcommand.as_ref().map(|sc| &*sc.name) @@ -1111,8 +1199,8 @@ pub(crate) struct SubCommand { /// # Examples /// /// ```rust -/// # use clap::{App, Arg}; -/// let m = App::new("myapp") +/// # use clap::{Command, Arg}; +/// let m = Command::new("myapp") /// .arg(Arg::new("output") /// .short('o') /// .multiple_occurrences(true) @@ -1126,8 +1214,7 @@ pub(crate) struct SubCommand { /// assert_eq!(values.next(), None); /// ``` /// [`ArgMatches::values_of`]: ArgMatches::values_of() -#[derive(Clone)] -#[allow(missing_debug_implementations)] +#[derive(Clone, Debug)] pub struct Values<'a> { #[allow(clippy::type_complexity)] iter: Map>>, for<'r> fn(&'r OsString) -> &'r str>, @@ -1208,11 +1295,11 @@ impl<'a> Default for GroupedValues<'a> { /// #[cfg_attr(not(unix), doc = " ```ignore")] #[cfg_attr(unix, doc = " ```")] -/// # use clap::{App, arg}; +/// # use clap::{Command, arg}; /// use std::ffi::OsString; /// use std::os::unix::ffi::{OsStrExt,OsStringExt}; /// -/// let m = App::new("utf8") +/// let m = Command::new("utf8") /// .arg(arg!( "some arg") /// .allow_invalid_utf8(true)) /// .get_matches_from(vec![OsString::from("myprog"), @@ -1221,8 +1308,7 @@ impl<'a> Default for GroupedValues<'a> { /// assert_eq!(&*m.value_of_os("arg").unwrap().as_bytes(), [b'H', b'i', b' ', 0xe9, b'!']); /// ``` /// [`ArgMatches::values_of_os`]: ArgMatches::values_of_os() -#[derive(Clone)] -#[allow(missing_debug_implementations)] +#[derive(Clone, Debug)] pub struct OsValues<'a> { #[allow(clippy::type_complexity)] iter: Map>>, fn(&OsString) -> &OsStr>, @@ -1264,8 +1350,8 @@ impl Default for OsValues<'_> { /// # Examples /// /// ```rust -/// # use clap::{App, Arg}; -/// let m = App::new("myapp") +/// # use clap::{Command, Arg}; +/// let m = Command::new("myapp") /// .arg(Arg::new("output") /// .short('o') /// .multiple_values(true) @@ -1279,8 +1365,7 @@ impl Default for OsValues<'_> { /// assert_eq!(indices.next(), None); /// ``` /// [`ArgMatches::indices_of`]: ArgMatches::indices_of() -#[derive(Clone)] -#[allow(missing_debug_implementations)] +#[derive(Clone, Debug)] pub struct Indices<'a> { iter: Cloned>, len: usize, @@ -1382,7 +1467,7 @@ mod tests { #[test] fn values_exact_size() { - let l = crate::App::new("test") + let l = crate::Command::new("test") .arg( crate::Arg::new("POTATO") .takes_value(true) @@ -1399,7 +1484,7 @@ mod tests { #[test] fn os_values_exact_size() { - let l = crate::App::new("test") + let l = crate::Command::new("test") .arg( crate::Arg::new("POTATO") .takes_value(true) @@ -1417,7 +1502,7 @@ mod tests { #[test] fn indices_exact_size() { - let l = crate::App::new("test") + let l = crate::Command::new("test") .arg( crate::Arg::new("POTATO") .takes_value(true) diff --git a/third_party/rust/clap/src/parse/matches/matched_arg.rs b/third_party/rust/clap/src/parse/matches/matched_arg.rs index e453b1e18ef7..55326ecd08eb 100644 --- a/third_party/rust/clap/src/parse/matches/matched_arg.rs +++ b/third_party/rust/clap/src/parse/matches/matched_arg.rs @@ -5,13 +5,15 @@ use std::{ slice::Iter, }; +use crate::build::ArgPredicate; +use crate::parse::ValueSource; use crate::util::eq_ignore_case; use crate::INTERNAL_ERROR_MSG; #[derive(Debug, Clone, PartialEq, Eq)] pub(crate) struct MatchedArg { - pub(crate) occurs: u64, - pub(crate) ty: ValueType, + occurs: u64, + ty: Option, indices: Vec, vals: Vec>, ignore_case: bool, @@ -22,7 +24,7 @@ impl MatchedArg { pub(crate) fn new() -> Self { MatchedArg { occurs: 0, - ty: ValueType::Unknown, + ty: None, indices: Vec::new(), vals: Vec::new(), ignore_case: false, @@ -30,6 +32,14 @@ impl MatchedArg { } } + pub(crate) fn inc_occurrences(&mut self) { + self.occurs += 1; + } + + pub(crate) fn get_occurrences(&self) -> u64 { + self.occurs + } + pub(crate) fn indices(&self) -> Cloned> { self.indices.iter().cloned() } @@ -108,19 +118,34 @@ impl MatchedArg { } } - pub(crate) fn contains_val(&self, val: &str) -> bool { - self.vals_flatten().any(|v| { - if self.ignore_case { - // If `v` isn't utf8, it can't match `val`, so `OsStr::to_str` should be fine - v.to_str().map_or(false, |v| eq_ignore_case(v, val)) - } else { - OsString::as_os_str(v) == OsStr::new(val) - } - }) + pub(crate) fn check_explicit(&self, predicate: ArgPredicate) -> bool { + if self.ty == Some(ValueSource::DefaultValue) { + return false; + } + + match predicate { + ArgPredicate::Equals(val) => self.vals_flatten().any(|v| { + if self.ignore_case { + // If `v` isn't utf8, it can't match `val`, so `OsStr::to_str` should be fine + eq_ignore_case(&v.to_string_lossy(), &val.to_string_lossy()) + } else { + OsString::as_os_str(v) == OsStr::new(val) + } + }), + ArgPredicate::IsPresent => true, + } } - pub(crate) fn set_ty(&mut self, ty: ValueType) { - self.ty = ty; + pub(crate) fn source(&self) -> Option { + self.ty + } + + pub(crate) fn update_ty(&mut self, ty: ValueSource) { + if let Some(existing) = self.ty { + self.ty = Some(existing.max(ty)); + } else { + self.ty = Some(ty) + } } pub(crate) fn set_ignore_case(&mut self, yes: bool) { @@ -142,15 +167,6 @@ impl Default for MatchedArg { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) enum ValueType { - Unknown, - #[cfg(feature = "env")] - EnvVariable, - CommandLine, - DefaultValue, -} - #[cfg(test)] mod tests { use super::*; diff --git a/third_party/rust/clap/src/parse/matches/mod.rs b/third_party/rust/clap/src/parse/matches/mod.rs index 6d6bd0d14353..7efa8b414f30 100644 --- a/third_party/rust/clap/src/parse/matches/mod.rs +++ b/third_party/rust/clap/src/parse/matches/mod.rs @@ -1,9 +1,9 @@ mod arg_matches; mod matched_arg; +mod value_source; -pub(crate) use self::{ - arg_matches::SubCommand, - matched_arg::{MatchedArg, ValueType}, -}; +pub use arg_matches::{ArgMatches, Indices, OsValues, Values}; +pub use value_source::ValueSource; -pub use self::arg_matches::{ArgMatches, Indices, OsValues, Values}; +pub(crate) use arg_matches::SubCommand; +pub(crate) use matched_arg::MatchedArg; diff --git a/third_party/rust/clap/src/parse/matches/value_source.rs b/third_party/rust/clap/src/parse/matches/value_source.rs new file mode 100644 index 000000000000..fb762d2af683 --- /dev/null +++ b/third_party/rust/clap/src/parse/matches/value_source.rs @@ -0,0 +1,11 @@ +/// Origin of the argument's value +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[non_exhaustive] +pub enum ValueSource { + /// Value came [`Arg::default_value`][crate::Arg::default_value] + DefaultValue, + /// Value came [`Arg::env`][crate::Arg::env] + EnvVariable, + /// Value was passed in on the command-line + CommandLine, +} diff --git a/third_party/rust/clap/src/parse/mod.rs b/third_party/rust/clap/src/parse/mod.rs index f2f0d1fd034d..298862e13468 100644 --- a/third_party/rust/clap/src/parse/mod.rs +++ b/third_party/rust/clap/src/parse/mod.rs @@ -1,16 +1,13 @@ -pub mod errors; -pub mod features; - mod arg_matcher; -pub mod matches; mod parser; mod validator; -pub(crate) use self::{ - arg_matcher::ArgMatcher, - matches::{MatchedArg, SubCommand, ValueType}, - parser::{Input, ParseState, Parser}, - validator::Validator, -}; +pub mod features; +pub mod matches; -pub use self::matches::{ArgMatches, Indices, OsValues, Values}; +pub(crate) use self::arg_matcher::ArgMatcher; +pub(crate) use self::matches::{MatchedArg, SubCommand}; +pub(crate) use self::parser::{ParseState, Parser}; +pub(crate) use self::validator::Validator; + +pub use self::matches::{ArgMatches, Indices, OsValues, ValueSource, Values}; diff --git a/third_party/rust/clap/src/parse/parser.rs b/third_party/rust/clap/src/parse/parser.rs index 7e4db3bad7c4..6262d18d887a 100644 --- a/third_party/rust/clap/src/parse/parser.rs +++ b/third_party/rust/clap/src/parse/parser.rs @@ -1,101 +1,62 @@ // Std use std::{ - cell::{Cell, RefCell}, + cell::Cell, ffi::{OsStr, OsString}, }; // Third Party -use os_str_bytes::RawOsStr; +use clap_lex::RawOsStr; // Internal -use crate::{ - build::AppSettings as AS, - build::{App, Arg, ArgSettings}, - mkeymap::KeyType, - output::{fmt::Colorizer, Help, HelpWriter, Usage}, - parse::errors::Error as ClapError, - parse::errors::ErrorKind, - parse::errors::Result as ClapResult, - parse::features::suggestions, - parse::{ArgMatcher, SubCommand}, - parse::{Validator, ValueType}, - util::{color::ColorChoice, ChildGraph, Id}, - INTERNAL_ERROR_MSG, INVALID_UTF8, -}; +use crate::build::AppSettings as AS; +use crate::build::{Arg, Command}; +use crate::error::Error as ClapError; +use crate::error::Result as ClapResult; +use crate::mkeymap::KeyType; +use crate::output::fmt::Stream; +use crate::output::{fmt::Colorizer, Usage}; +use crate::parse::features::suggestions; +use crate::parse::{ArgMatcher, SubCommand}; +use crate::parse::{Validator, ValueSource}; +use crate::util::Id; +use crate::{INTERNAL_ERROR_MSG, INVALID_UTF8}; -pub(crate) struct Parser<'help, 'app> { - pub(crate) app: &'app mut App<'help>, - pub(crate) required: ChildGraph, - pub(crate) overridden: RefCell>, - pub(crate) seen: Vec, - pub(crate) cur_idx: Cell, +pub(crate) struct Parser<'help, 'cmd> { + cmd: &'cmd mut Command<'help>, + seen: Vec, + cur_idx: Cell, /// Index of the previous flag subcommand in a group of flags. - pub(crate) flag_subcmd_at: Option, + flag_subcmd_at: Option, /// Counter indicating the number of items to skip /// when revisiting the group of flags which includes the flag subcommand. - pub(crate) flag_subcmd_skip: usize, + flag_subcmd_skip: usize, } // Initializing Methods -impl<'help, 'app> Parser<'help, 'app> { - pub(crate) fn new(app: &'app mut App<'help>) -> Self { - let mut reqs = ChildGraph::with_capacity(5); - for a in app - .args - .args() - .filter(|a| a.settings.is_set(ArgSettings::Required)) - { - reqs.insert(a.id.clone()); - } - +impl<'help, 'cmd> Parser<'help, 'cmd> { + pub(crate) fn new(cmd: &'cmd mut Command<'help>) -> Self { Parser { - app, - required: reqs, - overridden: Default::default(), + cmd, seen: Vec::new(), cur_idx: Cell::new(0), flag_subcmd_at: None, flag_subcmd_skip: 0, } } - - // Does all the initializing and prepares the parser - pub(crate) fn _build(&mut self) { - debug!("Parser::_build"); - - for group in &self.app.groups { - if group.required { - let idx = self.required.insert(group.id.clone()); - for a in &group.requires { - self.required.insert_child(idx, a.clone()); - } - } - } - } - - // Should we color the help? - pub(crate) fn color_help(&self) -> ColorChoice { - #[cfg(feature = "color")] - if self.is_set(AS::DisableColoredHelp) { - return ColorChoice::Never; - } - - self.app.get_color() - } } // Parsing Methods -impl<'help, 'app> Parser<'help, 'app> { +impl<'help, 'cmd> Parser<'help, 'cmd> { // The actual parsing function #[allow(clippy::cognitive_complexity)] pub(crate) fn get_matches_with( &mut self, matcher: &mut ArgMatcher, - it: &mut Input, + raw_args: &mut clap_lex::RawArgs, + mut args_cursor: clap_lex::ArgCursor, ) -> ClapResult<()> { debug!("Parser::get_matches_with"); // Verify all positional assertions pass - self._build(); let mut subcmd_name: Option = None; let mut keep_state = false; @@ -108,31 +69,34 @@ impl<'help, 'app> Parser<'help, 'app> { let mut trailing_values = false; // Count of positional args - let positional_count = self.app.args.keys().filter(|x| x.is_position()).count(); + let positional_count = self + .cmd + .get_keymap() + .keys() + .filter(|x| x.is_position()) + .count(); // If any arg sets .last(true) - let contains_last = self.app.args.args().any(|x| x.is_set(ArgSettings::Last)); + let contains_last = self.cmd.get_arguments().any(|x| x.is_last_set()); - while let Some((arg_os, remaining_args)) = it.next() { + while let Some(arg_os) = raw_args.next(&mut args_cursor) { // Recover the replaced items if any. - if let Some((_replacer, replaced_items)) = self - .app - .replacers - .iter() - .find(|(key, _)| OsStr::new(key) == arg_os) + if let Some(replaced_items) = arg_os + .to_value() + .ok() + .and_then(|a| self.cmd.get_replacement(a)) { - it.insert(replaced_items); debug!( "Parser::get_matches_with: found replacer: {:?}, target: {:?}", - _replacer, replaced_items + arg_os, replaced_items ); + raw_args.insert(&args_cursor, replaced_items); continue; } - let arg_os = RawOsStr::new(arg_os); debug!( "Parser::get_matches_with: Begin parsing '{:?}' ({:?})", - arg_os, - arg_os.as_raw_bytes() + arg_os.to_value_os(), + arg_os.to_value_os().as_raw_bytes() ); // Correct pos_counter. @@ -143,16 +107,16 @@ impl<'help, 'app> Parser<'help, 'app> { // argument may be set to .multiple_values(true) or `.multiple_occurrences(true)` let low_index_mults = is_second_to_last && self - .app + .cmd .get_positionals() .any(|a| a.is_multiple() && (positional_count != a.index.unwrap_or(0))) && self - .app + .cmd .get_positionals() .last() - .map_or(false, |p_name| !p_name.is_set(ArgSettings::Last)); + .map_or(false, |p_name| !p_name.is_last_set()); - let missing_pos = self.is_set(AS::AllowMissingPositional) + let missing_pos = self.cmd.is_allow_missing_positional_set() && is_second_to_last && !trailing_values; @@ -166,9 +130,9 @@ impl<'help, 'app> Parser<'help, 'app> { ); if low_index_mults || missing_pos { - let skip_current = if let Some(n) = remaining_args.get(0) { + let skip_current = if let Some(n) = raw_args.peek(&args_cursor) { if let Some(p) = self - .app + .cmd .get_positionals() .find(|p| p.index == Some(pos_counter)) { @@ -177,9 +141,10 @@ impl<'help, 'app> Parser<'help, 'app> { // pos_counter(which means current value cannot be a // positional argument with a value next to it), assume // current value matches the next arg. - let n = RawOsStr::new(n); self.is_new_arg(&n, p) - || self.possible_subcommand(&n, valid_arg_found).is_some() + || self + .possible_subcommand(n.to_value(), valid_arg_found) + .is_some() } else { true } @@ -194,7 +159,7 @@ impl<'help, 'app> Parser<'help, 'app> { pos_counter } } else if trailing_values - && (self.is_set(AS::AllowMissingPositional) || contains_last) + && (self.cmd.is_allow_missing_positional_set() || contains_last) { // Came to -- and one positional has .last(true) set, so we go immediately // to the last (highest index) positional @@ -207,28 +172,39 @@ impl<'help, 'app> Parser<'help, 'app> { // Has the user already passed '--'? Meaning only positional args follow if !trailing_values { - if self.is_set(AS::SubcommandPrecedenceOverArg) + if self.cmd.is_subcommand_precedence_over_arg_set() || !matches!(parse_state, ParseState::Opt(_) | ParseState::Pos(_)) { // Does the arg match a subcommand name, or any of its aliases (if defined) - let sc_name = self.possible_subcommand(&arg_os, valid_arg_found); + let sc_name = self.possible_subcommand(arg_os.to_value(), valid_arg_found); debug!("Parser::get_matches_with: sc={:?}", sc_name); if let Some(sc_name) = sc_name { if sc_name == "help" && !self.is_set(AS::NoAutoHelp) - && !self.is_set(AS::DisableHelpSubcommand) + && !self.cmd.is_disable_help_subcommand_set() { - self.parse_help_subcommand(remaining_args)?; + self.parse_help_subcommand(raw_args.remaining(&mut args_cursor))?; } subcmd_name = Some(sc_name.to_owned()); break; } } - if let Some(long_arg) = arg_os.strip_prefix("--") { + if arg_os.is_escape() { + if matches!(&parse_state, ParseState::Opt(opt) | ParseState::Pos(opt) if + self.cmd[opt].is_allow_hyphen_values_set()) + { + // ParseResult::MaybeHyphenValue, do nothing + } else { + debug!("Parser::get_matches_with: setting TrailingVals=true"); + trailing_values = true; + continue; + } + } else if let Some((long_arg, long_value)) = arg_os.to_long() { let parse_result = self.parse_long_arg( matcher, long_arg, + long_value, &parse_state, &mut valid_arg_found, trailing_values, @@ -239,9 +215,7 @@ impl<'help, 'app> Parser<'help, 'app> { ); match parse_result { ParseResult::NoArg => { - debug!("Parser::get_matches_with: setting TrailingVals=true"); - trailing_values = true; - continue; + unreachable!("`to_long` always has the flag specified") } ParseResult::ValuesDone => { parse_state = ParseState::ValuesDone; @@ -261,28 +235,28 @@ impl<'help, 'app> Parser<'help, 'app> { } ParseResult::EqualsNotProvided { arg } => { return Err(ClapError::no_equals( - self.app, + self.cmd, arg, - Usage::new(self).create_usage_with_title(&[]), + Usage::new(self.cmd).create_usage_with_title(&[]), )); } ParseResult::NoMatchingArg { arg } => { - let remaining_args: Vec<_> = remaining_args - .iter() + let remaining_args: Vec<_> = raw_args + .remaining(&mut args_cursor) .map(|x| x.to_str().expect(INVALID_UTF8)) .collect(); return Err(self.did_you_mean_error(&arg, matcher, &remaining_args)); } ParseResult::UnneededAttachedValue { rest, used, arg } => { return Err(ClapError::too_many_values( - self.app, + self.cmd, rest, arg, - Usage::new(self).create_usage_no_title(&used), + Usage::new(self.cmd).create_usage_no_title(&used), )) } ParseResult::HelpFlag => { - return Err(self.help_err(true)); + return Err(self.help_err(true, Stream::Stdout)); } ParseResult::VersionFlag => { return Err(self.version_err(true)); @@ -294,10 +268,10 @@ impl<'help, 'app> Parser<'help, 'app> { unreachable!() } } - } else if let Some(short_arg) = arg_os.strip_prefix("-") { + } else if let Some(short_arg) = arg_os.to_short() { // Arg looks like a short flag, and not a possible number - // Try to parse short args like normal, if AllowHyphenValues or + // Try to parse short args like normal, if allow_hyphen_values or // AllowNegativeNumbers is set, parse_short_arg will *not* throw // an error, and instead return Ok(None) let parse_result = self.parse_short_arg( @@ -331,7 +305,8 @@ impl<'help, 'app> Parser<'help, 'app> { keep_state = self .flag_subcmd_at .map(|at| { - it.cursor -= 1; + raw_args + .seek(&mut args_cursor, clap_lex::SeekFrom::Current(-1)); // Since we are now saving the current state, the number of flags to skip during state recovery should // be the current index (`cur_idx`) minus ONE UNIT TO THE LEFT of the starting position. self.flag_subcmd_skip = self.cur_idx.get() - at + 1; @@ -350,21 +325,21 @@ impl<'help, 'app> Parser<'help, 'app> { } ParseResult::EqualsNotProvided { arg } => { return Err(ClapError::no_equals( - self.app, + self.cmd, arg, - Usage::new(self).create_usage_with_title(&[]), + Usage::new(self.cmd).create_usage_with_title(&[]), )) } ParseResult::NoMatchingArg { arg } => { return Err(ClapError::unknown_argument( - self.app, + self.cmd, arg, None, - Usage::new(self).create_usage_with_title(&[]), + Usage::new(self.cmd).create_usage_with_title(&[]), )); } ParseResult::HelpFlag => { - return Err(self.help_err(false)); + return Err(self.help_err(false, Stream::Stdout)); } ParseResult::VersionFlag => { return Err(self.version_err(false)); @@ -382,10 +357,10 @@ impl<'help, 'app> Parser<'help, 'app> { // get the option so we can check the settings let parse_result = self.add_val_to_arg( - &self.app[id], - &arg_os, + &self.cmd[id], + arg_os.to_value_os(), matcher, - ValueType::CommandLine, + ValueSource::CommandLine, true, trailing_values, ); @@ -399,17 +374,17 @@ impl<'help, 'app> Parser<'help, 'app> { } } - if let Some(p) = self.app.args.get(&pos_counter) { - if p.is_set(ArgSettings::Last) && !trailing_values { + if let Some(p) = self.cmd.get_keymap().get(&pos_counter) { + if p.is_last_set() && !trailing_values { return Err(ClapError::unknown_argument( - self.app, - arg_os.to_str_lossy().into_owned(), + self.cmd, + arg_os.display().to_string(), None, - Usage::new(self).create_usage_with_title(&[]), + Usage::new(self.cmd).create_usage_with_title(&[]), )); } - if self.is_set(AS::TrailingVarArg) && pos_counter == positional_count { + if self.cmd.is_trailing_var_arg_set() && pos_counter == positional_count { trailing_values = true; } @@ -424,9 +399,9 @@ impl<'help, 'app> Parser<'help, 'app> { let append = self.has_val_groups(matcher, p); self.add_val_to_arg( p, - &arg_os, + arg_os.to_value_os(), matcher, - ValueType::CommandLine, + ValueSource::CommandLine, append, trailing_values, ); @@ -439,53 +414,60 @@ impl<'help, 'app> Parser<'help, 'app> { parse_state = ParseState::Pos(p.id.clone()); } valid_arg_found = true; - } else if self.is_set(AS::AllowExternalSubcommands) { + } else if self.cmd.is_allow_external_subcommands_set() { // Get external subcommand name - let sc_name = match arg_os.to_str() { - Some(s) => s.to_string(), - None => { + let sc_name = match arg_os.to_value() { + Ok(s) => s.to_string(), + Err(_) => { return Err(ClapError::invalid_utf8( - self.app, - Usage::new(self).create_usage_with_title(&[]), + self.cmd, + Usage::new(self.cmd).create_usage_with_title(&[]), )); } }; // Collect the external subcommand args - let mut sc_m = ArgMatcher::new(self.app); + let mut sc_m = ArgMatcher::new(self.cmd); + let allow_invalid_utf8 = self + .cmd + .is_allow_invalid_utf8_for_external_subcommands_set(); + #[cfg(feature = "unstable-v4")] + { + sc_m.inc_occurrence_of_external(allow_invalid_utf8); + } - while let Some((v, _)) = it.next() { - let allow_invalid_utf8 = - self.is_set(AS::AllowInvalidUtf8ForExternalSubcommands); + for v in raw_args.remaining(&mut args_cursor) { if !allow_invalid_utf8 && v.to_str().is_none() { return Err(ClapError::invalid_utf8( - self.app, - Usage::new(self).create_usage_with_title(&[]), + self.cmd, + Usage::new(self.cmd).create_usage_with_title(&[]), )); } + let external_id = &Id::empty_hash(); sc_m.add_val_to( - &Id::empty_hash(), + external_id, v.to_os_string(), - ValueType::CommandLine, + ValueSource::CommandLine, false, ); - sc_m.get_mut(&Id::empty_hash()) - .expect("just inserted") - .invalid_utf8_allowed(allow_invalid_utf8); + #[cfg(not(feature = "unstable-v4"))] + { + sc_m.get_mut(external_id) + .expect("just inserted") + .invalid_utf8_allowed(allow_invalid_utf8); + } } matcher.subcommand(SubCommand { - name: sc_name.clone(), - id: sc_name.into(), + id: Id::from(&*sc_name), + name: sc_name, matches: sc_m.into_inner(), }); - return Validator::new(self).validate( - parse_state, - subcmd_name.is_some(), - matcher, - trailing_values, - ); + #[cfg(feature = "env")] + self.add_env(matcher, trailing_values)?; + self.add_defaults(matcher, trailing_values); + return Validator::new(self.cmd).validate(parse_state, matcher); } else { // Start error processing return Err(self.match_arg_error(&arg_os, valid_arg_found, trailing_values)); @@ -494,51 +476,44 @@ impl<'help, 'app> Parser<'help, 'app> { if let Some(ref pos_sc_name) = subcmd_name { let sc_name = self - .app + .cmd .find_subcommand(pos_sc_name) .expect(INTERNAL_ERROR_MSG) - .name - .clone(); - self.parse_subcommand(&sc_name, matcher, it, keep_state)?; - } else if self.is_set(AS::SubcommandRequired) { - let bn = self.app.bin_name.as_ref().unwrap_or(&self.app.name); - return Err(ClapError::missing_subcommand( - self.app, - bn.to_string(), - Usage::new(self).create_usage_with_title(&[]), - )); - } else if self.is_set(AS::SubcommandRequiredElseHelp) { - debug!("Parser::get_matches_with: SubcommandRequiredElseHelp=true"); - let message = self.write_help_err()?; - return Err(ClapError::new( - message, - ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand, - self.app.settings.is_set(AS::WaitOnError), - )); + .get_name() + .to_owned(); + self.parse_subcommand(&sc_name, matcher, raw_args, args_cursor, keep_state)?; } - Validator::new(self).validate(parse_state, subcmd_name.is_some(), matcher, trailing_values) + #[cfg(feature = "env")] + self.add_env(matcher, trailing_values)?; + self.add_defaults(matcher, trailing_values); + Validator::new(self.cmd).validate(parse_state, matcher) } fn match_arg_error( &self, - arg_os: &RawOsStr, + arg_os: &clap_lex::ParsedArg<'_>, valid_arg_found: bool, trailing_values: bool, ) -> ClapError { // If argument follows a `--` if trailing_values { // If the arg matches a subcommand name, or any of its aliases (if defined) - if self.possible_subcommand(arg_os, valid_arg_found).is_some() { + if self + .possible_subcommand(arg_os.to_value(), valid_arg_found) + .is_some() + { return ClapError::unnecessary_double_dash( - self.app, - arg_os.to_str_lossy().into_owned(), - Usage::new(self).create_usage_with_title(&[]), + self.cmd, + arg_os.display().to_string(), + Usage::new(self.cmd).create_usage_with_title(&[]), ); } } - let candidates = - suggestions::did_you_mean(&arg_os.to_str_lossy(), self.app.all_subcommand_names()); + let candidates = suggestions::did_you_mean( + &arg_os.display().to_string(), + self.cmd.all_subcommand_names(), + ); // If the argument looks like a subcommand. if !candidates.is_empty() { let candidates: Vec<_> = candidates @@ -546,49 +521,50 @@ impl<'help, 'app> Parser<'help, 'app> { .map(|candidate| format!("'{}'", candidate)) .collect(); return ClapError::invalid_subcommand( - self.app, - arg_os.to_str_lossy().into_owned(), + self.cmd, + arg_os.display().to_string(), candidates.join(" or "), - self.app - .bin_name - .as_ref() - .unwrap_or(&self.app.name) - .to_string(), - Usage::new(self).create_usage_with_title(&[]), + self.cmd + .get_bin_name() + .unwrap_or_else(|| self.cmd.get_name()) + .to_owned(), + Usage::new(self.cmd).create_usage_with_title(&[]), ); } // If the argument must be a subcommand. - if !self.app.has_args() || self.is_set(AS::InferSubcommands) && self.app.has_subcommands() { + if !self.cmd.has_args() || self.cmd.is_infer_subcommands_set() && self.cmd.has_subcommands() + { return ClapError::unrecognized_subcommand( - self.app, - arg_os.to_str_lossy().into_owned(), - self.app - .bin_name - .as_ref() - .unwrap_or(&self.app.name) - .to_string(), + self.cmd, + arg_os.display().to_string(), + Usage::new(self.cmd).create_usage_with_title(&[]), ); } ClapError::unknown_argument( - self.app, - arg_os.to_str_lossy().into_owned(), + self.cmd, + arg_os.display().to_string(), None, - Usage::new(self).create_usage_with_title(&[]), + Usage::new(self.cmd).create_usage_with_title(&[]), ) } // Checks if the arg matches a subcommand name, or any of its aliases (if defined) - fn possible_subcommand(&self, arg_os: &RawOsStr, valid_arg_found: bool) -> Option<&str> { - debug!("Parser::possible_subcommand: arg={:?}", arg_os); + fn possible_subcommand( + &self, + arg: Result<&str, &RawOsStr>, + valid_arg_found: bool, + ) -> Option<&str> { + debug!("Parser::possible_subcommand: arg={:?}", arg); + let arg = arg.ok()?; - if !(self.is_set(AS::ArgsNegateSubcommands) && valid_arg_found) { - if self.is_set(AS::InferSubcommands) { + if !(self.cmd.is_args_conflicts_with_subcommands_set() && valid_arg_found) { + if self.cmd.is_infer_subcommands_set() { // For subcommand `test`, we accepts it's prefix: `t`, `te`, // `tes` and `test`. let v = self - .app + .cmd .all_subcommand_names() - .filter(|s| RawOsStr::from_str(s).starts_with_os(arg_os)) + .filter(|s| s.starts_with(arg)) .collect::>(); if v.len() == 1 { @@ -598,29 +574,26 @@ impl<'help, 'app> Parser<'help, 'app> { // If there is any ambiguity, fallback to non-infer subcommand // search. } - if let Some(sc) = self.app.find_subcommand(arg_os) { - return Some(&sc.name); + if let Some(sc) = self.cmd.find_subcommand(arg) { + return Some(sc.get_name()); } } None } // Checks if the arg matches a long flag subcommand name, or any of its aliases (if defined) - fn possible_long_flag_subcommand(&self, arg_os: &RawOsStr) -> Option<&str> { - debug!("Parser::possible_long_flag_subcommand: arg={:?}", arg_os); - if self.is_set(AS::InferSubcommands) { + fn possible_long_flag_subcommand(&self, arg: &str) -> Option<&str> { + debug!("Parser::possible_long_flag_subcommand: arg={:?}", arg); + if self.cmd.is_infer_subcommands_set() { let options = self - .app + .cmd .get_subcommands() .fold(Vec::new(), |mut options, sc| { - if let Some(long) = sc.long_flag { - if RawOsStr::from_str(long).starts_with_os(arg_os) { + if let Some(long) = sc.get_long_flag() { + if long.starts_with(arg) { options.push(long); } - options.extend( - sc.get_all_aliases() - .filter(|alias| RawOsStr::from_str(alias).starts_with_os(arg_os)), - ) + options.extend(sc.get_all_aliases().filter(|alias| alias.starts_with(arg))) } options }); @@ -629,81 +602,75 @@ impl<'help, 'app> Parser<'help, 'app> { } for sc in options { - if sc == arg_os { + if sc == arg { return Some(sc); } } - } else if let Some(sc_name) = self.app.find_long_subcmd(arg_os) { + } else if let Some(sc_name) = self.cmd.find_long_subcmd(arg) { return Some(sc_name); } None } - fn parse_help_subcommand(&self, cmds: &[OsString]) -> ClapResult { + fn parse_help_subcommand( + &self, + cmds: impl Iterator, + ) -> ClapResult { debug!("Parser::parse_help_subcommand"); - let mut bin_name = self.app.bin_name.as_ref().unwrap_or(&self.app.name).clone(); + let mut cmd = self.cmd.clone(); + let sc = { + let mut sc = &mut cmd; - let mut sc = { - let mut sc = self.app.clone(); - - for cmd in cmds.iter() { - sc = if let Some(c) = sc.find_subcommand(cmd) { - c - } else if let Some(c) = sc.find_subcommand(&cmd.to_string_lossy()) { - c + for cmd in cmds { + sc = if let Some(sc_name) = + sc.find_subcommand(cmd).map(|sc| sc.get_name().to_owned()) + { + sc._build_subcommand(&sc_name).unwrap() } else { return Err(ClapError::unrecognized_subcommand( - self.app, + sc, cmd.to_string_lossy().into_owned(), - self.app - .bin_name - .as_ref() - .unwrap_or(&self.app.name) - .to_string(), + Usage::new(sc).create_usage_with_title(&[]), )); - } - .clone(); - - sc._build(); - bin_name.push(' '); - bin_name.push_str(&sc.name); + }; } sc }; - sc = sc.bin_name(bin_name); + let parser = Parser::new(sc); - let parser = Parser::new(&mut sc); - - Err(parser.help_err(self.app.is_set(AS::UseLongFormatForHelpSubcommand))) + Err(parser.help_err(true, Stream::Stdout)) } - fn is_new_arg(&self, next: &RawOsStr, current_positional: &Arg) -> bool { + fn is_new_arg(&self, next: &clap_lex::ParsedArg<'_>, current_positional: &Arg) -> bool { + #![allow(clippy::needless_bool)] // Prefer consistent if/else-if ladder + debug!( "Parser::is_new_arg: {:?}:{:?}", - next, current_positional.name + next.to_value_os(), + current_positional.name ); - if self.is_set(AS::AllowHyphenValues) - || self.app[¤t_positional.id].is_set(ArgSettings::AllowHyphenValues) - || (self.is_set(AS::AllowNegativeNumbers) && next.to_str_lossy().parse::().is_ok()) + if self.cmd.is_allow_hyphen_values_set() + || self.cmd[¤t_positional.id].is_allow_hyphen_values_set() + || (self.cmd.is_allow_negative_numbers_set() && next.is_number()) { // If allow hyphen, this isn't a new arg. debug!("Parser::is_new_arg: Allow hyphen"); false - } else if next.starts_with("--") { + } else if next.is_long() { // If this is a long flag, this is a new arg. - debug!("Parser::is_new_arg: -- found"); + debug!("Parser::is_new_arg: -- found"); true - } else if next.starts_with("-") { - debug!("Parser::is_new_arg: - found"); + } else if next.is_short() { // If this is a short flag, this is a new arg. But a singe '-' by // itself is a value and typically means "stdin" on unix systems. - next.raw_len() != 1 + debug!("Parser::is_new_arg: - found"); + true } else { - debug!("Parser::is_new_arg: value"); // Nothing special, this is a value. + debug!("Parser::is_new_arg: value"); false } } @@ -712,64 +679,21 @@ impl<'help, 'app> Parser<'help, 'app> { &mut self, sc_name: &str, matcher: &mut ArgMatcher, - it: &mut Input, + raw_args: &mut clap_lex::RawArgs, + args_cursor: clap_lex::ArgCursor, keep_state: bool, ) -> ClapResult<()> { debug!("Parser::parse_subcommand"); - let mut mid_string = String::from(" "); - - if !self.is_set(AS::SubcommandsNegateReqs) { - let reqs = Usage::new(self).get_required_usage_from(&[], None, true); // maybe Some(m) - - for s in &reqs { - mid_string.push_str(s); - mid_string.push(' '); - } - } - - let partial_parsing_enabled = self.is_set(AS::IgnoreErrors); - - if let Some(sc) = self.app.subcommands.iter_mut().find(|s| s.name == sc_name) { - // Display subcommand name, short and long in usage - let mut sc_names = sc.name.clone(); - let mut flag_subcmd = false; - if let Some(l) = sc.long_flag { - sc_names.push_str(&format!(", --{}", l)); - flag_subcmd = true; - } - if let Some(s) = sc.short_flag { - sc_names.push_str(&format!(", -{}", s)); - flag_subcmd = true; - } - - if flag_subcmd { - sc_names = format!("{{{}}}", sc_names); - } - - sc.usage = Some( - self.app - .bin_name - .as_ref() - .map(|bin_name| format!("{}{}{}", bin_name, mid_string, sc_names)) - .unwrap_or(sc_names), - ); - - // bin_name should be parent's bin_name + [] + the sc's name separated by - // a space - sc.bin_name = Some(format!( - "{}{}{}", - self.app.bin_name.as_ref().unwrap_or(&String::new()), - if self.app.bin_name.is_some() { " " } else { "" }, - &*sc.name - )); - - // Ensure all args are built and ready to parse - sc._build(); + let partial_parsing_enabled = self.cmd.is_ignore_errors_set(); + if let Some(sc) = self.cmd._build_subcommand(sc_name) { let mut sc_matcher = ArgMatcher::new(sc); - debug!("Parser::parse_subcommand: About to parse sc={}", sc.name); + debug!( + "Parser::parse_subcommand: About to parse sc={}", + sc.get_name() + ); { let mut p = Parser::new(sc); @@ -780,7 +704,7 @@ impl<'help, 'app> Parser<'help, 'app> { p.flag_subcmd_at = self.flag_subcmd_at; p.flag_subcmd_skip = self.flag_subcmd_skip; } - if let Err(error) = p.get_matches_with(&mut sc_matcher, it) { + if let Err(error) = p.get_matches_with(&mut sc_matcher, raw_args, args_cursor) { if partial_parsing_enabled { debug!( "Parser::parse_subcommand: ignored error in subcommand {}: {:?}", @@ -792,8 +716,8 @@ impl<'help, 'app> Parser<'help, 'app> { } } matcher.subcommand(SubCommand { - id: sc.id.clone(), - name: sc.name.clone(), + id: sc.get_id(), + name: sc.get_name().to_owned(), matches: sc_matcher.into_inner(), }); } @@ -809,20 +733,21 @@ impl<'help, 'app> Parser<'help, 'app> { arg ); - if let Some(help) = self.app.find(&Id::help_hash()) { + if let Some(help) = self.cmd.find(&Id::help_hash()) { if let Some(h) = help.long { - if arg == h && !self.is_set(AS::NoAutoHelp) && !self.is_set(AS::DisableHelpFlag) { + if arg == h && !self.is_set(AS::NoAutoHelp) && !self.cmd.is_disable_help_flag_set() + { debug!("Help"); return Some(ParseResult::HelpFlag); } } } - if let Some(version) = self.app.find(&Id::version_hash()) { + if let Some(version) = self.cmd.find(&Id::version_hash()) { if let Some(v) = version.long { if arg == v && !self.is_set(AS::NoAutoVersion) - && !self.is_set(AS::DisableVersionFlag) + && !self.cmd.is_disable_version_flag_set() { debug!("Version"); return Some(ParseResult::VersionFlag); @@ -841,20 +766,21 @@ impl<'help, 'app> Parser<'help, 'app> { arg ); - if let Some(help) = self.app.find(&Id::help_hash()) { + if let Some(help) = self.cmd.find(&Id::help_hash()) { if let Some(h) = help.short { - if arg == h && !self.is_set(AS::NoAutoHelp) && !self.is_set(AS::DisableHelpFlag) { + if arg == h && !self.is_set(AS::NoAutoHelp) && !self.cmd.is_disable_help_flag_set() + { debug!("Help"); return Some(ParseResult::HelpFlag); } } } - if let Some(version) = self.app.find(&Id::version_hash()) { + if let Some(version) = self.cmd.find(&Id::version_hash()) { if let Some(v) = version.short { if arg == v && !self.is_set(AS::NoAutoVersion) - && !self.is_set(AS::DisableVersionFlag) + && !self.cmd.is_disable_version_flag_set() { debug!("Version"); return Some(ParseResult::VersionFlag); @@ -866,30 +792,11 @@ impl<'help, 'app> Parser<'help, 'app> { None } - fn use_long_help(&self) -> bool { - debug!("Parser::use_long_help"); - // In this case, both must be checked. This allows the retention of - // original formatting, but also ensures that the actual -h or --help - // specified by the user is sent through. If HiddenShortHelp is not included, - // then items specified with hidden_short_help will also be hidden. - let should_long = |v: &Arg| { - v.long_help.is_some() - || v.is_set(ArgSettings::HiddenLongHelp) - || v.is_set(ArgSettings::HiddenShortHelp) - }; - - // Subcommands aren't checked because we prefer short help for them, deferring to - // `cmd subcmd --help` for more. - self.app.long_about.is_some() - || self.app.before_long_help.is_some() - || self.app.after_long_help.is_some() - || self.app.args.args().any(should_long) - } - fn parse_long_arg( &mut self, matcher: &mut ArgMatcher, - long_arg: &RawOsStr, + long_arg: Result<&str, &RawOsStr>, + long_value: Option<&RawOsStr>, parse_state: &ParseState, valid_arg_found: &mut bool, trailing_values: bool, @@ -898,7 +805,7 @@ impl<'help, 'app> Parser<'help, 'app> { debug!("Parser::parse_long_arg"); if matches!(parse_state, ParseState::Opt(opt) | ParseState::Pos(opt) if - self.app[opt].is_set(ArgSettings::AllowHyphenValues)) + self.cmd[opt].is_allow_hyphen_values_set()) { return ParseResult::MaybeHyphenValue; } @@ -908,31 +815,31 @@ impl<'help, 'app> Parser<'help, 'app> { debug!("Parser::parse_long_arg: cur_idx:={}", self.cur_idx.get()); debug!("Parser::parse_long_arg: Does it contain '='..."); + let long_arg = match long_arg { + Ok(long_arg) => long_arg, + Err(long_arg) => { + return ParseResult::NoMatchingArg { + arg: long_arg.to_str_lossy().into_owned(), + }; + } + }; if long_arg.is_empty() { + debug_assert!(long_value.is_none(), "{:?}", long_value); return ParseResult::NoArg; } - let (arg, val) = if let Some(index) = long_arg.find("=") { - let (p0, p1) = long_arg.split_at(index); - debug!("Yes '{:?}'", p1); - (p0, Some(p1)) - } else { - debug!("No"); - (long_arg, None) - }; - let opt = if let Some(opt) = self.app.args.get(&*arg.to_os_str()) { + let opt = if let Some(opt) = self.cmd.get_keymap().get(long_arg) { debug!( "Parser::parse_long_arg: Found valid opt or flag '{}'", opt.to_string() ); Some(opt) - } else if self.is_set(AS::InferLongArgs) { - let arg_str = arg.to_str_lossy(); - self.app.args.args().find(|a| { - a.long.map_or(false, |long| long.starts_with(&*arg_str)) + } else if self.cmd.is_infer_long_args_set() { + self.cmd.get_arguments().find(|a| { + a.long.map_or(false, |long| long.starts_with(long_arg)) || a.aliases .iter() - .any(|(alias, _)| alias.starts_with(&*arg_str)) + .any(|(alias, _)| alias.starts_with(long_arg)) }) } else { None @@ -941,20 +848,22 @@ impl<'help, 'app> Parser<'help, 'app> { if let Some(opt) = opt { *valid_arg_found = true; self.seen.push(opt.id.clone()); - if opt.is_set(ArgSettings::TakesValue) { + if opt.is_takes_value_set() { debug!( "Parser::parse_long_arg: Found an opt with value '{:?}'", - &val + &long_value ); - self.parse_opt(val, opt, matcher, trailing_values) - } else if let Some(rest) = val { + let has_eq = long_value.is_some(); + self.parse_opt(long_value, opt, matcher, trailing_values, has_eq) + } else if let Some(rest) = long_value { + let required = self.cmd.required_graph(); debug!("Parser::parse_long_arg: Got invalid literal `{:?}`", rest); let used: Vec = matcher .arg_names() .filter(|&n| { - self.app.find(n).map_or(true, |a| { - !(a.is_set(ArgSettings::Hidden) || self.required.contains(&a.id)) - }) + self.cmd + .find(n) + .map_or(true, |a| !(a.is_hide_set() || required.contains(&a.id))) }) .cloned() .collect(); @@ -964,19 +873,21 @@ impl<'help, 'app> Parser<'help, 'app> { used, arg: opt.to_string(), } - } else if let Some(parse_result) = self.check_for_help_and_version_str(arg) { + } else if let Some(parse_result) = + self.check_for_help_and_version_str(RawOsStr::from_str(long_arg)) + { parse_result } else { debug!("Parser::parse_long_arg: Presence validated"); self.parse_flag(opt, matcher) } - } else if let Some(sc_name) = self.possible_long_flag_subcommand(arg) { + } else if let Some(sc_name) = self.possible_long_flag_subcommand(long_arg) { ParseResult::FlagSubCommand(sc_name.to_string()) - } else if self.is_set(AS::AllowHyphenValues) { + } else if self.cmd.is_allow_hyphen_values_set() { ParseResult::MaybeHyphenValue } else { ParseResult::NoMatchingArg { - arg: arg.to_str_lossy().into_owned(), + arg: long_arg.to_owned(), } } } @@ -984,7 +895,7 @@ impl<'help, 'app> Parser<'help, 'app> { fn parse_short_arg( &mut self, matcher: &mut ArgMatcher, - short_arg: &RawOsStr, + mut short_arg: clap_lex::ShortFlags<'_>, parse_state: &ParseState, // change this to possible pos_arg when removing the usage of &mut Parser. pos_counter: usize, @@ -992,25 +903,31 @@ impl<'help, 'app> Parser<'help, 'app> { trailing_values: bool, ) -> ParseResult { debug!("Parser::parse_short_arg: short_arg={:?}", short_arg); - let arg = short_arg.to_str_lossy(); #[allow(clippy::blocks_in_if_conditions)] - if self.is_set(AS::AllowNegativeNumbers) && arg.parse::().is_ok() { + if self.cmd.is_allow_negative_numbers_set() && short_arg.is_number() { debug!("Parser::parse_short_arg: negative number"); return ParseResult::MaybeHyphenValue; - } else if self.is_set(AS::AllowHyphenValues) - && arg.chars().any(|c| !self.app.contains_short(c)) + } else if self.cmd.is_allow_hyphen_values_set() + && short_arg + .clone() + .any(|c| !c.map(|c| self.cmd.contains_short(c)).unwrap_or_default()) { debug!("Parser::parse_short_args: contains non-short flag"); return ParseResult::MaybeHyphenValue; } else if matches!(parse_state, ParseState::Opt(opt) | ParseState::Pos(opt) - if self.app[opt].is_set(ArgSettings::AllowHyphenValues)) + if self.cmd[opt].is_allow_hyphen_values_set()) { debug!("Parser::parse_short_args: prior arg accepts hyphenated values",); return ParseResult::MaybeHyphenValue; - } else if self.app.args.get(&pos_counter).map_or(false, |arg| { - arg.is_set(ArgSettings::AllowHyphenValues) && !arg.is_set(ArgSettings::Last) - }) { + } else if self + .cmd + .get_keymap() + .get(&pos_counter) + .map_or(false, |arg| { + arg.is_allow_hyphen_values_set() && !arg.is_last_set() + }) + { debug!( "Parser::parse_short_args: positional at {} allows hyphens", pos_counter @@ -1022,7 +939,22 @@ impl<'help, 'app> Parser<'help, 'app> { let skip = self.flag_subcmd_skip; self.flag_subcmd_skip = 0; - for c in arg.chars().skip(skip) { + let res = short_arg.advance_by(skip); + debug_assert_eq!( + res, + Ok(()), + "tracking of `flag_subcmd_skip` is off for `{:?}`", + short_arg + ); + while let Some(c) = short_arg.next_flag() { + let c = match c { + Ok(c) => c, + Err(rest) => { + return ParseResult::NoMatchingArg { + arg: format!("-{}", rest.to_str_lossy()), + }; + } + }; debug!("Parser::parse_short_arg:iter:{}", c); // update each index because `-abcd` is four indices to clap @@ -1037,14 +969,14 @@ impl<'help, 'app> Parser<'help, 'app> { // concatenated value: -oval // Option: -o // Value: val - if let Some(opt) = self.app.args.get(&c) { + if let Some(opt) = self.cmd.get_keymap().get(&c) { debug!( "Parser::parse_short_arg:iter:{}: Found valid opt or flag", c ); *valid_arg_found = true; self.seen.push(opt.id.clone()); - if !opt.is_set(ArgSettings::TakesValue) { + if !opt.is_takes_value_set() { if let Some(parse_result) = self.check_for_help_and_version_char(c) { return parse_result; } @@ -1053,7 +985,9 @@ impl<'help, 'app> Parser<'help, 'app> { } // Check for trailing concatenated value - let val = short_arg.split_once(c).expect(INTERNAL_ERROR_MSG).1; + // + // Cloning the iterator, so we rollback if it isn't there. + let val = short_arg.clone().next_value_os().unwrap_or_default(); debug!( "Parser::parse_short_arg:iter:{}: val={:?} (bytes), val={:?} (ascii), short_arg={:?}", c, val, val.as_raw_bytes(), short_arg @@ -1065,28 +999,28 @@ impl<'help, 'app> Parser<'help, 'app> { // If attached value is not consumed, we may have more short // flags to parse, continue. // - // e.g. `-xvf`, when RequireEquals && x.min_vals == 0, we don't + // e.g. `-xvf`, when require_equals && x.min_vals == 0, we don't // consume the `vf`, even if it's provided as value. - match self.parse_opt(val, opt, matcher, trailing_values) { + let (val, has_eq) = if let Some(val) = val.and_then(|v| v.strip_prefix('=')) { + (Some(val), true) + } else { + (val, false) + }; + match self.parse_opt(val, opt, matcher, trailing_values, has_eq) { ParseResult::AttachedValueNotConsumed => continue, x => return x, } } - return if let Some(sc_name) = self.app.find_short_subcmd(c) { + return if let Some(sc_name) = self.cmd.find_short_subcmd(c) { debug!("Parser::parse_short_arg:iter:{}: subcommand={}", c, sc_name); let name = sc_name.to_string(); - let done_short_args = { - let cur_idx = self.cur_idx.get(); - // Get the index of the previously saved flag subcommand in the group of flags (if exists). - // If it is a new flag subcommand, then the formentioned index should be the current one - // (ie. `cur_idx`), and should be registered. - let at = *self.flag_subcmd_at.get_or_insert(cur_idx); - // If we are done, then the difference of indices (cur_idx - at) should be (end - at) which - // should equal to (arg.len() - 1), - // where `end` is the index of the end of the group. - cur_idx - at == arg.len() - 1 - }; + // Get the index of the previously saved flag subcommand in the group of flags (if exists). + // If it is a new flag subcommand, then the formentioned index should be the current one + // (ie. `cur_idx`), and should be registered. + let cur_idx = self.cur_idx.get(); + self.flag_subcmd_at.get_or_insert(cur_idx); + let done_short_args = short_arg.is_empty(); if done_short_args { self.flag_subcmd_at = None; } @@ -1106,18 +1040,17 @@ impl<'help, 'app> Parser<'help, 'app> { opt: &Arg<'help>, matcher: &mut ArgMatcher, trailing_values: bool, + has_eq: bool, ) -> ParseResult { debug!( - "Parser::parse_opt; opt={}, val={:?}", - opt.name, attached_value + "Parser::parse_opt; opt={}, val={:?}, has_eq={:?}", + opt.name, attached_value, has_eq ); debug!("Parser::parse_opt; opt.settings={:?}", opt.settings); - // has_eq: --flag=value - let has_eq = matches!(attached_value, Some(fv) if fv.starts_with("=")); debug!("Parser::parse_opt; Checking for val..."); - // RequireEquals is set, but no '=' is provided, try throwing error. - if opt.is_set(ArgSettings::RequireEquals) && !has_eq { + // require_equals is set, but no '=' is provided, try throwing error. + if opt.is_require_equals_set() && !has_eq { if opt.min_vals == Some(0) { debug!("Requires equals, but min_vals == 0"); self.inc_occurrence_of_arg(matcher, opt); @@ -1128,7 +1061,7 @@ impl<'help, 'app> Parser<'help, 'app> { opt, opt.default_missing_vals.iter().map(OsString::from), matcher, - ValueType::CommandLine, + ValueSource::CommandLine, false, ); }; @@ -1143,20 +1076,13 @@ impl<'help, 'app> Parser<'help, 'app> { arg: opt.to_string(), } } - } else if let Some(fv) = attached_value { - let v = fv.strip_prefix("=").unwrap_or(fv); - debug!("Found - {:?}, len: {}", v, v.raw_len()); - debug!( - "Parser::parse_opt: {:?} contains '='...{:?}", - fv, - fv.starts_with("=") - ); + } else if let Some(v) = attached_value { self.inc_occurrence_of_arg(matcher, opt); self.add_val_to_arg( opt, v, matcher, - ValueType::CommandLine, + ValueSource::CommandLine, false, trailing_values, ); @@ -1165,7 +1091,7 @@ impl<'help, 'app> Parser<'help, 'app> { debug!("Parser::parse_opt: More arg vals required..."); self.inc_occurrence_of_arg(matcher, opt); matcher.new_val_group(&opt.id); - for group in self.app.groups_for_arg(&opt.id) { + for group in self.cmd.groups_for_arg(&opt.id) { matcher.new_val_group(&group); } ParseResult::Opt(opt.id.clone()) @@ -1177,7 +1103,7 @@ impl<'help, 'app> Parser<'help, 'app> { arg: &Arg<'help>, val: &RawOsStr, matcher: &mut ArgMatcher, - ty: ValueType, + ty: ValueSource, append: bool, trailing_values: bool, ) -> ParseResult { @@ -1185,35 +1111,21 @@ impl<'help, 'app> Parser<'help, 'app> { debug!( "Parser::add_val_to_arg; trailing_values={:?}, DontDelimTrailingVals={:?}", trailing_values, - self.is_set(AS::DontDelimitTrailingValues) + self.cmd.is_dont_delimit_trailing_values_set() ); - if !(trailing_values && self.is_set(AS::DontDelimitTrailingValues)) { + if !(trailing_values && self.cmd.is_dont_delimit_trailing_values_set()) { if let Some(delim) = arg.val_delim { - let arg_split = val.split(delim); - let vals = if let Some(t) = arg.terminator { - let mut vals = vec![]; - for val in arg_split { - if t == val { - break; - } - vals.push(val); - } - vals - } else { - arg_split.collect() - }; - self.add_multiple_vals_to_arg( - arg, - vals.into_iter().map(|x| x.to_os_str().into_owned()), - matcher, - ty, - append, - ); + let terminator = arg.terminator.map(OsStr::new); + let vals = val + .split(delim) + .map(|x| x.to_os_str().into_owned()) + .take_while(|val| Some(val.as_os_str()) != terminator); + self.add_multiple_vals_to_arg(arg, vals, matcher, ty, append); // If there was a delimiter used or we must use the delimiter to // separate the values or no more vals is needed, we're not // looking for more values. return if val.contains(delim) - || arg.is_set(ArgSettings::RequireDelimiter) + || arg.is_require_value_delimiter_set() || !matcher.needs_more_vals(arg) { ParseResult::ValuesDone @@ -1240,13 +1152,13 @@ impl<'help, 'app> Parser<'help, 'app> { arg: &Arg<'help>, vals: impl Iterator, matcher: &mut ArgMatcher, - ty: ValueType, + ty: ValueSource, append: bool, ) { // If not appending, create a new val group and then append vals in. if !append { matcher.new_val_group(&arg.id); - for group in self.app.groups_for_arg(&arg.id) { + for group in self.cmd.groups_for_arg(&arg.id) { matcher.new_val_group(&group); } } @@ -1260,7 +1172,7 @@ impl<'help, 'app> Parser<'help, 'app> { arg: &Arg<'help>, val: OsString, matcher: &mut ArgMatcher, - ty: ValueType, + ty: ValueSource, append: bool, ) { debug!("Parser::add_single_val_to_arg: adding val...{:?}", val); @@ -1273,7 +1185,7 @@ impl<'help, 'app> Parser<'help, 'app> { ); // Increment or create the group "args" - for group in self.app.groups_for_arg(&arg.id) { + for group in self.cmd.groups_for_arg(&arg.id) { matcher.add_val_to(&group, val.clone(), ty, append); } @@ -1289,7 +1201,7 @@ impl<'help, 'app> Parser<'help, 'app> { debug!("Parser::parse_flag"); self.inc_occurrence_of_arg(matcher, flag); - matcher.add_index_to(&flag.id, self.cur_idx.get(), ValueType::CommandLine); + matcher.add_index_to(&flag.id, self.cur_idx.get(), ValueSource::CommandLine); ParseResult::ValuesDone } @@ -1299,13 +1211,12 @@ impl<'help, 'app> Parser<'help, 'app> { for override_id in &arg.overrides { debug!("Parser::remove_overrides:iter:{:?}: removing", override_id); matcher.remove(override_id); - self.overridden.borrow_mut().push(override_id.clone()); } // Override anything that can override us let mut transitive = Vec::new(); for arg_id in matcher.arg_names() { - if let Some(overrider) = self.app.find(arg_id) { + if let Some(overrider) = self.cmd.find(arg_id) { if overrider.overrides.contains(&arg.id) { transitive.push(&overrider.id); } @@ -1314,21 +1225,20 @@ impl<'help, 'app> Parser<'help, 'app> { for overrider_id in transitive { debug!("Parser::remove_overrides:iter:{:?}: removing", overrider_id); matcher.remove(overrider_id); - self.overridden.borrow_mut().push(overrider_id.clone()); } } pub(crate) fn add_defaults(&mut self, matcher: &mut ArgMatcher, trailing_values: bool) { debug!("Parser::add_defaults"); - for o in self.app.get_opts() { + for o in self.cmd.get_opts() { debug!("Parser::add_defaults:iter:{}:", o.name); - self.add_value(o, matcher, ValueType::DefaultValue, trailing_values); + self.add_value(o, matcher, ValueSource::DefaultValue, trailing_values); } - for p in self.app.get_positionals() { + for p in self.cmd.get_positionals() { debug!("Parser::add_defaults:iter:{}:", p.name); - self.add_value(p, matcher, ValueType::DefaultValue, trailing_values); + self.add_value(p, matcher, ValueSource::DefaultValue, trailing_values); } } @@ -1336,7 +1246,7 @@ impl<'help, 'app> Parser<'help, 'app> { &self, arg: &Arg<'help>, matcher: &mut ArgMatcher, - ty: ValueType, + ty: ValueSource, trailing_values: bool, ) { if !arg.default_vals_ifs.is_empty() { @@ -1344,10 +1254,11 @@ impl<'help, 'app> Parser<'help, 'app> { if matcher.get(&arg.id).is_none() { for (id, val, default) in arg.default_vals_ifs.iter() { let add = if let Some(a) = matcher.get(id) { - if let Some(v) = val { - a.vals_flatten().any(|value| v == value) - } else { - true + match val { + crate::build::ArgPredicate::Equals(v) => { + a.vals_flatten().any(|value| v == value) + } + crate::build::ArgPredicate::IsPresent => true, } } else { false @@ -1458,10 +1369,13 @@ impl<'help, 'app> Parser<'help, 'app> { ) -> ClapResult<()> { use crate::util::str_to_bool; - self.app.args.args().try_for_each(|a| { + self.cmd.get_arguments().try_for_each(|a| { // Use env only if the arg was absent among command line args, // early return if this is not the case. - if matcher.get(&a.id).map_or(false, |a| a.occurs != 0) { + if matcher + .get(&a.id) + .map_or(false, |a| a.get_occurrences() != 0) + { debug!("Parser::add_env: Skipping existing arg `{}`", a); return Ok(()); } @@ -1470,7 +1384,7 @@ impl<'help, 'app> Parser<'help, 'app> { if let Some((_, Some(ref val))) = a.env { let val = RawOsStr::new(val); - if a.is_set(ArgSettings::TakesValue) { + if a.is_takes_value_set() { debug!( "Parser::add_env: Found an opt with value={:?}, trailing={:?}", val, trailing_values @@ -1479,7 +1393,7 @@ impl<'help, 'app> Parser<'help, 'app> { a, &val, matcher, - ValueType::EnvVariable, + ValueSource::EnvVariable, false, trailing_values, ); @@ -1490,7 +1404,7 @@ impl<'help, 'app> Parser<'help, 'app> { // Early return on `HelpFlag` or `VersionFlag`. match self.check_for_help_and_version_str(&val) { Some(ParseResult::HelpFlag) => { - return Err(self.help_err(true)); + return Err(self.help_err(true, Stream::Stdout)); } Some(ParseResult::VersionFlag) => { return Err(self.version_err(true)); @@ -1502,7 +1416,7 @@ impl<'help, 'app> Parser<'help, 'app> { let predicate = str_to_bool(val.to_str_lossy()); debug!("Parser::add_env: Found boolean literal `{}`", predicate); if predicate { - matcher.add_index_to(&a.id, self.cur_idx.get(), ValueType::EnvVariable); + matcher.add_index_to(&a.id, self.cur_idx.get(), ValueSource::EnvVariable); } } @@ -1517,14 +1431,14 @@ impl<'help, 'app> Parser<'help, 'app> { matcher.inc_occurrence_of_arg(arg); // Increment or create the group "args" - for group in self.app.groups_for_arg(&arg.id) { + for group in self.cmd.groups_for_arg(&arg.id) { matcher.inc_occurrence_of_group(&group); } } } // Error, Help, and Version Methods -impl<'help, 'app> Parser<'help, 'app> { +impl<'help, 'cmd> Parser<'help, 'cmd> { /// Is only used for the long flag(which is the only one needs fuzzy searching) fn did_you_mean_error( &mut self, @@ -1535,8 +1449,8 @@ impl<'help, 'app> Parser<'help, 'app> { debug!("Parser::did_you_mean_error: arg={}", arg); // Didn't match a flag or option let longs = self - .app - .args + .cmd + .get_keymap() .keys() .filter_map(|x| match x { KeyType::Long(l) => Some(l.to_string_lossy().into_owned()), @@ -1549,120 +1463,54 @@ impl<'help, 'app> Parser<'help, 'app> { arg, remaining_args, longs.iter().map(|x| &x[..]), - self.app.subcommands.as_mut_slice(), + self.cmd.get_subcommands_mut(), ); // Add the arg to the matches to build a proper usage string if let Some((name, _)) = did_you_mean.as_ref() { - if let Some(opt) = self.app.args.get(&name.as_ref()) { + if let Some(opt) = self.cmd.get_keymap().get(&name.as_ref()) { self.inc_occurrence_of_arg(matcher, opt); } } + let required = self.cmd.required_graph(); let used: Vec = matcher .arg_names() - .filter(|n| { - self.app.find(n).map_or(true, |a| { - !(self.required.contains(&a.id) || a.is_set(ArgSettings::Hidden)) - }) - }) + .filter(|n| self.cmd.find(n).map_or(true, |a| !a.is_hide_set())) .cloned() .collect(); ClapError::unknown_argument( - self.app, + self.cmd, format!("--{}", arg), did_you_mean, - Usage::new(self).create_usage_with_title(&*used), + Usage::new(self.cmd) + .required(&required) + .create_usage_with_title(&*used), ) } - pub(crate) fn write_help_err(&self) -> ClapResult { - let mut c = Colorizer::new(true, self.color_help()); - Help::new(HelpWriter::Buffer(&mut c), self, false).write_help()?; - Ok(c) - } - - fn help_err(&self, mut use_long: bool) -> ClapError { - debug!( - "Parser::help_err: use_long={:?}", - use_long && self.use_long_help() - ); - - use_long = use_long && self.use_long_help(); - let mut c = Colorizer::new(false, self.color_help()); - - match Help::new(HelpWriter::Buffer(&mut c), self, use_long).write_help() { - Err(e) => e.into(), - _ => ClapError::new( - c, - ErrorKind::DisplayHelp, - self.app.settings.is_set(AS::WaitOnError), - ), + fn help_err(&self, use_long: bool, stream: Stream) -> ClapError { + match self.cmd.write_help_err(use_long, stream) { + Ok(c) => ClapError::display_help(self.cmd, c), + Err(e) => e, } } fn version_err(&self, use_long: bool) -> ClapError { debug!("Parser::version_err"); - let msg = self.app._render_version(use_long); - let mut c = Colorizer::new(false, self.color_help()); + let msg = self.cmd._render_version(use_long); + let mut c = Colorizer::new(Stream::Stdout, self.cmd.color_help()); c.none(msg); - ClapError::new( - c, - ErrorKind::DisplayVersion, - self.app.settings.is_set(AS::WaitOnError), - ) + ClapError::display_version(self.cmd, c) } } // Query Methods -impl<'help, 'app> Parser<'help, 'app> { +impl<'help, 'cmd> Parser<'help, 'cmd> { pub(crate) fn is_set(&self, s: AS) -> bool { - self.app.is_set(s) - } -} - -#[derive(Debug)] -pub(crate) struct Input { - items: Vec, - cursor: usize, -} - -impl From for Input -where - I: Iterator, - T: Into + Clone, -{ - fn from(val: I) -> Self { - Self { - items: val.map(|x| x.into()).collect(), - cursor: 0, - } - } -} - -impl Input { - pub(crate) fn next(&mut self) -> Option<(&OsStr, &[OsString])> { - if self.cursor >= self.items.len() { - None - } else { - let current = &self.items[self.cursor]; - self.cursor += 1; - let remaining = &self.items[self.cursor..]; - Some((current, remaining)) - } - } - - /// Insert some items to the Input items just after current parsing cursor. - /// Usually used by replaced items recovering. - pub(crate) fn insert(&mut self, insert_items: &[&str]) { - self.items = insert_items - .iter() - .map(OsString::from) - .chain(self.items.drain(self.cursor..)) - .collect(); - self.cursor = 0; + self.cmd.is_set(s) } } diff --git a/third_party/rust/clap/src/parse/validator.rs b/third_party/rust/clap/src/parse/validator.rs index 7e92ab206fcd..2e2766f65154 100644 --- a/third_party/rust/clap/src/parse/validator.rs +++ b/third_party/rust/clap/src/parse/validator.rs @@ -1,75 +1,90 @@ // Internal -use crate::{ - build::{arg::PossibleValue, App, AppSettings as AS, Arg, ArgSettings}, - output::Usage, - parse::{ - errors::{Error, ErrorKind, Result as ClapResult}, - ArgMatcher, MatchedArg, ParseState, Parser, - }, - util::Id, - INTERNAL_ERROR_MSG, INVALID_UTF8, -}; +use crate::build::{AppSettings, Arg, ArgPredicate, Command, PossibleValue}; +use crate::error::{Error, Result as ClapResult}; +use crate::output::fmt::Stream; +use crate::output::Usage; +use crate::parse::{ArgMatcher, MatchedArg, ParseState}; +use crate::util::ChildGraph; +use crate::util::Id; +use crate::{INTERNAL_ERROR_MSG, INVALID_UTF8}; -pub(crate) struct Validator<'help, 'app, 'parser> { - p: &'parser mut Parser<'help, 'app>, +pub(crate) struct Validator<'help, 'cmd> { + cmd: &'cmd Command<'help>, + required: ChildGraph, } -impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> { - pub(crate) fn new(p: &'parser mut Parser<'help, 'app>) -> Self { - Validator { p } +impl<'help, 'cmd> Validator<'help, 'cmd> { + pub(crate) fn new(cmd: &'cmd Command<'help>) -> Self { + let required = cmd.required_graph(); + Validator { cmd, required } } pub(crate) fn validate( &mut self, parse_state: ParseState, - is_subcmd: bool, matcher: &mut ArgMatcher, - trailing_values: bool, ) -> ClapResult<()> { debug!("Validator::validate"); - let mut reqs_validated = false; - - #[cfg(feature = "env")] - self.p.add_env(matcher, trailing_values)?; - - self.p.add_defaults(matcher, trailing_values); + let mut conflicts = Conflicts::new(); + let has_subcmd = matcher.subcommand_name().is_some(); if let ParseState::Opt(a) = parse_state { debug!("Validator::validate: needs_val_of={:?}", a); - self.validate_required(matcher)?; - self.validate_required_unless(matcher)?; - let o = &self.p.app[&a]; - reqs_validated = true; - let should_err = if let Some(v) = matcher.0.args.get(&o.id) { + let o = &self.cmd[&a]; + let should_err = if let Some(v) = matcher.args.get(&o.id) { v.all_val_groups_empty() && !(o.min_vals.is_some() && o.min_vals.unwrap() == 0) } else { true }; if should_err { return Err(Error::empty_value( - self.p.app, + self.cmd, + &o.possible_vals + .iter() + .filter(|pv| !pv.is_hide_set()) + .map(PossibleValue::get_name) + .collect::>(), o, - Usage::new(self.p).create_usage_with_title(&[]), + Usage::new(self.cmd) + .required(&self.required) + .create_usage_with_title(&[]), )); } } - if matcher.is_empty() - && matcher.subcommand_name().is_none() - && self.p.is_set(AS::ArgRequiredElseHelp) - { - let message = self.p.write_help_err()?; - return Err(Error::new( - message, - ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand, - self.p.is_set(AS::WaitOnError), - )); + if !has_subcmd && self.cmd.is_arg_required_else_help_set() { + let num_user_values = matcher + .arg_names() + .filter(|arg_id| matcher.check_explicit(arg_id, ArgPredicate::IsPresent)) + .count(); + if num_user_values == 0 { + let message = self.cmd.write_help_err(false, Stream::Stderr)?; + return Err(Error::display_help_error(self.cmd, message)); + } } - self.validate_conflicts(matcher)?; - if !(self.p.is_set(AS::SubcommandsNegateReqs) && is_subcmd || reqs_validated) { - self.validate_required(matcher)?; - self.validate_required_unless(matcher)?; + #[allow(deprecated)] + if !has_subcmd && self.cmd.is_subcommand_required_set() { + let bn = self + .cmd + .get_bin_name() + .unwrap_or_else(|| self.cmd.get_name()); + return Err(Error::missing_subcommand( + self.cmd, + bn.to_string(), + Usage::new(self.cmd) + .required(&self.required) + .create_usage_with_title(&[]), + )); + } else if !has_subcmd && self.cmd.is_set(AppSettings::SubcommandRequiredElseHelp) { + debug!("Validator::new::get_matches_with: SubcommandRequiredElseHelp=true"); + let message = self.cmd.write_help_err(false, Stream::Stderr)?; + return Err(Error::display_help_error(self.cmd, message)); + } + + self.validate_conflicts(matcher, &mut conflicts)?; + if !(self.cmd.is_subcommand_negates_reqs_set() && has_subcmd) { + self.validate_required(matcher, &mut conflicts)?; } self.validate_matched_args(matcher)?; @@ -84,14 +99,16 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> { ) -> ClapResult<()> { debug!("Validator::validate_arg_values: arg={:?}", arg.name); for val in ma.vals_flatten() { - if !arg.is_set(ArgSettings::AllowInvalidUtf8) && val.to_str().is_none() { + if !arg.is_allow_invalid_utf8_set() && val.to_str().is_none() { debug!( "Validator::validate_arg_values: invalid UTF-8 found in val {:?}", val ); return Err(Error::invalid_utf8( - self.p.app, - Usage::new(self.p).create_usage_with_title(&[]), + self.cmd, + Usage::new(self.cmd) + .required(&self.required) + .create_usage_with_title(&[]), )); } if !arg.possible_vals.is_empty() { @@ -103,38 +120,42 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> { let ok = arg .possible_vals .iter() - .any(|pv| pv.matches(&val_str, arg.is_set(ArgSettings::IgnoreCase))); + .any(|pv| pv.matches(&val_str, arg.is_ignore_case_set())); if !ok { let used: Vec = matcher .arg_names() - .filter(|&n| { - self.p.app.find(n).map_or(true, |a| { - !(a.is_set(ArgSettings::Hidden) || self.p.required.contains(&a.id)) - }) - }) + .filter(|arg_id| matcher.check_explicit(arg_id, ArgPredicate::IsPresent)) + .filter(|&n| self.cmd.find(n).map_or(true, |a| !a.is_hide_set())) .cloned() .collect(); return Err(Error::invalid_value( - self.p.app, + self.cmd, val_str.into_owned(), &arg.possible_vals .iter() - .filter_map(PossibleValue::get_visible_name) + .filter(|pv| !pv.is_hide_set()) + .map(PossibleValue::get_name) .collect::>(), arg, - Usage::new(self.p).create_usage_with_title(&used), + Usage::new(self.cmd) + .required(&self.required) + .create_usage_with_title(&used), )); } } - if arg.is_set(ArgSettings::ForbidEmptyValues) - && val.is_empty() - && matcher.contains(&arg.id) - { + if arg.is_forbid_empty_values_set() && val.is_empty() && matcher.contains(&arg.id) { debug!("Validator::validate_arg_values: illegal empty val found"); return Err(Error::empty_value( - self.p.app, + self.cmd, + &arg.possible_vals + .iter() + .filter(|pv| !pv.is_hide_set()) + .map(PossibleValue::get_name) + .collect::>(), arg, - Usage::new(self.p).create_usage_with_title(&[]), + Usage::new(self.cmd) + .required(&self.required) + .create_usage_with_title(&[]), )); } @@ -144,11 +165,11 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> { if let Err(e) = vtor(&*val.to_string_lossy()) { debug!("error"); return Err(Error::value_validation( - self.p.app, arg.to_string(), val.to_string_lossy().into_owned(), e, - )); + ) + .with_cmd(self.cmd)); } else { debug!("good"); } @@ -159,11 +180,11 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> { if let Err(e) = vtor(val) { debug!("error"); return Err(Error::value_validation( - self.p.app, arg.to_string(), val.to_string_lossy().into(), e, - )); + ) + .with_cmd(self.cmd)); } else { debug!("good"); } @@ -172,18 +193,22 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> { Ok(()) } - fn validate_conflicts(&self, matcher: &ArgMatcher) -> ClapResult<()> { + fn validate_conflicts( + &mut self, + matcher: &ArgMatcher, + conflicts: &mut Conflicts, + ) -> ClapResult<()> { debug!("Validator::validate_conflicts"); self.validate_exclusive(matcher)?; - let mut conflicts = Conflicts::new(); for arg_id in matcher .arg_names() - .filter(|arg_id| matcher.contains_explicit(arg_id) && self.p.app.find(arg_id).is_some()) + .filter(|arg_id| matcher.check_explicit(arg_id, ArgPredicate::IsPresent)) + .filter(|arg_id| self.cmd.find(arg_id).is_some()) { debug!("Validator::validate_conflicts::iter: id={:?}", arg_id); - let conflicts = conflicts.gather_conflicts(self.p.app, matcher, arg_id); + let conflicts = conflicts.gather_conflicts(self.cmd, matcher, arg_id); self.build_conflict_err(arg_id, &conflicts, matcher)?; } @@ -192,24 +217,31 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> { fn validate_exclusive(&self, matcher: &ArgMatcher) -> ClapResult<()> { debug!("Validator::validate_exclusive"); + // Not bothering to filter for `check_explicit` since defaults shouldn't play into this let args_count = matcher.arg_names().count(); + if args_count <= 1 { + // Nothing present to conflict with + return Ok(()); + } + matcher .arg_names() .filter_map(|name| { debug!("Validator::validate_exclusive:iter:{:?}", name); - self.p - .app + self.cmd .find(name) // Find `arg`s which are exclusive but also appear with other args. - .filter(|&arg| arg.is_set(ArgSettings::Exclusive) && args_count > 1) + .filter(|&arg| arg.is_exclusive_set() && args_count > 1) }) // Throw an error for the first conflict found. .try_for_each(|arg| { Err(Error::argument_conflict( - self.p.app, + self.cmd, arg, Vec::new(), - Usage::new(self.p).create_usage_with_title(&[]), + Usage::new(self.cmd) + .required(&self.required) + .create_usage_with_title(&[]), )) }) } @@ -229,56 +261,71 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> { let conflicts = conflict_ids .iter() .flat_map(|c_id| { - if self.p.app.find_group(c_id).is_some() { - self.p.app.unroll_args_in_group(c_id) + if self.cmd.find_group(c_id).is_some() { + self.cmd.unroll_args_in_group(c_id) } else { vec![c_id.clone()] } }) .filter_map(|c_id| { seen.insert(c_id.clone()).then(|| { - let c_arg = self.p.app.find(&c_id).expect(INTERNAL_ERROR_MSG); + let c_arg = self.cmd.find(&c_id).expect(INTERNAL_ERROR_MSG); c_arg.to_string() }) }) .collect(); - let former_arg = self.p.app.find(name).expect(INTERNAL_ERROR_MSG); + let former_arg = self.cmd.find(name).expect(INTERNAL_ERROR_MSG); let usg = self.build_conflict_err_usage(matcher, conflict_ids); Err(Error::argument_conflict( - self.p.app, former_arg, conflicts, usg, + self.cmd, former_arg, conflicts, usg, )) } fn build_conflict_err_usage(&self, matcher: &ArgMatcher, conflicting_keys: &[Id]) -> String { let used_filtered: Vec = matcher .arg_names() + .filter(|arg_id| matcher.check_explicit(arg_id, ArgPredicate::IsPresent)) + .filter(|n| { + // Filter out the args we don't want to specify. + self.cmd.find(n).map_or(true, |a| !a.is_hide_set()) + }) .filter(|key| !conflicting_keys.contains(key)) .cloned() .collect(); let required: Vec = used_filtered .iter() - .filter_map(|key| self.p.app.find(key)) + .filter_map(|key| self.cmd.find(key)) .flat_map(|arg| arg.requires.iter().map(|item| &item.1)) .filter(|key| !used_filtered.contains(key) && !conflicting_keys.contains(key)) .chain(used_filtered.iter()) .cloned() .collect(); - Usage::new(self.p).create_usage_with_title(&required) + Usage::new(self.cmd) + .required(&self.required) + .create_usage_with_title(&required) } - fn gather_requirements(&mut self, matcher: &ArgMatcher) { - debug!("Validator::gather_requirements"); - for name in matcher.arg_names() { - debug!("Validator::gather_requirements:iter:{:?}", name); - if let Some(arg) = self.p.app.find(name) { - for req in self.p.app.unroll_requirements_for_arg(&arg.id, matcher) { - self.p.required.insert(req); + fn gather_requires(&mut self, matcher: &ArgMatcher) { + debug!("Validator::gather_requires"); + for name in matcher + .arg_names() + .filter(|arg_id| matcher.check_explicit(arg_id, ArgPredicate::IsPresent)) + { + debug!("Validator::gather_requires:iter:{:?}", name); + if let Some(arg) = self.cmd.find(name) { + let is_relevant = |(val, req_arg): &(ArgPredicate<'_>, Id)| -> Option { + let required = matcher.check_explicit(&arg.id, *val); + required.then(|| req_arg.clone()) + }; + + for req in self.cmd.unroll_arg_requires(is_relevant, &arg.id) { + self.required.insert(req); } - } else if let Some(g) = self.p.app.find_group(name) { - debug!("Validator::gather_requirements:iter:{:?}:group", name); + } else if let Some(g) = self.cmd.find_group(name) { + debug!("Validator::gather_requires:iter:{:?}:group", name); for r in &g.requires { - self.p.required.insert(r.clone()); + self.required.insert(r.clone()); } } } @@ -292,7 +339,7 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> { name, ma.vals_flatten() ); - if let Some(arg) = self.p.app.find(name) { + if let Some(arg) = self.cmd.find(name) { self.validate_arg_num_vals(arg, ma)?; self.validate_arg_values(arg, ma, matcher)?; self.validate_arg_num_occurs(arg, ma)?; @@ -304,16 +351,19 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> { fn validate_arg_num_occurs(&self, a: &Arg, ma: &MatchedArg) -> ClapResult<()> { debug!( "Validator::validate_arg_num_occurs: {:?}={}", - a.name, ma.occurs + a.name, + ma.get_occurrences() ); // Occurrence of positional argument equals to number of values rather // than number of grouped values. - if ma.occurs > 1 && !a.is_set(ArgSettings::MultipleOccurrences) && !a.is_positional() { + if ma.get_occurrences() > 1 && !a.is_multiple_occurrences_set() && !a.is_positional() { // Not the first time, and we don't allow multiples return Err(Error::unexpected_multiple_usage( - self.p.app, + self.cmd, a, - Usage::new(self.p).create_usage_with_title(&[]), + Usage::new(self.cmd) + .required(&self.required) + .create_usage_with_title(&[]), )); } if let Some(max_occurs) = a.max_occurs { @@ -321,14 +371,16 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> { "Validator::validate_arg_num_occurs: max_occurs set...{}", max_occurs ); - let occurs = ma.occurs as usize; + let occurs = ma.get_occurrences() as usize; if occurs > max_occurs { return Err(Error::too_many_occurrences( - self.p.app, + self.cmd, a, max_occurs, occurs, - Usage::new(self.p).create_usage_with_title(&[]), + Usage::new(self.cmd) + .required(&self.required) + .create_usage_with_title(&[]), )); } } @@ -341,7 +393,7 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> { if let Some(num) = a.num_vals { let total_num = ma.num_vals(); debug!("Validator::validate_arg_num_vals: num_vals set...{}", num); - let should_err = if a.is_set(ArgSettings::MultipleOccurrences) { + let should_err = if a.is_multiple_occurrences_set() { total_num % num != 0 } else { num != total_num @@ -349,15 +401,17 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> { if should_err { debug!("Validator::validate_arg_num_vals: Sending error WrongNumberOfValues"); return Err(Error::wrong_number_of_values( - self.p.app, + self.cmd, a, num, - if a.is_set(ArgSettings::MultipleOccurrences) { + if a.is_multiple_occurrences_set() { total_num % num } else { total_num }, - Usage::new(self.p).create_usage_with_title(&[]), + Usage::new(self.cmd) + .required(&self.required) + .create_usage_with_title(&[]), )); } } @@ -366,7 +420,7 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> { if ma.num_vals() > num { debug!("Validator::validate_arg_num_vals: Sending error TooManyValues"); return Err(Error::too_many_values( - self.p.app, + self.cmd, ma.vals_flatten() .last() .expect(INTERNAL_ERROR_MSG) @@ -374,7 +428,9 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> { .expect(INVALID_UTF8) .to_string(), a.to_string(), - Usage::new(self.p).create_usage_with_title(&[]), + Usage::new(self.cmd) + .required(&self.required) + .create_usage_with_title(&[]), )); } } @@ -383,11 +439,13 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> { if ma.num_vals() < num && num != 0 { debug!("Validator::validate_arg_num_vals: Sending error TooFewValues"); return Err(Error::too_few_values( - self.p.app, + self.cmd, a, num, ma.num_vals(), - Usage::new(self.p).create_usage_with_title(&[]), + Usage::new(self.cmd) + .required(&self.required) + .create_usage_with_title(&[]), )); } num == 0 @@ -396,35 +454,53 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> { }; // Issue 665 (https://github.com/clap-rs/clap/issues/665) // Issue 1105 (https://github.com/clap-rs/clap/issues/1105) - if a.is_set(ArgSettings::TakesValue) && !min_vals_zero && ma.all_val_groups_empty() { + if a.is_takes_value_set() && !min_vals_zero && ma.all_val_groups_empty() { return Err(Error::empty_value( - self.p.app, + self.cmd, + &a.possible_vals + .iter() + .filter(|pv| !pv.is_hide_set()) + .map(PossibleValue::get_name) + .collect::>(), a, - Usage::new(self.p).create_usage_with_title(&[]), + Usage::new(self.cmd) + .required(&self.required) + .create_usage_with_title(&[]), )); } Ok(()) } - fn validate_required(&mut self, matcher: &ArgMatcher) -> ClapResult<()> { - debug!( - "Validator::validate_required: required={:?}", - self.p.required - ); - self.gather_requirements(matcher); + fn validate_required( + &mut self, + matcher: &ArgMatcher, + conflicts: &mut Conflicts, + ) -> ClapResult<()> { + debug!("Validator::validate_required: required={:?}", self.required); + self.gather_requires(matcher); - for arg_or_group in self.p.required.iter().filter(|r| !matcher.contains(r)) { + let is_exclusive_present = matcher.arg_names().any(|name| { + self.cmd + .find(name) + .map(|arg| arg.is_exclusive_set()) + .unwrap_or_default() + }); + debug!( + "Validator::validate_required: is_exclusive_present={}", + is_exclusive_present + ); + + for arg_or_group in self.required.iter().filter(|r| !matcher.contains(r)) { debug!("Validator::validate_required:iter:aog={:?}", arg_or_group); - if let Some(arg) = self.p.app.find(arg_or_group) { + if let Some(arg) = self.cmd.find(arg_or_group) { debug!("Validator::validate_required:iter: This is an arg"); - if !self.is_missing_required_ok(arg, matcher) { + if !is_exclusive_present && !self.is_missing_required_ok(arg, matcher, conflicts) { return self.missing_required_error(matcher, vec![]); } - } else if let Some(group) = self.p.app.find_group(arg_or_group) { + } else if let Some(group) = self.cmd.find_group(arg_or_group) { debug!("Validator::validate_required:iter: This is a group"); if !self - .p - .app + .cmd .unroll_args_in_group(&group.id) .iter() .any(|a| matcher.contains(a)) @@ -435,50 +511,44 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> { } // Validate the conditionally required args - for a in self.p.app.args.args() { + for a in self.cmd.get_arguments() { for (other, val) in &a.r_ifs { - if let Some(ma) = matcher.get(other) { - if ma.contains_val(val) && !matcher.contains(&a.id) { - return self.missing_required_error(matcher, vec![a.id.clone()]); - } + if matcher.check_explicit(other, ArgPredicate::Equals(std::ffi::OsStr::new(*val))) + && !matcher.contains(&a.id) + { + return self.missing_required_error(matcher, vec![a.id.clone()]); } } - let match_all = a - .r_ifs_all - .iter() - .all(|(other, val)| matcher.get(other).map_or(false, |ma| ma.contains_val(val))); + let match_all = a.r_ifs_all.iter().all(|(other, val)| { + matcher.check_explicit(other, ArgPredicate::Equals(std::ffi::OsStr::new(*val))) + }); if match_all && !a.r_ifs_all.is_empty() && !matcher.contains(&a.id) { return self.missing_required_error(matcher, vec![a.id.clone()]); } } + + self.validate_required_unless(matcher)?; + Ok(()) } - fn is_missing_required_ok(&self, a: &Arg<'help>, matcher: &ArgMatcher) -> bool { + fn is_missing_required_ok( + &self, + a: &Arg<'help>, + matcher: &ArgMatcher, + conflicts: &mut Conflicts, + ) -> bool { debug!("Validator::is_missing_required_ok: {}", a.name); - self.validate_arg_conflicts(a, matcher) || self.p.overridden.borrow().contains(&a.id) - } - - fn validate_arg_conflicts(&self, a: &Arg<'help>, matcher: &ArgMatcher) -> bool { - debug!("Validator::validate_arg_conflicts: a={:?}", a.name); - a.blacklist.iter().any(|conf| { - matcher.contains(conf) - || self - .p - .app - .find_group(conf) - .map_or(false, |g| g.args.iter().any(|arg| matcher.contains(arg))) - }) + let conflicts = conflicts.gather_conflicts(self.cmd, matcher, &a.id); + !conflicts.is_empty() } fn validate_required_unless(&self, matcher: &ArgMatcher) -> ClapResult<()> { debug!("Validator::validate_required_unless"); let failed_args: Vec<_> = self - .p - .app - .args - .args() + .cmd + .get_arguments() .filter(|&a| { (!a.r_unless.is_empty() || !a.r_unless_all.is_empty()) && !matcher.contains(&a.id) @@ -496,7 +566,7 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> { // Failing a required unless means, the arg's "unless" wasn't present, and neither were they fn fails_arg_required_unless(&self, a: &Arg<'help>, matcher: &ArgMatcher) -> bool { debug!("Validator::fails_arg_required_unless: a={:?}", a.name); - let exists = |id| matcher.contains(id); + let exists = |id| matcher.check_explicit(id, ArgPredicate::IsPresent); (a.r_unless_all.is_empty() || !a.r_unless_all.iter().all(exists)) && !a.r_unless.iter().any(exists) @@ -507,12 +577,15 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> { debug!("Validator::missing_required_error; incl={:?}", incl); debug!( "Validator::missing_required_error: reqs={:?}", - self.p.required + self.required ); - let usg = Usage::new(self.p); + let usg = Usage::new(self.cmd).required(&self.required); - let req_args = usg.get_required_usage_from(&incl, Some(matcher), true); + let req_args = usg + .get_required_usage_from(&incl, Some(matcher), true) + .into_iter() + .collect::>(); debug!( "Validator::missing_required_error: req_args={:#?}", @@ -521,20 +594,17 @@ impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> { let used: Vec = matcher .arg_names() + .filter(|arg_id| matcher.check_explicit(arg_id, ArgPredicate::IsPresent)) .filter(|n| { // Filter out the args we don't want to specify. - self.p.app.find(n).map_or(true, |a| { - !a.is_set(ArgSettings::Hidden) - && a.default_vals.is_empty() - && !self.p.required.contains(&a.id) - }) + self.cmd.find(n).map_or(true, |a| !a.is_hide_set()) }) .cloned() .chain(incl) .collect(); Err(Error::missing_required_argument( - self.p.app, + self.cmd, req_args, usg.create_usage_with_title(&used), )) @@ -551,39 +621,40 @@ impl Conflicts { Self::default() } - fn gather_conflicts(&mut self, app: &App, matcher: &ArgMatcher, arg_id: &Id) -> Vec { - debug!("Conflicts::gather_conflicts"); + fn gather_conflicts(&mut self, cmd: &Command, matcher: &ArgMatcher, arg_id: &Id) -> Vec { + debug!("Conflicts::gather_conflicts: arg={:?}", arg_id); let mut conflicts = Vec::new(); for other_arg_id in matcher .arg_names() - .filter(|arg_id| matcher.contains_explicit(arg_id)) + .filter(|arg_id| matcher.check_explicit(arg_id, ArgPredicate::IsPresent)) { if arg_id == other_arg_id { continue; } if self - .gather_direct_conflicts(app, arg_id) + .gather_direct_conflicts(cmd, arg_id) .contains(other_arg_id) { conflicts.push(other_arg_id.clone()); } if self - .gather_direct_conflicts(app, other_arg_id) + .gather_direct_conflicts(cmd, other_arg_id) .contains(arg_id) { conflicts.push(other_arg_id.clone()); } } + debug!("Conflicts::gather_conflicts: conflicts={:?}", conflicts); conflicts } - fn gather_direct_conflicts(&mut self, app: &App, arg_id: &Id) -> &[Id] { + fn gather_direct_conflicts(&mut self, cmd: &Command, arg_id: &Id) -> &[Id] { self.potential.entry(arg_id.clone()).or_insert_with(|| { - let conf = if let Some(arg) = app.find(arg_id) { + let conf = if let Some(arg) = cmd.find(arg_id) { let mut conf = arg.blacklist.clone(); - for group_id in app.groups_for_arg(arg_id) { - let group = app.find_group(&group_id).expect(INTERNAL_ERROR_MSG); + for group_id in cmd.groups_for_arg(arg_id) { + let group = cmd.find_group(&group_id).expect(INTERNAL_ERROR_MSG); conf.extend(group.conflicts.iter().cloned()); if !group.multiple { for member_id in &group.args { @@ -593,8 +664,12 @@ impl Conflicts { } } } + + // Overrides are implicitly conflicts + conf.extend(arg.overrides.iter().cloned()); + conf - } else if let Some(group) = app.find_group(arg_id) { + } else if let Some(group) = cmd.find_group(arg_id) { group.conflicts.clone() } else { debug_assert!(false, "id={:?} is unknown", arg_id); diff --git a/third_party/rust/clap/src/util/color.rs b/third_party/rust/clap/src/util/color.rs index 272a6c91688f..15c9901a079b 100644 --- a/third_party/rust/clap/src/util/color.rs +++ b/third_party/rust/clap/src/util/color.rs @@ -13,8 +13,8 @@ pub enum ColorChoice { /// #[cfg_attr(not(feature = "color"), doc = " ```ignore")] #[cfg_attr(feature = "color", doc = " ```no_run")] - /// # use clap::{App, ColorChoice}; - /// App::new("myprog") + /// # use clap::{Command, ColorChoice}; + /// Command::new("myprog") /// .color(ColorChoice::Auto) /// .get_matches(); /// ``` @@ -30,8 +30,8 @@ pub enum ColorChoice { /// #[cfg_attr(not(feature = "color"), doc = " ```ignore")] #[cfg_attr(feature = "color", doc = " ```no_run")] - /// # use clap::{App, ColorChoice}; - /// App::new("myprog") + /// # use clap::{Command, ColorChoice}; + /// Command::new("myprog") /// .color(ColorChoice::Always) /// .get_matches(); /// ``` @@ -47,8 +47,8 @@ pub enum ColorChoice { /// #[cfg_attr(not(feature = "color"), doc = " ```ignore")] #[cfg_attr(feature = "color", doc = " ```no_run")] - /// # use clap::{App, ColorChoice}; - /// App::new("myprog") + /// # use clap::{Command, ColorChoice}; + /// Command::new("myprog") /// .color(ColorChoice::Never) /// .get_matches(); /// ``` diff --git a/third_party/rust/clap_lex/.cargo-checksum.json b/third_party/rust/clap_lex/.cargo-checksum.json new file mode 100644 index 000000000000..f1eeaae84215 --- /dev/null +++ b/third_party/rust/clap_lex/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"1551ef7db77ad698c6afe7102648d7de8747d873e8c8da4ba0dd8264ac09cbf8","LICENSE-APACHE":"c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4","LICENSE-MIT":"6725d1437fc6c77301f2ff0e7d52914cf4f9509213e1078dc77d9356dbe6eac5","README.md":"5de0646bd99b085f12e86777bc18e6a38731b577e6550d92de7dc1023bf265ca","src/lib.rs":"d812637d82a2c89bc2e85deefc342727e746a8d4354d574bd8586485c5eca958"},"package":"a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213"} \ No newline at end of file diff --git a/third_party/rust/clap_lex/Cargo.toml b/third_party/rust/clap_lex/Cargo.toml new file mode 100644 index 000000000000..bbb41ad981ad --- /dev/null +++ b/third_party/rust/clap_lex/Cargo.toml @@ -0,0 +1,84 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2018" +name = "clap_lex" +version = "0.2.0" +include = [ + "src/**/*", + "Cargo.toml", + "LICENSE-*", + "README.md", +] +description = "Minimal, flexible command line parser" +documentation = "https://docs.rs/clap_lex" +readme = "README.md" +keywords = [ + "argument", + "cli", + "arg", + "parser", + "parse", +] +categories = ["command-line-interface"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/clap-rs/clap/tree/master/clap_lex" + +[[package.metadata.release.pre-release-replacements]] +file = "CHANGELOG.md" +search = "Unreleased" +replace = "{{version}}" +min = 1 + +[[package.metadata.release.pre-release-replacements]] +file = "CHANGELOG.md" +search = '\.\.\.HEAD' +replace = "...{{tag_name}}" +exactly = 1 + +[[package.metadata.release.pre-release-replacements]] +file = "CHANGELOG.md" +search = "ReleaseDate" +replace = "{{date}}" +min = 1 + +[[package.metadata.release.pre-release-replacements]] +file = "CHANGELOG.md" +search = "" +replace = """ + +## [Unreleased] - ReleaseDate +""" +exactly = 1 + +[[package.metadata.release.pre-release-replacements]] +file = "CHANGELOG.md" +search = "" +replace = """ + +[Unreleased]: https://github.com/clap-rs/clap/compare/{{tag_name}}...HEAD""" +exactly = 1 + +[[package.metadata.release.pre-release-replacements]] +file = "README.md" +search = "github.com/clap-rs/clap/blob/[^/]+/" +replace = "github.com/clap-rs/clap/blob/{{tag_name}}/" +exactly = 4 +prerelease = true + +[lib] +bench = false + +[dependencies.os_str_bytes] +version = "6.0" +features = ["raw_os_str"] +default-features = false diff --git a/third_party/rust/clap_lex/LICENSE-APACHE b/third_party/rust/clap_lex/LICENSE-APACHE new file mode 100644 index 000000000000..261eeb9e9f8b --- /dev/null +++ b/third_party/rust/clap_lex/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/third_party/rust/clap_lex/LICENSE-MIT b/third_party/rust/clap_lex/LICENSE-MIT new file mode 100644 index 000000000000..5acedf041221 --- /dev/null +++ b/third_party/rust/clap_lex/LICENSE-MIT @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015-2016 Kevin B. Knapp + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/third_party/rust/clap_lex/README.md b/third_party/rust/clap_lex/README.md new file mode 100644 index 000000000000..374dc48a41b0 --- /dev/null +++ b/third_party/rust/clap_lex/README.md @@ -0,0 +1,19 @@ + +# clap_lex + +> **Minimal, flexible command line parser** + +[![Crates.io](https://img.shields.io/crates/v/clap_lex?style=flat-square)](https://crates.io/crates/clap_lex) +[![Crates.io](https://img.shields.io/crates/d/clap_lex?style=flat-square)](https://crates.io/crates/clap_lex) +[![License](https://img.shields.io/badge/license-Apache%202.0-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/clap_lex-v0.2.0/LICENSE-APACHE) +[![License](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](https://github.com/clap-rs/clap/blob/clap_lex-v0.2.0/LICENSE-MIT) + +Dual-licensed under [Apache 2.0](LICENSE-APACHE) or [MIT](LICENSE-MIT). + +1. [About](#about) +2. [API Reference](https://docs.rs/clap_lex) +3. [Questions & Discussions](https://github.com/clap-rs/clap/discussions) +4. [CONTRIBUTING](https://github.com/clap-rs/clap/blob/clap_lex-v0.2.0/clap_lex/CONTRIBUTING.md) +5. [Sponsors](https://github.com/clap-rs/clap/blob/clap_lex-v0.2.0/README.md#sponsors) + +## About diff --git a/third_party/rust/clap_lex/src/lib.rs b/third_party/rust/clap_lex/src/lib.rs new file mode 100644 index 000000000000..f1c8da35be41 --- /dev/null +++ b/third_party/rust/clap_lex/src/lib.rs @@ -0,0 +1,479 @@ +//! Minimal, flexible command-line parser +//! +//! As opposed to a declarative parser, this processes arguments as a stream of tokens. As lexing +//! a command-line is not context-free, we rely on the caller to decide how to interpret the +//! arguments. +//! +//! # Examples +//! +//! ```rust +//! # use std::path::PathBuf; +//! # type BoxedError = Box; +//! #[derive(Debug)] +//! struct Args { +//! paths: Vec, +//! color: Color, +//! verbosity: usize, +//! } +//! +//! #[derive(Debug)] +//! enum Color { +//! Always, +//! Auto, +//! Never, +//! } +//! +//! impl Color { +//! fn parse(s: Option<&clap_lex::RawOsStr>) -> Result { +//! let s = s.map(|s| s.to_str().ok_or(s)); +//! match s { +//! Some(Ok("always")) | Some(Ok("")) | None => { +//! Ok(Color::Always) +//! } +//! Some(Ok("auto")) => { +//! Ok(Color::Auto) +//! } +//! Some(Ok("never")) => { +//! Ok(Color::Never) +//! } +//! Some(invalid) => { +//! Err(format!("Invalid value for `--color`, {:?}", invalid).into()) +//! } +//! } +//! } +//! } +//! +//! fn parse_args( +//! raw: impl IntoIterator> +//! ) -> Result { +//! let mut args = Args { +//! paths: Vec::new(), +//! color: Color::Auto, +//! verbosity: 0, +//! }; +//! +//! let raw = clap_lex::RawArgs::new(raw); +//! let mut cursor = raw.cursor(); +//! raw.next(&mut cursor); // Skip the bin +//! while let Some(arg) = raw.next(&mut cursor) { +//! if arg.is_escape() { +//! args.paths.extend(raw.remaining(&mut cursor).map(PathBuf::from)); +//! } else if arg.is_stdio() { +//! args.paths.push(PathBuf::from("-")); +//! } else if let Some((long, value)) = arg.to_long() { +//! match long { +//! Ok("verbose") => { +//! if let Some(value) = value { +//! return Err(format!("`--verbose` does not take a value, got `{:?}`", value).into()); +//! } +//! args.verbosity += 1; +//! } +//! Ok("color") => { +//! args.color = Color::parse(value)?; +//! } +//! _ => { +//! return Err( +//! format!("Unexpected flag: --{}", arg.display()).into() +//! ); +//! } +//! } +//! } else if let Some(mut shorts) = arg.to_short() { +//! while let Some(short) = shorts.next_flag() { +//! match short { +//! Ok('v') => { +//! args.verbosity += 1; +//! } +//! Ok('c') => { +//! let value = shorts.next_value_os(); +//! args.color = Color::parse(value)?; +//! } +//! Ok(c) => { +//! return Err(format!("Unexpected flag: -{}", c).into()); +//! } +//! Err(e) => { +//! return Err(format!("Unexpected flag: -{}", e.to_str_lossy()).into()); +//! } +//! } +//! } +//! } else { +//! args.paths.push(PathBuf::from(arg.to_value_os().to_os_str().into_owned())); +//! } +//! } +//! +//! Ok(args) +//! } +//! +//! let args = parse_args(["bin", "--hello", "world"]); +//! println!("{:?}", args); +//! ``` + +use std::ffi::OsStr; +use std::ffi::OsString; + +pub use std::io::SeekFrom; + +pub use os_str_bytes::RawOsStr; +pub use os_str_bytes::RawOsString; + +/// Command-line arguments +#[derive(Default, Clone, Debug, PartialEq, Eq)] +pub struct RawArgs { + items: Vec, +} + +impl RawArgs { + //// Create an argument list to parse + /// + /// **NOTE:** The argument returned will be the current binary. + /// + /// # Example + /// + /// ```rust,no_run + /// # use std::path::PathBuf; + /// let raw = clap_lex::RawArgs::from_args(); + /// let mut cursor = raw.cursor(); + /// let _bin = raw.next_os(&mut cursor); + /// + /// let mut paths = raw.remaining(&mut cursor).map(PathBuf::from).collect::>(); + /// println!("{:?}", paths); + /// ``` + pub fn from_args() -> Self { + Self::new(std::env::args_os()) + } + + //// Create an argument list to parse + /// + /// # Example + /// + /// ```rust,no_run + /// # use std::path::PathBuf; + /// let raw = clap_lex::RawArgs::new(["bin", "foo.txt"]); + /// let mut cursor = raw.cursor(); + /// let _bin = raw.next_os(&mut cursor); + /// + /// let mut paths = raw.remaining(&mut cursor).map(PathBuf::from).collect::>(); + /// println!("{:?}", paths); + /// ``` + pub fn new(iter: impl IntoIterator>) -> Self { + let iter = iter.into_iter(); + Self::from(iter) + } + + /// Create a cursor for walking the arguments + /// + /// # Example + /// + /// ```rust,no_run + /// # use std::path::PathBuf; + /// let raw = clap_lex::RawArgs::new(["bin", "foo.txt"]); + /// let mut cursor = raw.cursor(); + /// let _bin = raw.next_os(&mut cursor); + /// + /// let mut paths = raw.remaining(&mut cursor).map(PathBuf::from).collect::>(); + /// println!("{:?}", paths); + /// ``` + pub fn cursor(&self) -> ArgCursor { + ArgCursor::new() + } + + /// Advance the cursor, returning the next [`ParsedArg`] + pub fn next(&self, cursor: &mut ArgCursor) -> Option> { + self.next_os(cursor).map(ParsedArg::new) + } + + /// Advance the cursor, returning a raw argument value. + pub fn next_os(&self, cursor: &mut ArgCursor) -> Option<&OsStr> { + let next = self.items.get(cursor.cursor).map(|s| s.as_os_str()); + cursor.cursor = cursor.cursor.saturating_add(1); + next + } + + /// Return the next [`ParsedArg`] + pub fn peek(&self, cursor: &ArgCursor) -> Option> { + self.peek_os(cursor).map(ParsedArg::new) + } + + /// Return a raw argument value. + pub fn peek_os(&self, cursor: &ArgCursor) -> Option<&OsStr> { + self.items.get(cursor.cursor).map(|s| s.as_os_str()) + } + + /// Return all remaining raw arguments, advancing the cursor to the end + /// + /// # Example + /// + /// ```rust,no_run + /// # use std::path::PathBuf; + /// let raw = clap_lex::RawArgs::new(["bin", "foo.txt"]); + /// let mut cursor = raw.cursor(); + /// let _bin = raw.next_os(&mut cursor); + /// + /// let mut paths = raw.remaining(&mut cursor).map(PathBuf::from).collect::>(); + /// println!("{:?}", paths); + /// ``` + pub fn remaining(&self, cursor: &mut ArgCursor) -> impl Iterator { + let remaining = self.items[cursor.cursor..].iter().map(|s| s.as_os_str()); + cursor.cursor = self.items.len(); + remaining + } + + /// Adjust the cursor's position + pub fn seek(&self, cursor: &mut ArgCursor, pos: SeekFrom) { + let pos = match pos { + SeekFrom::Start(pos) => pos, + SeekFrom::End(pos) => (self.items.len() as i64).saturating_add(pos).max(0) as u64, + SeekFrom::Current(pos) => (cursor.cursor as i64).saturating_add(pos).max(0) as u64, + }; + let pos = (pos as usize).min(self.items.len()); + cursor.cursor = pos; + } + + /// Inject arguments before the [`RawArgs::next`] + pub fn insert(&mut self, cursor: &ArgCursor, insert_items: &[&str]) { + self.items.splice( + cursor.cursor..cursor.cursor, + insert_items.iter().map(OsString::from), + ); + } +} + +impl From for RawArgs +where + I: Iterator, + T: Into, +{ + fn from(val: I) -> Self { + Self { + items: val.map(|x| x.into()).collect(), + } + } +} + +/// Position within [`RawArgs`] +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct ArgCursor { + cursor: usize, +} + +impl ArgCursor { + fn new() -> Self { + Self { cursor: 0 } + } +} + +/// Command-line Argument +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct ParsedArg<'s> { + inner: std::borrow::Cow<'s, RawOsStr>, + utf8: Option<&'s str>, +} + +impl<'s> ParsedArg<'s> { + fn new(inner: &'s OsStr) -> Self { + let utf8 = inner.to_str(); + let inner = RawOsStr::new(inner); + Self { inner, utf8 } + } + + /// Argument is length of 0 + pub fn is_empty(&self) -> bool { + self.inner.as_ref().is_empty() + } + + /// Does the argument look like a stdio argument (`-`) + pub fn is_stdio(&self) -> bool { + self.inner.as_ref() == "-" + } + + /// Does the argument look like an argument escape (`--`) + pub fn is_escape(&self) -> bool { + self.inner.as_ref() == "--" + } + + /// Does the argument look like a number + pub fn is_number(&self) -> bool { + self.to_value() + .map(|s| s.parse::().is_ok()) + .unwrap_or_default() + } + + /// Treat as a long-flag + pub fn to_long(&self) -> Option<(Result<&str, &RawOsStr>, Option<&RawOsStr>)> { + if let Some(raw) = self.utf8 { + let remainder = raw.strip_prefix("--")?; + if remainder.is_empty() { + debug_assert!(self.is_escape()); + return None; + } + + let (flag, value) = if let Some((p0, p1)) = remainder.split_once('=') { + (p0, Some(p1)) + } else { + (remainder, None) + }; + let flag = Ok(flag); + let value = value.map(RawOsStr::from_str); + Some((flag, value)) + } else { + let raw = self.inner.as_ref(); + let remainder = raw.strip_prefix("--")?; + if remainder.is_empty() { + debug_assert!(self.is_escape()); + return None; + } + + let (flag, value) = if let Some((p0, p1)) = remainder.split_once('=') { + (p0, Some(p1)) + } else { + (remainder, None) + }; + let flag = flag.to_str().ok_or(flag); + Some((flag, value)) + } + } + + /// Can treat as a long-flag + pub fn is_long(&self) -> bool { + self.inner.as_ref().starts_with("--") && !self.is_escape() + } + + /// Treat as a short-flag + pub fn to_short(&self) -> Option> { + if let Some(remainder_os) = self.inner.as_ref().strip_prefix('-') { + if remainder_os.starts_with('-') { + None + } else if remainder_os.is_empty() { + debug_assert!(self.is_stdio()); + None + } else { + let remainder = self.utf8.map(|s| &s[1..]); + Some(ShortFlags::new(remainder_os, remainder)) + } + } else { + None + } + } + + /// Can treat as a short-flag + pub fn is_short(&self) -> bool { + self.inner.as_ref().starts_with('-') + && !self.is_stdio() + && !self.inner.as_ref().starts_with("--") + } + + /// Treat as a value + /// + /// **NOTE:** May return a flag or an escape. + pub fn to_value_os(&self) -> &RawOsStr { + self.inner.as_ref() + } + + /// Treat as a value + /// + /// **NOTE:** May return a flag or an escape. + pub fn to_value(&self) -> Result<&str, &RawOsStr> { + self.utf8.ok_or_else(|| self.inner.as_ref()) + } + + /// Safely print an argument that may contain non-UTF8 content + /// + /// This may perform lossy conversion, depending on the platform. If you would like an implementation which escapes the path please use Debug instead. + pub fn display(&self) -> impl std::fmt::Display + '_ { + self.inner.to_str_lossy() + } +} + +/// Walk through short flags within a [`ParsedArg`] +#[derive(Clone, Debug)] +pub struct ShortFlags<'s> { + inner: &'s RawOsStr, + utf8_prefix: std::str::CharIndices<'s>, + invalid_suffix: Option<&'s RawOsStr>, +} + +impl<'s> ShortFlags<'s> { + fn new(inner: &'s RawOsStr, utf8: Option<&'s str>) -> Self { + let (utf8_prefix, invalid_suffix) = if let Some(utf8) = utf8 { + (utf8, None) + } else { + split_nonutf8_once(inner) + }; + let utf8_prefix = utf8_prefix.char_indices(); + Self { + inner, + utf8_prefix, + invalid_suffix, + } + } + + /// Move the iterator forward by `n` short flags + pub fn advance_by(&mut self, n: usize) -> Result<(), usize> { + for i in 0..n { + self.next().ok_or(i)?.map_err(|_| i)?; + } + Ok(()) + } + + /// No short flags left + pub fn is_empty(&self) -> bool { + self.invalid_suffix.is_none() && self.utf8_prefix.as_str().is_empty() + } + + /// Does the short flag look like a number + /// + /// Ideally call this before doing any iterator + pub fn is_number(&self) -> bool { + self.invalid_suffix.is_none() && self.utf8_prefix.as_str().parse::().is_ok() + } + + /// Advance the iterator, returning the next short flag on success + /// + /// On error, returns the invalid-UTF8 value + pub fn next_flag(&mut self) -> Option> { + if let Some((_, flag)) = self.utf8_prefix.next() { + return Some(Ok(flag)); + } + + if let Some(suffix) = self.invalid_suffix { + self.invalid_suffix = None; + return Some(Err(suffix)); + } + + None + } + + /// Advance the iterator, returning everything left as a value + pub fn next_value_os(&mut self) -> Option<&'s RawOsStr> { + if let Some((index, _)) = self.utf8_prefix.next() { + self.utf8_prefix = "".char_indices(); + self.invalid_suffix = None; + return Some(&self.inner[index..]); + } + + if let Some(suffix) = self.invalid_suffix { + self.invalid_suffix = None; + return Some(suffix); + } + + None + } +} + +impl<'s> Iterator for ShortFlags<'s> { + type Item = Result; + + fn next(&mut self) -> Option { + self.next_flag() + } +} + +fn split_nonutf8_once(b: &RawOsStr) -> (&str, Option<&RawOsStr>) { + match std::str::from_utf8(b.as_raw_bytes()) { + Ok(s) => (s, None), + Err(err) => { + let (valid, after_valid) = b.split_at(err.valid_up_to()); + let valid = std::str::from_utf8(valid.as_raw_bytes()).unwrap(); + (valid, Some(after_valid)) + } + } +} diff --git a/third_party/rust/textwrap/.cargo-checksum.json b/third_party/rust/textwrap/.cargo-checksum.json index 969ca7a12652..d415b14476cf 100644 --- a/third_party/rust/textwrap/.cargo-checksum.json +++ b/third_party/rust/textwrap/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"CHANGELOG.md":"02783800b8187cfd90e09a1362be4381748f29a96adc0157c06833d2c7f4ac4e","Cargo.toml":"5d55b1e1537d331df5ab2ffb81385544ffc1194ddb7243024284225a76fa9395","LICENSE":"ce93600c49fbb3e14df32efe752264644f6a2f8e08a735ba981725799e5309ef","README.md":"36c2030accc46c6df69c2254925d27e66b2b3c34c8ab263fd3d5daf14bfeb219","src/core.rs":"db8263e64a1b606343c7cc45d09b77d65f0a0442e17f0d14b45d612fc04a7946","src/indentation.rs":"49896f6e166f08a6f57f71a5d1bf706342c25e8410aa301fad590e001eb49799","src/lib.rs":"71f0a7d010cdb808179279c5dd6df3492eeeb7fca3132e60ca73139016ff343e","src/word_separators.rs":"99b5dddff8a89b850f002ebc4f8fb6d431c38d2ec10d3ee580393b5fe853c663","src/word_splitters.rs":"1f5b6faaa0a8f58edad5b65850b5d1c908f10af700af1bbb1f313d0fee33452d","src/wrap_algorithms.rs":"1bf0617704b24c0125e03a2032c816fff7b37ca2b80eaba00d80285d9b88b6fb","src/wrap_algorithms/optimal_fit.rs":"f27c5fd2f2360774bbf2d0d2e43036e61a995fddbceea578c0d9e110258023e2","tests/indent.rs":"51f977db11632a32fafecf86af88413d51238fe6efcf18ec52fac89133714278","tests/traits.rs":"ae5f7a6d5dc0ebc6ec41d5d933fc7ee3180af568248f495f072f3f73c43440b0","tests/version-numbers.rs":"9e964f58dbdf051fc6fe0d6542ab312d3e95f26c3fd14bce84449bb625e45761"},"package":"0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"} \ No newline at end of file +{"files":{"CHANGELOG.md":"08d11f8d78f3b02bbc720ff731f0cd9ba17701c6bf1b01277bc30e49e399f89d","Cargo.lock":"d07c958634869340b2058322968f69a1e6a9d663c8e857bc8c2609f1117b40f5","Cargo.toml":"a2f9890125e961e9fb895538230e0abaca164125c7492e0ed23c569a6d896c1e","LICENSE":"ce93600c49fbb3e14df32efe752264644f6a2f8e08a735ba981725799e5309ef","README.md":"7711269f7654bdd11c1a183c4f4213cfe63416c55a393c270d57e7bee19de4ac","rustfmt.toml":"02637ad90caa19885b25b1ce8230657b3703402775d9db83687a3f55de567509","src/core.rs":"533b1516778553d01b6e3ec5962877b38605545a5041cbfffdd265a93e7de3af","src/indentation.rs":"49896f6e166f08a6f57f71a5d1bf706342c25e8410aa301fad590e001eb49799","src/lib.rs":"dbebc4de1134e269036b41590f1ffd5df58f49cda6141be3707678f3fd090079","src/word_separators.rs":"0931ef25d1340b4295cb4f1ff075de26d8dee48eafcc96d9ed11eab66f74d28f","src/word_splitters.rs":"5a3a601414433227aff009d721fa60a94a28a0c7501b54bbbecedda9a2add3ba","src/wrap_algorithms.rs":"277216193595d85d464d3888d79c937a2265f89b8cf51077e078ff1aa1281dbf","src/wrap_algorithms/optimal_fit.rs":"5ae852cdafbd7926042ee0336dae30b88ba62322604f3040d49ed8b9380e68d4","tests/indent.rs":"51f977db11632a32fafecf86af88413d51238fe6efcf18ec52fac89133714278","tests/version-numbers.rs":"9e964f58dbdf051fc6fe0d6542ab312d3e95f26c3fd14bce84449bb625e45761"},"package":"b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"} \ No newline at end of file diff --git a/third_party/rust/textwrap/CHANGELOG.md b/third_party/rust/textwrap/CHANGELOG.md index cdc703efcf5b..093b9dc2533e 100644 --- a/third_party/rust/textwrap/CHANGELOG.md +++ b/third_party/rust/textwrap/CHANGELOG.md @@ -3,6 +3,64 @@ This file lists the most important changes made in each release of `textwrap`. +## Version 0.15.0 (2022-02-27) + +This is a major feature release with two main changes: + +* [#421](https://github.com/mgeisler/textwrap/pull/421): Use `f64` + instead of `usize` for fragment widths. + + This fixes problems with overflows in the internal computations of + `wrap_optimal_fit` when fragments (words) or line lenghts had + extreme values, such as `usize::MAX`. + +* [#438](https://github.com/mgeisler/textwrap/pull/438): Simplify + `Options` by removing generic type parameters. + + This change removes the new generic parameters introduced in version + 0.14, as well as the original `WrapSplitter` parameter which has + been present since very early versions. + + The result is a simplification of function and struct signatures + across the board. So what used to be + + ```rust + let options: Options< + wrap_algorithms::FirstFit, + word_separators::AsciiSpace, + word_splitters::HyphenSplitter, + > = Options::new(80); + ``` + + if types are fully written out, is now simply + + ```rust + let options: Options<'_> = Options::new(80); + ``` + + The anonymous lifetime represent the lifetime of the + `initial_indent` and `subsequent_indent` strings. The change is + nearly performance neutral (a 1-2% regression). + +Smaller improvements and changes: + +* [#404](https://github.com/mgeisler/textwrap/pull/404): Make + documentation for short last-line penalty more precise. +* [#405](https://github.com/mgeisler/textwrap/pull/405): Cleanup and + simplify `Options` docstring. +* [#411](https://github.com/mgeisler/textwrap/pull/411): Default to + `OptimalFit` in interactive example. +* [#415](https://github.com/mgeisler/textwrap/pull/415): Add demo + program to help compute binary sizes. +* [#423](https://github.com/mgeisler/textwrap/pull/423): Add fuzz + tests with fully arbitrary fragments. +* [#424](https://github.com/mgeisler/textwrap/pull/424): Change + `wrap_optimal_fit` penalties to non-negative numbers. +* [#430](https://github.com/mgeisler/textwrap/pull/430): Add + `debug-words` example. +* [#432](https://github.com/mgeisler/textwrap/pull/432): Use precise + dependency versions in Cargo.toml. + ## Version 0.14.2 (2021-06-27) The 0.14.1 release included more changes than intended and has been diff --git a/third_party/rust/textwrap/Cargo.lock b/third_party/rust/textwrap/Cargo.lock new file mode 100644 index 000000000000..4207038f7713 --- /dev/null +++ b/third_party/rust/textwrap/Cargo.lock @@ -0,0 +1,962 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "lazy_static", + "memchr", + "regex-automata", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" + +[[package]] +name = "cast" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c24dab4283a142afa2fdca129b80ad2c6284e073930f964c3a1293c225ee39a" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "bitflags", + "textwrap 0.11.0", + "unicode-width", +] + +[[package]] +name = "criterion" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1604dafd25fba2fe2d5895a9da139f8dc9b319a5fe5354ca137cbbce4e178d10" +dependencies = [ + "atty", + "cast", + "clap", + "criterion-plot", + "csv", + "itertools", + "lazy_static", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_cbor", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d00996de9f2f7559f7f4dc286073197f83e92256a59ed395f9aac01fe717da57" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c00d6d2ea26e8b151d99093005cb442fb9a37aeaca582a03ec70946f49ab5ed9" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e5bed1f1c269533fa816a0a5492b3545209a205ca1a54842be180eb63a16a6" +dependencies = [ + "cfg-if", + "lazy_static", +] + +[[package]] +name = "csv" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" +dependencies = [ + "bstr", + "csv-core", + "itoa 0.4.8", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +dependencies = [ + "memchr", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "fst" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab85b9b05e3978cc9a9cf8fea7f01b494e1a09ed3037e16ba39edc7a29eb61a" + +[[package]] +name = "getrandom" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "half" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hyphenation" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf4dd4c44ae85155502a52c48739c8a48185d1449fff1963cffee63c28a50f0" +dependencies = [ + "bincode", + "fst", + "hyphenation_commons", + "pocket-resources", + "serde", +] + +[[package]] +name = "hyphenation_commons" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5febe7a2ade5c7d98eb8b75f946c046b335324b06a14ea0998271504134c05bf" +dependencies = [ + "fst", + "serde", +] + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "itertools" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "itoa" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" + +[[package]] +name = "js-sys" +version = "0.3.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" + +[[package]] +name = "lipsum" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ee7271c76a89032dcc7e595c0a739a9c5514eab483deb0e82981fe2098c56a" +dependencies = [ + "rand", + "rand_chacha", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "numtoa" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" + +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "plotters" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a3fd9ec30b9749ce28cd91f255d569591cdf937fe280c312143e3c4bad6f2a" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d88417318da0eaf0fdcdb51a0ee6c3bed624333bff8f946733049380be67ac1c" + +[[package]] +name = "plotters-svg" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521fa9638fa597e1dc53e9412a4f9cefb01187ee1f7413076f9e6749e2885ba9" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "pocket-resources" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c135f38778ad324d9e9ee68690bac2c1a51f340fdf96ca13e2ab3914eb2e51d8" + +[[package]] +name = "ppv-lite86" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" + +[[package]] +name = "proc-macro2" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "pulldown-cmark" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffade02495f22453cd593159ea2f59827aae7f53fa8323f756799b670881dcf8" +dependencies = [ + "bitflags", + "memchr", + "unicase", +] + +[[package]] +name = "quote" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rayon" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + +[[package]] +name = "redox_syscall" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_termios" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8440d8acb4fd3d277125b4bd01a6f38aee8d814b3b5fc09b3f2b825d37d3fe8f" +dependencies = [ + "redox_syscall", +] + +[[package]] +name = "regex" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "semver" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a3381e03edd24287172047536f20cabde766e2cd3e65e6b00fb3af51c4f38d" + +[[package]] +name = "serde" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_cbor" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" +dependencies = [ + "half", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.136" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" +dependencies = [ + "itoa 1.0.1", + "ryu", + "serde", +] + +[[package]] +name = "smawk" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043" + +[[package]] +name = "syn" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "terminal_size" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "termion" +version = "1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "077185e2eac69c3f8379a4298e1e07cd36beb962290d4a51199acf0fdc10607e" +dependencies = [ + "libc", + "numtoa", + "redox_syscall", + "redox_termios", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "textwrap" +version = "0.15.0" +dependencies = [ + "criterion", + "hyphenation", + "lipsum", + "smawk", + "terminal_size", + "termion", + "unic-emoji-char", + "unicode-linebreak", + "unicode-width", + "version-sync", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "tinyvec" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-emoji-char" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b07221e68897210270a38bde4babb655869637af0f69407f96053a34f76494d" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" + +[[package]] +name = "unicode-linebreak" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a52dcaab0c48d931f7cc8ef826fa51690a08e1ea55117ef26f89864f532383f" +dependencies = [ + "regex", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "version-sync" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d0801cec07737d88cb900e6419f6f68733867f90b3faaa837e84692e101bf0" +dependencies = [ + "proc-macro2", + "pulldown-cmark", + "regex", + "semver", + "syn", + "toml", + "url", +] + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[package]] +name = "wasm-bindgen" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2" + +[[package]] +name = "web-sys" +version = "0.3.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/third_party/rust/textwrap/Cargo.toml b/third_party/rust/textwrap/Cargo.toml index 69acb0f1a0d5..0e4c788e46cb 100644 --- a/third_party/rust/textwrap/Cargo.toml +++ b/third_party/rust/textwrap/Cargo.toml @@ -3,17 +3,16 @@ # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies +# to registry (e.g., crates.io) dependencies. # -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "textwrap" -version = "0.14.2" +version = "0.15.0" authors = ["Martin Geisler "] exclude = [".github/", ".gitignore", "benches/", "examples/", "fuzz/", "images/"] description = "Powerful library for word wrapping, indenting, and dedenting strings" @@ -26,6 +25,16 @@ repository = "https://github.com/mgeisler/textwrap" [package.metadata.docs.rs] all-features = true +[[example]] +name = "hyphenation" +path = "examples/hyphenation.rs" +required-features = ["hyphenation"] + +[[example]] +name = "termwidth" +path = "examples/termwidth.rs" +required-features = ["terminal_size"] + [[bench]] name = "linear" path = "benches/linear.rs" @@ -36,38 +45,38 @@ name = "indent" path = "benches/indent.rs" harness = false [dependencies.hyphenation] -version = "0.8.2" +version = "0.8.4" features = ["embed_en-us"] optional = true [dependencies.smawk] -version = "0.3" +version = "0.3.1" optional = true [dependencies.terminal_size] -version = "0.1" +version = "0.1.17" optional = true [dependencies.unicode-linebreak] -version = "0.1" +version = "0.1.2" optional = true [dependencies.unicode-width] -version = "0.1" +version = "0.1.9" optional = true [dev-dependencies.criterion] -version = "0.3" +version = "0.3.5" [dev-dependencies.lipsum] -version = "0.8" +version = "0.8.0" [dev-dependencies.unic-emoji-char] version = "0.9.0" [dev-dependencies.version-sync] -version = "0.9" +version = "0.9.4" [features] default = ["unicode-linebreak", "unicode-width", "smawk"] [target."cfg(unix)".dev-dependencies.termion] -version = "1.5" +version = "1.5.6" diff --git a/third_party/rust/textwrap/README.md b/third_party/rust/textwrap/README.md index b32924c6ec02..9eeea0793577 100644 --- a/third_party/rust/textwrap/README.md +++ b/third_party/rust/textwrap/README.md @@ -16,16 +16,19 @@ drawn on a [HTML5 canvas using WebAssembly][wasm-demo]. To use the textwrap crate, add this to your `Cargo.toml` file: ```toml [dependencies] -textwrap = "0.14" +textwrap = "0.15" ``` By default, this enables word wrapping with support for Unicode strings. Extra features can be enabled with Cargo featuresโ€”and the Unicode support can be disabled if needed. This allows you slim down the library and so you will only pay for the features you actually -use. Please see the [_Cargo Features_ in the crate +use. + +Please see the [_Cargo Features_ in the crate documentation](https://docs.rs/textwrap/#cargo-features) for a full -list of the available features. +list of the available features as well as their impact on the size of +your binary. ## Documentation @@ -33,23 +36,22 @@ list of the available features. ## Getting Started -Word wrapping is easy using the `fill` function: +Word wrapping is easy using the `wrap` and `fill` functions: ```rust -fn main() { - let text = "textwrap: an efficient and powerful library for wrapping text."; - println!("{}", textwrap::fill(text, 28)); +#[cfg(feature = "smawk")] { +let text = "textwrap: an efficient and powerful library for wrapping text."; +assert_eq!( + textwrap::wrap(text, 28), + vec![ + "textwrap: an efficient", + "and powerful library for", + "wrapping text.", + ] +); } ``` -The output is wrapped within 28 columns: - -``` -textwrap: an efficient -and powerful library for -wrapping text. -``` - Sharp-eyed readers will notice that the first line is 22 columns wide. So why is the word โ€œandโ€ put in the second line when there is space for it in the first line? @@ -57,14 +59,24 @@ for it in the first line? The explanation is that textwrap does not just wrap text one line at a time. Instead, it uses an optimal-fit algorithm which looks ahead and chooses line breaks which minimize the gaps left at ends of lines. +This is controlled with the `smawk` Cargo feature, which is why the +example is wrapped in the `cfg`-block. Without look-ahead, the first line would be longer and the text would look like this: -``` -textwrap: an efficient and -powerful library for -wrapping text. +```rust +#[cfg(not(feature = "smawk"))] { +let text = "textwrap: an efficient and powerful library for wrapping text."; +assert_eq!( + textwrap::wrap(text, 28), + vec![ + "textwrap: an efficient and", + "powerful library for", + "wrapping text.", + ] +); +} ``` The second line is now shorter and the text is more ragged. The kind @@ -78,24 +90,25 @@ Your program must load the hyphenation pattern and configure `Options::word_splitter` to use it: ```rust +#[cfg(feature = "hyphenation")] { use hyphenation::{Language, Load, Standard}; -use textwrap::Options; +use textwrap::{fill, Options, WordSplitter}; -fn main() { - let hyphenator = Standard::from_embedded(Language::EnglishUS).unwrap(); - let options = Options::new(28).word_splitter(hyphenator); - let text = "textwrap: an efficient and powerful library for wrapping text."; - println!("{}", fill(text, &options); +let dictionary = Standard::from_embedded(Language::EnglishUS).unwrap(); +let options = textwrap::Options::new(28).word_splitter(WordSplitter::Hyphenation(dictionary)); +let text = "textwrap: an efficient and powerful library for wrapping text."; + +assert_eq!( + textwrap::wrap(text, &options), + vec![ + "textwrap: an efficient and", + "powerful library for wrap-", + "ping text." + ] +); } ``` -The output now looks like this: -``` -textwrap: an efficient and -powerful library for wrap- -ping text. -``` - The US-English hyphenation patterns are embedded when you enable the `hyphenation` feature. They are licensed under a [permissive license][en-us license] and take up about 88 KB in your binary. If you diff --git a/third_party/rust/textwrap/rustfmt.toml b/third_party/rust/textwrap/rustfmt.toml new file mode 100644 index 000000000000..c1578aafbcf2 --- /dev/null +++ b/third_party/rust/textwrap/rustfmt.toml @@ -0,0 +1 @@ +imports_granularity = "Module" diff --git a/third_party/rust/textwrap/src/core.rs b/third_party/rust/textwrap/src/core.rs index af024603384d..0ab4ef8134e9 100644 --- a/third_party/rust/textwrap/src/core.rs +++ b/third_party/rust/textwrap/src/core.rs @@ -13,10 +13,9 @@ //! how to do this for text. //! //! 2. Potentially split your fragments into smaller pieces. This -//! allows you to implement things like hyphenation. If you are -//! wrapping text represented as a sequence of [`Word`]s, then you -//! can use [`split_words`](crate::word_splitters::split_words) can -//! help you do this. +//! allows you to implement things like hyphenation. If you use the +//! `Word` type, you can use [`WordSplitter`](crate::WordSplitter) +//! enum for this. //! //! 3. Potentially break apart fragments that are still too large to //! fit on a single line. This is implemented in [`break_words`]. @@ -197,15 +196,15 @@ pub fn display_width(text: &str) -> usize { /// the displayed width of each part, which this trait provides. pub trait Fragment: std::fmt::Debug { /// Displayed width of word represented by this fragment. - fn width(&self) -> usize; + fn width(&self) -> f64; /// Displayed width of the whitespace that must follow the word /// when the word is not at the end of a line. - fn whitespace_width(&self) -> usize; + fn whitespace_width(&self) -> f64; /// Displayed width of the penalty that must be inserted if the /// word falls at the end of a line. - fn penalty_width(&self) -> usize; + fn penalty_width(&self) -> f64; } /// A piece of wrappable text, including any trailing whitespace. @@ -241,7 +240,7 @@ impl<'a> Word<'a> { let trimmed = word.trim_end_matches(' '); Word { word: trimmed, - width: display_width(&trimmed), + width: display_width(trimmed), whitespace: &word[trimmed.len()..], penalty: "", } @@ -304,22 +303,22 @@ impl<'a> Word<'a> { impl Fragment for Word<'_> { #[inline] - fn width(&self) -> usize { - self.width + fn width(&self) -> f64 { + self.width as f64 } // We assume the whitespace consist of ' ' only. This allows us to // compute the display width in constant time. #[inline] - fn whitespace_width(&self) -> usize { - self.whitespace.len() + fn whitespace_width(&self) -> f64 { + self.whitespace.len() as f64 } // We assume the penalty is `""` or `"-"`. This allows us to // compute the display width in constant time. #[inline] - fn penalty_width(&self) -> usize { - self.penalty.len() + fn penalty_width(&self) -> f64 { + self.penalty.len() as f64 } } @@ -334,7 +333,7 @@ where { let mut shortened_words = Vec::new(); for word in words { - if word.width() > line_width { + if word.width() > line_width as f64 { shortened_words.extend(word.break_apart(line_width)); } else { shortened_words.push(word); diff --git a/third_party/rust/textwrap/src/lib.rs b/third_party/rust/textwrap/src/lib.rs index f2f5542d586f..6d68309a2a44 100644 --- a/third_party/rust/textwrap/src/lib.rs +++ b/third_party/rust/textwrap/src/lib.rs @@ -7,47 +7,35 @@ //! you want to format dynamic output nicely so it looks good in a //! terminal. A quick example: //! -//! ```no_run -//! fn main() { -//! let text = "textwrap: a small library for wrapping text."; -//! println!("{}", textwrap::fill(text, 18)); -//! } +//! ``` +//! # #[cfg(feature = "smawk")] { +//! let text = "textwrap: a small library for wrapping text."; +//! assert_eq!(textwrap::wrap(text, 18), +//! vec!["textwrap: a", +//! "small library for", +//! "wrapping text."]); +//! # } //! ``` //! -//! When you run this program, it will display the following output: -//! -//! ```text -//! textwrap: a small -//! library for -//! wrapping text. -//! ``` +//! The [`wrap`] function returns the individual lines, use [`fill`] +//! is you want the lines joined with `'\n'` to form a `String`. //! //! If you enable the `hyphenation` Cargo feature, you can get //! automatic hyphenation for a number of languages: //! -//! ```no_run -//! # #[cfg(feature = "hyphenation")] -//! use hyphenation::{Language, Load, Standard}; -//! use textwrap::{fill, Options}; -//! -//! # #[cfg(feature = "hyphenation")] -//! fn main() { -//! let text = "textwrap: a small library for wrapping text."; -//! let dictionary = Standard::from_embedded(Language::EnglishUS).unwrap(); -//! let options = Options::new(18).word_splitter(dictionary); -//! println!("{}", fill(text, &options)); -//! } -//! -//! # #[cfg(not(feature = "hyphenation"))] -//! # fn main() { } //! ``` +//! #[cfg(feature = "hyphenation")] { +//! use hyphenation::{Language, Load, Standard}; +//! use textwrap::{wrap, Options, WordSplitter}; //! -//! The program will now output: -//! -//! ```text -//! textwrap: a small -//! library for wrap- -//! ping text. +//! let text = "textwrap: a small library for wrapping text."; +//! let dictionary = Standard::from_embedded(Language::EnglishUS).unwrap(); +//! let options = Options::new(18).word_splitter(WordSplitter::Hyphenation(dictionary)); +//! assert_eq!(wrap(text, &options), +//! vec!["textwrap: a small", +//! "library for wrap-", +//! "ping text."]); +//! } //! ``` //! //! See also the [`unfill`] and [`refill`] functions which allow you to @@ -124,7 +112,7 @@ //! The full dependency graph, where dashed lines indicate optional //! dependencies, is shown below: //! -//! +//! //! //! ## Default Features //! @@ -138,8 +126,7 @@ //! This feature can be disabled if you are happy to find words //! separated by ASCII space characters only. People wrapping text //! with emojis or East-Asian characters will want most likely want -//! to enable this feature. See the -//! [`word_separators::WordSeparator`] trait for details. +//! to enable this feature. See [`WordSeparator`] for details. //! //! * `unicode-width`: enables correct width computation of non-ASCII //! characters via the [unicode-width] crate. Without this feature, @@ -159,6 +146,29 @@ //! This feature can be disabled if you only ever intend to use //! [`wrap_algorithms::wrap_first_fit`]. //! +//! With Rust 1.59.0, the size impact of the above features on your +//! binary is as follows: +//! +//! | Configuration | Binary Size | Delta | +//! | :--- | ---: | ---: | +//! | quick-and-dirty implementation | 289 KB | โ€” KB | +//! | textwrap without default features | 301 KB | 12 KB | +//! | textwrap with smawk | 317 KB | 28 KB | +//! | textwrap with unicode-width | 313 KB | 24 KB | +//! | textwrap with unicode-linebreak | 395 KB | 106 KB | +//! +//! The above sizes are the stripped sizes and the binary is compiled +//! in release mode with this profile: +//! +//! ```toml +//! [profile.release] +//! lto = true +//! codegen-units = 1 +//! ``` +//! +//! See the [binary-sizes demo] if you want to reproduce these +//! results. +//! //! ## Optional Features //! //! These Cargo features enable new functionality: @@ -168,71 +178,61 @@ //! [`Options::with_termwidth`] constructor for details. //! //! * `hyphenation`: enables language-sensitive hyphenation via the -//! [hyphenation] crate. See the [`word_splitters::WordSplitter`] trait for details. +//! [hyphenation] crate. See the [`word_splitters::WordSplitter`] +//! trait for details. //! //! [unicode-linebreak]: https://docs.rs/unicode-linebreak/ //! [unicode-width]: https://docs.rs/unicode-width/ //! [smawk]: https://docs.rs/smawk/ +//! [binary-sizes demo]: https://github.com/mgeisler/textwrap/tree/master/examples/binary-sizes //! [textwrap-macros]: https://docs.rs/textwrap-macros/ //! [terminal_size]: https://docs.rs/terminal_size/ //! [hyphenation]: https://docs.rs/hyphenation/ -#![doc(html_root_url = "https://docs.rs/textwrap/0.14.2")] +#![doc(html_root_url = "https://docs.rs/textwrap/0.15.0")] #![forbid(unsafe_code)] // See https://github.com/mgeisler/textwrap/issues/210 #![deny(missing_docs)] #![deny(missing_debug_implementations)] #![allow(clippy::redundant_field_names)] +// Make `cargo test` execute the README doctests. +#[cfg(doctest)] +#[doc = include_str!("../README.md")] +mod readme_doctest {} + use std::borrow::Cow; mod indentation; -pub use crate::indentation::dedent; -pub use crate::indentation::indent; +pub use crate::indentation::{dedent, indent}; + +mod word_separators; +pub use word_separators::WordSeparator; -pub mod word_separators; pub mod word_splitters; +pub use word_splitters::WordSplitter; + pub mod wrap_algorithms; +pub use wrap_algorithms::WrapAlgorithm; pub mod core; -// These private macros lets us hide the actual WrapAlgorithm and -// WordSeperator used in the function signatures below. -#[cfg(feature = "smawk")] -macro_rules! DefaultWrapAlgorithm { - () => { - wrap_algorithms::OptimalFit - }; -} - -#[cfg(not(feature = "smawk"))] -macro_rules! DefaultWrapAlgorithm { - () => { - wrap_algorithms::FirstFit - }; -} - #[cfg(feature = "unicode-linebreak")] macro_rules! DefaultWordSeparator { () => { - word_separators::UnicodeBreakProperties + WordSeparator::UnicodeBreakProperties }; } #[cfg(not(feature = "unicode-linebreak"))] macro_rules! DefaultWordSeparator { () => { - word_separators::AsciiSpace + WordSeparator::AsciiSpace }; } -/// Holds settings for wrapping and filling text. +/// Holds configuration options for wrapping and filling text. #[derive(Debug, Clone)] -pub struct Options< - 'a, - WrapAlgo = Box, - WordSep = Box, - WordSplit = Box, -> { +pub struct Options<'a> { /// The width in columns at which the text will be wrapped. pub width: usize, /// Indentation used for the first line of output. See the @@ -247,62 +247,42 @@ pub struct Options< pub break_words: bool, /// Wrapping algorithm to use, see the implementations of the /// [`wrap_algorithms::WrapAlgorithm`] trait for details. - pub wrap_algorithm: WrapAlgo, + pub wrap_algorithm: WrapAlgorithm, /// The line breaking algorithm to use, see /// [`word_separators::WordSeparator`] trait for an overview and /// possible implementations. - pub word_separator: WordSep, + pub word_separator: WordSeparator, /// The method for splitting words. This can be used to prohibit /// splitting words on hyphens, or it can be used to implement - /// language-aware machine hyphenation. Please see the - /// [`word_splitters::WordSplitter`] trait for details. - pub word_splitter: WordSplit, + /// language-aware machine hyphenation. + pub word_splitter: WordSplitter, } -impl<'a, WrapAlgo, WordSep, WordSplit> From<&'a Options<'a, WrapAlgo, WordSep, WordSplit>> - for Options<'a, WrapAlgo, WordSep, WordSplit> -where - WrapAlgo: Clone, - WordSep: Clone, - WordSplit: Clone, -{ - fn from(options: &'a Options<'a, WrapAlgo, WordSep, WordSplit>) -> Self { +impl<'a> From<&'a Options<'a>> for Options<'a> { + fn from(options: &'a Options<'a>) -> Self { Self { width: options.width, initial_indent: options.initial_indent, subsequent_indent: options.subsequent_indent, break_words: options.break_words, - word_separator: options.word_separator.clone(), - wrap_algorithm: options.wrap_algorithm.clone(), + word_separator: options.word_separator, + wrap_algorithm: options.wrap_algorithm, word_splitter: options.word_splitter.clone(), } } } -impl<'a> From - for Options< - 'a, - DefaultWrapAlgorithm!(), - DefaultWordSeparator!(), - word_splitters::HyphenSplitter, - > -{ +impl<'a> From for Options<'a> { fn from(width: usize) -> Self { Options::new(width) } } -/// Constructors for boxed Options, specifically. -impl<'a> - Options<'a, DefaultWrapAlgorithm!(), DefaultWordSeparator!(), word_splitters::HyphenSplitter> -{ - /// Creates a new [`Options`] with the specified width and static - /// dispatch using the [`word_splitters::HyphenSplitter`]. - /// Equivalent to +impl<'a> Options<'a> { + /// Creates a new [`Options`] with the specified width. Equivalent to /// /// ``` - /// # use textwrap::word_splitters::{HyphenSplitter, WordSplitter}; - /// # use textwrap::Options; + /// # use textwrap::{Options, WordSplitter, WordSeparator, WrapAlgorithm}; /// # let width = 80; /// # let actual = Options::new(width); /// # let expected = @@ -312,76 +292,36 @@ impl<'a> /// subsequent_indent: "", /// break_words: true, /// #[cfg(feature = "unicode-linebreak")] - /// word_separator: textwrap::word_separators::UnicodeBreakProperties, + /// word_separator: WordSeparator::UnicodeBreakProperties, /// #[cfg(not(feature = "unicode-linebreak"))] - /// word_separator: textwrap::word_separators::AsciiSpace, + /// word_separator: WordSeparator::AsciiSpace, /// #[cfg(feature = "smawk")] - /// wrap_algorithm: textwrap::wrap_algorithms::OptimalFit, + /// wrap_algorithm: WrapAlgorithm::new_optimal_fit(), /// #[cfg(not(feature = "smawk"))] - /// wrap_algorithm: textwrap::wrap_algorithms::FirstFit, - /// word_splitter: textwrap::word_splitters::HyphenSplitter, + /// wrap_algorithm: WrapAlgorithm::FirstFit, + /// word_splitter: WordSplitter::HyphenSplitter, /// } /// # ; /// # assert_eq!(actual.width, expected.width); /// # assert_eq!(actual.initial_indent, expected.initial_indent); /// # assert_eq!(actual.subsequent_indent, expected.subsequent_indent); /// # assert_eq!(actual.break_words, expected.break_words); + /// # assert_eq!(actual.word_splitter, expected.word_splitter); /// ``` /// /// Note that the default word separator and wrap algorithms /// changes based on the available Cargo features. The best - /// available algorithm is used by default. - /// - /// Static dispatch means here, that the word splitter is stored as-is - /// and the type is known at compile-time. Thus the returned value - /// is actually a `Options`. - /// - /// Dynamic dispatch on the other hand, means that the word - /// separator and/or word splitter is stored as a trait object - /// such as a `Box`. This way - /// the word splitter's inner type can be changed without changing - /// the type of this struct, which then would be just `Options` as - /// a short cut for `Options, Box>`. - /// - /// The value and type of the word splitter can be choose from the - /// start using the [`Options::with_word_splitter`] constructor or - /// changed afterwards using the [`Options::word_splitter`] - /// method. Whether static or dynamic dispatch is used, depends on - /// whether these functions are given a boxed - /// [`word_splitters::WordSplitter`] or not. Take for example: - /// - /// ``` - /// use textwrap::Options; - /// use textwrap::word_splitters::{HyphenSplitter, NoHyphenation}; - /// # use textwrap::word_splitters::WordSplitter; - /// # use textwrap::word_separators::AsciiSpace; - /// # let width = 80; - /// - /// // uses HyphenSplitter with static dispatch - /// // the actual type: Options - /// let opt = Options::new(width); - /// - /// // uses NoHyphenation with static dispatch - /// // the actual type: Options - /// let opt = Options::new(width).word_splitter(NoHyphenation); - /// - /// // uses HyphenSplitter with dynamic dispatch - /// // the actual type: Options> - /// let opt: Options<_, _, _> = Options::new(width).word_splitter(Box::new(HyphenSplitter)); - /// - /// // uses NoHyphenation with dynamic dispatch - /// // the actual type: Options> - /// let opt: Options<_, _, _> = Options::new(width).word_splitter(Box::new(NoHyphenation)); - /// ``` - /// - /// Notice that the last two variables have the same type, despite - /// the different `WordSplitter` in use. Thus dynamic dispatch - /// allows to change the word splitter at run-time without - /// changing the variables type. + /// available algorithms are used by default. pub const fn new(width: usize) -> Self { - Options::with_word_splitter(width, word_splitters::HyphenSplitter) + Options { + width, + initial_indent: "", + subsequent_indent: "", + break_words: true, + word_separator: DefaultWordSeparator!(), + wrap_algorithm: WrapAlgorithm::new(), + word_splitter: WordSplitter::HyphenSplitter, + } } /// Creates a new [`Options`] with `width` set to the current @@ -407,97 +347,7 @@ impl<'a> } } -impl<'a, WordSplit> Options<'a, DefaultWrapAlgorithm!(), DefaultWordSeparator!(), WordSplit> { - /// Creates a new [`Options`] with the specified width and - /// word splitter. Equivalent to - /// - /// ``` - /// # use textwrap::Options; - /// # use textwrap::word_splitters::{NoHyphenation, HyphenSplitter}; - /// # const word_splitter: NoHyphenation = NoHyphenation; - /// # const width: usize = 80; - /// # let actual = Options::with_word_splitter(width, word_splitter); - /// # let expected = - /// Options { - /// width: width, - /// initial_indent: "", - /// subsequent_indent: "", - /// break_words: true, - /// #[cfg(feature = "unicode-linebreak")] - /// word_separator: textwrap::word_separators::UnicodeBreakProperties, - /// #[cfg(not(feature = "unicode-linebreak"))] - /// word_separator: textwrap::word_separators::AsciiSpace, - /// #[cfg(feature = "smawk")] - /// wrap_algorithm: textwrap::wrap_algorithms::OptimalFit, - /// #[cfg(not(feature = "smawk"))] - /// wrap_algorithm: textwrap::wrap_algorithms::FirstFit, - /// word_splitter: word_splitter, - /// } - /// # ; - /// # assert_eq!(actual.width, expected.width); - /// # assert_eq!(actual.initial_indent, expected.initial_indent); - /// # assert_eq!(actual.subsequent_indent, expected.subsequent_indent); - /// # assert_eq!(actual.break_words, expected.break_words); - /// ``` - /// - /// This constructor allows to specify the word splitter to be - /// used. It is like a short-cut for - /// `Options::new(w).word_splitter(s)`, but this function is a - /// `const fn`. The given word splitter may be in a [`Box`], which - /// then can be coerced into a trait object for dynamic dispatch: - /// - /// ``` - /// use textwrap::Options; - /// use textwrap::word_splitters::{HyphenSplitter, NoHyphenation, WordSplitter}; - /// # const width: usize = 80; - /// - /// // This opt contains a boxed trait object as splitter. - /// // The type annotation is important, otherwise it will be not a trait object - /// let mut opt: Options<_, _, Box> - /// = Options::with_word_splitter(width, Box::new(NoHyphenation)); - /// // Its type is actually: `Options>`: - /// let opt_coerced: Options<_, _, Box> = opt; - /// - /// // Thus, it can be overridden with a different word splitter. - /// opt = Options::with_word_splitter(width, Box::new(HyphenSplitter)); - /// // Now, containing a `HyphenSplitter` instead. - /// ``` - /// - /// Since the word splitter is given by value, which determines - /// the generic type parameter, it can be used to produce both an - /// [`Options`] with static and dynamic dispatch, respectively. - /// While dynamic dispatch allows to change the type of the inner - /// word splitter at run time as seen above, static dispatch - /// especially can store the word splitter directly, without the - /// need for a box. This in turn allows it to be used in constant - /// and static context: - /// - /// ``` - /// use textwrap::word_splitters::HyphenSplitter; use textwrap::{ Options}; - /// use textwrap::word_separators::AsciiSpace; - /// use textwrap::wrap_algorithms::FirstFit; - /// # const width: usize = 80; - /// - /// # #[cfg(all(not(feature = "smawk"), not(feature = "unicode-linebreak")))] { - /// const FOO: Options = - /// Options::with_word_splitter(width, HyphenSplitter); - /// static BAR: Options = FOO; - /// # } - /// ``` - pub const fn with_word_splitter(width: usize, word_splitter: WordSplit) -> Self { - Options { - width, - initial_indent: "", - subsequent_indent: "", - break_words: true, - word_separator: DefaultWordSeparator!(), - wrap_algorithm: DefaultWrapAlgorithm!(), - word_splitter: word_splitter, - } - } -} - -impl<'a, WrapAlgo, WordSep, WordSplit> Options<'a, WrapAlgo, WordSep, WordSplit> { +impl<'a> Options<'a> { /// Change [`self.initial_indent`]. The initial indentation is /// used on the very first line of output. /// @@ -507,7 +357,7 @@ impl<'a, WrapAlgo, WordSep, WordSplit> Options<'a, WrapAlgo, WordSep, WordSplit> /// initial indentation and wrapping each paragraph by itself: /// /// ``` - /// use textwrap::{Options, wrap}; + /// use textwrap::{wrap, Options}; /// /// let options = Options::new(16).initial_indent(" "); /// assert_eq!(wrap("This is a little example.", options), @@ -532,7 +382,7 @@ impl<'a, WrapAlgo, WordSep, WordSplit> Options<'a, WrapAlgo, WordSep, WordSplit> /// single paragraph as a bullet list: /// /// ``` - /// use textwrap::{Options, wrap}; + /// use textwrap::{wrap, Options}; /// /// let options = Options::new(12) /// .initial_indent("* ") @@ -591,10 +441,7 @@ impl<'a, WrapAlgo, WordSep, WordSplit> Options<'a, WrapAlgo, WordSep, WordSplit> /// See [`word_separators::WordSeparator`] for details on the choices. /// /// [`self.word_separator`]: #structfield.word_separator - pub fn word_separator( - self, - word_separator: NewWordSep, - ) -> Options<'a, WrapAlgo, NewWordSep, WordSplit> { + pub fn word_separator(self, word_separator: WordSeparator) -> Options<'a> { Options { width: self.width, initial_indent: self.initial_indent, @@ -612,10 +459,7 @@ impl<'a, WrapAlgo, WordSep, WordSplit> Options<'a, WrapAlgo, WordSep, WordSplit> /// the choices. /// /// [`self.wrap_algorithm`]: #structfield.wrap_algorithm - pub fn wrap_algorithm( - self, - wrap_algorithm: NewWrapAlgo, - ) -> Options<'a, NewWrapAlgo, WordSep, WordSplit> { + pub fn wrap_algorithm(self, wrap_algorithm: WrapAlgorithm) -> Options<'a> { Options { width: self.width, initial_indent: self.initial_indent, @@ -631,25 +475,18 @@ impl<'a, WrapAlgo, WordSep, WordSplit> Options<'a, WrapAlgo, WordSep, WordSplit> /// [`word_splitters::WordSplitter`] is used to fit part of a word /// into the current line when wrapping text. /// - /// This function may return a different type than `Self`. That is - /// the case when the given `splitter` is of a different type the - /// the currently stored one in the `splitter` field. Take for - /// example: + /// # Examples /// /// ``` - /// use textwrap::word_splitters::{HyphenSplitter, NoHyphenation}; - /// use textwrap::Options; - /// // The default type returned by `new`: - /// let opt: Options<_, _, HyphenSplitter> = Options::new(80); - /// // Setting a different word splitter changes the type - /// let opt: Options<_, _, NoHyphenation> = opt.word_splitter(NoHyphenation); + /// use textwrap::{Options, WordSplitter}; + /// let opt = Options::new(80); + /// assert_eq!(opt.word_splitter, WordSplitter::HyphenSplitter); + /// let opt = opt.word_splitter(WordSplitter::NoHyphenation); + /// assert_eq!(opt.word_splitter, WordSplitter::NoHyphenation); /// ``` /// /// [`self.word_splitter`]: #structfield.word_splitter - pub fn word_splitter( - self, - word_splitter: NewWordSplit, - ) -> Options<'a, WrapAlgo, WordSep, NewWordSplit> { + pub fn word_splitter(self, word_splitter: WordSplitter) -> Options<'a> { Options { width: self.width, initial_indent: self.initial_indent, @@ -675,11 +512,9 @@ impl<'a, WrapAlgo, WordSep, WordSplit> Options<'a, WrapAlgo, WordSep, WordSplit> /// /// ```no_run /// use textwrap::{termwidth, Options}; -/// use textwrap::word_splitters::NoHyphenation; /// /// let width = termwidth() - 4; // Two columns on each side. /// let options = Options::new(width) -/// .word_splitter(NoHyphenation) /// .initial_indent(" ") /// .subsequent_indent(" "); /// ``` @@ -723,12 +558,9 @@ pub fn termwidth() -> usize { /// "- Memory safety\n without\n garbage\n collection." /// ); /// ``` -pub fn fill<'a, WrapAlgo, WordSep, WordSplit, Opt>(text: &str, width_or_options: Opt) -> String +pub fn fill<'a, Opt>(text: &str, width_or_options: Opt) -> String where - WrapAlgo: wrap_algorithms::WrapAlgorithm, - WordSep: word_separators::WordSeparator, - WordSplit: word_splitters::WordSplitter, - Opt: Into>, + Opt: Into>, { // This will avoid reallocation in simple cases (no // indentation, no hyphenation). @@ -738,7 +570,7 @@ where if i > 0 { result.push('\n'); } - result.push_str(&line); + result.push_str(line); } result @@ -790,12 +622,7 @@ where /// assert_eq!(options.initial_indent, "* "); /// assert_eq!(options.subsequent_indent, " "); /// ``` -pub fn unfill( - text: &str, -) -> ( - String, - Options<'_, DefaultWrapAlgorithm!(), DefaultWordSeparator!(), word_splitters::HyphenSplitter>, -) { +pub fn unfill(text: &str) -> (String, Options<'_>) { let trimmed = text.trim_end_matches('\n'); let prefix_chars: &[_] = &[' ', '-', '+', '*', '>', '#', '/']; @@ -890,15 +717,9 @@ pub fn unfill( /// item. /// "); /// ``` -pub fn refill<'a, WrapAlgo, WordSep, WordSplit, Opt>( - filled_text: &str, - new_width_or_options: Opt, -) -> String +pub fn refill<'a, Opt>(filled_text: &str, new_width_or_options: Opt) -> String where - WrapAlgo: wrap_algorithms::WrapAlgorithm, - WordSep: word_separators::WordSeparator, - WordSplit: word_splitters::WordSplitter, - Opt: Into>, + Opt: Into>, { let trimmed = filled_text.trim_end_matches('\n'); let (text, options) = unfill(trimmed); @@ -964,7 +785,7 @@ where /// narrow column with room for only 10 characters looks like this: /// /// ``` -/// # use textwrap::{wrap_algorithms::FirstFit, Options, wrap}; +/// # use textwrap::{WrapAlgorithm::FirstFit, Options, wrap}; /// # /// # let lines = wrap("To be, or not to be: that is the question", /// # Options::new(10).wrap_algorithm(FirstFit)); @@ -988,11 +809,12 @@ where /// /// ``` /// # #[cfg(feature = "smawk")] { -/// # use textwrap::{Options, wrap}; -/// # use textwrap::wrap_algorithms::OptimalFit; +/// # use textwrap::{Options, WrapAlgorithm, wrap}; /// # -/// # let lines = wrap("To be, or not to be: that is the question", -/// # Options::new(10).wrap_algorithm(OptimalFit)); +/// # let lines = wrap( +/// # "To be, or not to be: that is the question", +/// # Options::new(10).wrap_algorithm(WrapAlgorithm::new_optimal_fit()) +/// # ); /// # assert_eq!(lines.join("\n") + "\n", "\ /// To be, /// or not to @@ -1002,7 +824,7 @@ where /// # "); } /// ``` /// -/// Please see the [`wrap_algorithms::WrapAlgorithm`] trait for details. +/// Please see [`WrapAlgorithm`] for details on the choices. /// /// # Examples /// @@ -1079,15 +901,9 @@ where /// assert_eq!(wrap(" foo bar", 8), vec![" foo", "bar"]); /// assert_eq!(wrap(" foo bar", 4), vec!["", "foo", "bar"]); /// ``` -pub fn wrap<'a, WrapAlgo, WordSep, WordSplit, Opt>( - text: &str, - width_or_options: Opt, -) -> Vec> +pub fn wrap<'a, Opt>(text: &str, width_or_options: Opt) -> Vec> where - WrapAlgo: wrap_algorithms::WrapAlgorithm, - WordSep: word_separators::WordSeparator, - WordSplit: word_splitters::WordSplitter, - Opt: Into>, + Opt: Into>, { let options = width_or_options.into(); @@ -1155,7 +971,7 @@ where result += &line[idx..idx + len]; if !last_word.penalty.is_empty() { - result.to_mut().push_str(&last_word.penalty); + result.to_mut().push_str(last_word.penalty); } lines.push(result); @@ -1227,7 +1043,7 @@ where /// "| example text, | columns. | shorter than |", /// "| which is | Notice how | the others. |", /// "| wrapped into | the final | |"]); -pub fn wrap_columns<'a, WrapAlgo, WordSep, WordSplit, Opt>( +pub fn wrap_columns<'a, Opt>( text: &str, columns: usize, total_width_or_options: Opt, @@ -1236,10 +1052,7 @@ pub fn wrap_columns<'a, WrapAlgo, WordSep, WordSplit, Opt>( right_gap: &str, ) -> Vec where - WrapAlgo: wrap_algorithms::WrapAlgorithm, - WordSep: word_separators::WordSeparator, - WordSplit: word_splitters::WordSplitter, - Opt: Into>, + Opt: Into>, { assert!(columns > 0); @@ -1263,8 +1076,8 @@ where for column_no in 0..columns { match wrapped_lines.get(line_no + column_no * lines_per_column) { Some(column_line) => { - line.push_str(&column_line); - line.push_str(&" ".repeat(column_width - core::display_width(&column_line))); + line.push_str(column_line); + line.push_str(&" ".repeat(column_width - core::display_width(column_line))); } None => { line.push_str(&" ".repeat(column_width)); @@ -1298,21 +1111,20 @@ where /// [`fill`] with these options: /// /// ``` -/// # use textwrap::{core, Options}; -/// # use textwrap::{word_separators, word_splitters, wrap_algorithms}; +/// # use textwrap::{core, Options, WordSplitter, WordSeparator, WrapAlgorithm}; /// # let width = 80; /// Options { /// width: width, /// initial_indent: "", /// subsequent_indent: "", /// break_words: false, -/// word_separator: word_separators::AsciiSpace, -/// wrap_algorithm: wrap_algorithms::FirstFit, -/// word_splitter: word_splitters::NoHyphenation, +/// word_separator: WordSeparator::AsciiSpace, +/// wrap_algorithm: WrapAlgorithm::FirstFit, +/// word_splitter: WordSplitter::NoHyphenation, /// }; /// ``` /// -/// The wrap algorithm is [`wrap_algorithms::FirstFit`] since this +/// The wrap algorithm is [`WrapAlgorithm::FirstFit`] since this /// is the fastest algorithm โ€” and the main reason to use /// `fill_inplace` is to get the string broken into newlines as fast /// as possible. @@ -1338,15 +1150,14 @@ where /// benchmark](https://github.com/mgeisler/textwrap/blob/master/benches/linear.rs) /// for details. pub fn fill_inplace(text: &mut String, width: usize) { - use word_separators::WordSeparator; let mut indices = Vec::new(); let mut offset = 0; for line in text.split('\n') { - let words = word_separators::AsciiSpace + let words = WordSeparator::AsciiSpace .find_words(line) .collect::>(); - let wrapped_words = wrap_algorithms::wrap_first_fit(&words, &[width]); + let wrapped_words = wrap_algorithms::wrap_first_fit(&words, &[width as f64]); let mut line_offset = offset; for words in &wrapped_words[..wrapped_words.len() - 1] { @@ -1376,8 +1187,6 @@ pub fn fill_inplace(text: &mut String, width: usize) { #[cfg(test)] mod tests { use super::*; - use crate::word_splitters::WordSplitter; - use crate::{word_splitters, wrap_algorithms}; #[cfg(feature = "hyphenation")] use hyphenation::{Language, Load, Standard}; @@ -1412,7 +1221,7 @@ mod tests { assert_eq!( wrap( "To be, or not to be, that is the question.", - Options::new(10).wrap_algorithm(wrap_algorithms::FirstFit) + Options::new(10).wrap_algorithm(WrapAlgorithm::FirstFit) ), vec!["To be, or", "not to be,", "that is", "the", "question."] ); @@ -1435,7 +1244,11 @@ mod tests { #[test] fn max_width() { - assert_eq!(wrap("foo bar", usize::max_value()), vec!["foo bar"]); + assert_eq!(wrap("foo bar", usize::MAX), vec!["foo bar"]); + + let text = "Hello there! This is some English text. \ + It should not be wrapped given the extents below."; + assert_eq!(wrap(text, usize::MAX), vec![text]); } #[test] @@ -1474,18 +1287,17 @@ mod tests { fn issue_129() { // The dash is an em-dash which takes up four bytes. We used // to panic since we tried to index into the character. - let options = Options::new(1).word_separator(word_separators::AsciiSpace); + let options = Options::new(1).word_separator(WordSeparator::AsciiSpace); assert_eq!(wrap("x โ€“ x", options), vec!["x", "โ€“", "x"]); } #[test] - #[cfg(feature = "unicode-width")] fn wide_character_handling() { assert_eq!(wrap("Hello, World!", 15), vec!["Hello, World!"]); assert_eq!( wrap( "๏ผจ๏ฝ…๏ฝŒ๏ฝŒ๏ฝ, ๏ผท๏ฝ๏ฝ’๏ฝŒ๏ฝ„!", - Options::new(15).word_separator(word_separators::AsciiSpace) + Options::new(15).word_separator(WordSeparator::AsciiSpace) ), vec!["๏ผจ๏ฝ…๏ฝŒ๏ฝŒ๏ฝ,", "๏ผท๏ฝ๏ฝ’๏ฝŒ๏ฝ„!"] ); @@ -1496,7 +1308,7 @@ mod tests { assert_eq!( wrap( "๏ผจ๏ฝ…๏ฝŒ๏ฝŒ๏ฝ, ๏ผท๏ฝ๏ฝ’๏ฝŒ๏ฝ„!", - Options::new(15).word_separator(word_separators::UnicodeBreakProperties) + Options::new(15).word_separator(WordSeparator::UnicodeBreakProperties) ), vec!["๏ผจ๏ฝ…๏ฝŒ๏ฝŒ๏ฝ, ๏ผท", "๏ฝ๏ฝ’๏ฝŒ๏ฝ„!"] ); @@ -1519,7 +1331,6 @@ mod tests { } #[test] - #[cfg(feature = "unicode-width")] fn indent_first_emoji() { let options = Options::new(10).initial_indent("๐Ÿ‘‰๐Ÿ‘‰"); assert_eq!( @@ -1627,34 +1438,20 @@ mod tests { } #[test] - fn simple_hyphens_static() { - let options = Options::new(8).word_splitter(word_splitters::HyphenSplitter); + fn simple_hyphens() { + let options = Options::new(8).word_splitter(WordSplitter::HyphenSplitter); assert_eq!(wrap("foo bar-baz", &options), vec!["foo bar-", "baz"]); } #[test] - fn simple_hyphens_dynamic() { - let options: Options<_, _> = - Options::new(8).word_splitter(Box::new(word_splitters::HyphenSplitter)); - assert_eq!(wrap("foo bar-baz", &options), vec!["foo bar-", "baz"]); - } - - #[test] - fn no_hyphenation_static() { - let options = Options::new(8).word_splitter(word_splitters::NoHyphenation); - assert_eq!(wrap("foo bar-baz", &options), vec!["foo", "bar-baz"]); - } - - #[test] - fn no_hyphenation_dynamic() { - let options: Options<_, _> = - Options::new(8).word_splitter(Box::new(word_splitters::NoHyphenation)); + fn no_hyphenation() { + let options = Options::new(8).word_splitter(WordSplitter::NoHyphenation); assert_eq!(wrap("foo bar-baz", &options), vec!["foo", "bar-baz"]); } #[test] #[cfg(feature = "hyphenation")] - fn auto_hyphenation_double_hyphenation_static() { + fn auto_hyphenation_double_hyphenation() { let dictionary = Standard::from_embedded(Language::EnglishUS).unwrap(); let options = Options::new(10); assert_eq!( @@ -1662,25 +1459,7 @@ mod tests { vec!["Internatio", "nalization"] ); - let options = Options::new(10).word_splitter(dictionary); - assert_eq!( - wrap("Internationalization", &options), - vec!["Interna-", "tionaliza-", "tion"] - ); - } - - #[test] - #[cfg(feature = "hyphenation")] - fn auto_hyphenation_double_hyphenation_dynamic() { - let dictionary = Standard::from_embedded(Language::EnglishUS).unwrap(); - let mut options: Options<_, _, Box> = - Options::new(10).word_splitter(Box::new(word_splitters::HyphenSplitter)); - assert_eq!( - wrap("Internationalization", &options), - vec!["Internatio", "nalization"] - ); - - options = Options::new(10).word_splitter(Box::new(dictionary)); + let options = Options::new(10).word_splitter(WordSplitter::Hyphenation(dictionary)); assert_eq!( wrap("Internationalization", &options), vec!["Interna-", "tionaliza-", "tion"] @@ -1697,7 +1476,7 @@ mod tests { vec!["participat", "ion is", "the key to", "success"] ); - let options = Options::new(10).word_splitter(dictionary); + let options = Options::new(10).word_splitter(WordSplitter::Hyphenation(dictionary)); assert_eq!( wrap("participation is the key to success", &options), vec!["partici-", "pation is", "the key to", "success"] @@ -1707,10 +1486,10 @@ mod tests { #[test] #[cfg(feature = "hyphenation")] fn split_len_hyphenation() { - // Test that hyphenation takes the width of the wihtespace + // Test that hyphenation takes the width of the whitespace // into account. let dictionary = Standard::from_embedded(Language::EnglishUS).unwrap(); - let options = Options::new(15).word_splitter(dictionary); + let options = Options::new(15).word_splitter(WordSplitter::Hyphenation(dictionary)); assert_eq!( wrap("garbage collection", &options), vec!["garbage col-", "lection"] @@ -1724,8 +1503,9 @@ mod tests { // line is borrowed. use std::borrow::Cow::{Borrowed, Owned}; let dictionary = Standard::from_embedded(Language::EnglishUS).unwrap(); - let options = Options::new(10).word_splitter(dictionary); + let options = Options::new(10).word_splitter(WordSplitter::Hyphenation(dictionary)); let lines = wrap("Internationalization", &options); + assert_eq!(lines, vec!["Interna-", "tionaliza-", "tion"]); if let Borrowed(s) = lines[0] { assert!(false, "should not have been borrowed: {:?}", s); } @@ -1747,7 +1527,7 @@ mod tests { vec!["over-", "caffinated"] ); - let options = options.word_splitter(dictionary); + let options = options.word_splitter(WordSplitter::Hyphenation(dictionary)); assert_eq!( wrap("over-caffinated", &options), vec!["over-", "caffi-", "nated"] @@ -1763,7 +1543,7 @@ mod tests { fn break_words_wide_characters() { // Even the poor man's version of `ch_width` counts these // characters as wide. - let options = Options::new(5).word_separator(word_separators::AsciiSpace); + let options = Options::new(5).word_separator(WordSeparator::AsciiSpace); assert_eq!(wrap("๏ผจ๏ฝ…๏ฝŒ๏ฝŒ๏ฝ", options), vec!["๏ผจ๏ฝ…", "๏ฝŒ๏ฝŒ", "๏ฝ"]); } @@ -1801,14 +1581,14 @@ mod tests { assert_eq!( fill( "1 3 5 7\n1 3 5 7", - Options::new(7).wrap_algorithm(wrap_algorithms::FirstFit) + Options::new(7).wrap_algorithm(WrapAlgorithm::FirstFit) ), "1 3 5 7\n1 3 5 7" ); assert_eq!( fill( "1 3 5 7\n1 3 5 7", - Options::new(5).wrap_algorithm(wrap_algorithms::FirstFit) + Options::new(5).wrap_algorithm(WrapAlgorithm::FirstFit) ), "1 3 5\n7\n1 3 5\n7" ); @@ -1856,20 +1636,6 @@ mod tests { fill("\u{1b}!ฯฟ", 10); } - #[test] - #[cfg(not(feature = "smawk"))] - #[cfg(not(feature = "unicode-linebreak"))] - fn cloning_works() { - static OPT: Options< - wrap_algorithms::FirstFit, - word_separators::AsciiSpace, - word_splitters::HyphenSplitter, - > = Options::with_word_splitter(80, word_splitters::HyphenSplitter); - #[allow(clippy::clone_on_copy)] - let opt = OPT.clone(); - assert_eq!(opt.width, 80); - } - #[test] fn fill_inplace_empty() { let mut text = String::from(""); @@ -2005,60 +1771,6 @@ mod tests { assert_eq!(unfill("foo bar").0, "foo bar"); } - #[test] - fn trait_object_vec() { - // Create a vector of Options containing trait-objects. - let mut vector: Vec< - Options< - _, - Box, - Box, - >, - > = Vec::new(); - // Expected result from each options - let mut results = Vec::new(); - - let opt_full_type: Options< - _, - Box, - Box, - > = - Options::new(10) - .word_splitter(Box::new(word_splitters::HyphenSplitter) - as Box) - .word_separator(Box::new(word_separators::AsciiSpace) - as Box); - vector.push(opt_full_type); - results.push(vec!["over-", "caffinated"]); - - // Actually: Options, Box> - let opt_abbreviated_type = - Options::new(10) - .break_words(false) - .word_splitter(Box::new(word_splitters::NoHyphenation) - as Box) - .word_separator(Box::new(word_separators::AsciiSpace) - as Box); - vector.push(opt_abbreviated_type); - results.push(vec!["over-caffinated"]); - - #[cfg(feature = "hyphenation")] - { - let dictionary = Standard::from_embedded(Language::EnglishUS).unwrap(); - let opt_hyp = Options::new(8) - .word_splitter(Box::new(dictionary) as Box) - .word_separator(Box::new(word_separators::AsciiSpace) - as Box); - vector.push(opt_hyp); - results.push(vec!["over-", "caffi-", "nated"]); - } - - // Test each entry - for (opt, expected) in vector.into_iter().zip(results) { - assert_eq!(wrap("over-caffinated", opt), expected); - } - } - #[test] fn wrap_columns_empty_text() { assert_eq!(wrap_columns("", 1, 10, "| ", "", " |"), vec!["| |"]); diff --git a/third_party/rust/textwrap/src/word_separators.rs b/third_party/rust/textwrap/src/word_separators.rs index db03a91f8542..25adf31b1253 100644 --- a/third_party/rust/textwrap/src/word_separators.rs +++ b/third_party/rust/textwrap/src/word_separators.rs @@ -25,247 +25,162 @@ use crate::core::Word; /// without emojis. A more complex approach is to use the Unicode line /// breaking algorithm, which finds break points in non-ASCII text. /// -/// The line breaks occur between words, please see the -/// [`WordSplitter`](crate::word_splitters::WordSplitter) trait for -/// options of how to handle hyphenation of individual words. +/// The line breaks occur between words, please see +/// [`WordSplitter`](crate::WordSplitter) for options of how to handle +/// hyphenation of individual words. /// /// # Examples /// /// ``` /// use textwrap::core::Word; -/// use textwrap::word_separators::{WordSeparator, AsciiSpace}; +/// use textwrap::WordSeparator::AsciiSpace; /// /// let words = AsciiSpace.find_words("Hello World!").collect::>(); /// assert_eq!(words, vec![Word::from("Hello "), Word::from("World!")]); /// ``` -pub trait WordSeparator: WordSeparatorClone + std::fmt::Debug { - // This trait should really return impl Iterator, but +#[derive(Clone, Copy)] +pub enum WordSeparator { + /// Find words by splitting on runs of `' '` characters. + /// + /// # Examples + /// + /// ``` + /// use textwrap::core::Word; + /// use textwrap::WordSeparator::AsciiSpace; + /// + /// let words = AsciiSpace.find_words("Hello World!").collect::>(); + /// assert_eq!(words, vec![Word::from("Hello "), + /// Word::from("World!")]); + /// ``` + AsciiSpace, + + /// Split `line` into words using Unicode break properties. + /// + /// This word separator uses the Unicode line breaking algorithm + /// described in [Unicode Standard Annex + /// #14](https://www.unicode.org/reports/tr14/) to find legal places + /// to break lines. There is a small difference in that the U+002D + /// (Hyphen-Minus) and U+00AD (Soft Hyphen) donโ€™t create a line break: + /// to allow a line break at a hyphen, use + /// [`WordSplitter::HyphenSplitter`](crate::WordSplitter::HyphenSplitter). + /// Soft hyphens are not currently supported. + /// + /// # Examples + /// + /// Unlike [`WordSeparator::AsciiSpace`], the Unicode line + /// breaking algorithm will find line break opportunities between + /// some characters with no intervening whitespace: + /// + /// ``` + /// #[cfg(feature = "unicode-linebreak")] { + /// use textwrap::core::Word; + /// use textwrap::WordSeparator::UnicodeBreakProperties; + /// + /// assert_eq!(UnicodeBreakProperties.find_words("Emojis: ๐Ÿ˜‚๐Ÿ˜").collect::>(), + /// vec![Word::from("Emojis: "), + /// Word::from("๐Ÿ˜‚"), + /// Word::from("๐Ÿ˜")]); + /// + /// assert_eq!(UnicodeBreakProperties.find_words("CJK: ไฝ ๅฅฝ").collect::>(), + /// vec![Word::from("CJK: "), + /// Word::from("ไฝ "), + /// Word::from("ๅฅฝ")]); + /// } + /// ``` + /// + /// A U+2060 (Word Joiner) character can be inserted if you want to + /// manually override the defaults and keep the characters together: + /// + /// ``` + /// #[cfg(feature = "unicode-linebreak")] { + /// use textwrap::core::Word; + /// use textwrap::WordSeparator::UnicodeBreakProperties; + /// + /// assert_eq!(UnicodeBreakProperties.find_words("Emojis: ๐Ÿ˜‚\u{2060}๐Ÿ˜").collect::>(), + /// vec![Word::from("Emojis: "), + /// Word::from("๐Ÿ˜‚\u{2060}๐Ÿ˜")]); + /// } + /// ``` + /// + /// The Unicode line breaking algorithm will also automatically + /// suppress break breaks around certain punctuation characters:: + /// + /// ``` + /// #[cfg(feature = "unicode-linebreak")] { + /// use textwrap::core::Word; + /// use textwrap::WordSeparator::UnicodeBreakProperties; + /// + /// assert_eq!(UnicodeBreakProperties.find_words("[ foo ] bar !").collect::>(), + /// vec![Word::from("[ foo ] "), + /// Word::from("bar !")]); + /// } + /// ``` + #[cfg(feature = "unicode-linebreak")] + UnicodeBreakProperties, + + /// Find words using a custom word separator + Custom(fn(line: &str) -> Box> + '_>), +} + +impl std::fmt::Debug for WordSeparator { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + WordSeparator::AsciiSpace => f.write_str("AsciiSpace"), + #[cfg(feature = "unicode-linebreak")] + WordSeparator::UnicodeBreakProperties => f.write_str("UnicodeBreakProperties"), + WordSeparator::Custom(_) => f.write_str("Custom(...)"), + } + } +} + +impl WordSeparator { + // This function should really return impl Iterator, but // this isn't possible until Rust supports higher-kinded types: // https://github.com/rust-lang/rfcs/blob/master/text/1522-conservative-impl-trait.md /// Find all words in `line`. - fn find_words<'a>(&self, line: &'a str) -> Box> + 'a>; -} - -// The internal `WordSeparatorClone` trait is allows us to implement -// `Clone` for `Box`. This in used in the -// `From<&Options<'_, WrapAlgo, WordSep, WordSplit>> for Options<'a, -// WrapAlgo, WordSep, WordSplit>` implementation. -#[doc(hidden)] -pub trait WordSeparatorClone { - fn clone_box(&self) -> Box; -} - -impl WordSeparatorClone for T { - fn clone_box(&self) -> Box { - Box::new(self.clone()) + pub fn find_words<'a>(&self, line: &'a str) -> Box> + 'a> { + match self { + WordSeparator::AsciiSpace => find_words_ascii_space(line), + #[cfg(feature = "unicode-linebreak")] + WordSeparator::UnicodeBreakProperties => find_words_unicode_break_properties(line), + WordSeparator::Custom(func) => func(line), + } } } -impl Clone for Box { - fn clone(&self) -> Box { - use std::ops::Deref; - self.deref().clone_box() - } -} - -impl WordSeparator for Box { - fn find_words<'a>(&self, line: &'a str) -> Box> + 'a> { - use std::ops::Deref; - self.deref().find_words(line) - } -} - -/// Find words by splitting on regions of `' '` characters. -#[derive(Clone, Copy, Debug, Default)] -pub struct AsciiSpace; - -/// Split `line` into words separated by regions of `' '` characters. -/// -/// # Examples -/// -/// ``` -/// use textwrap::core::Word; -/// use textwrap::word_separators::{AsciiSpace, WordSeparator}; -/// -/// let words = AsciiSpace.find_words("Hello World!").collect::>(); -/// assert_eq!(words, vec![Word::from("Hello "), -/// Word::from("World!")]); -/// ``` -impl WordSeparator for AsciiSpace { - fn find_words<'a>(&self, line: &'a str) -> Box> + 'a> { - let mut start = 0; - let mut in_whitespace = false; - let mut char_indices = line.char_indices(); - - Box::new(std::iter::from_fn(move || { - // for (idx, ch) in char_indices does not work, gives this - // error: - // - // > cannot move out of `char_indices`, a captured variable in - // > an `FnMut` closure - #[allow(clippy::while_let_on_iterator)] - while let Some((idx, ch)) = char_indices.next() { - if in_whitespace && ch != ' ' { - let word = Word::from(&line[start..idx]); - start = idx; - in_whitespace = ch == ' '; - return Some(word); - } +fn find_words_ascii_space<'a>(line: &'a str) -> Box> + 'a> { + let mut start = 0; + let mut in_whitespace = false; + let mut char_indices = line.char_indices(); + Box::new(std::iter::from_fn(move || { + // for (idx, ch) in char_indices does not work, gives this + // error: + // + // > cannot move out of `char_indices`, a captured variable in + // > an `FnMut` closure + #[allow(clippy::while_let_on_iterator)] + while let Some((idx, ch)) = char_indices.next() { + if in_whitespace && ch != ' ' { + let word = Word::from(&line[start..idx]); + start = idx; in_whitespace = ch == ' '; - } - - if start < line.len() { - let word = Word::from(&line[start..]); - start = line.len(); return Some(word); } - None - })) - } + in_whitespace = ch == ' '; + } + + if start < line.len() { + let word = Word::from(&line[start..]); + start = line.len(); + return Some(word); + } + + None + })) } -/// Find words using the Unicode line breaking algorithm. -#[cfg(feature = "unicode-linebreak")] -#[derive(Clone, Copy, Debug, Default)] -pub struct UnicodeBreakProperties; - -/// Split `line` into words using Unicode break properties. -/// -/// This word separator uses the Unicode line breaking algorithm -/// described in [Unicode Standard Annex -/// #14](https://www.unicode.org/reports/tr14/) to find legal places -/// to break lines. There is a small difference in that the U+002D -/// (Hyphen-Minus) and U+00AD (Soft Hyphen) donโ€™t create a line break: -/// to allow a line break at a hyphen, use the -/// [`HyphenSplitter`](crate::word_splitters::HyphenSplitter). Soft -/// hyphens are not currently supported. -/// -/// # Examples -/// -/// Unlike [`AsciiSpace`], the Unicode line breaking algorithm will -/// find line break opportunities between some characters with no -/// intervening whitespace: -/// -/// ``` -/// #[cfg(feature = "unicode-linebreak")] { -/// use textwrap::word_separators::{WordSeparator, UnicodeBreakProperties}; -/// use textwrap::core::Word; -/// -/// assert_eq!(UnicodeBreakProperties.find_words("Emojis: ๐Ÿ˜‚๐Ÿ˜").collect::>(), -/// vec![Word::from("Emojis: "), -/// Word::from("๐Ÿ˜‚"), -/// Word::from("๐Ÿ˜")]); -/// -/// assert_eq!(UnicodeBreakProperties.find_words("CJK: ไฝ ๅฅฝ").collect::>(), -/// vec![Word::from("CJK: "), -/// Word::from("ไฝ "), -/// Word::from("ๅฅฝ")]); -/// } -/// ``` -/// -/// A U+2060 (Word Joiner) character can be inserted if you want to -/// manually override the defaults and keep the characters together: -/// -/// ``` -/// #[cfg(feature = "unicode-linebreak")] { -/// use textwrap::word_separators::{UnicodeBreakProperties, WordSeparator}; -/// use textwrap::core::Word; -/// -/// assert_eq!(UnicodeBreakProperties.find_words("Emojis: ๐Ÿ˜‚\u{2060}๐Ÿ˜").collect::>(), -/// vec![Word::from("Emojis: "), -/// Word::from("๐Ÿ˜‚\u{2060}๐Ÿ˜")]); -/// } -/// ``` -/// -/// The Unicode line breaking algorithm will also automatically -/// suppress break breaks around certain punctuation characters:: -/// -/// ``` -/// #[cfg(feature = "unicode-linebreak")] { -/// use textwrap::word_separators::{UnicodeBreakProperties, WordSeparator}; -/// use textwrap::core::Word; -/// -/// assert_eq!(UnicodeBreakProperties.find_words("[ foo ] bar !").collect::>(), -/// vec![Word::from("[ foo ] "), -/// Word::from("bar !")]); -/// } -/// ``` -#[cfg(feature = "unicode-linebreak")] -impl WordSeparator for UnicodeBreakProperties { - fn find_words<'a>(&self, line: &'a str) -> Box> + 'a> { - // Construct an iterator over (original index, stripped index) - // tuples. We find the Unicode linebreaks on a stripped string, - // but we need the original indices so we can form words based on - // the original string. - let mut last_stripped_idx = 0; - let mut char_indices = line.char_indices(); - let mut idx_map = std::iter::from_fn(move || match char_indices.next() { - Some((orig_idx, ch)) => { - let stripped_idx = last_stripped_idx; - if !skip_ansi_escape_sequence(ch, &mut char_indices.by_ref().map(|(_, ch)| ch)) { - last_stripped_idx += ch.len_utf8(); - } - Some((orig_idx, stripped_idx)) - } - None => None, - }); - - let stripped = strip_ansi_escape_sequences(&line); - let mut opportunities = unicode_linebreak::linebreaks(&stripped) - .filter(|(idx, _)| { - #[allow(clippy::match_like_matches_macro)] - match &stripped[..*idx].chars().next_back() { - // We suppress breaks at โ€˜-โ€™ since we want to control - // this via the WordSplitter. - Some('-') => false, - // Soft hyphens are currently not supported since we - // require all `Word` fragments to be continuous in - // the input string. - Some(SHY) => false, - // Other breaks should be fine! - _ => true, - } - }) - .collect::>() - .into_iter(); - - // Remove final break opportunity, we will add it below using - // &line[start..]; This ensures that we correctly include a - // trailing ANSI escape sequence. - opportunities.next_back(); - - let mut start = 0; - Box::new(std::iter::from_fn(move || { - #[allow(clippy::while_let_on_iterator)] - while let Some((idx, _)) = opportunities.next() { - if let Some((orig_idx, _)) = idx_map.find(|&(_, stripped_idx)| stripped_idx == idx) - { - let word = Word::from(&line[start..orig_idx]); - start = orig_idx; - return Some(word); - } - } - - if start < line.len() { - let word = Word::from(&line[start..]); - start = line.len(); - return Some(word); - } - - None - })) - } -} - -/// Soft hyphen, also knows as a โ€œshy hyphenโ€. Should show up as โ€˜-โ€™ -/// if a line is broken at this point, and otherwise be invisible. -/// Textwrap does not currently support breaking words at soft -/// hyphens. -#[cfg(feature = "unicode-linebreak")] -const SHY: char = '\u{00ad}'; - // Strip all ANSI escape sequences from `text`. #[cfg(feature = "unicode-linebreak")] fn strip_ansi_escape_sequences(text: &str) -> String { @@ -282,8 +197,83 @@ fn strip_ansi_escape_sequences(text: &str) -> String { result } +/// Soft hyphen, also knows as a โ€œshy hyphenโ€. Should show up as โ€˜-โ€™ +/// if a line is broken at this point, and otherwise be invisible. +/// Textwrap does not currently support breaking words at soft +/// hyphens. +#[cfg(feature = "unicode-linebreak")] +const SHY: char = '\u{00ad}'; + +/// Find words in line. ANSI escape sequences are ignored in `line`. +#[cfg(feature = "unicode-linebreak")] +fn find_words_unicode_break_properties<'a>( + line: &'a str, +) -> Box> + 'a> { + // Construct an iterator over (original index, stripped index) + // tuples. We find the Unicode linebreaks on a stripped string, + // but we need the original indices so we can form words based on + // the original string. + let mut last_stripped_idx = 0; + let mut char_indices = line.char_indices(); + let mut idx_map = std::iter::from_fn(move || match char_indices.next() { + Some((orig_idx, ch)) => { + let stripped_idx = last_stripped_idx; + if !skip_ansi_escape_sequence(ch, &mut char_indices.by_ref().map(|(_, ch)| ch)) { + last_stripped_idx += ch.len_utf8(); + } + Some((orig_idx, stripped_idx)) + } + None => None, + }); + + let stripped = strip_ansi_escape_sequences(line); + let mut opportunities = unicode_linebreak::linebreaks(&stripped) + .filter(|(idx, _)| { + #[allow(clippy::match_like_matches_macro)] + match &stripped[..*idx].chars().next_back() { + // We suppress breaks at โ€˜-โ€™ since we want to control + // this via the WordSplitter. + Some('-') => false, + // Soft hyphens are currently not supported since we + // require all `Word` fragments to be continuous in + // the input string. + Some(SHY) => false, + // Other breaks should be fine! + _ => true, + } + }) + .collect::>() + .into_iter(); + + // Remove final break opportunity, we will add it below using + // &line[start..]; This ensures that we correctly include a + // trailing ANSI escape sequence. + opportunities.next_back(); + + let mut start = 0; + Box::new(std::iter::from_fn(move || { + #[allow(clippy::while_let_on_iterator)] + while let Some((idx, _)) = opportunities.next() { + if let Some((orig_idx, _)) = idx_map.find(|&(_, stripped_idx)| stripped_idx == idx) { + let word = Word::from(&line[start..orig_idx]); + start = orig_idx; + return Some(word); + } + } + + if start < line.len() { + let word = Word::from(&line[start..]); + start = line.len(); + return Some(word); + } + + None + })) +} + #[cfg(test)] mod tests { + use super::WordSeparator::*; use super::*; // Like assert_eq!, but the left expression is an iterator. @@ -293,89 +283,121 @@ mod tests { }; } - #[test] - fn ascii_space_empty() { - assert_iter_eq!(AsciiSpace.find_words(""), vec![]); + fn to_words<'a>(words: Vec<&'a str>) -> Vec> { + words.into_iter().map(|w: &str| Word::from(&w)).collect() } - #[test] - fn ascii_space_single_word() { - assert_iter_eq!(AsciiSpace.find_words("foo"), vec![Word::from("foo")]); + macro_rules! test_find_words { + ($ascii_name:ident, + $unicode_name:ident, + $([ $line:expr, $ascii_words:expr, $unicode_words:expr ]),+) => { + #[test] + fn $ascii_name() { + $( + let expected_words = to_words($ascii_words.to_vec()); + let actual_words = WordSeparator::AsciiSpace + .find_words($line) + .collect::>(); + assert_eq!(actual_words, expected_words, "Line: {:?}", $line); + )+ + } + + #[test] + #[cfg(feature = "unicode-linebreak")] + fn $unicode_name() { + $( + let expected_words = to_words($unicode_words.to_vec()); + let actual_words = WordSeparator::UnicodeBreakProperties + .find_words($line) + .collect::>(); + assert_eq!(actual_words, expected_words, "Line: {:?}", $line); + )+ + } + }; } - #[test] - fn ascii_space_two_words() { - assert_iter_eq!( - AsciiSpace.find_words("foo bar"), - vec![Word::from("foo "), Word::from("bar")] - ); - } + test_find_words!(ascii_space_empty, unicode_empty, ["", [], []]); - #[test] - fn ascii_space_multiple_words() { - assert_iter_eq!( - AsciiSpace.find_words("foo bar baz"), - vec![Word::from("foo "), Word::from("bar "), Word::from("baz")] - ); - } + test_find_words!( + ascii_single_word, + unicode_single_word, + ["foo", ["foo"], ["foo"]] + ); - #[test] - fn ascii_space_only_whitespace() { - assert_iter_eq!(AsciiSpace.find_words(" "), vec![Word::from(" ")]); - } + test_find_words!( + ascii_two_words, + unicode_two_words, + ["foo bar", ["foo ", "bar"], ["foo ", "bar"]] + ); - #[test] - fn ascii_space_inter_word_whitespace() { - assert_iter_eq!( - AsciiSpace.find_words("foo bar"), - vec![Word::from("foo "), Word::from("bar")] - ) - } + test_find_words!( + ascii_multiple_words, + unicode_multiple_words, + ["foo bar", ["foo ", "bar"], ["foo ", "bar"]], + ["x y z", ["x ", "y ", "z"], ["x ", "y ", "z"]] + ); - #[test] - fn ascii_space_trailing_whitespace() { - assert_iter_eq!(AsciiSpace.find_words("foo "), vec![Word::from("foo ")]); - } + test_find_words!( + ascii_only_whitespace, + unicode_only_whitespace, + [" ", [" "], [" "]], + [" ", [" "], [" "]] + ); - #[test] - fn ascii_space_leading_whitespace() { - assert_iter_eq!( - AsciiSpace.find_words(" foo"), - vec![Word::from(" "), Word::from("foo")] - ); - } + test_find_words!( + ascii_inter_word_whitespace, + unicode_inter_word_whitespace, + ["foo bar", ["foo ", "bar"], ["foo ", "bar"]] + ); - #[test] - fn ascii_space_multi_column_char() { - assert_iter_eq!( - AsciiSpace.find_words("\u{1f920}"), // cowboy emoji ๐Ÿค  - vec![Word::from("\u{1f920}")] - ); - } + test_find_words!( + ascii_trailing_whitespace, + unicode_trailing_whitespace, + ["foo ", ["foo "], ["foo "]] + ); - #[test] - fn ascii_space_hyphens() { - assert_iter_eq!( - AsciiSpace.find_words("foo-bar"), - vec![Word::from("foo-bar")] - ); - assert_iter_eq!( - AsciiSpace.find_words("foo- bar"), - vec![Word::from("foo- "), Word::from("bar")] - ); - assert_iter_eq!( - AsciiSpace.find_words("foo - bar"), - vec![Word::from("foo "), Word::from("- "), Word::from("bar")] - ); - assert_iter_eq!( - AsciiSpace.find_words("foo -bar"), - vec![Word::from("foo "), Word::from("-bar")] - ); - } + test_find_words!( + ascii_leading_whitespace, + unicode_leading_whitespace, + [" foo", [" ", "foo"], [" ", "foo"]] + ); + + test_find_words!( + ascii_multi_column_char, + unicode_multi_column_char, + ["\u{1f920}", ["\u{1f920}"], ["\u{1f920}"]] // cowboy emoji ๐Ÿค  + ); + + test_find_words!( + ascii_hyphens, + unicode_hyphens, + ["foo-bar", ["foo-bar"], ["foo-bar"]], + ["foo- bar", ["foo- ", "bar"], ["foo- ", "bar"]], + ["foo - bar", ["foo ", "- ", "bar"], ["foo ", "- ", "bar"]], + ["foo -bar", ["foo ", "-bar"], ["foo ", "-bar"]] + ); + + test_find_words!( + ascii_newline, + unicode_newline, + ["foo\nbar", ["foo\nbar"], ["foo\n", "bar"]] + ); + + test_find_words!( + ascii_tab, + unicode_tab, + ["foo\tbar", ["foo\tbar"], ["foo\t", "bar"]] + ); + + test_find_words!( + ascii_non_breaking_space, + unicode_non_breaking_space, + ["foo\u{00A0}bar", ["foo\u{00A0}bar"], ["foo\u{00A0}bar"]] + ); #[test] #[cfg(unix)] - fn ascii_space_colored_text() { + fn find_words_colored_text() { use termion::color::{Blue, Fg, Green, Reset}; let green_hello = format!("{}Hello{} ", Fg(Green), Fg(Reset)); @@ -393,7 +415,7 @@ mod tests { } #[test] - fn ascii_space_color_inside_word() { + fn find_words_color_inside_word() { let text = "foo\u{1b}[0m\u{1b}[32mbar\u{1b}[0mbaz"; assert_iter_eq!(AsciiSpace.find_words(&text), vec![Word::from(text)]); diff --git a/third_party/rust/textwrap/src/word_splitters.rs b/third_party/rust/textwrap/src/word_splitters.rs index f4d94c7021b1..69e246f0b8cc 100644 --- a/third_party/rust/textwrap/src/word_splitters.rs +++ b/third_party/rust/textwrap/src/word_splitters.rs @@ -1,30 +1,26 @@ //! Word splitting functionality. //! //! To wrap text into lines, long words sometimes need to be split -//! across lines. The [`WordSplitter`] trait defines this -//! functionality. [`HyphenSplitter`] is the default implementation of -//! this treat: it will simply split words on existing hyphens. - -use std::ops::Deref; +//! across lines. The [`WordSplitter`] enum defines this +//! functionality. use crate::core::{display_width, Word}; -/// The `WordSplitter` trait describes where words can be split. +/// The `WordSplitter` enum describes where words can be split. /// /// If the textwrap crate has been compiled with the `hyphenation` -/// Cargo feature enabled, you will find an implementation of -/// `WordSplitter` by the `hyphenation::Standard` struct. Use this -/// struct for language-aware hyphenation: +/// Cargo feature enabled, you will find a +/// [`WordSplitter::Hyphenation`] variant. Use this struct for +/// language-aware hyphenation: /// /// ``` -/// #[cfg(feature = "hyphenation")] -/// { +/// #[cfg(feature = "hyphenation")] { /// use hyphenation::{Language, Load, Standard}; -/// use textwrap::{wrap, Options}; +/// use textwrap::{wrap, Options, WordSplitter}; /// /// let text = "Oxidation is the loss of electrons."; /// let dictionary = Standard::from_embedded(Language::EnglishUS).unwrap(); -/// let options = Options::new(8).word_splitter(dictionary); +/// let options = Options::new(8).word_splitter(WordSplitter::Hyphenation(dictionary)); /// assert_eq!(wrap(text, &options), vec!["Oxida-", /// "tion is", /// "the loss", @@ -37,124 +33,142 @@ use crate::core::{display_width, Word}; /// details. /// /// [hyphenation]: https://docs.rs/hyphenation/ -pub trait WordSplitter: WordSplitterClone + std::fmt::Debug { - /// Return all possible indices where `word` can be split. +#[derive(Clone)] +pub enum WordSplitter { + /// Use this as a [`Options.word_splitter`] to avoid any kind of + /// hyphenation: /// - /// The indices returned must be in range `0..word.len()`. They - /// should point to the index _after_ the split point, i.e., after - /// `-` if splitting on hyphens. This way, `word.split_at(idx)` - /// will break the word into two well-formed pieces. + /// ``` + /// use textwrap::{wrap, Options, WordSplitter}; + /// + /// let options = Options::new(8).word_splitter(WordSplitter::NoHyphenation); + /// assert_eq!(wrap("foo bar-baz", &options), + /// vec!["foo", "bar-baz"]); + /// ``` + /// + /// [`Options.word_splitter`]: super::Options::word_splitter + NoHyphenation, + + /// `HyphenSplitter` is the default `WordSplitter` used by + /// [`Options::new`](super::Options::new). It will split words on + /// existing hyphens in the word. + /// + /// It will only use hyphens that are surrounded by alphanumeric + /// characters, which prevents a word like `"--foo-bar"` from + /// being split into `"--"` and `"foo-bar"`. /// /// # Examples /// /// ``` - /// use textwrap::word_splitters::{HyphenSplitter, NoHyphenation, WordSplitter}; - /// assert_eq!(NoHyphenation.split_points("cannot-be-split"), vec![]); - /// assert_eq!(HyphenSplitter.split_points("can-be-split"), vec![4, 7]); + /// use textwrap::WordSplitter; + /// + /// assert_eq!(WordSplitter::HyphenSplitter.split_points("--foo-bar"), + /// vec![6]); /// ``` - fn split_points(&self, word: &str) -> Vec; + HyphenSplitter, + + /// Use a custom function as the word splitter. + /// + /// This varian lets you implement a custom word splitter using + /// your own function. + /// + /// # Examples + /// + /// ``` + /// use textwrap::WordSplitter; + /// + /// fn split_at_underscore(word: &str) -> Vec { + /// word.match_indices('_').map(|(idx, _)| idx + 1).collect() + /// } + /// + /// let word_splitter = WordSplitter::Custom(split_at_underscore); + /// assert_eq!(word_splitter.split_points("a_long_identifier"), + /// vec![2, 7]); + /// ``` + Custom(fn(word: &str) -> Vec), + + /// A hyphenation dictionary can be used to do language-specific + /// hyphenation using patterns from the [hyphenation] crate. + /// + /// **Note:** Only available when the `hyphenation` Cargo feature is + /// enabled. + /// + /// [hyphenation]: https://docs.rs/hyphenation/ + #[cfg(feature = "hyphenation")] + Hyphenation(hyphenation::Standard), } -// The internal `WordSplitterClone` trait is allows us to implement -// `Clone` for `Box`. This in used in the -// `From<&Options<'_, WrapAlgo, WordSep, WordSplit>> for Options<'a, -// WrapAlgo, WordSep, WordSplit>` implementation. -#[doc(hidden)] -pub trait WordSplitterClone { - fn clone_box(&self) -> Box; -} - -impl WordSplitterClone for T { - fn clone_box(&self) -> Box { - Box::new(self.clone()) +impl std::fmt::Debug for WordSplitter { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + WordSplitter::NoHyphenation => f.write_str("NoHyphenation"), + WordSplitter::HyphenSplitter => f.write_str("HyphenSplitter"), + WordSplitter::Custom(_) => f.write_str("Custom(...)"), + #[cfg(feature = "hyphenation")] + WordSplitter::Hyphenation(dict) => write!(f, "Hyphenation({})", dict.language()), + } } } -impl Clone for Box { - fn clone(&self) -> Box { - self.deref().clone_box() +impl PartialEq for WordSplitter { + fn eq(&self, other: &WordSplitter) -> bool { + match (self, other) { + (WordSplitter::NoHyphenation, WordSplitter::NoHyphenation) => true, + (WordSplitter::HyphenSplitter, WordSplitter::HyphenSplitter) => true, + #[cfg(feature = "hyphenation")] + (WordSplitter::Hyphenation(this_dict), WordSplitter::Hyphenation(other_dict)) => { + this_dict.language() == other_dict.language() + } + (_, _) => false, + } } } -impl WordSplitter for Box { - fn split_points(&self, word: &str) -> Vec { - self.deref().split_points(word) - } -} +impl WordSplitter { + /// Return all possible indices where `word` can be split. + /// + /// The indices are in the range `0..word.len()`. They point to + /// the index _after_ the split point, i.e., after `-` if + /// splitting on hyphens. This way, `word.split_at(idx)` will + /// break the word into two well-formed pieces. + /// + /// # Examples + /// + /// ``` + /// use textwrap::WordSplitter; + /// assert_eq!(WordSplitter::NoHyphenation.split_points("cannot-be-split"), vec![]); + /// assert_eq!(WordSplitter::HyphenSplitter.split_points("can-be-split"), vec![4, 7]); + /// assert_eq!(WordSplitter::Custom(|word| vec![word.len()/2]).split_points("middle"), vec![3]); + /// ``` + pub fn split_points(&self, word: &str) -> Vec { + match self { + WordSplitter::NoHyphenation => Vec::new(), + WordSplitter::HyphenSplitter => { + let mut splits = Vec::new(); -/// Use this as a [`Options.word_splitter`] to avoid any kind of -/// hyphenation: -/// -/// ``` -/// use textwrap::{wrap, Options}; -/// use textwrap::word_splitters::NoHyphenation; -/// -/// let options = Options::new(8).word_splitter(NoHyphenation); -/// assert_eq!(wrap("foo bar-baz", &options), -/// vec!["foo", "bar-baz"]); -/// ``` -/// -/// [`Options.word_splitter`]: super::Options::word_splitter -#[derive(Clone, Copy, Debug)] -pub struct NoHyphenation; + for (idx, _) in word.match_indices('-') { + // We only use hyphens that are surrounded by alphanumeric + // characters. This is to avoid splitting on repeated hyphens, + // such as those found in --foo-bar. + let prev = word[..idx].chars().next_back(); + let next = word[idx + 1..].chars().next(); -/// `NoHyphenation` implements `WordSplitter` by not splitting the -/// word at all. -impl WordSplitter for NoHyphenation { - fn split_points(&self, _: &str) -> Vec { - Vec::new() - } -} + if prev.filter(|ch| ch.is_alphanumeric()).is_some() + && next.filter(|ch| ch.is_alphanumeric()).is_some() + { + splits.push(idx + 1); // +1 due to width of '-'. + } + } -/// Simple and default way to split words: splitting on existing -/// hyphens only. -/// -/// You probably don't need to use this type since it's already used -/// by default by [`Options::new`](super::Options::new). -#[derive(Clone, Copy, Debug)] -pub struct HyphenSplitter; - -/// `HyphenSplitter` is the default `WordSplitter` used by -/// [`Options::new`](super::Options::new). It will split words on any -/// existing hyphens in the word. -/// -/// It will only use hyphens that are surrounded by alphanumeric -/// characters, which prevents a word like `"--foo-bar"` from being -/// split into `"--"` and `"foo-bar"`. -impl WordSplitter for HyphenSplitter { - fn split_points(&self, word: &str) -> Vec { - let mut splits = Vec::new(); - - for (idx, _) in word.match_indices('-') { - // We only use hyphens that are surrounded by alphanumeric - // characters. This is to avoid splitting on repeated hyphens, - // such as those found in --foo-bar. - let prev = word[..idx].chars().next_back(); - let next = word[idx + 1..].chars().next(); - - if prev.filter(|ch| ch.is_alphanumeric()).is_some() - && next.filter(|ch| ch.is_alphanumeric()).is_some() - { - splits.push(idx + 1); // +1 due to width of '-'. + splits + } + WordSplitter::Custom(splitter_func) => splitter_func(word), + #[cfg(feature = "hyphenation")] + WordSplitter::Hyphenation(dictionary) => { + use hyphenation::Hyphenator; + dictionary.hyphenate(word).breaks } } - - splits - } -} - -/// A hyphenation dictionary can be used to do language-specific -/// hyphenation using patterns from the [hyphenation] crate. -/// -/// **Note:** Only available when the `hyphenation` Cargo feature is -/// enabled. -/// -/// [hyphenation]: https://docs.rs/hyphenation/ -#[cfg(feature = "hyphenation")] -impl WordSplitter for hyphenation::Standard { - fn split_points(&self, word: &str) -> Vec { - use hyphenation::Hyphenator; - self.hyphenate(word).breaks } } @@ -164,31 +178,12 @@ impl WordSplitter for hyphenation::Standard { /// Note that we split all words, regardless of their length. This is /// to more cleanly separate the business of splitting (including /// automatic hyphenation) from the business of word wrapping. -/// -/// # Examples -/// -/// ``` -/// use textwrap::core::Word; -/// use textwrap::word_splitters::{split_words, NoHyphenation, HyphenSplitter}; -/// -/// assert_eq!( -/// split_words(vec![Word::from("foo-bar")], &HyphenSplitter).collect::>(), -/// vec![Word::from("foo-"), Word::from("bar")] -/// ); -/// -/// // The NoHyphenation splitter ignores the '-': -/// assert_eq!( -/// split_words(vec![Word::from("foo-bar")], &NoHyphenation).collect::>(), -/// vec![Word::from("foo-bar")] -/// ); -/// ``` -pub fn split_words<'a, I, WordSplit>( +pub fn split_words<'a, I>( words: I, - word_splitter: &'a WordSplit, + word_splitter: &'a WordSplitter, ) -> impl Iterator> where I: IntoIterator>, - WordSplit: WordSplitter, { words.into_iter().flat_map(move |word| { let mut prev = 0; @@ -235,13 +230,13 @@ mod tests { #[test] fn split_words_no_words() { - assert_iter_eq!(split_words(vec![], &HyphenSplitter), vec![]); + assert_iter_eq!(split_words(vec![], &WordSplitter::HyphenSplitter), vec![]); } #[test] fn split_words_empty_word() { assert_iter_eq!( - split_words(vec![Word::from(" ")], &HyphenSplitter), + split_words(vec![Word::from(" ")], &WordSplitter::HyphenSplitter), vec![Word::from(" ")] ); } @@ -249,7 +244,7 @@ mod tests { #[test] fn split_words_single_word() { assert_iter_eq!( - split_words(vec![Word::from("foobar")], &HyphenSplitter), + split_words(vec![Word::from("foobar")], &WordSplitter::HyphenSplitter), vec![Word::from("foobar")] ); } @@ -257,23 +252,28 @@ mod tests { #[test] fn split_words_hyphen_splitter() { assert_iter_eq!( - split_words(vec![Word::from("foo-bar")], &HyphenSplitter), + split_words(vec![Word::from("foo-bar")], &WordSplitter::HyphenSplitter), vec![Word::from("foo-"), Word::from("bar")] ); } + #[test] + fn split_words_no_hyphenation() { + assert_iter_eq!( + split_words(vec![Word::from("foo-bar")], &WordSplitter::NoHyphenation), + vec![Word::from("foo-bar")] + ); + } + #[test] fn split_words_adds_penalty() { - #[derive(Clone, Debug)] - struct FixedSplitPoint; - impl WordSplitter for FixedSplitPoint { - fn split_points(&self, _: &str) -> Vec { - vec![3] - } - } + let fixed_split_point = |_: &str| vec![3]; assert_iter_eq!( - split_words(vec![Word::from("foobar")].into_iter(), &FixedSplitPoint), + split_words( + vec![Word::from("foobar")].into_iter(), + &WordSplitter::Custom(fixed_split_point) + ), vec![ Word { word: "foo", @@ -291,7 +291,10 @@ mod tests { ); assert_iter_eq!( - split_words(vec![Word::from("fo-bar")].into_iter(), &FixedSplitPoint), + split_words( + vec![Word::from("fo-bar")].into_iter(), + &WordSplitter::Custom(fixed_split_point) + ), vec![ Word { word: "fo-", diff --git a/third_party/rust/textwrap/src/wrap_algorithms.rs b/third_party/rust/textwrap/src/wrap_algorithms.rs index 368ef2a4fdff..5ca49c3352db 100644 --- a/third_party/rust/textwrap/src/wrap_algorithms.rs +++ b/third_party/rust/textwrap/src/wrap_algorithms.rs @@ -18,69 +18,149 @@ #[cfg(feature = "smawk")] mod optimal_fit; #[cfg(feature = "smawk")] -pub use optimal_fit::{wrap_optimal_fit, OptimalFit}; +pub use optimal_fit::{wrap_optimal_fit, OverflowError, Penalties}; use crate::core::{Fragment, Word}; /// Describes how to wrap words into lines. /// -/// The simplest approach is to wrap words one word at a time. This is -/// implemented by [`FirstFit`]. If the `smawk` Cargo feature is -/// enabled, a more complex algorithm is available, implemented by -/// [`OptimalFit`], which will look at an entire paragraph at a time -/// in order to find optimal line breaks. -pub trait WrapAlgorithm: WrapAlgorithmClone + std::fmt::Debug { +/// The simplest approach is to wrap words one word at a time and +/// accept the first way of wrapping which fit +/// ([`WrapAlgorithm::FirstFit`]). If the `smawk` Cargo feature is +/// enabled, a more complex algorithm is available which will look at +/// an entire paragraph at a time in order to find optimal line breaks +/// ([`WrapAlgorithm::OptimalFit`]). +#[derive(Clone, Copy)] +pub enum WrapAlgorithm { + /// Wrap words using a fast and simple algorithm. + /// + /// This algorithm uses no look-ahead when finding line breaks. + /// Implemented by [`wrap_first_fit`], please see that function for + /// details and examples. + FirstFit, + + /// Wrap words using an advanced algorithm with look-ahead. + /// + /// This wrapping algorithm considers the entire paragraph to find + /// optimal line breaks. When wrapping text, "penalties" are + /// assigned to line breaks based on the gaps left at the end of + /// lines. See [`Penalties`] for details. + /// + /// The underlying wrapping algorithm is implemented by + /// [`wrap_optimal_fit`], please see that function for examples. + /// + /// **Note:** Only available when the `smawk` Cargo feature is + /// enabled. + #[cfg(feature = "smawk")] + OptimalFit(Penalties), + + /// Custom wrapping function. + /// + /// Use this if you want to implement your own wrapping algorithm. + /// The function can freely decide how to turn a slice of + /// [`Word`]s into lines. + /// + /// # Example + /// + /// ``` + /// use textwrap::core::Word; + /// use textwrap::{wrap, Options, WrapAlgorithm}; + /// + /// fn stair<'a, 'b>(words: &'b [Word<'a>], _: &'b [usize]) -> Vec<&'b [Word<'a>]> { + /// let mut lines = Vec::new(); + /// let mut step = 1; + /// let mut start_idx = 0; + /// while start_idx + step <= words.len() { + /// lines.push(&words[start_idx .. start_idx+step]); + /// start_idx += step; + /// step += 1; + /// } + /// lines + /// } + /// + /// let options = Options::new(10).wrap_algorithm(WrapAlgorithm::Custom(stair)); + /// assert_eq!(wrap("First, second, third, fourth, fifth, sixth", options), + /// vec!["First,", + /// "second, third,", + /// "fourth, fifth, sixth"]); + /// ``` + Custom(for<'a, 'b> fn(words: &'b [Word<'a>], line_widths: &'b [usize]) -> Vec<&'b [Word<'a>]>), +} + +impl std::fmt::Debug for WrapAlgorithm { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + WrapAlgorithm::FirstFit => f.write_str("FirstFit"), + #[cfg(feature = "smawk")] + WrapAlgorithm::OptimalFit(penalties) => write!(f, "OptimalFit({:?})", penalties), + WrapAlgorithm::Custom(_) => f.write_str("Custom(...)"), + } + } +} + +impl WrapAlgorithm { + /// Create new wrap algorithm. + /// + /// The best wrapping algorithm is used by default, i.e., + /// [`WrapAlgorithm::OptimalFit`] if available, otherwise + /// [`WrapAlgorithm::FirstFit`]. + pub const fn new() -> Self { + #[cfg(not(feature = "smawk"))] + { + WrapAlgorithm::FirstFit + } + + #[cfg(feature = "smawk")] + { + WrapAlgorithm::new_optimal_fit() + } + } + + /// New [`WrapAlgorithm::OptimalFit`] with default penalties. This + /// works well for monospace text. + /// + /// **Note:** Only available when the `smawk` Cargo feature is + /// enabled. + #[cfg(feature = "smawk")] + pub const fn new_optimal_fit() -> Self { + WrapAlgorithm::OptimalFit(Penalties::new()) + } + /// Wrap words according to line widths. /// /// The `line_widths` slice gives the target line width for each /// line (the last slice element is repeated as necessary). This /// can be used to implement hanging indentation. - /// - /// Please see the implementors of the trait for examples. - fn wrap<'a, 'b>(&self, words: &'b [Word<'a>], line_widths: &'b [usize]) -> Vec<&'b [Word<'a>]>; -} - -// The internal `WrapAlgorithmClone` trait is allows us to implement -// `Clone` for `Box`. This in used in the -// `From<&Options<'_, WrapAlgo, WordSep, WordSplit>> for Options<'a, -// WrapAlgo, WordSep, WordSplit>` implementation. -#[doc(hidden)] -pub trait WrapAlgorithmClone { - fn clone_box(&self) -> Box; -} - -impl WrapAlgorithmClone for T { - fn clone_box(&self) -> Box { - Box::new(self.clone()) - } -} - -impl Clone for Box { - fn clone(&self) -> Box { - use std::ops::Deref; - self.deref().clone_box() - } -} - -impl WrapAlgorithm for Box { - fn wrap<'a, 'b>(&self, words: &'b [Word<'a>], line_widths: &'b [usize]) -> Vec<&'b [Word<'a>]> { - use std::ops::Deref; - self.deref().wrap(words, line_widths) - } -} - -/// Wrap words using a fast and simple algorithm. -/// -/// This algorithm uses no look-ahead when finding line breaks. -/// Implemented by [`wrap_first_fit`], please see that function for -/// details and examples. -#[derive(Clone, Copy, Debug, Default)] -pub struct FirstFit; - -impl WrapAlgorithm for FirstFit { #[inline] - fn wrap<'a, 'b>(&self, words: &'b [Word<'a>], line_widths: &'b [usize]) -> Vec<&'b [Word<'a>]> { - wrap_first_fit(words, line_widths) + pub fn wrap<'a, 'b>( + &self, + words: &'b [Word<'a>], + line_widths: &'b [usize], + ) -> Vec<&'b [Word<'a>]> { + // Every integer up to 2u64.pow(f64::MANTISSA_DIGITS) = 2**53 + // = 9_007_199_254_740_992 can be represented without loss by + // a f64. Larger line widths will be rounded to the nearest + // representable number. + let f64_line_widths = line_widths.iter().map(|w| *w as f64).collect::>(); + + match self { + WrapAlgorithm::FirstFit => wrap_first_fit(words, &f64_line_widths), + + #[cfg(feature = "smawk")] + WrapAlgorithm::OptimalFit(penalties) => { + // The computation cannnot overflow when the line + // widths are restricted to usize. + wrap_optimal_fit(words, &f64_line_widths, penalties).unwrap() + } + + WrapAlgorithm::Custom(func) => func(words, line_widths), + } + } +} + +impl Default for WrapAlgorithm { + fn default() -> Self { + WrapAlgorithm::new() } } @@ -107,8 +187,8 @@ impl WrapAlgorithm for FirstFit { /// /// ``` /// use textwrap::core::Word; -/// use textwrap::wrap_algorithms; -/// use textwrap::word_separators::{AsciiSpace, WordSeparator}; +/// use textwrap::wrap_algorithms::wrap_first_fit; +/// use textwrap::WordSeparator; /// /// // Helper to convert wrapped lines to a Vec. /// fn lines_to_strings(lines: Vec<&[Word<'_>]>) -> Vec { @@ -118,8 +198,8 @@ impl WrapAlgorithm for FirstFit { /// } /// /// let text = "These few words will unfortunately not wrap nicely."; -/// let words = AsciiSpace.find_words(text).collect::>(); -/// assert_eq!(lines_to_strings(wrap_algorithms::wrap_first_fit(&words, &[15])), +/// let words = WordSeparator::AsciiSpace.find_words(text).collect::>(); +/// assert_eq!(lines_to_strings(wrap_first_fit(&words, &[15.0])), /// vec!["These few words", /// "will", // <-- short line /// "unfortunately", @@ -128,7 +208,9 @@ impl WrapAlgorithm for FirstFit { /// /// // We can avoid the short line if we look ahead: /// #[cfg(feature = "smawk")] -/// assert_eq!(lines_to_strings(wrap_algorithms::wrap_optimal_fit(&words, &[15])), +/// use textwrap::wrap_algorithms::{wrap_optimal_fit, Penalties}; +/// #[cfg(feature = "smawk")] +/// assert_eq!(lines_to_strings(wrap_optimal_fit(&words, &[15.0], &Penalties::new()).unwrap()), /// vec!["These few", /// "words will", /// "unfortunately", @@ -157,47 +239,47 @@ impl WrapAlgorithm for FirstFit { /// on your estimates. You can model this with a program like this: /// /// ``` -/// use textwrap::wrap_algorithms::wrap_first_fit; /// use textwrap::core::{Fragment, Word}; +/// use textwrap::wrap_algorithms::wrap_first_fit; /// /// #[derive(Debug)] /// struct Task<'a> { /// name: &'a str, -/// hours: usize, // Time needed to complete task. -/// sweep: usize, // Time needed for a quick sweep after task during the day. -/// cleanup: usize, // Time needed for full cleanup if day ends with this task. +/// hours: f64, // Time needed to complete task. +/// sweep: f64, // Time needed for a quick sweep after task during the day. +/// cleanup: f64, // Time needed for full cleanup if day ends with this task. /// } /// /// impl Fragment for Task<'_> { -/// fn width(&self) -> usize { self.hours } -/// fn whitespace_width(&self) -> usize { self.sweep } -/// fn penalty_width(&self) -> usize { self.cleanup } +/// fn width(&self) -> f64 { self.hours } +/// fn whitespace_width(&self) -> f64 { self.sweep } +/// fn penalty_width(&self) -> f64 { self.cleanup } /// } /// /// // The morning tasks /// let tasks = vec![ -/// Task { name: "Foundation", hours: 4, sweep: 2, cleanup: 3 }, -/// Task { name: "Framing", hours: 3, sweep: 1, cleanup: 2 }, -/// Task { name: "Plumbing", hours: 2, sweep: 2, cleanup: 2 }, -/// Task { name: "Electrical", hours: 2, sweep: 1, cleanup: 2 }, -/// Task { name: "Insulation", hours: 2, sweep: 1, cleanup: 2 }, -/// Task { name: "Drywall", hours: 3, sweep: 1, cleanup: 2 }, -/// Task { name: "Floors", hours: 3, sweep: 1, cleanup: 2 }, -/// Task { name: "Countertops", hours: 1, sweep: 1, cleanup: 2 }, -/// Task { name: "Bathrooms", hours: 2, sweep: 1, cleanup: 2 }, +/// Task { name: "Foundation", hours: 4.0, sweep: 2.0, cleanup: 3.0 }, +/// Task { name: "Framing", hours: 3.0, sweep: 1.0, cleanup: 2.0 }, +/// Task { name: "Plumbing", hours: 2.0, sweep: 2.0, cleanup: 2.0 }, +/// Task { name: "Electrical", hours: 2.0, sweep: 1.0, cleanup: 2.0 }, +/// Task { name: "Insulation", hours: 2.0, sweep: 1.0, cleanup: 2.0 }, +/// Task { name: "Drywall", hours: 3.0, sweep: 1.0, cleanup: 2.0 }, +/// Task { name: "Floors", hours: 3.0, sweep: 1.0, cleanup: 2.0 }, +/// Task { name: "Countertops", hours: 1.0, sweep: 1.0, cleanup: 2.0 }, +/// Task { name: "Bathrooms", hours: 2.0, sweep: 1.0, cleanup: 2.0 }, /// ]; /// /// // Fill tasks into days, taking `day_length` into account. The /// // output shows the hours worked per day along with the names of /// // the tasks for that day. -/// fn assign_days<'a>(tasks: &[Task<'a>], day_length: usize) -> Vec<(usize, Vec<&'a str>)> { +/// fn assign_days<'a>(tasks: &[Task<'a>], day_length: f64) -> Vec<(f64, Vec<&'a str>)> { /// let mut days = Vec::new(); /// // Assign tasks to days. The assignment is a vector of slices, /// // with a slice per day. /// let assigned_days: Vec<&[Task<'a>]> = wrap_first_fit(&tasks, &[day_length]); /// for day in assigned_days.iter() { /// let last = day.last().unwrap(); -/// let work_hours: usize = day.iter().map(|t| t.hours + t.sweep).sum(); +/// let work_hours: f64 = day.iter().map(|t| t.hours + t.sweep).sum(); /// let names = day.iter().map(|t| t.name).collect::>(); /// days.push((work_hours - last.sweep + last.cleanup, names)); /// } @@ -206,24 +288,24 @@ impl WrapAlgorithm for FirstFit { /// /// // With a single crew working 8 hours a day: /// assert_eq!( -/// assign_days(&tasks, 8), +/// assign_days(&tasks, 8.0), /// [ -/// (7, vec!["Foundation"]), -/// (8, vec!["Framing", "Plumbing"]), -/// (7, vec!["Electrical", "Insulation"]), -/// (5, vec!["Drywall"]), -/// (7, vec!["Floors", "Countertops"]), -/// (4, vec!["Bathrooms"]), +/// (7.0, vec!["Foundation"]), +/// (8.0, vec!["Framing", "Plumbing"]), +/// (7.0, vec!["Electrical", "Insulation"]), +/// (5.0, vec!["Drywall"]), +/// (7.0, vec!["Floors", "Countertops"]), +/// (4.0, vec!["Bathrooms"]), /// ] /// ); /// /// // With two crews working in shifts, 16 hours a day: /// assert_eq!( -/// assign_days(&tasks, 16), +/// assign_days(&tasks, 16.0), /// [ -/// (14, vec!["Foundation", "Framing", "Plumbing"]), -/// (15, vec!["Electrical", "Insulation", "Drywall", "Floors"]), -/// (6, vec!["Countertops", "Bathrooms"]), +/// (14.0, vec!["Foundation", "Framing", "Plumbing"]), +/// (15.0, vec!["Electrical", "Insulation", "Drywall", "Floors"]), +/// (6.0, vec!["Countertops", "Bathrooms"]), /// ] /// ); /// ``` @@ -232,13 +314,13 @@ impl WrapAlgorithm for FirstFit { /// knows how long each step takes :-) pub fn wrap_first_fit<'a, 'b, T: Fragment>( fragments: &'a [T], - line_widths: &'b [usize], + line_widths: &'b [f64], ) -> Vec<&'a [T]> { // The final line width is used for all remaining lines. - let default_line_width = line_widths.last().copied().unwrap_or(0); + let default_line_width = line_widths.last().copied().unwrap_or(0.0); let mut lines = Vec::new(); let mut start = 0; - let mut width = 0; + let mut width = 0.0; for (idx, fragment) in fragments.iter().enumerate() { let line_width = line_widths @@ -248,10 +330,52 @@ pub fn wrap_first_fit<'a, 'b, T: Fragment>( if width + fragment.width() + fragment.penalty_width() > line_width && idx > start { lines.push(&fragments[start..idx]); start = idx; - width = 0; + width = 0.0; } width += fragment.width() + fragment.whitespace_width(); } lines.push(&fragments[start..]); lines } + +#[cfg(test)] +mod tests { + use super::*; + + #[derive(Debug, PartialEq)] + struct Word(f64); + + #[rustfmt::skip] + impl Fragment for Word { + fn width(&self) -> f64 { self.0 } + fn whitespace_width(&self) -> f64 { 1.0 } + fn penalty_width(&self) -> f64 { 0.0 } + } + + #[test] + fn wrap_string_longer_than_f64() { + let words = vec![ + Word(1e307), + Word(2e307), + Word(3e307), + Word(4e307), + Word(5e307), + Word(6e307), + ]; + // Wrap at just under f64::MAX (~19e307). The tiny + // whitespace_widths disappear because of loss of precision. + assert_eq!( + wrap_first_fit(&words, &[15e307]), + &[ + vec![ + Word(1e307), + Word(2e307), + Word(3e307), + Word(4e307), + Word(5e307) + ], + vec![Word(6e307)] + ] + ); + } +} diff --git a/third_party/rust/textwrap/src/wrap_algorithms/optimal_fit.rs b/third_party/rust/textwrap/src/wrap_algorithms/optimal_fit.rs index 95ecf1f7886d..0625e28851fb 100644 --- a/third_party/rust/textwrap/src/wrap_algorithms/optimal_fit.rs +++ b/third_party/rust/textwrap/src/wrap_algorithms/optimal_fit.rs @@ -1,23 +1,157 @@ use std::cell::RefCell; -use crate::core::{Fragment, Word}; -use crate::wrap_algorithms::WrapAlgorithm; +use crate::core::Fragment; -/// Wrap words using an advanced algorithm with look-ahead. +/// Penalties for +/// [`WrapAlgorithm::OptimalFit`](crate::WrapAlgorithm::OptimalFit) +/// and [`wrap_optimal_fit`]. /// -/// This wrapping algorithm considers the entire paragraph to find -/// optimal line breaks. Implemented by [`wrap_optimal_fit`], please -/// see that function for details and examples. +/// This wrapping algorithm in [`wrap_optimal_fit`] considers the +/// entire paragraph to find optimal line breaks. When wrapping text, +/// "penalties" are assigned to line breaks based on the gaps left at +/// the end of lines. The penalties are given by this struct, with +/// [`Penalties::default`] assigning penalties that work well for +/// monospace text. +/// +/// If you are wrapping proportional text, you are advised to assign +/// your own penalties according to your font size. See the individual +/// penalties below for details. /// /// **Note:** Only available when the `smawk` Cargo feature is /// enabled. -#[derive(Clone, Copy, Debug, Default)] -pub struct OptimalFit; +#[derive(Clone, Copy, Debug)] +pub struct Penalties { + /// Per-line penalty. This is added for every line, which makes it + /// expensive to output more lines than the minimum required. + pub nline_penalty: usize, -impl WrapAlgorithm for OptimalFit { - #[inline] - fn wrap<'a, 'b>(&self, words: &'b [Word<'a>], line_widths: &'b [usize]) -> Vec<&'b [Word<'a>]> { - wrap_optimal_fit(words, line_widths) + /// Per-character cost for lines that overflow the target line width. + /// + /// With a default value of 50ยฒ, every single character costs as + /// much as leaving a gap of 50 characters behind. This is because + /// we assign as cost of `gap * gap` to a short line. When + /// wrapping monospace text, we can overflow the line by 1 + /// character in extreme cases: + /// + /// ``` + /// use textwrap::core::Word; + /// use textwrap::wrap_algorithms::{wrap_optimal_fit, Penalties}; + /// + /// let short = "foo "; + /// let long = "x".repeat(50); + /// let length = (short.len() + long.len()) as f64; + /// let fragments = vec![Word::from(short), Word::from(&long)]; + /// let penalties = Penalties::new(); + /// + /// // Perfect fit, both words are on a single line with no overflow. + /// let wrapped = wrap_optimal_fit(&fragments, &[length], &penalties).unwrap(); + /// assert_eq!(wrapped, vec![&[Word::from(short), Word::from(&long)]]); + /// + /// // The words no longer fit, yet we get a single line back. While + /// // the cost of overflow (`1 * 2500`) is the same as the cost of the + /// // gap (`50 * 50 = 2500`), the tie is broken by `nline_penalty` + /// // which makes it cheaper to overflow than to use two lines. + /// let wrapped = wrap_optimal_fit(&fragments, &[length - 1.0], &penalties).unwrap(); + /// assert_eq!(wrapped, vec![&[Word::from(short), Word::from(&long)]]); + /// + /// // The cost of overflow would be 2 * 2500, whereas the cost of + /// // the gap is only `49 * 49 + nline_penalty = 2401 + 1000 = + /// // 3401`. We therefore get two lines. + /// let wrapped = wrap_optimal_fit(&fragments, &[length - 2.0], &penalties).unwrap(); + /// assert_eq!(wrapped, vec![&[Word::from(short)], + /// &[Word::from(&long)]]); + /// ``` + /// + /// This only happens if the overflowing word is 50 characters + /// long _and_ if the word overflows the line by exactly one + /// character. If it overflows by more than one character, the + /// overflow penalty will quickly outgrow the cost of the gap, as + /// seen above. + pub overflow_penalty: usize, + + /// When should the a single word on the last line be considered + /// "too short"? + /// + /// If the last line of the text consist of a single word and if + /// this word is shorter than `1 / short_last_line_fraction` of + /// the line width, then the final line will be considered "short" + /// and `short_last_line_penalty` is added as an extra penalty. + /// + /// The effect of this is to avoid a final line consisting of a + /// single small word. For example, with a + /// `short_last_line_penalty` of 25 (the default), a gap of up to + /// 5 columns will be seen as more desirable than having a final + /// short line. + /// + /// ## Examples + /// + /// ``` + /// use textwrap::{wrap, wrap_algorithms, Options, WrapAlgorithm}; + /// + /// let text = "This is a demo of the short last line penalty."; + /// + /// // The first-fit algorithm leaves a single short word on the last line: + /// assert_eq!(wrap(text, Options::new(37).wrap_algorithm(WrapAlgorithm::FirstFit)), + /// vec!["This is a demo of the short last line", + /// "penalty."]); + /// + /// #[cfg(feature = "smawk")] { + /// let mut penalties = wrap_algorithms::Penalties::new(); + /// + /// // Since "penalty." is shorter than 25% of the line width, the + /// // optimal-fit algorithm adds a penalty of 25. This is enough + /// // to move "line " down: + /// assert_eq!(wrap(text, Options::new(37).wrap_algorithm(WrapAlgorithm::OptimalFit(penalties))), + /// vec!["This is a demo of the short last", + /// "line penalty."]); + /// + /// // We can change the meaning of "short" lines. Here, only words + /// // shorter than 1/10th of the line width will be considered short: + /// penalties.short_last_line_fraction = 10; + /// assert_eq!(wrap(text, Options::new(37).wrap_algorithm(WrapAlgorithm::OptimalFit(penalties))), + /// vec!["This is a demo of the short last line", + /// "penalty."]); + /// + /// // If desired, the penalty can also be disabled: + /// penalties.short_last_line_fraction = 4; + /// penalties.short_last_line_penalty = 0; + /// assert_eq!(wrap(text, Options::new(37).wrap_algorithm(WrapAlgorithm::OptimalFit(penalties))), + /// vec!["This is a demo of the short last line", + /// "penalty."]); + /// } + /// ``` + pub short_last_line_fraction: usize, + + /// Penalty for a last line with a single short word. + /// + /// Set this to zero if you do not want to penalize short last lines. + pub short_last_line_penalty: usize, + + /// Penalty for lines ending with a hyphen. + pub hyphen_penalty: usize, +} + +impl Penalties { + /// Default penalties for monospace text. + /// + /// The penalties here work well for monospace text. This is + /// because they expect the gaps at the end of lines to be roughly + /// in the range `0..100`. If the gaps are larger, the + /// `overflow_penalty` and `hyphen_penalty` become insignificant. + pub const fn new() -> Self { + Penalties { + nline_penalty: 1000, + overflow_penalty: 50 * 50, + short_last_line_fraction: 4, + short_last_line_penalty: 25, + hyphen_penalty: 25, + } + } +} + +impl Default for Penalties { + fn default() -> Self { + Self::new() } } @@ -39,7 +173,7 @@ impl LineNumbers { fn get(&self, i: usize, minima: &[(usize, T)]) -> usize { while self.line_numbers.borrow_mut().len() < i + 1 { let pos = self.line_numbers.borrow().len(); - let line_number = 1 + self.get(minima[pos].0, &minima); + let line_number = 1 + self.get(minima[pos].0, minima); self.line_numbers.borrow_mut().push(line_number); } @@ -47,58 +181,17 @@ impl LineNumbers { } } -/// Per-line penalty. This is added for every line, which makes it -/// expensive to output more lines than the minimum required. -const NLINE_PENALTY: i32 = 1000; +/// Overflow error during the [`wrap_optimal_fit`] computation. +#[derive(Debug, PartialEq, Eq)] +pub struct OverflowError; -/// Per-character cost for lines that overflow the target line width. -/// -/// With a value of 50ยฒ, every single character costs as much as -/// leaving a gap of 50 characters behind. This is becuase we assign -/// as cost of `gap * gap` to a short line. This means that we can -/// overflow the line by 1 character in extreme cases: -/// -/// ``` -/// use textwrap::wrap_algorithms::wrap_optimal_fit; -/// use textwrap::core::Word; -/// -/// let short = "foo "; -/// let long = "x".repeat(50); -/// let fragments = vec![Word::from(short), Word::from(&long)]; -/// -/// // Perfect fit, both words are on a single line with no overflow. -/// let wrapped = wrap_optimal_fit(&fragments, &[short.len() + long.len()]); -/// assert_eq!(wrapped, vec![&[Word::from(short), Word::from(&long)]]); -/// -/// // The words no longer fit, yet we get a single line back. While -/// // the cost of overflow (`1 * 2500`) is the same as the cost of the -/// // gap (`50 * 50 = 2500`), the tie is broken by `NLINE_PENALTY` -/// // which makes it cheaper to overflow than to use two lines. -/// let wrapped = wrap_optimal_fit(&fragments, &[short.len() + long.len() - 1]); -/// assert_eq!(wrapped, vec![&[Word::from(short), Word::from(&long)]]); -/// -/// // The cost of overflow would be 2 * 2500, whereas the cost of the -/// // gap is only `49 * 49 + NLINE_PENALTY = 2401 + 1000 = 3401`. We -/// // therefore get two lines. -/// let wrapped = wrap_optimal_fit(&fragments, &[short.len() + long.len() - 2]); -/// assert_eq!(wrapped, vec![&[Word::from(short)], -/// &[Word::from(&long)]]); -/// ``` -/// -/// This only happens if the overflowing word is 50 characters long -/// _and_ if it happens to overflow the line by exactly one character. -/// If it overflows by more than one character, the overflow penalty -/// will quickly outgrow the cost of the gap, as seen above. -const OVERFLOW_PENALTY: i32 = 50 * 50; +impl std::fmt::Display for OverflowError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "wrap_optimal_fit cost computation overflowed") + } +} -/// The last line is short if it is less than 1/4 of the target width. -const SHORT_LINE_FRACTION: usize = 4; - -/// Penalize a short last line. -const SHORT_LAST_LINE_PENALTY: i32 = 25; - -/// Penalty for lines ending with a hyphen. -const HYPHEN_PENALTY: i32 = 25; +impl std::error::Error for OverflowError {} /// Wrap abstract fragments into lines with an optimal-fit algorithm. /// @@ -173,16 +266,48 @@ const HYPHEN_PENALTY: i32 = 25; /// code by David /// Eppstein](https://github.com/jfinkels/PADS/blob/master/pads/wrap.py). /// +/// # Errors +/// +/// In case of an overflow during the cost computation, an `Err` is +/// returned. Overflows happens when fragments or lines have infinite +/// widths (`f64::INFINITY`) or if the widths are so large that the +/// gaps at the end of lines have sizes larger than `f64::MAX.sqrt()` +/// (approximately 1e154): +/// +/// ``` +/// use textwrap::core::Fragment; +/// use textwrap::wrap_algorithms::{wrap_optimal_fit, OverflowError, Penalties}; +/// +/// #[derive(Debug, PartialEq)] +/// struct Word(f64); +/// +/// impl Fragment for Word { +/// fn width(&self) -> f64 { self.0 } +/// fn whitespace_width(&self) -> f64 { 1.0 } +/// fn penalty_width(&self) -> f64 { 0.0 } +/// } +/// +/// // Wrapping overflows because 1e155 * 1e155 = 1e310, which is +/// // larger than f64::MAX: +/// assert_eq!(wrap_optimal_fit(&[Word(0.0), Word(0.0)], &[1e155], &Penalties::default()), +/// Err(OverflowError)); +/// ``` +/// +/// When using fragment widths and line widths which fit inside an +/// `u64`, overflows cannot happen. This means that fragments derived +/// from a `&str` cannot cause overflows. +/// /// **Note:** Only available when the `smawk` Cargo feature is /// enabled. pub fn wrap_optimal_fit<'a, 'b, T: Fragment>( fragments: &'a [T], - line_widths: &'b [usize], -) -> Vec<&'a [T]> { + line_widths: &'b [f64], + penalties: &'b Penalties, +) -> Result, OverflowError> { // The final line width is used for all remaining lines. - let default_line_width = line_widths.last().copied().unwrap_or(0); + let default_line_width = line_widths.last().copied().unwrap_or(0.0); let mut widths = Vec::with_capacity(fragments.len() + 1); - let mut width = 0; + let mut width = 0.0; widths.push(width); for fragment in fragments { width += fragment.width() + fragment.whitespace_width(); @@ -191,18 +316,18 @@ pub fn wrap_optimal_fit<'a, 'b, T: Fragment>( let line_numbers = LineNumbers::new(fragments.len()); - let minima = smawk::online_column_minima(0, widths.len(), |minima, i, j| { + let minima = smawk::online_column_minima(0.0, widths.len(), |minima, i, j| { // Line number for fragment `i`. - let line_number = line_numbers.get(i, &minima); + let line_number = line_numbers.get(i, minima); let line_width = line_widths .get(line_number) .copied() .unwrap_or(default_line_width); - let target_width = std::cmp::max(1, line_width); + let target_width = line_width.max(1.0); // Compute the width of a line spanning fragments[i..j] in // constant time. We need to adjust widths[j] by subtracting - // the whitespace of fragment[j-i] and then add the penalty. + // the whitespace of fragment[j-1] and then add the penalty. let line_width = widths[j] - widths[i] - fragments[j - 1].whitespace_width() + fragments[j - 1].penalty_width(); @@ -211,35 +336,43 @@ pub fn wrap_optimal_fit<'a, 'b, T: Fragment>( // breaking before fragments[i]. // // First, every extra line cost NLINE_PENALTY. - let mut cost = minima[i].1 + NLINE_PENALTY; + let mut cost = minima[i].1 + penalties.nline_penalty as f64; // Next, we add a penalty depending on the line length. if line_width > target_width { // Lines that overflow get a hefty penalty. - let overflow = (line_width - target_width) as i32; - cost += overflow * OVERFLOW_PENALTY; + let overflow = line_width - target_width; + cost += overflow * penalties.overflow_penalty as f64; } else if j < fragments.len() { // Other lines (except for the last line) get a milder // penalty which depend on the size of the gap. - let gap = (target_width - line_width) as i32; + let gap = target_width - line_width; cost += gap * gap; - } else if i + 1 == j && line_width < target_width / SHORT_LINE_FRACTION { + } else if i + 1 == j + && line_width < target_width / penalties.short_last_line_fraction as f64 + { // The last line can have any size gap, but we do add a // penalty if the line is very short (typically because it // contains just a single word). - cost += SHORT_LAST_LINE_PENALTY; + cost += penalties.short_last_line_penalty as f64; } // Finally, we discourage hyphens. - if fragments[j - 1].penalty_width() > 0 { + if fragments[j - 1].penalty_width() > 0.0 { // TODO: this should use a penalty value from the fragment // instead. - cost += HYPHEN_PENALTY; + cost += penalties.hyphen_penalty as f64; } cost }); + for (_, cost) in &minima { + if cost.is_infinite() { + return Err(OverflowError); + } + } + let mut lines = Vec::with_capacity(line_numbers.get(fragments.len(), &minima)); let mut pos = fragments.len(); loop { @@ -252,5 +385,49 @@ pub fn wrap_optimal_fit<'a, 'b, T: Fragment>( } lines.reverse(); - lines + Ok(lines) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[derive(Debug, PartialEq)] + struct Word(f64); + + #[rustfmt::skip] + impl Fragment for Word { + fn width(&self) -> f64 { self.0 } + fn whitespace_width(&self) -> f64 { 1.0 } + fn penalty_width(&self) -> f64 { 0.0 } + } + + #[test] + fn wrap_fragments_with_infinite_widths() { + let words = vec![Word(f64::INFINITY)]; + assert_eq!( + wrap_optimal_fit(&words, &[0.0], &Penalties::default()), + Err(OverflowError) + ); + } + + #[test] + fn wrap_fragments_with_huge_widths() { + let words = vec![Word(1e200), Word(1e250), Word(1e300)]; + assert_eq!( + wrap_optimal_fit(&words, &[1e300], &Penalties::default()), + Err(OverflowError) + ); + } + + #[test] + fn wrap_fragments_with_large_widths() { + // The gaps will be of the sizes between 1e25 and 1e75. This + // makes the `gap * gap` cost fit comfortably in a f64. + let words = vec![Word(1e25), Word(1e50), Word(1e75)]; + assert_eq!( + wrap_optimal_fit(&words, &[1e100], &Penalties::default()), + Ok(vec![&vec![Word(1e25), Word(1e50), Word(1e75)][..]]) + ); + } } diff --git a/third_party/rust/textwrap/tests/traits.rs b/third_party/rust/textwrap/tests/traits.rs deleted file mode 100644 index cd0d73c831a3..000000000000 --- a/third_party/rust/textwrap/tests/traits.rs +++ /dev/null @@ -1,86 +0,0 @@ -use textwrap::word_separators::{AsciiSpace, WordSeparator}; -use textwrap::word_splitters::{HyphenSplitter, NoHyphenation, WordSplitter}; -use textwrap::wrap_algorithms::{FirstFit, WrapAlgorithm}; -use textwrap::Options; - -/// Cleaned up type name. -fn type_name(_val: &T) -> String { - std::any::type_name::().replace("alloc::boxed::Box", "Box") -} - -#[test] -#[cfg(not(feature = "smawk"))] -#[cfg(not(feature = "unicode-linebreak"))] -fn static_hyphensplitter() { - // Inferring the full type. - let options = Options::new(10); - assert_eq!( - type_name(&options), - format!( - "textwrap::Options<{}, {}, {}>", - "textwrap::wrap_algorithms::FirstFit", - "textwrap::word_separators::AsciiSpace", - "textwrap::word_splitters::HyphenSplitter" - ) - ); - - // Inferring part of the type. - let options: Options<_, _, HyphenSplitter> = Options::new(10); - assert_eq!( - type_name(&options), - format!( - "textwrap::Options<{}, {}, {}>", - "textwrap::wrap_algorithms::FirstFit", - "textwrap::word_separators::AsciiSpace", - "textwrap::word_splitters::HyphenSplitter" - ) - ); - - // Explicitly making all parameters inferred. - let options: Options<_, _, _> = Options::new(10); - assert_eq!( - type_name(&options), - format!( - "textwrap::Options<{}, {}, {}>", - "textwrap::wrap_algorithms::FirstFit", - "textwrap::word_separators::AsciiSpace", - "textwrap::word_splitters::HyphenSplitter" - ) - ); -} - -#[test] -fn box_static_nohyphenation() { - // Inferred static type. - let options = Options::new(10) - .wrap_algorithm(Box::new(FirstFit)) - .word_splitter(Box::new(NoHyphenation)) - .word_separator(Box::new(AsciiSpace)); - assert_eq!( - type_name(&options), - format!( - "textwrap::Options<{}, {}, {}>", - "Box", - "Box", - "Box" - ) - ); -} - -#[test] -fn box_dyn_wordsplitter() { - // Inferred dynamic type due to default type parameter. - let options = Options::new(10) - .wrap_algorithm(Box::new(FirstFit) as Box) - .word_splitter(Box::new(HyphenSplitter) as Box) - .word_separator(Box::new(AsciiSpace) as Box); - assert_eq!( - type_name(&options), - format!( - "textwrap::Options<{}, {}, {}>", - "Box", - "Box", - "Box" - ) - ); -}