├── .github ├── dependabot.yml └── workflows │ ├── check-and-lint.yaml │ └── deploy-docs.yaml ├── .gitignore ├── Cargo.toml ├── Changelog.md ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── bpaf_cauwugo ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md └── src │ ├── add.rs │ ├── build.rs │ ├── check.rs │ ├── clean.rs │ ├── lib.rs │ ├── main.rs │ ├── metadata.rs │ ├── opts.rs │ ├── run.rs │ ├── shared.rs │ └── test.rs ├── bpaf_derive ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md └── src │ ├── attrs.rs │ ├── custom_path.rs │ ├── field.rs │ ├── field_tests.rs │ ├── help.rs │ ├── lib.rs │ ├── named_field.rs │ ├── td.rs │ ├── top.rs │ ├── top_tests.rs │ └── utils.rs ├── comptester ├── Cargo.toml ├── build.rs ├── src │ ├── lib.rs │ └── vterm.rs └── tests │ ├── coreutils.rs │ ├── derive_show_asm.rs │ └── simple_dynamic.rs ├── docs2 ├── Cargo.toml ├── README.md ├── build.rs └── src │ ├── adjacent_argument │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── adjacent_command │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── adjacent_struct_0 │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── adjacent_struct_1 │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── adjacent_struct_3 │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── adjacent_struct_4 │ ├── cases.md │ └── combine.rs │ ├── any_literal │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── any_simple │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── any_switch │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── argument │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── boxed │ ├── cases.md │ └── combine.rs │ ├── cargo_helper │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── choice │ ├── cases.md │ └── combine.rs │ ├── collect │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── command │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── command_enum │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── complete │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── compose_basic_argument │ ├── cases.md │ └── combine.rs │ ├── compose_basic_choice │ ├── cases.md │ └── combine.rs │ ├── compose_basic_command │ ├── cases.md │ └── combine.rs │ ├── compose_basic_construct │ ├── cases.md │ └── combine.rs │ ├── compose_basic_many │ ├── cases.md │ └── combine.rs │ ├── compose_basic_positional │ ├── cases.md │ └── combine.rs │ ├── compose_basic_switch │ ├── cases.md │ └── combine.rs │ ├── compose_basic_to_options │ ├── cases.md │ └── combine.rs │ ├── count │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── custom_help_version │ ├── cases.md │ └── combine.rs │ ├── custom_usage │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── dd │ └── cases.md │ ├── deb_fallback │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── deb_fallback_with │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── derive_basic │ ├── cases.md │ └── derive.rs │ ├── derive_basic_commands │ ├── cases.md │ └── derive.rs │ ├── derive_basic_custom_consumer │ ├── cases.md │ └── derive.rs │ ├── derive_basic_custom_name │ ├── cases.md │ └── derive.rs │ ├── derive_basic_enum │ ├── cases.md │ └── derive.rs │ ├── derive_basic_intro │ ├── cases.md │ └── derive.rs │ ├── derive_basic_nesting │ ├── cases.md │ └── derive.rs │ ├── derive_basic_postpr │ ├── cases.md │ └── derive.rs │ ├── derive_show_asm │ └── cases.md │ ├── dis_fallback │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── dis_fallback_with │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── fallback │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── fallback_with │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── find │ └── cases.md │ ├── flag │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── format_fallback │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── format_fallback_with │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── group_help │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── guard │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── help │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── hide │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── hide_usage │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── intro │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── last │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── lib.rs │ ├── many │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── many_catch │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── map │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── named_arg_combine │ ├── cases.md │ └── combine.rs │ ├── named_arg_derive │ ├── cases.md │ └── derive.rs │ ├── numeric_prefix │ └── cases.md │ ├── optional │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── optional_catch │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── parse │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── positional │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── positional_strict │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── pure │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── pure_with │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── req_flag │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── short_long_env │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── simple_dynamic │ └── cases.md │ ├── some │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── some_catch │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── switch │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── switch_help │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── to_options │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── usage │ ├── cases.md │ ├── combine.rs │ └── derive.rs │ ├── with_group_help │ ├── cases.md │ └── combine.rs │ ├── with_usage │ ├── cases.md │ └── combine.rs │ └── xorg │ └── cases.md ├── documentation ├── Cargo.toml ├── _documentation │ ├── _0_intro │ │ └── index.md │ ├── _1_tutorials │ │ ├── _0_types_of_arguments │ │ │ ├── _0_switch │ │ │ │ └── index.md │ │ │ ├── _1_argument │ │ │ │ └── index.md │ │ │ ├── _2_positional │ │ │ │ └── index.md │ │ │ ├── _3_command │ │ │ │ └── index.md │ │ │ ├── _4_exotic │ │ │ │ └── index.md │ │ │ └── index.md │ │ ├── _1_combinatoric_api │ │ │ ├── _0_simple_parser │ │ │ │ ├── _0_switch │ │ │ │ │ └── index.md │ │ │ │ ├── _1_argument │ │ │ │ │ └── index.md │ │ │ │ ├── _2_positional │ │ │ │ │ └── index.md │ │ │ │ └── index.md │ │ │ ├── _1_chaining │ │ │ │ └── index.md │ │ │ ├── _2_combining │ │ │ │ └── index.md │ │ │ ├── _3_subcommands │ │ │ │ └── index.md │ │ │ ├── _4_decorating │ │ │ │ └── index.md │ │ │ └── index.md │ │ ├── _2_derive_api │ │ │ ├── _0_intro │ │ │ │ └── index.md │ │ │ ├── _1_custom_names │ │ │ │ └── index.md │ │ │ ├── _2_custom_consumers │ │ │ │ └── index.md │ │ │ ├── _3_postpr │ │ │ │ └── index.md │ │ │ ├── _4_enums_and_structs │ │ │ │ └── index.md │ │ │ ├── _5_generate │ │ │ │ └── index.md │ │ │ ├── _6_nesting │ │ │ │ └── index.md │ │ │ ├── _7_commands │ │ │ │ └── index.md │ │ │ ├── _8_cargo │ │ │ │ └── index.md │ │ │ └── index.md │ │ ├── _3_picking_type │ │ │ └── index.md │ │ └── index.md │ ├── _2_howto │ │ ├── _0_testing │ │ │ └── index.md │ │ ├── _1_completion │ │ │ └── index.md │ │ └── index.md │ ├── _3_cookbook │ │ ├── _00_find │ │ │ └── index.md │ │ ├── _01_dd │ │ │ └── index.md │ │ ├── _02_xorg │ │ │ └── index.md │ │ ├── _03_command_chaining │ │ │ └── index.md │ │ ├── _04_multi_value │ │ │ └── index.md │ │ ├── _05_struct_groups │ │ │ └── index.md │ │ ├── _06_multi_flag │ │ │ └── index.md │ │ ├── _07_skip_positional │ │ │ └── index.md │ │ ├── _08_cargo_helper │ │ │ └── index.md │ │ ├── _09_numeric_flags │ │ │ └── index.md │ │ └── index.md │ ├── _4_explanation │ │ └── index.md │ └── index.md └── src │ ├── lib.rs │ └── main.rs ├── examples ├── at_least_two.rs ├── basic.rs ├── cargo-cmd.rs ├── cat.rs ├── compression.rs ├── confusing.rs ├── coreutils.rs ├── csample.rs ├── customize_help.rs ├── dd.rs ├── derive-smart-pointer.rs ├── derive.rs ├── derive_commands.rs ├── derive_rudo.rs ├── derive_show_asm.rs ├── derive_this_or_that.rs ├── dynamic-tree.rs ├── dynamic.rs ├── enum_in_args.rs ├── enum_tuple.rs ├── env_logger.rs ├── env_variable.rs ├── ex_positional.rs ├── fallback_command.rs ├── filenames.rs ├── find.rs ├── flatten.rs ├── git.rs ├── many_comma_separated_args.rs ├── many_comma_separated_args_derive.rs ├── multiple_fallback.rs ├── negative.rs ├── no_import.rs ├── numeric_prefix.rs ├── positional_derive.rs ├── rectangle.rs ├── sensors.rs ├── shared_args.rs ├── simple_dynamic.rs ├── top_to_bottom.rs ├── travel.rs ├── verbose.rs ├── very_custom_usage.rs └── xorg.rs ├── legacy ├── Cargo.toml └── src │ └── main.rs ├── src ├── _documentation.rs ├── _unusual.rs ├── arg.rs ├── args.rs ├── batteries.rs ├── buffer.rs ├── buffer │ ├── console.rs │ ├── html.rs │ ├── manpage.rs │ ├── manpage │ │ ├── escape.rs │ │ ├── monoid.rs │ │ └── roff.rs │ └── splitter.rs ├── complete_gen.rs ├── complete_run.rs ├── complete_shell.rs ├── doc.rs ├── docs2 │ ├── adjacent_argument.md │ ├── adjacent_command.md │ ├── adjacent_struct_0.md │ ├── adjacent_struct_1.md │ ├── adjacent_struct_3.md │ ├── adjacent_struct_4.md │ ├── adjacent_struct_5.md │ ├── any_literal.md │ ├── any_simple.md │ ├── any_switch.md │ ├── argument.md │ ├── boxed.md │ ├── cargo_helper.md │ ├── choice.md │ ├── collect.md │ ├── command.md │ ├── command_enum.md │ ├── complete.md │ ├── compose_basic_argument.md │ ├── compose_basic_choice.md │ ├── compose_basic_command.md │ ├── compose_basic_construct.md │ ├── compose_basic_many.md │ ├── compose_basic_positional.md │ ├── compose_basic_switch.md │ ├── compose_basic_to_options.md │ ├── count.md │ ├── custom_help_version.md │ ├── custom_usage.md │ ├── dd.md │ ├── deb_fallback.md │ ├── deb_fallback_with.md │ ├── derive_basic.md │ ├── derive_basic_commands.md │ ├── derive_basic_custom_consumer.md │ ├── derive_basic_custom_name.md │ ├── derive_basic_enum.md │ ├── derive_basic_intro.md │ ├── derive_basic_nesting.md │ ├── derive_basic_postpr.md │ ├── derive_show_asm.md │ ├── dis_fallback.md │ ├── dis_fallback_with.md │ ├── fallback.md │ ├── fallback_with.md │ ├── find.md │ ├── flag.md │ ├── format_fallback.md │ ├── format_fallback_with.md │ ├── group_help.md │ ├── guard.md │ ├── help.md │ ├── hide.md │ ├── hide_usage.md │ ├── intro.md │ ├── last.md │ ├── many.md │ ├── many_catch.md │ ├── map.md │ ├── named_arg_combine.md │ ├── named_arg_derive.md │ ├── numeric_prefix.md │ ├── optional.md │ ├── optional_catch.md │ ├── parse.md │ ├── positional.md │ ├── positional_strict.md │ ├── pure.md │ ├── pure_with.md │ ├── req_flag.md │ ├── short_long_env.md │ ├── simple_dynamic.md │ ├── some.md │ ├── some_catch.md │ ├── switch.md │ ├── switch_help.md │ ├── to_options.md │ ├── usage.md │ ├── with_group_help.md │ ├── with_usage.md │ └── xorg.md ├── error.rs ├── from_os_str.rs ├── info.rs ├── item.rs ├── lib.rs ├── meta.rs ├── meta_help.rs ├── meta_youmean.rs ├── params.rs ├── structs.rs └── tests.rs ├── tarp.sh └── tests ├── adjacent.rs ├── anywhere.rs ├── batteries.rs ├── chezmoi.rs ├── completion.rs ├── derive.rs ├── errors.rs ├── help_format.rs ├── invariant_checker.rs ├── manpage.rs ├── markdown.md ├── markdown.rs ├── meta_usage.rs ├── meta_youmean.rs ├── nested.1 ├── params.rs ├── polymorph.rs ├── positionals.rs ├── pure_with.rs ├── simple.1 ├── tests.rs └── very_nested.1 /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "cargo" 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /.github/workflows/deploy-docs.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - master 5 | 6 | jobs: 7 | check: 8 | name: Deploy master docs to github pages 9 | runs-on: ubuntu-latest 10 | concurrency: ci-${{ github.ref }} 11 | 12 | steps: 13 | - name: Checkout repo 14 | uses: actions/checkout@v3 15 | 16 | - name: Install rust toolchain 17 | uses: dtolnay/rust-toolchain@stable 18 | with: 19 | toolchain: stable 20 | 21 | - name: Build docs 22 | run: cargo doc --all-features --no-deps 23 | 24 | - name: Make a new repo with generated docs 25 | run: | 26 | cd target/doc 27 | touch .nojekyll 28 | git init 29 | git add -A 30 | git config --local user.email "action@github.com" 31 | git config --local user.name "GitHub Action" 32 | git commit -m 'deploy' 33 | 34 | - name: Deploy docs to gh-pages 35 | uses: ad-m/github-push-action@master 36 | with: 37 | github_token: ${{ secrets.GITHUB_TOKEN }} 38 | branch: gh-pages 39 | force: true 40 | directory: target/doc 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | .xwin-cache/ 4 | /tarp 5 | /dotfiles 6 | /legacy/target 7 | .idea 8 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /bpaf_cauwugo/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bpaf_cauwugo" 3 | version = "0.1.0" 4 | edition = "2021" 5 | license = "MIT OR Apache-2.0" 6 | categories = ["command-line-interface", "command-line-utilities"] 7 | repository = "https://github.com/pacak/bpaf" 8 | keywords = ["cargo", "cargo-frontend", "cli", "completion", "dynamic-completion"] 9 | readme = "README.md" 10 | description = "A cargo frontend with dynamic completion" 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | 14 | [dependencies] 15 | bpaf = { version = "0.9.4", path = "../", features = ["derive", "autocomplete"] } 16 | cargo_metadata = "0.18.0" 17 | once_cell = "1.13.1" 18 | 19 | [[bin]] 20 | name = "cauwugo" 21 | path = "src/main.rs" 22 | -------------------------------------------------------------------------------- /bpaf_cauwugo/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /bpaf_cauwugo/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /bpaf_cauwugo/src/build.rs: -------------------------------------------------------------------------------- 1 | use crate::shared::{cargo_opts, package_and_testables, CargoOpts, PackageAndTestables}; 2 | use bpaf::*; 3 | use std::process::Command; 4 | 5 | #[derive(Debug, Clone, Bpaf)] 6 | #[bpaf(command, short('b'), long("build"), generate(parse_build))] 7 | /// Compile a local package or parts of it and all of its dependencies 8 | pub struct Build { 9 | #[bpaf(external)] 10 | pub cargo_opts: CargoOpts, 11 | 12 | /// Package to check 13 | #[bpaf(external)] 14 | pub package_and_testables: PackageAndTestables, 15 | 16 | /// Build the library 17 | pub lib: bool, 18 | 19 | /// Build all the binaries 20 | pub bins: bool, 21 | 22 | /// Build all examples 23 | pub examples: bool, 24 | 25 | /// Build all tests 26 | pub tests: bool, 27 | 28 | /// Build all benchmarks 29 | pub benches: bool, 30 | } 31 | 32 | impl Build { 33 | pub fn pass_to_cmd(&self, cmd: &mut Command) { 34 | cmd.arg("build"); 35 | self.package_and_testables.pass_to_cmd(cmd); 36 | self.cargo_opts.pass_to_cmd(cmd); 37 | pass_flag!(cmd, self.lib, "--lib"); 38 | pass_flag!(cmd, self.bins, "--bins"); 39 | pass_flag!(cmd, self.examples, "--examples"); 40 | pass_flag!(cmd, self.benches, "--benches"); 41 | pass_flag!(cmd, self.tests, "--tests"); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /bpaf_cauwugo/src/check.rs: -------------------------------------------------------------------------------- 1 | use crate::shared::{cargo_opts, package_and_testables, CargoOpts, PackageAndTestables}; 2 | use bpaf::*; 3 | use std::process::Command; 4 | 5 | #[derive(Debug, Clone, Bpaf)] 6 | #[bpaf(command, short('c'), long("check"), generate(parse_check))] 7 | /// Check a local package or parts of it and all of its dependencies for errors 8 | pub struct Check { 9 | #[bpaf(external)] 10 | pub cargo_opts: CargoOpts, 11 | 12 | /// Package to check 13 | #[bpaf(external)] 14 | pub package_and_testables: PackageAndTestables, 15 | 16 | /// Check the library 17 | pub lib: bool, 18 | 19 | /// Check all the binaries 20 | pub bins: bool, 21 | 22 | /// Check all examples 23 | pub examples: bool, 24 | 25 | /// Check all tests 26 | pub tests: bool, 27 | 28 | /// Check all benchmarks 29 | pub benches: bool, 30 | } 31 | 32 | impl Check { 33 | pub fn pass_to_cmd(&self, cmd: &mut Command) { 34 | cmd.arg("check"); 35 | self.package_and_testables.pass_to_cmd(cmd); 36 | self.cargo_opts.pass_to_cmd(cmd); 37 | pass_flag!(cmd, self.lib, "--lib"); 38 | pass_flag!(cmd, self.bins, "--bins"); 39 | pass_flag!(cmd, self.examples, "--examples"); 40 | pass_flag!(cmd, self.benches, "--benches"); 41 | pass_flag!(cmd, self.tests, "--tests"); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /bpaf_cauwugo/src/clean.rs: -------------------------------------------------------------------------------- 1 | use crate::shared::{cargo_opts, parse_package, CargoOpts}; 2 | use bpaf::*; 3 | use std::process::Command; 4 | 5 | #[derive(Debug, Clone, Bpaf)] 6 | #[bpaf(command, generate(parse_clean))] 7 | /// Remove artifacts that cargo has generated in the past 8 | pub struct Clean { 9 | #[bpaf(external)] 10 | pub cargo_opts: CargoOpts, 11 | 12 | /// Package to clean 13 | #[bpaf(external)] 14 | pub package: Option<&'static str>, 15 | } 16 | 17 | impl Clean { 18 | pub fn pass_to_cmd(&self, cmd: &mut Command) { 19 | cmd.arg("clean"); 20 | pass_arg!(cmd, self.package, "--package"); 21 | self.cargo_opts.pass_to_cmd(cmd); 22 | } 23 | } 24 | 25 | fn package() -> impl Parser> { 26 | parse_package("Package to clean artifacts for") 27 | } 28 | -------------------------------------------------------------------------------- /bpaf_derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bpaf_derive" 3 | version = "0.5.17" 4 | edition = "2018" 5 | categories = ["command-line-interface"] 6 | description = "Derive macros for bpaf Command Line Argument Parser" 7 | keywords = ["args", "arguments", "cli", "parser", "parse"] 8 | authors = ["Michael Baykov "] 9 | readme = "README.md" 10 | license = "MIT OR Apache-2.0" 11 | repository = "https://github.com/pacak/bpaf" 12 | 13 | [lib] 14 | name = "bpaf_derive" 15 | proc-macro = true 16 | 17 | [dependencies] 18 | syn = { version = "2.0.2", features = ["full", "extra-traits", "visit-mut"] } 19 | proc-macro2 = "1.0.27" 20 | quote = "1.0.9" 21 | 22 | [dev-dependencies] 23 | pretty_assertions = "1.3" 24 | -------------------------------------------------------------------------------- /bpaf_derive/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /bpaf_derive/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /bpaf_derive/README.md: -------------------------------------------------------------------------------- 1 | # Derive macro for bpaf command line parser 2 | 3 | For documentation refer to bpaf library: 4 | -------------------------------------------------------------------------------- /bpaf_derive/src/help.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::ToTokens; 3 | use syn::{ 4 | parse::{Parse, ParseStream}, 5 | Expr, Result, 6 | }; 7 | 8 | #[derive(Debug, Clone)] 9 | pub(crate) enum Help { 10 | Custom(Box), 11 | Doc(String), 12 | } 13 | 14 | impl ToTokens for Help { 15 | fn to_tokens(&self, tokens: &mut TokenStream) { 16 | match self { 17 | Help::Custom(c) => c.to_tokens(tokens), 18 | Help::Doc(d) => d.to_tokens(tokens), 19 | } 20 | } 21 | } 22 | 23 | impl From<&str> for Help { 24 | fn from(value: &str) -> Self { 25 | Help::Doc(value.to_string()) 26 | } 27 | } 28 | 29 | impl Parse for Help { 30 | fn parse(input: ParseStream) -> Result { 31 | Ok(Help::Custom(input.parse()?)) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /bpaf_derive/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Derive macro for bpaf command line parser 2 | //! 3 | //! For documentation refer to `bpaf` library docs: 4 | 5 | mod attrs; 6 | mod field; 7 | mod named_field; 8 | mod top; 9 | mod utils; 10 | 11 | #[cfg(test)] 12 | mod field_tests; 13 | #[cfg(test)] 14 | mod top_tests; 15 | 16 | mod help; 17 | 18 | mod custom_path; 19 | mod td; 20 | 21 | use top::Top; 22 | 23 | /// Derive macro for bpaf command line parser 24 | /// 25 | /// For documentation refer to bpaf library: 26 | #[proc_macro_derive(Bpaf, attributes(bpaf))] 27 | pub fn derive_macro(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 28 | quote::ToTokens::to_token_stream(&syn::parse_macro_input!(input as Top)).into() 29 | } 30 | -------------------------------------------------------------------------------- /comptester/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "comptester" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | vte = { version = "0.13.0", default-features = true, features = ["ansi"] } 10 | ptyprocess = "0.4.1" 11 | anyhow = "1.0.71" 12 | 13 | [dev-dependencies] 14 | pretty_assertions = "1.3" 15 | -------------------------------------------------------------------------------- /comptester/tests/simple_dynamic.rs: -------------------------------------------------------------------------------- 1 | use comptester::*; 2 | use pretty_assertions::assert_eq; 3 | 4 | //let buf = zsh_comptest("derive_show_asm ^X?", false).unwrap(); 5 | #[test] 6 | fn sd_all_options_zsh() { 7 | let buf = zsh_comptest("simple_dynamic --crate \t").unwrap(); 8 | let expected = "% simple_dynamic --crate 9 | NAME: Select crate for analysis 10 | cargo-hackerman -- Workspace hack management and package/feature query 11 | cargo-prebuilt -- Download prebuilt crate binaries 12 | cargo-show-asm -- Display generated assembly 13 | cargo-supply-chain -- Gather author, contributor, publisher data on crates 14 | chezmoi_modify_manager -- Chezmoi addon to patch ini files 15 | xvf -- Easy archive extraction 16 | newdoc -- Generate pre-populated module files 17 | nust64 -- Tools for compiling a Rust project into an N64 ROM 18 | uggo -- CLI tool to query builds from u.gg"; 19 | 20 | assert_eq!(buf, expected); 21 | // let buf = zsh_comptest("simple_dynamic ?").unwrap(); 22 | // todo!("\n{}", buf); 23 | } 24 | -------------------------------------------------------------------------------- /docs2/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "docs2" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | bpaf = { path = "../", features = ["autocomplete", "derive"] } 10 | pretty_assertions = "1.3" 11 | comptester = { path = "../comptester/", optional = true } 12 | 13 | [build-dependencies] 14 | shell-words = "1.1.0" 15 | -------------------------------------------------------------------------------- /docs2/README.md: -------------------------------------------------------------------------------- 1 | # `docs2` 2 | 3 | A crate containing detailed documentation for `bpaf`. This is the source of the 4 | Markdown documentation files in `src/docs2`; to change those files, update the 5 | files here and then regenerate the documentation. 6 | 7 | All the examples in this crate are tested before the new documentation will be 8 | generated. To test the examples and generate the unified documentation files in 9 | `src/docs2`, run `cargo test -p docs2`. 10 | -------------------------------------------------------------------------------- /docs2/src/adjacent_argument/cases.md: -------------------------------------------------------------------------------- 1 | > --help 2 | 3 | As with regular [`argument`](NamedArg::argument) its `adjacent` variant is required by default 4 | 5 | > 6 | 7 | But unlike regular variant `adjacent` requires name and value to be separated by `=` only 8 | 9 | > -p=htb 10 | > --package=bpaf 11 | 12 | Separating them by space results in parse failure 13 | 14 | > --package htb 15 | > -p htb 16 | > --package 17 | -------------------------------------------------------------------------------- /docs2/src/adjacent_argument/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | pub struct Options { 5 | package: String, 6 | } 7 | 8 | fn package() -> impl Parser { 9 | long("package") 10 | .short('p') 11 | .help("Package to use") 12 | .argument("SPEC") 13 | .adjacent() 14 | } 15 | 16 | pub fn options() -> OptionParser { 17 | construct!(Options { package() }).to_options() 18 | } 19 | -------------------------------------------------------------------------------- /docs2/src/adjacent_argument/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone, Bpaf)] 4 | #[bpaf(options)] 5 | pub struct Options { 6 | #[bpaf(short, long, argument("SPEC"), adjacent)] 7 | /// Package to use 8 | package: String, 9 | } 10 | -------------------------------------------------------------------------------- /docs2/src/adjacent_command/cases.md: -------------------------------------------------------------------------------- 1 | Example implements a parser that supports one of three possible commands: 2 | 3 | > --help 4 | 5 | As usual every command comes with its own help 6 | 7 | > drink --help 8 | 9 | Normally you can use one command at a time, but making commands `adjacent` lets 10 | parser to succeed after consuming an adjacent block only and leaving leftovers for the rest of 11 | the parser, consuming them as a `Vec` with [`many`](Parser::many) allows to chain multiple 12 | items sequentially 13 | 14 | > eat Fastfood drink --coffee sleep --time=5 15 | 16 | The way this works is by running parsers for each command. In the first iteration `eat` succeeds, 17 | it consumes `eat fastfood` portion and appends its value to the resulting vector. Then second 18 | iteration runs on leftovers, in this case it will be `drink --coffee sleep --time=5`. 19 | Here `drink` succeeds and consumes `drink --coffee` portion, then `sleep` parser runs, etc. 20 | 21 | You can mix chained commands with regular arguments that belong to the top level parser 22 | 23 | > sleep --time 10 --premium eat 'Bak Kut Teh' drink 24 | 25 | But not inside the command itself since values consumed by the command are not going to be 26 | adjacent 27 | 28 | > sleep --time 10 eat --premium 'Bak Kut Teh' drink 29 | -------------------------------------------------------------------------------- /docs2/src/adjacent_command/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | pub struct Options { 5 | premium: bool, 6 | commands: Vec, 7 | } 8 | 9 | #[derive(Debug, Clone)] 10 | // shape of the variants doesn't really matter, let's use all of them :) 11 | enum Cmd { 12 | Eat(String), 13 | Drink { coffee: bool }, 14 | Sleep { time: usize }, 15 | } 16 | 17 | fn cmd() -> impl Parser { 18 | let eat = positional::("FOOD") 19 | .to_options() 20 | .descr("Performs eating action") 21 | .command("eat") 22 | .adjacent() 23 | .map(Cmd::Eat); 24 | 25 | let coffee = long("coffee") 26 | .help("Are you going to drink coffee?") 27 | .switch(); 28 | let drink = construct!(Cmd::Drink { coffee }) 29 | .to_options() 30 | .descr("Performs drinking action") 31 | .command("drink") 32 | .adjacent(); 33 | 34 | let time = long("time").argument::("HOURS"); 35 | let sleep = construct!(Cmd::Sleep { time }) 36 | .to_options() 37 | .descr("Performs taking a nap action") 38 | .command("sleep") 39 | .adjacent(); 40 | 41 | construct!([eat, drink, sleep]) 42 | } 43 | 44 | pub fn options() -> OptionParser { 45 | let premium = short('p') 46 | .long("premium") 47 | .help("Opt in for premium serivces") 48 | .switch(); 49 | let commands = cmd().many(); 50 | construct!(Options { premium, commands }).to_options() 51 | } 52 | -------------------------------------------------------------------------------- /docs2/src/adjacent_command/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone, Bpaf)] 4 | #[bpaf(options)] 5 | pub struct Options { 6 | #[bpaf(short, long)] 7 | /// Opt in for premium serivces 8 | pub premium: bool, 9 | #[bpaf(external(cmd), many)] 10 | pub commands: Vec, 11 | } 12 | 13 | #[derive(Debug, Clone, Bpaf)] 14 | pub enum Cmd { 15 | #[bpaf(command, adjacent)] 16 | /// Performs eating action 17 | Eat(#[bpaf(positional("FOOD"))] String), 18 | #[bpaf(command, adjacent)] 19 | /// Performs drinking action 20 | Drink { 21 | /// Are you going to drink coffee? 22 | coffee: bool, 23 | }, 24 | #[bpaf(command, adjacent)] 25 | /// Performs taking a nap action 26 | Sleep { 27 | #[bpaf(argument("HOURS"))] 28 | time: usize, 29 | }, 30 | } 31 | -------------------------------------------------------------------------------- /docs2/src/adjacent_struct_0/cases.md: -------------------------------------------------------------------------------- 1 | Fields can have different types, including `Option` or `Vec`, in this example they are two 2 | `usize` and one `f64`. 3 | 4 | > --help 5 | 6 | flag `--point` takes 3 positional arguments: two integers for X and Y coordinates and one floating point for height, order is 7 | important, switch `--rotate` can go on either side of it 8 | 9 | > --rotate --point 10 20 3.1415 10 | 11 | parser accepts multiple points, they must not interleave 12 | 13 | > --point 10 20 3.1415 --point 1 2 0.0 14 | 15 | `--rotate` can't go in the middle of the point definition as the parser expects the second item 16 | 17 | > --point 10 20 --rotate 3.1415 18 | -------------------------------------------------------------------------------- /docs2/src/adjacent_struct_0/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | pub struct Options { 5 | point: Vec, 6 | rotate: bool, 7 | } 8 | 9 | #[derive(Debug, Clone)] 10 | struct Point { 11 | point: (), 12 | x: usize, 13 | y: usize, 14 | z: f64, 15 | } 16 | 17 | fn point() -> impl Parser { 18 | let point = short('p') 19 | .long("point") 20 | .help("Point coordinates") 21 | .req_flag(()); 22 | let x = positional::("X").help("X coordinate of a point"); 23 | let y = positional::("Y").help("Y coordinate of a point"); 24 | let z = positional::("Z").help("Height of a point above the plane"); 25 | construct!(Point { point, x, y, z }).adjacent() 26 | } 27 | 28 | pub fn options() -> OptionParser { 29 | let rotate = short('r') 30 | .long("rotate") 31 | .help("Face the camera towards the first point") 32 | .switch(); 33 | let point = point().many(); 34 | construct!(Options { point, rotate }).to_options() 35 | } 36 | -------------------------------------------------------------------------------- /docs2/src/adjacent_struct_0/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone, Bpaf)] 4 | #[bpaf(options)] 5 | pub struct Options { 6 | #[bpaf(external, many)] 7 | point: Vec, 8 | #[bpaf(short, long)] 9 | /// Face the camera towards the first point 10 | rotate: bool, 11 | } 12 | 13 | #[derive(Debug, Clone, Bpaf)] 14 | #[bpaf(adjacent)] 15 | struct Point { 16 | #[bpaf(short, long)] 17 | /// Point coordinates 18 | point: (), 19 | #[bpaf(positional("X"))] 20 | /// X coordinate of a point 21 | x: usize, 22 | #[bpaf(positional("Y"))] 23 | /// Y coordinate of a point 24 | y: usize, 25 | #[bpaf(positional("Z"))] 26 | /// Height of a point above the plane 27 | z: f64, 28 | } 29 | -------------------------------------------------------------------------------- /docs2/src/adjacent_struct_1/cases.md: -------------------------------------------------------------------------------- 1 | This example parses multipe rectangles from a command line defined by dimensions and the fact 2 | if its filled or not, to make things more interesting - every group of coordinates must be 3 | prefixed with `--rect` 4 | 5 | > --help 6 | 7 | Order of items within the rectangle is not significant and you can have several of them, 8 | because fields are still regular arguments - order doesn't matter for as long as they belong 9 | to some rectangle 10 | > --rect --width 10 --height 10 --rect --height=10 --width=10 11 | 12 | You can have optional values that belong to the group inside and outer flags in the middle 13 | > --rect --width 10 --painted --height 10 --mirror --rect --height 10 --width 10 14 | 15 | But with `adjacent` they cannot interleave 16 | > --rect --rect --width 10 --painted --height 10 --height 10 --width 10 17 | 18 | Or have items that don't belong to the group inside them 19 | > --rect --width 10 --mirror --painted --height 10 --rect --height 10 --width 10 20 | -------------------------------------------------------------------------------- /docs2/src/adjacent_struct_1/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | pub struct Options { 5 | rect: Vec, 6 | mirror: bool, 7 | } 8 | 9 | #[derive(Debug, Clone)] 10 | struct Rect { 11 | rect: (), 12 | width: usize, 13 | height: usize, 14 | painted: bool, 15 | } 16 | 17 | fn rect() -> impl Parser { 18 | let rect = long("rect").help("Define a new rectangle").req_flag(()); 19 | let width = short('w') 20 | .long("width") 21 | .help("Rectangle width in pixels") 22 | .argument::("PX"); 23 | let height = short('h') 24 | .long("height") 25 | .help("Rectangle height in pixels") 26 | .argument::("PX"); 27 | let painted = short('p') 28 | .long("painted") 29 | .help("Should rectangle be filled?") 30 | .switch(); 31 | construct!(Rect { 32 | rect, 33 | width, 34 | height, 35 | painted, 36 | }) 37 | .adjacent() 38 | } 39 | 40 | pub fn options() -> OptionParser { 41 | let mirror = long("mirror").help("Mirror the image").switch(); 42 | let rect = rect().many(); 43 | construct!(Options { rect, mirror }).to_options() 44 | } 45 | -------------------------------------------------------------------------------- /docs2/src/adjacent_struct_1/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone, Bpaf)] 4 | #[bpaf(options)] 5 | pub struct Options { 6 | #[bpaf(external, many)] 7 | rect: Vec, 8 | /// Mirror the image 9 | mirror: bool, 10 | } 11 | 12 | #[derive(Debug, Clone, Bpaf)] 13 | #[bpaf(adjacent)] 14 | struct Rect { 15 | /// Define a new rectangle 16 | rect: (), 17 | #[bpaf(short, long, argument("PX"))] 18 | /// Rectangle width in pixels 19 | width: usize, 20 | #[bpaf(short, long, argument("PX"))] 21 | /// Rectangle height in pixels 22 | height: usize, 23 | #[bpaf(short, long)] 24 | /// Should rectangle be filled? 25 | painted: bool, 26 | } 27 | -------------------------------------------------------------------------------- /docs2/src/adjacent_struct_3/cases.md: -------------------------------------------------------------------------------- 1 | Generated `--help` message is somewhat descriptive of the purpose 2 | 3 | > --help 4 | 5 | You can have as many items between `--exec` and `;` as you want, they all will be captured 6 | inside the exec vector. Extra options can go either before or after the block. 7 | 8 | > --exec foo --bar ; -s 9 | 10 | This example uses [`some`](Parser::some) to make sure there are some parameters, but that's 11 | optional. 12 | 13 | > --exec ; 14 | -------------------------------------------------------------------------------- /docs2/src/adjacent_struct_3/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use std::ffi::OsString; 3 | // 4 | use bpaf::*; 5 | #[derive(Debug, Clone)] 6 | pub struct Options { 7 | exec: Option>, 8 | switch: bool, 9 | } 10 | 11 | fn exec() -> impl Parser>> { 12 | // this defines starting token - "--exec" 13 | let start = long("exec") 14 | .help("Spawn a process for each file found") 15 | .req_flag(()); 16 | // this consumes everything that is not ";" 17 | let body = any("COMMAND", |s| (s != ";").then_some(s)) 18 | .help("Command and arguments, {} will be replaced with a file name") 19 | .some("You need to pass some arguments to exec"); 20 | // this defines endint goken - ";" 21 | let end = literal(";"); 22 | // this consumes everything between starting token and ending token 23 | construct!(start, body, end) 24 | // this makes it so everything between those tokens is consumed 25 | .adjacent() 26 | // drop the surrounding tokens leaving just the arguments 27 | .map(|x| x.1) 28 | // and make it optional so that instead of an empty Vec 29 | // it is `None` when no `--exec` flags was passed. 30 | .optional() 31 | } 32 | 33 | pub fn options() -> OptionParser { 34 | let switch = short('s') 35 | .long("switch") 36 | .help("Regular top level switch") 37 | .switch(); 38 | construct!(Options { exec(), switch }).to_options() 39 | } 40 | -------------------------------------------------------------------------------- /docs2/src/adjacent_struct_3/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use std::ffi::OsString; 3 | // 4 | use bpaf::*; 5 | #[derive(Debug, Clone, Bpaf)] 6 | #[bpaf(options)] 7 | pub struct Options { 8 | #[bpaf(external(execs))] 9 | exec: Option>, 10 | #[bpaf(long, short)] 11 | /// Regular top level switch 12 | switch: bool, 13 | } 14 | 15 | #[derive(Debug, Clone, Bpaf)] 16 | #[bpaf(adjacent)] 17 | struct Exec { 18 | /// Spawn a process for each file found 19 | exec: (), 20 | 21 | #[bpaf( 22 | any("COMMAND", not_semi), 23 | some("Command and arguments, {} will be replaced with a file name") 24 | )] 25 | /// Command and arguments, {} will be replaced with a file name 26 | body: Vec, 27 | 28 | #[bpaf(external(is_semi))] 29 | end: (), 30 | } 31 | 32 | fn not_semi(s: OsString) -> Option { 33 | (s != ";").then_some(s) 34 | } 35 | 36 | fn is_semi() -> impl Parser<()> { 37 | // TODO - support literal in bpaf_derive 38 | literal(";") 39 | } 40 | 41 | // a different alternative would be to put a singular Exec 42 | fn execs() -> impl Parser>> { 43 | exec().map(|e| e.body).optional() 44 | } 45 | -------------------------------------------------------------------------------- /docs2/src/adjacent_struct_4/cases.md: -------------------------------------------------------------------------------- 1 | > --help 2 | 3 | Let's start simple - a single flag accepts a bunch of stuff, and eveything is present 4 | 5 | > --meal 330 --spicy 10 --drink 6 | 7 | You can omit some parts, but also have multiple groups thank to `many` 8 | 9 | > --meal 100 --drink --meal 30 --spicy 10 --meal 50 10 | 11 | As usual it can be mixed with standalone flags 12 | 13 | > --premium --meal 42 14 | 15 | Thanks to `many` whole meal part is optional 16 | 17 | > --premium 18 | 19 | Error messages should be somewhat descriptive 20 | 21 | > --meal --drink --spicy 500 22 | -------------------------------------------------------------------------------- /docs2/src/adjacent_struct_4/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | pub struct Options { 5 | meal: Vec, 6 | premium: bool, 7 | } 8 | 9 | #[derive(Debug, Clone)] 10 | struct Meal { 11 | m: (), 12 | spicy: Option, 13 | drink: bool, 14 | dish: usize, 15 | } 16 | 17 | /// You can mix all sorts of things inside the adjacent group 18 | fn meal() -> impl Parser { 19 | let m = short('o') 20 | .long("meal") 21 | .help("A meal [o]rder consists of a main dish with an optional drink") 22 | .req_flag(()); 23 | let spicy = long("spicy") 24 | .help("On a scale from 1 to a lot, how spicy do you want your meal?") 25 | .argument::("SPICY") 26 | .optional(); 27 | let drink = long("drink") 28 | .help("Do you want drink with your meal?") 29 | .switch(); 30 | let dish = positional::("DISH").help("Main dish number"); 31 | construct!(Meal { 32 | m, 33 | spicy, 34 | drink, 35 | dish 36 | }) 37 | .adjacent() 38 | } 39 | 40 | pub fn options() -> OptionParser { 41 | let premium = short('p') 42 | .long("premium") 43 | .help("Do you want to opt in for premium service?") 44 | .switch(); 45 | let meal = meal().many(); 46 | construct!(Options { meal, premium }).to_options() 47 | } 48 | -------------------------------------------------------------------------------- /docs2/src/any_literal/cases.md: -------------------------------------------------------------------------------- 1 | Instead of usual metavariable `any` parsers take something that can represent any value 2 | 3 | > --help 4 | 5 | Output file is required in this parser, other values are optional 6 | 7 | > 8 | > of=simple.txt 9 | 10 | Since options are defined with `anywhere` - order doesn't matter 11 | 12 | > bs=10 of=output.rs +turbo 13 | > +turbo bs=10 of=output.rs 14 | 15 | 16 | > bs=65536 count=12 of=hello_world.rs 17 | -------------------------------------------------------------------------------- /docs2/src/any_simple/cases.md: -------------------------------------------------------------------------------- 1 | `--help` keeps working for as long as `any` captures only intended values - that is it ignores 2 | `--help` flag specifically 3 | 4 | > --help 5 | 6 | You can mix `any` with regular options, here [`switch`](NamedArg::switch) `turbo` works because it goes 7 | before `rest` in the parser declaration 8 | 9 | > --turbo git commit -m "hello world" 10 | 11 | "before" in the previous line means in the parser definition, not on the user input, here 12 | `--turbo` gets consumed by `turbo` parser even the argument goes 13 | 14 | > git commit -m="hello world" --turbo 15 | 16 | 17 | 18 | > -- git commit -m="hello world" --turbo 19 | > git commit -m="hello world" -- --turbo 20 | -------------------------------------------------------------------------------- /docs2/src/any_simple/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use std::ffi::OsString; 3 | // 4 | use bpaf::*; 5 | #[derive(Debug, Clone)] 6 | // 7 | #[allow(dead_code)] 8 | pub struct Options { 9 | turbo: bool, 10 | rest: Vec, 11 | } 12 | 13 | pub fn options() -> OptionParser { 14 | let turbo = short('t') 15 | .long("turbo") 16 | .help("Engage the turbo mode") 17 | .switch(); 18 | let rest = any::("REST", |x| (x != "--help").then_some(x)) 19 | .help("app will pass anything unused to a child process") 20 | .many(); 21 | construct!(Options { turbo, rest }).to_options() 22 | } 23 | -------------------------------------------------------------------------------- /docs2/src/any_simple/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use std::ffi::OsString; 3 | // 4 | use bpaf::*; 5 | #[derive(Debug, Clone, Bpaf)] 6 | // 7 | #[allow(dead_code)] 8 | #[bpaf(options)] 9 | pub struct Options { 10 | #[bpaf(short, long)] 11 | /// Engage the turbo mode 12 | turbo: bool, 13 | #[bpaf(any("REST", not_help), many)] 14 | /// app will pass anything unused to a child process 15 | rest: Vec, 16 | } 17 | 18 | fn not_help(s: OsString) -> Option { 19 | if s == "--help" { 20 | None 21 | } else { 22 | Some(s) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /docs2/src/any_switch/cases.md: -------------------------------------------------------------------------------- 1 | `--help` message describes all the flags as expected 2 | 3 | > --help 4 | 5 | Parser obeys the defaults 6 | 7 | > 8 | 9 | And can handle custom values 10 | 11 | > --turbo -xinerama +backing 12 | 13 | `bpaf` won't be able to generate good error messages or suggest to fix typos to users since it 14 | doesn't really knows what the function inside `any` is going to consume 15 | 16 | > --turbo -xinerama +backin 17 | -------------------------------------------------------------------------------- /docs2/src/any_switch/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::{doc::*, *}; 3 | #[derive(Debug, Clone)] 4 | pub struct Options { 5 | turbo: bool, 6 | backing: bool, 7 | xinerama: bool, 8 | } 9 | 10 | fn toggle_option(name: &'static str, help: &'static str) -> impl Parser { 11 | // parse +name and -name into a bool 12 | any::(name, move |s: String| { 13 | if let Some(rest) = s.strip_prefix('+') { 14 | (rest == name).then_some(true) 15 | } else if let Some(rest) = s.strip_prefix('-') { 16 | (rest == name).then_some(false) 17 | } else { 18 | None 19 | } 20 | }) 21 | // set a custom usage and help metavariable 22 | .metavar( 23 | &[ 24 | ("+", Style::Literal), 25 | (name, Style::Literal), 26 | (" | ", Style::Text), 27 | ("-", Style::Literal), 28 | (name, Style::Literal), 29 | ][..], 30 | ) 31 | // set a custom help description 32 | .help(help) 33 | // apply this parser to all unconsumed items 34 | .anywhere() 35 | } 36 | 37 | pub fn options() -> OptionParser { 38 | let backing = toggle_option("backing", "Enable or disable backing") 39 | .fallback(false) 40 | .debug_fallback(); 41 | let xinerama = toggle_option("xinerama", "enable or disable Xinerama") 42 | .fallback(true) 43 | .debug_fallback(); 44 | let turbo = short('t') 45 | .long("turbo") 46 | .help("Engage the turbo mode") 47 | .switch(); 48 | construct!(Options { 49 | turbo, 50 | backing, 51 | xinerama, 52 | }) 53 | .to_options() 54 | } 55 | -------------------------------------------------------------------------------- /docs2/src/any_switch/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::{doc::*, *}; 3 | #[derive(Debug, Clone, Bpaf)] 4 | #[bpaf(options)] 5 | pub struct Options { 6 | /// Engage the turbo mode 7 | #[bpaf(short, long)] 8 | turbo: bool, 9 | #[bpaf(external(backing), fallback(false), debug_fallback)] 10 | backing: bool, 11 | #[bpaf(external(xinerama), fallback(true), debug_fallback)] 12 | xinerama: bool, 13 | } 14 | 15 | fn toggle_option(name: &'static str, help: &'static str) -> impl Parser { 16 | // parse +name and -name into a bool 17 | any::(name, move |s: String| { 18 | if let Some(rest) = s.strip_prefix('+') { 19 | (rest == name).then_some(true) 20 | } else if let Some(rest) = s.strip_prefix('-') { 21 | (rest == name).then_some(false) 22 | } else { 23 | None 24 | } 25 | }) 26 | // set a custom usage and help metavariable 27 | .metavar( 28 | &[ 29 | ("+", Style::Literal), 30 | (name, Style::Literal), 31 | (" | ", Style::Text), 32 | ("-", Style::Literal), 33 | (name, Style::Literal), 34 | ][..], 35 | ) 36 | // set a custom help description 37 | .help(help) 38 | // apply this parser to all unconsumed items 39 | .anywhere() 40 | } 41 | 42 | fn backing() -> impl Parser { 43 | toggle_option("backing", "Enable or disable backing") 44 | } 45 | 46 | fn xinerama() -> impl Parser { 47 | toggle_option("xinerama", "enable or disable Xinerama") 48 | } 49 | -------------------------------------------------------------------------------- /docs2/src/argument/cases.md: -------------------------------------------------------------------------------- 1 | > --help 2 | 3 | `--help` shows arguments as a short name with attached metavariable 4 | 5 | Value can be separated from flag by space, `=` sign 6 | 7 | > --name Bob --age 12 8 | > --name "Bob" --age=12 9 | > --name=Bob 10 | > --name="Bob" 11 | 12 | Or in case of short name - be directly adjacent to it 13 | 14 | > -nBob 15 | 16 | For long names - this doesn't work since parser can't tell where name 17 | stops and argument begins: 18 | 19 | > --age12 20 | 21 | Either way - value is required, passing just the argument name results in parse failure 22 | 23 | > --name 24 | -------------------------------------------------------------------------------- /docs2/src/argument/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | pub struct Options { 5 | name: String, 6 | age: usize, 7 | } 8 | 9 | pub fn options() -> OptionParser { 10 | let name = short('n') 11 | .long("name") 12 | .help("Specify user name") 13 | // you can specify exact type argument should produce 14 | // for as long as it implements `FromStr` 15 | .argument::("NAME"); 16 | 17 | let age = long("age") 18 | .help("Specify user age") 19 | // but often rust can figure it out from the context, 20 | // here age is going to be `usize` 21 | .argument("AGE") 22 | .fallback(18) 23 | .display_fallback(); 24 | 25 | construct!(Options { name, age }).to_options() 26 | } 27 | -------------------------------------------------------------------------------- /docs2/src/argument/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone, Bpaf)] 4 | #[bpaf(options)] 5 | pub struct Options { 6 | // you can specify exact type argument should produce 7 | // for as long as it implements `FromStr` 8 | #[bpaf(short, long, argument::("NAME"))] 9 | /// Specify user name 10 | name: String, 11 | // but often rust can figure it out from the context, 12 | // here age is going to be `usize` 13 | #[bpaf(argument("AGE"), fallback(18), display_fallback)] 14 | /// Specify user age 15 | age: usize, 16 | } 17 | -------------------------------------------------------------------------------- /docs2/src/boxed/cases.md: -------------------------------------------------------------------------------- 1 | It is also possible to make dynamic choice about the parsers. This example defines two parsers 2 | for distance - imperial and metric and picks one from some source available at runtime only. 3 | 4 | Help message will contain only one parser 5 | 6 | > --help 7 | 8 | and only one parser will produce a result 9 | 10 | > --distance 10 11 | -------------------------------------------------------------------------------- /docs2/src/boxed/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | 4 | pub fn options() -> OptionParser { 5 | let miles = long("distance") 6 | .help("distance in miles") 7 | .argument::("MILES") 8 | .map(|d| d * 1.609344); 9 | 10 | let km = long("distance") 11 | .help("distance in km") 12 | .argument::("KM"); 13 | 14 | // suppose this is reading from config fule 15 | let use_metric = true; 16 | 17 | // without use of `boxed` here branches have different types so it won't typecheck 18 | // boxed make it so branches have the same type as long as they return the same type 19 | let distance = if use_metric { 20 | km.boxed() 21 | } else { 22 | miles.boxed() 23 | }; 24 | 25 | distance.to_options() 26 | } 27 | -------------------------------------------------------------------------------- /docs2/src/cargo_helper/cases.md: -------------------------------------------------------------------------------- 1 | Let's say the goal is to parse an argument and a switch: 2 | 3 | > --argument 15 4 | 5 | But when used as a `cargo` subcommand, cargo will also pass the command name. For example 6 | you can invoke an app with binary name `cargo-asm` 7 | 8 | ```console 9 | $ cargo asm --lib --everything 10 | ... 11 | ``` 12 | 13 | `cargo` will then spawn the executable and pass it following parameters: 14 | 15 | ```console 16 | $ cargo-asm asm --lib --everything 17 | ... 18 | ``` 19 | 20 | If you are not using `cargo_helper` - parser won't know what to do with `asm` part. 21 | `cargo-helper` allows the parser to strip it from the front and everything works as expected. 22 | 23 | And it doesn't show up in `--help` so not to confuse users 24 | 25 | > --help 26 | -------------------------------------------------------------------------------- /docs2/src/cargo_helper/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | // 4 | #[allow(dead_code)] 5 | #[derive(Debug, Clone)] 6 | pub struct Options { 7 | argument: usize, 8 | switch: bool, 9 | } 10 | 11 | pub fn options() -> OptionParser { 12 | let argument = long("argument") 13 | .help("An argument") 14 | .argument::("ARG"); 15 | let switch = short('s').help("A switch").switch(); 16 | let options = construct!(Options { argument, switch }); 17 | 18 | // Given the cargo command is `cargo pretty`. 19 | cargo_helper("pretty", options).to_options() 20 | } 21 | -------------------------------------------------------------------------------- /docs2/src/cargo_helper/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | // 4 | #[allow(dead_code)] 5 | #[derive(Debug, Clone, Bpaf)] 6 | #[bpaf(options("pretty"))] // Given the cargo command is `cargo pretty`. 7 | pub struct Options { 8 | /// An argument 9 | argument: usize, 10 | /// A switch 11 | #[bpaf(short)] 12 | switch: bool, 13 | } 14 | -------------------------------------------------------------------------------- /docs2/src/choice/cases.md: -------------------------------------------------------------------------------- 1 | Here [`choice`] function is used to create an option for each possible desert item 2 | 3 | > --help 4 | 5 | User can pick any item 6 | 7 | > --apple 8 | 9 | Since parser consumes only one value you can't specify multiple flags of the same type 10 | 11 | > --orange --grape 12 | 13 | And [`Parser::optional`] makes it so when value is not specified - `None` is produced instead 14 | 15 | > 16 | -------------------------------------------------------------------------------- /docs2/src/choice/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | 4 | #[derive(Debug, Clone)] 5 | pub struct Options { 6 | desert: Option<&'static str>, 7 | } 8 | 9 | pub fn options() -> OptionParser { 10 | let desert = ["apple", "banana", "orange", "grape", "strawberry"] 11 | .iter() 12 | .map(|name| { 13 | long(name) 14 | .help("Pick one of the options") 15 | .req_flag(*name) 16 | .boxed() 17 | }); 18 | let desert = choice(desert).optional(); 19 | construct!(Options { desert }).to_options() 20 | } 21 | -------------------------------------------------------------------------------- /docs2/src/collect/cases.md: -------------------------------------------------------------------------------- 1 | In usage lines `collect` items are indicated with `...` 2 | > --help 3 | 4 | Run inner parser as many times as possible collecting all the new results 5 | First `false` is collected from a switch even if it is not consuming anything 6 | 7 | > --argument 10 --argument 20 --argument 20 8 | 9 | If there's no matching parameters - it would produce an empty set. Note, in case of 10 | [`switch`](NamedArg::switch) parser or other parsers that can succeed without consuming anything 11 | it would capture that value so `many` captures the first one of those. 12 | You can use [`req_flag`](NamedArg::req_flag) to avoid that. 13 | 14 | > 15 | 16 | For parsers that can succeed without consuming anything such as `flag` or `switch` - `many` 17 | only collects values as long as they produce something 18 | 19 | > --switch --switch 20 | -------------------------------------------------------------------------------- /docs2/src/collect/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | use std::collections::BTreeSet; 4 | 5 | #[derive(Debug, Clone)] 6 | pub struct Options { 7 | argument: BTreeSet, 8 | switches: BTreeSet, 9 | } 10 | 11 | pub fn options() -> OptionParser { 12 | let argument = long("argument") 13 | .help("important argument") 14 | .argument("ARG") 15 | .collect(); 16 | let switches = long("switch").help("some switch").switch().collect(); 17 | construct!(Options { argument, switches }).to_options() 18 | } 19 | -------------------------------------------------------------------------------- /docs2/src/collect/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | use std::collections::BTreeSet; 4 | 5 | #[derive(Debug, Clone, Bpaf)] 6 | #[bpaf(options)] 7 | pub struct Options { 8 | /// important argument 9 | #[bpaf(argument::("ARG"), collect)] 10 | argument: BTreeSet, 11 | /// some switch 12 | #[bpaf(long("switch"), switch, collect)] 13 | switches: BTreeSet, 14 | } 15 | -------------------------------------------------------------------------------- /docs2/src/command/cases.md: -------------------------------------------------------------------------------- 1 | Commands show up on both outer level help 2 | 3 | > --help 4 | 5 | As well as showing their own help 6 | 7 | > cmd --help 8 | 9 | In this example there's only one command and it is required, so is the argument inside of it 10 | 11 | > cmd --arg 42 12 | 13 | If you don't specify this command - parsing will fail 14 | 15 | You can have the same flag names inside and outside of the command, but it might be confusing 16 | for the end user. This example enables the outer flag 17 | 18 | > --flag cmd --arg 42 19 | 20 | 21 | And this one - both inside and outside 22 | 23 | > --flag cmd --arg 42 --flag 24 | 25 | And that's the confusing part - unless you add context restrictions with 26 | [`adjacent`](crate::ParseCon::adjacent) and parse command first - outer flag wins. 27 | So it's best not to mix names on different levels 28 | 29 | > cmd --arg 42 --flag 30 | -------------------------------------------------------------------------------- /docs2/src/command/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | pub struct Cmd { 5 | flag: bool, 6 | arg: usize, 7 | } 8 | 9 | #[derive(Debug, Clone)] 10 | pub struct Options { 11 | flag: bool, 12 | cmd: Cmd, 13 | } 14 | 15 | fn cmd() -> impl Parser { 16 | let flag = long("flag") 17 | .help("This flag is specific to command") 18 | .switch(); 19 | let arg = long("arg").argument::("ARG"); 20 | construct!(Cmd { flag, arg }) 21 | .to_options() 22 | .descr("Command to do something") 23 | .command("cmd") 24 | // you can chain add extra short and long names 25 | .short('c') 26 | } 27 | 28 | pub fn options() -> OptionParser { 29 | let flag = long("flag") 30 | .help("This flag is specific to the outer layer") 31 | .switch(); 32 | construct!(Options { flag, cmd() }).to_options() 33 | } 34 | -------------------------------------------------------------------------------- /docs2/src/command/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone, Bpaf)] 4 | // `command` annotation with no name gets the name from the object it is attached to, 5 | // but you can override it using something like #[bpaf(command("my_command"))] 6 | // you can chain more short and long names here to serve as aliases 7 | #[bpaf(command("cmd"), short('c'))] 8 | /// Command to do something 9 | pub struct Cmd { 10 | /// This flag is specific to command 11 | flag: bool, 12 | arg: usize, 13 | } 14 | 15 | #[derive(Debug, Clone, Bpaf)] 16 | #[bpaf(options)] 17 | pub struct Options { 18 | /// This flag is specific to the outer layer 19 | flag: bool, 20 | #[bpaf(external)] 21 | cmd: Cmd, 22 | } 23 | -------------------------------------------------------------------------------- /docs2/src/command_enum/cases.md: -------------------------------------------------------------------------------- 1 | Help contains both commands, bpaf takes short command description from the inner command 2 | description 3 | 4 | > --help 5 | 6 | Same as before each command gets its own help message 7 | 8 | > run --help 9 | 10 | And can be executed separately 11 | 12 | > run --bin basic 13 | > build --bin demo --release 14 | -------------------------------------------------------------------------------- /docs2/src/command_enum/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | pub enum Options { 5 | /// Run a binary 6 | Run { 7 | /// Name of a binary to run 8 | bin: String, 9 | 10 | /// Arguments to pass to a binary 11 | args: Vec, 12 | }, 13 | /// Compile a binary 14 | Build { 15 | /// Name of a binary to build 16 | bin: String, 17 | 18 | /// Compile the binary in release mode 19 | release: bool, 20 | }, 21 | } 22 | 23 | // combine mode gives more flexibility to share the same code across multiple parsers 24 | fn run() -> impl Parser { 25 | let bin = long("bin").help("Name of a binary to run").argument("BIN"); 26 | let args = positional("ARG") 27 | .strict() 28 | .help("Arguments to pass to a binary") 29 | .many(); 30 | 31 | construct!(Options::Run { bin, args }) 32 | } 33 | 34 | pub fn options() -> OptionParser { 35 | let run = run().to_options().descr("Run a binary").command("run"); 36 | 37 | let bin = long("bin") 38 | .help("Name of a binary to build ") 39 | .argument("BIN"); 40 | let release = long("release") 41 | .help("Compile the binary in release mode") 42 | .switch(); 43 | let build = construct!(Options::Build { bin, release }) 44 | .to_options() 45 | .descr("Compile a binary") 46 | .command("build"); 47 | 48 | construct!([run, build]).to_options() 49 | } 50 | -------------------------------------------------------------------------------- /docs2/src/command_enum/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone, Bpaf)] 4 | #[bpaf(options)] 5 | pub enum Options { 6 | #[bpaf(command)] 7 | /// Run a binary 8 | Run { 9 | #[bpaf(argument("BIN"))] 10 | /// Name of a binary to run 11 | bin: String, 12 | 13 | #[bpaf(positional("ARG"), strict, many)] 14 | /// Arguments to pass to a binary 15 | args: Vec, 16 | }, 17 | #[bpaf(command)] 18 | /// Compile a binary 19 | Build { 20 | #[bpaf(argument("BIN"))] 21 | /// Name of a binary to build 22 | bin: String, 23 | 24 | /// Compile the binary in release mode 25 | release: bool, 26 | }, 27 | } 28 | -------------------------------------------------------------------------------- /docs2/src/complete/cases.md: -------------------------------------------------------------------------------- 1 | `complete` annotation does not affect parsing results or generated help message 2 | 3 | > --help 4 | 5 | > --name Bob 6 | 7 | But when invoked with shell completion can generate suggestions for user to what to type: 8 | 9 | ```console 10 | $ app --name L 11 | $ app --name Lupisregina 12 | ``` 13 | -------------------------------------------------------------------------------- /docs2/src/complete/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | pub struct Options { 5 | name: String, 6 | } 7 | 8 | fn completer(input: &String) -> Vec<(&'static str, Option<&'static str>)> { 9 | let names = ["Yuri", "Lupusregina", "Solution", "Shizu", "Entoma"]; 10 | names 11 | .iter() 12 | .filter(|name| name.starts_with(input)) 13 | .map(|name| (*name, None)) 14 | .collect::>() 15 | } 16 | 17 | pub fn options() -> OptionParser { 18 | let name = short('n') 19 | .long("name") 20 | .help("Specify character's name") 21 | .argument("NAME") 22 | .complete(completer); 23 | construct!(Options { name }).to_options() 24 | } 25 | -------------------------------------------------------------------------------- /docs2/src/complete/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | /// suggest completions for the input 4 | fn completer(input: &String) -> Vec<(&'static str, Option<&'static str>)> { 5 | let names = ["Yuri", "Lupusregina", "Solution", "Shizu", "Entoma"]; 6 | names 7 | .iter() 8 | .filter(|name| name.starts_with(input)) 9 | .map(|name| (*name, None)) 10 | .collect::>() 11 | } 12 | 13 | #[derive(Debug, Clone, Bpaf)] 14 | #[bpaf(options)] 15 | pub struct Options { 16 | #[bpaf(short, long, argument("NAME"), complete(completer))] 17 | /// Specify character's name 18 | name: String, 19 | } 20 | -------------------------------------------------------------------------------- /docs2/src/compose_basic_argument/cases.md: -------------------------------------------------------------------------------- 1 | By default all arguments are required so running with no arguments produces an error 2 | 3 | > 4 | 5 | Bpaf accepts various combinations of names and adjacencies: 6 | 7 | > -s100 8 | > --size 300 9 | > -s=42 10 | > --size=14 11 | 12 | Since not every string is a valid number - bpaf would report any parsing failures to the user 13 | directly 14 | 15 | > --size fifty 16 | 17 | In addition to the switch you defined `bpaf` generates switch for user help which will include 18 | the description from the `help` method 19 | 20 | > --help 21 | -------------------------------------------------------------------------------- /docs2/src/compose_basic_argument/combine.rs: -------------------------------------------------------------------------------- 1 | use bpaf::*; 2 | 3 | pub fn options() -> OptionParser { 4 | short('s') 5 | .long("size") 6 | .help("Defines size of an object") 7 | .argument::("SIZE") 8 | .to_options() 9 | } 10 | -------------------------------------------------------------------------------- /docs2/src/compose_basic_choice/cases.md: -------------------------------------------------------------------------------- 1 | Help message describes all the parser combined 2 | > --help 3 | 4 | Users can pass value that satisfy either parser 5 | 6 | > --miles 42 7 | > --kilo 15 8 | 9 | But not both at once or not at all: 10 | 11 | > --miles 53 --kilo 10 12 | > 13 | 14 | If those cases are valid you can handle them with `optional` and `many` 15 | -------------------------------------------------------------------------------- /docs2/src/compose_basic_choice/combine.rs: -------------------------------------------------------------------------------- 1 | use bpaf::*; 2 | 3 | pub fn options() -> OptionParser { 4 | let miles = long("miles").help("Distance in miles").argument("MI"); 5 | let km = long("kilo").help("Distance in kilometers").argument("KM"); 6 | construct!([miles, km]).to_options() 7 | } 8 | -------------------------------------------------------------------------------- /docs2/src/compose_basic_command/cases.md: -------------------------------------------------------------------------------- 1 | Help contains both commands, bpaf takes short command description from the inner command 2 | description 3 | 4 | > --help 5 | 6 | Same as before each command gets its own help message 7 | 8 | > run --help 9 | 10 | And can be executed separately 11 | 12 | > run --bin basic 13 | > build --bin demo --release 14 | -------------------------------------------------------------------------------- /docs2/src/compose_basic_command/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | pub enum Options { 5 | /// Run a binary 6 | Run { 7 | /// Name of a binary to run 8 | bin: String, 9 | 10 | /// Arguments to pass to a binary 11 | args: Vec, 12 | }, 13 | /// Compile a binary 14 | Build { 15 | /// Name of a binary to build 16 | bin: String, 17 | 18 | /// Compile the binary in release mode 19 | release: bool, 20 | }, 21 | } 22 | 23 | // combine mode gives more flexibility to share the same code across multiple parsers 24 | fn run() -> impl Parser { 25 | let bin = long("bin").help("Name of a binary to run").argument("BIN"); 26 | let args = positional("ARG") 27 | .strict() 28 | .help("Arguments to pass to a binary") 29 | .many(); 30 | 31 | construct!(Options::Run { bin, args }) 32 | } 33 | 34 | pub fn options() -> OptionParser { 35 | let run = run().to_options().descr("Run a binary").command("run"); 36 | 37 | let bin = long("bin") 38 | .help("Name of a binary to build ") 39 | .argument("BIN"); 40 | let release = long("release") 41 | .help("Compile the binary in release mode") 42 | .switch(); 43 | let build = construct!(Options::Build { bin, release }) 44 | .to_options() 45 | .descr("Compile a binary") 46 | .command("build"); 47 | 48 | construct!([run, build]).to_options() 49 | } 50 | -------------------------------------------------------------------------------- /docs2/src/compose_basic_construct/cases.md: -------------------------------------------------------------------------------- 1 | Help message describes all the parser combined 2 | > --help 3 | 4 | And users can pass any combinations of options, resulting parser will handle them as long as they are valid 5 | 6 | > --argument 10 --argument 20 7 | > --switch 8 | > --switch --switch --argument 20 9 | -------------------------------------------------------------------------------- /docs2/src/compose_basic_construct/combine.rs: -------------------------------------------------------------------------------- 1 | use bpaf::*; 2 | 3 | #[derive(Debug, Clone)] 4 | pub struct Options { 5 | argument: Vec, 6 | switches: Vec, 7 | } 8 | 9 | pub fn options() -> OptionParser { 10 | let argument = long("argument") 11 | .help("important argument") 12 | .argument("ARG") 13 | .many(); 14 | let switches = long("switch").help("some switch").switch().many(); 15 | construct!(Options { argument, switches }).to_options() 16 | } 17 | -------------------------------------------------------------------------------- /docs2/src/compose_basic_many/cases.md: -------------------------------------------------------------------------------- 1 | Run inner parser as many times as possible collecting all the new results 2 | First `false` is collected from a switch even if it is not consuming anything 3 | > --argument 10 --argument 20 4 | 5 | If there's no matching parameters - it would produce an empty vector. 6 | > 7 | 8 | In usage lines `many` items are indicated with `...` 9 | > --help 10 | -------------------------------------------------------------------------------- /docs2/src/compose_basic_many/combine.rs: -------------------------------------------------------------------------------- 1 | use bpaf::*; 2 | 3 | pub fn options() -> OptionParser> { 4 | let argument = long("argument") 5 | .help("important argument") 6 | .argument("ARG") 7 | .many(); 8 | argument.to_options() 9 | } 10 | -------------------------------------------------------------------------------- /docs2/src/compose_basic_positional/cases.md: -------------------------------------------------------------------------------- 1 | Same as with argument by default there's no fallback so with no arguments parser fails 2 | 3 | > 4 | 5 | Other than that any name that does not start with a dash or explicitly converted to positional 6 | parameter gets parsed: 7 | 8 | > https://lemmyrs.org 9 | > "strange url" 10 | > -- --can-start-with-dash-too 11 | 12 | And as usual there's help message 13 | 14 | > --help 15 | -------------------------------------------------------------------------------- /docs2/src/compose_basic_positional/combine.rs: -------------------------------------------------------------------------------- 1 | use bpaf::*; 2 | 3 | pub fn options() -> OptionParser { 4 | let simple = positional("URL").help("Url to open"); 5 | simple.to_options() 6 | } 7 | -------------------------------------------------------------------------------- /docs2/src/compose_basic_switch/cases.md: -------------------------------------------------------------------------------- 1 | If you run the app with no parameters - switch will parse as `false` 2 | 3 | > 4 | 5 | Both short and long names produce true 6 | 7 | > -s 8 | > --simple 9 | 10 | In addition to the switch you defined `bpaf` generates switch for user help 11 | 12 | > --help 13 | -------------------------------------------------------------------------------- /docs2/src/compose_basic_switch/combine.rs: -------------------------------------------------------------------------------- 1 | use bpaf::*; 2 | 3 | pub fn options() -> OptionParser { 4 | let simple = short('s').long("simple").switch(); 5 | simple.to_options() 6 | } 7 | -------------------------------------------------------------------------------- /docs2/src/compose_basic_to_options/cases.md: -------------------------------------------------------------------------------- 1 | In addition to all the arguments specified by user `bpaf` adds a few more. One of them is 2 | `--help`: 3 | 4 | > --help 5 | 6 | The other one is `--version` - passing a string literal or something like 7 | `env!("CARGO_PKG_VERSION")` to get version from `cargo` directly usually works 8 | 9 | > --version 10 | 11 | Other than that `bpaf` tries its best to provide a helpful error messages 12 | 13 | > 14 | 15 | And if all parsers are satisfied [`run`](OptionParser::run) produces the result 16 | 17 | > -i 10 18 | -------------------------------------------------------------------------------- /docs2/src/compose_basic_to_options/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | pub struct Options { 5 | argument: u32, 6 | } 7 | 8 | pub fn options() -> OptionParser { 9 | let argument = short('i').argument::("ARG"); 10 | construct!(Options { argument }) 11 | .to_options() 12 | .version("3.1415") 13 | .descr("This is a short description") 14 | .header("It can contain multiple blocks, this block goes before options") 15 | .footer("This one goes after") 16 | } 17 | -------------------------------------------------------------------------------- /docs2/src/count/cases.md: -------------------------------------------------------------------------------- 1 | In `--help` message `req_flag` look similarly to [`switch`](NamedArg::switch) and 2 | [`flag`](NamedArg::flag) 3 | 4 | > --help 5 | 6 | Since parser uses `req_flag` it succeeds exactly 0 times if there's no parameters 7 | 8 | > 9 | 10 | If it was specified - `count` tracks it a discards parsed values 11 | 12 | > -vvv 13 | > --verbose --verbose 14 | -------------------------------------------------------------------------------- /docs2/src/count/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | pub struct Options { 5 | verbosity: usize, 6 | } 7 | 8 | pub fn options() -> OptionParser { 9 | let verbosity = short('v') 10 | .long("verbose") 11 | .help("Increase the verbosity level") 12 | .req_flag(()) 13 | .count(); 14 | 15 | construct!(Options { verbosity }).to_options() 16 | } 17 | -------------------------------------------------------------------------------- /docs2/src/count/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone, Bpaf)] 4 | #[bpaf(options)] 5 | pub struct Options { 6 | /// Increase the verbosity level 7 | #[bpaf(short('v'), long("verbose"), req_flag(()), count)] 8 | verbosity: usize, 9 | } 10 | -------------------------------------------------------------------------------- /docs2/src/custom_help_version/cases.md: -------------------------------------------------------------------------------- 1 | This example replaces description and short name for `--help` parser. Long name works as is 2 | 3 | > --help 4 | 5 | Short name is now capitalized 6 | 7 | > -H 8 | 9 | and old short name no longer works. 10 | 11 | > -h 12 | 13 | Same with `--version` parser - new description, original long name and custom short name are 14 | both working 15 | 16 | > --version 17 | 18 | > -v 19 | -------------------------------------------------------------------------------- /docs2/src/custom_help_version/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | pub struct Options { 5 | package: Option, 6 | } 7 | 8 | pub fn options() -> OptionParser { 9 | let help = long("help").short('H').help("Renders help information"); 10 | let version = long("version") 11 | .short('v') 12 | .help("Renders version information"); 13 | let package = short('p') 14 | .help("Package to check") 15 | .argument("SPEC") 16 | .optional(); 17 | 18 | construct!(Options { package }) 19 | .to_options() 20 | .descr("Command with custom flags for help and version") 21 | .version("0.42") 22 | .help_parser(help) 23 | .version_parser(version) 24 | } 25 | -------------------------------------------------------------------------------- /docs2/src/custom_usage/cases.md: -------------------------------------------------------------------------------- 1 | `custom_usage` changes how parser shows up in the "Usage" section of generated `--help`, note 2 | lack of `[]`, long name instead of a short one and different metavariable value 3 | 4 | > --help 5 | 6 | Parsing behavior stays unchanged 7 | 8 | > --binary cargo-asm --package cargo-show-asm 9 | 10 | -------------------------------------------------------------------------------- /docs2/src/custom_usage/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::{doc::*, *}; 3 | #[derive(Debug, Clone)] 4 | pub struct Options { 5 | binary: Option, 6 | package: Option, 7 | } 8 | 9 | pub fn options() -> OptionParser { 10 | let binary = short('b') 11 | .long("binary") 12 | .help("Binary to run") 13 | .argument("BIN") 14 | .optional() 15 | .custom_usage(&[ 16 | ("--binary", Style::Literal), 17 | ("=", Style::Text), 18 | ("BINARY", Style::Metavar), 19 | ]); 20 | 21 | let package = short('p') 22 | .long("package") 23 | .help("Package to check") 24 | .argument("PACKAGE") 25 | .optional(); 26 | 27 | construct!(Options { binary, package }).to_options() 28 | } 29 | -------------------------------------------------------------------------------- /docs2/src/custom_usage/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::{doc::*, *}; 3 | const BINARY_USAGE: &[(&str, Style)] = &[ 4 | ("--binary", Style::Literal), 5 | ("=", Style::Text), 6 | ("BINARY", Style::Metavar), 7 | ]; 8 | 9 | #[derive(Debug, Clone, Bpaf)] 10 | #[bpaf(options)] 11 | pub struct Options { 12 | /// Binary to run 13 | #[bpaf(short, long, argument("BIN"), custom_usage(BINARY_USAGE))] 14 | binary: Option, 15 | 16 | /// Package to check 17 | #[bpaf(short, long, argument("PACKAGE"))] 18 | package: Option, 19 | } 20 | -------------------------------------------------------------------------------- /docs2/src/dd/cases.md: -------------------------------------------------------------------------------- 1 | `bpaf` generates usual help message with 2 | 3 | > --help 4 | 5 | Unlike usual application `dd` takes it arguments in shape of operations 6 | `KEY=VAL` without any dashes, plus usual `--help` and `--version` flags. 7 | 8 | To handle that we define custom basic parsers that make handling such operations easy 9 | 10 | > if=/dev/zero of=/tmp/blob bs=1024 11 | -------------------------------------------------------------------------------- /docs2/src/deb_fallback/cases.md: -------------------------------------------------------------------------------- 1 | `fallback` changes parser to fallback to a default value used when argument is not specified 2 | 3 | > 4 | 5 | If value is present - fallback value is ignored 6 | 7 | > --jobs 10 8 | 9 | Parsing errors are preserved and presented to the user 10 | 11 | > --jobs ten 12 | 13 | With [`display_fallback`](ParseFallback::display_fallback), 14 | [`debug_fallback`](ParseFallback::debug_fallback), and 15 | [`format_fallback`](ParseFallback::format_fallback), you can make it so the default value 16 | is visible in the `--help` output. 17 | 18 | > --help 19 | -------------------------------------------------------------------------------- /docs2/src/deb_fallback/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | pub struct Options { 5 | jobs: usize, 6 | } 7 | 8 | pub fn options() -> OptionParser { 9 | let jobs = long("jobs") 10 | .help("Number of jobs") 11 | .argument("JOBS") 12 | .fallback(42) 13 | .debug_fallback(); 14 | construct!(Options { jobs }).to_options() 15 | } 16 | -------------------------------------------------------------------------------- /docs2/src/deb_fallback/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone, Bpaf)] 4 | #[bpaf(options)] 5 | #[allow(dead_code)] 6 | pub struct Options { 7 | /// Number of jobs 8 | #[bpaf(argument("JOBS"), fallback(42), debug_fallback)] 9 | jobs: usize, 10 | } 11 | -------------------------------------------------------------------------------- /docs2/src/deb_fallback_with/cases.md: -------------------------------------------------------------------------------- 1 | `fallback_with` changes parser to fallback to a value that comes from a potentially failing 2 | computation when argument is not specified 3 | 4 | > 5 | 6 | If value is present - fallback value is ignored 7 | 8 | > --version 10 9 | 10 | Parsing errors are preserved and presented to the user 11 | 12 | > --version ten 13 | 14 | `bpaf` encases parsers with fallback value of some sort in usage with `[]` 15 | 16 | > --help 17 | -------------------------------------------------------------------------------- /docs2/src/deb_fallback_with/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | fn try_to_get_version() -> Result { 4 | Ok(42) 5 | } 6 | 7 | #[derive(Debug, Clone)] 8 | pub struct Options { 9 | version: usize, 10 | } 11 | 12 | pub fn options() -> OptionParser { 13 | let version = long("version") 14 | .help("Specify protocol version") 15 | .argument("VERS") 16 | .fallback_with(try_to_get_version) 17 | .debug_fallback(); 18 | construct!(Options { version }).to_options() 19 | } 20 | -------------------------------------------------------------------------------- /docs2/src/deb_fallback_with/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | fn try_to_get_version() -> Result { 4 | Ok(42) 5 | } 6 | 7 | #[derive(Debug, Clone, Bpaf)] 8 | #[bpaf(options)] 9 | pub struct Options { 10 | #[bpaf(argument("VERS"), fallback_with(try_to_get_version), debug_fallback)] 11 | /// Specify protocol version 12 | version: usize, 13 | } 14 | -------------------------------------------------------------------------------- /docs2/src/derive_basic/cases.md: -------------------------------------------------------------------------------- 1 | > --help 2 | 3 | `--help` shows arguments as a short name with attached metavariable 4 | 5 | Value can be separated from flag by space, `=` sign 6 | 7 | > --name Bob --age 12 8 | > --name "Bob" --age=12 9 | > --name=Bob 10 | > --name="Bob" 11 | 12 | Or in case of short name - be directly adjacent to it 13 | 14 | > -nBob 15 | 16 | For long names - this doesn't work since parser can't tell where name 17 | stops and argument begins: 18 | 19 | > --age12 20 | 21 | Either way - value is required, passing just the argument name results in parse failure 22 | 23 | > --name 24 | -------------------------------------------------------------------------------- /docs2/src/derive_basic/derive.rs: -------------------------------------------------------------------------------- 1 | use bpaf::*; 2 | 3 | #[derive(Debug, Clone, Bpaf)] 4 | #[bpaf(options)] 5 | pub struct Options { 6 | /// Specify user name 7 | name: String, 8 | 9 | /// Specify user age 10 | age: usize, 11 | } 12 | -------------------------------------------------------------------------------- /docs2/src/derive_basic_commands/cases.md: -------------------------------------------------------------------------------- 1 | Help message lists subcommand 2 | > --help 3 | 4 | Commands have their own arguments 5 | 6 | > run --name Bob 7 | 8 | > test 9 | 10 | > test --name bob 11 | -------------------------------------------------------------------------------- /docs2/src/derive_basic_commands/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone, Bpaf)] 4 | #[bpaf(options)] 5 | pub enum Options { 6 | #[bpaf(command("run"))] 7 | /// Run a binary 8 | Run { 9 | /// Name of a binary crate 10 | name: String, 11 | }, 12 | 13 | /// Run a self test 14 | #[bpaf(command)] 15 | Test, 16 | } 17 | -------------------------------------------------------------------------------- /docs2/src/derive_basic_custom_consumer/cases.md: -------------------------------------------------------------------------------- 1 | `bpaf` generates help message with a short name only as described 2 | 3 | > --help 4 | 5 | And accepts the short name only 6 | 7 | > -s 42 8 | 9 | long name is missing 10 | 11 | > --switch 42 12 | -------------------------------------------------------------------------------- /docs2/src/derive_basic_custom_consumer/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone, Bpaf)] 4 | #[bpaf(options)] 5 | pub struct Options { 6 | /// A custom switch 7 | #[bpaf(short, switch)] 8 | switch: bool, 9 | 10 | /// Custom number 11 | #[bpaf(positional("NUM"))] 12 | argument: usize, 13 | } 14 | -------------------------------------------------------------------------------- /docs2/src/derive_basic_custom_name/cases.md: -------------------------------------------------------------------------------- 1 | `bpaf` uses custom names in help message 2 | 3 | > --help 4 | 5 | As well as accepts them on a command line and uses in error message 6 | 7 | > --switch 8 | 9 | > -A 42 -s 10 | 11 | -------------------------------------------------------------------------------- /docs2/src/derive_basic_custom_name/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone, Bpaf)] 4 | #[bpaf(options)] 5 | pub struct Options { 6 | /// A custom switch 7 | #[bpaf(short, long)] 8 | switch: bool, 9 | 10 | /// A custom argument 11 | #[bpaf(long("my-argument"), short('A'))] 12 | argument: usize, 13 | } 14 | -------------------------------------------------------------------------------- /docs2/src/derive_basic_enum/cases.md: -------------------------------------------------------------------------------- 1 | Help message reflects mutually exclusive parts 2 | 3 | > --help 4 | 5 | At least one branch needs to succeed 6 | 7 | > 8 | 9 | And in this example only one branch can succeed 10 | 11 | > --name Cargo.toml 12 | 13 | > --url https://crates.io --auth-method digest 14 | 15 | While both branches can succeed at once - only one will actually succeed and afetr that 16 | parsing fails since there are unconsumed items 17 | 18 | > --url https://crates.io --auth-method digest --name Cargo.toml 19 | -------------------------------------------------------------------------------- /docs2/src/derive_basic_enum/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone, Bpaf)] 4 | #[bpaf(options)] 5 | pub enum Options { 6 | File { 7 | /// Read input from a file 8 | name: String, 9 | }, 10 | 11 | Url { 12 | /// Read input from URL 13 | url: String, 14 | /// Authentication method to use for the URL 15 | auth_method: String, 16 | }, 17 | } 18 | -------------------------------------------------------------------------------- /docs2/src/derive_basic_intro/cases.md: -------------------------------------------------------------------------------- 1 | bpaf generates a help message 2 | 3 | > --help 4 | 5 | And two parsers. Numeric argument is required, boolean switch is optional and fall back value 6 | is false. 7 | 8 | > --switch 9 | 10 | > --switch --argument 42 11 | 12 | > --argument 42 13 | -------------------------------------------------------------------------------- /docs2/src/derive_basic_intro/derive.rs: -------------------------------------------------------------------------------- 1 | use bpaf::*; 2 | 3 | #[derive(Debug, Clone, Bpaf)] 4 | #[bpaf(options)] 5 | pub struct Options { 6 | /// A custom switch 7 | switch: bool, 8 | 9 | /// A custom argument 10 | argument: usize, 11 | } 12 | -------------------------------------------------------------------------------- /docs2/src/derive_basic_nesting/cases.md: -------------------------------------------------------------------------------- 1 | Help message lists all possible options 2 | 3 | > --help 4 | 5 | Parser accepts one and only one value from enum in this example 6 | 7 | > --input Cargo.toml --html 8 | > --input Cargo.toml --manpage 9 | 10 | > --input hello 11 | -------------------------------------------------------------------------------- /docs2/src/derive_basic_nesting/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone, Bpaf)] 4 | pub enum Format { 5 | /// Produce output in HTML format 6 | Html, 7 | /// Produce output in Markdown format 8 | Markdown, 9 | /// Produce output in manpage format 10 | Manpage, 11 | } 12 | 13 | #[derive(Debug, Clone, Bpaf)] 14 | #[bpaf(options)] 15 | pub struct Options { 16 | /// File to process 17 | input: String, 18 | #[bpaf(external(format))] 19 | format: Format, 20 | } 21 | -------------------------------------------------------------------------------- /docs2/src/derive_basic_postpr/cases.md: -------------------------------------------------------------------------------- 1 | Help as usual 2 | 3 | > --help 4 | 5 | And parsed values are differnt from what user passes 6 | 7 | > --width 10 --height 3 8 | 9 | Additionally height cannot exceed 10 10 | 11 | > --width 555 --height 42 12 | -------------------------------------------------------------------------------- /docs2/src/derive_basic_postpr/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | fn small(size: &usize) -> bool { 4 | *size < 10 5 | } 6 | 7 | #[derive(Debug, Clone, Bpaf)] 8 | #[bpaf(options)] 9 | pub struct Options { 10 | // double the width 11 | #[bpaf(short, argument::("PX"), map(|w| w*2))] 12 | width: usize, 13 | 14 | // make sure the hight is below 10 15 | #[bpaf(argument::("LENGTH"), guard(small, "must be less than 10"))] 16 | height: usize, 17 | } 18 | -------------------------------------------------------------------------------- /docs2/src/derive_show_asm/cases.md: -------------------------------------------------------------------------------- 1 | Example defines this parser 2 | 3 | > --help 4 | 5 | By default completion system lists all possible cases 6 | 7 | zsh> derive_show_asm \t 8 | 9 | But when user tries to complete example name - it only lists examples produced by 10 | `comp_examples` function 11 | 12 | zsh> derive_show_asm --example \t 13 | 14 | And completes the full name when user gives enough information 15 | 16 | zsh> derive_show_asm --example cor\t 17 | -------------------------------------------------------------------------------- /docs2/src/dis_fallback/cases.md: -------------------------------------------------------------------------------- 1 | `fallback` changes parser to fallback to a default value used when argument is not specified 2 | 3 | > 4 | 5 | If value is present - fallback value is ignored 6 | 7 | > --jobs 10 8 | 9 | Parsing errors are preserved and presented to the user 10 | 11 | > --jobs ten 12 | 13 | With [`display_fallback`](ParseFallback::display_fallback), 14 | [`debug_fallback`](ParseFallback::debug_fallback), and 15 | [`format_fallback`](ParseFallback::format_fallback), you can make it so the default value 16 | is visible in the `--help` output. 17 | 18 | > --help 19 | -------------------------------------------------------------------------------- /docs2/src/dis_fallback/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | pub struct Options { 5 | jobs: usize, 6 | } 7 | 8 | pub fn options() -> OptionParser { 9 | let jobs = long("jobs") 10 | .help("Number of jobs") 11 | .argument("JOBS") 12 | .fallback(42) 13 | .display_fallback(); 14 | construct!(Options { jobs }).to_options() 15 | } 16 | -------------------------------------------------------------------------------- /docs2/src/dis_fallback/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone, Bpaf)] 4 | #[bpaf(options)] 5 | #[allow(dead_code)] 6 | pub struct Options { 7 | /// Number of jobs 8 | #[bpaf(argument("JOBS"), fallback(42), display_fallback)] 9 | jobs: usize, 10 | } 11 | -------------------------------------------------------------------------------- /docs2/src/dis_fallback_with/cases.md: -------------------------------------------------------------------------------- 1 | `fallback_with` changes parser to fallback to a value that comes from a potentially failing 2 | computation when argument is not specified 3 | 4 | > 5 | 6 | If value is present - fallback value is ignored 7 | 8 | > --version 10 9 | 10 | Parsing errors are preserved and presented to the user 11 | 12 | > --version ten 13 | 14 | `bpaf` encases parsers with fallback value of some sort in usage with `[]` 15 | 16 | > --help 17 | -------------------------------------------------------------------------------- /docs2/src/dis_fallback_with/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | fn try_to_get_version() -> Result { 4 | Ok(42) 5 | } 6 | 7 | #[derive(Debug, Clone)] 8 | pub struct Options { 9 | version: usize, 10 | } 11 | 12 | pub fn options() -> OptionParser { 13 | let version = long("version") 14 | .help("Specify protocol version") 15 | .argument("VERS") 16 | .fallback_with(try_to_get_version) 17 | .display_fallback(); 18 | construct!(Options { version }).to_options() 19 | } 20 | -------------------------------------------------------------------------------- /docs2/src/dis_fallback_with/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | fn try_to_get_version() -> Result { 4 | Ok(42) 5 | } 6 | 7 | #[derive(Debug, Clone, Bpaf)] 8 | #[bpaf(options)] 9 | pub struct Options { 10 | #[bpaf(argument("VERS"), fallback_with(try_to_get_version), display_fallback)] 11 | /// Specify protocol version 12 | version: usize, 13 | } 14 | -------------------------------------------------------------------------------- /docs2/src/fallback/cases.md: -------------------------------------------------------------------------------- 1 | `fallback` changes parser to fallback to a default value used when argument is not specified 2 | 3 | > 4 | 5 | If value is present - fallback value is ignored 6 | 7 | > --jobs 10 8 | 9 | Parsing errors are preserved and presented to the user 10 | 11 | > --jobs ten 12 | 13 | With [`display_fallback`](ParseFallback::display_fallback) and 14 | [`debug_fallback`](ParseFallback::debug_fallback) you can make it so default value 15 | is visible in `--help` output 16 | 17 | > --help 18 | -------------------------------------------------------------------------------- /docs2/src/fallback/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | pub struct Options { 5 | jobs: usize, 6 | } 7 | 8 | pub fn options() -> OptionParser { 9 | let jobs = long("jobs") 10 | .help("Number of jobs") 11 | .argument("JOBS") 12 | .fallback(42) 13 | .display_fallback(); 14 | construct!(Options { jobs }).to_options() 15 | } 16 | -------------------------------------------------------------------------------- /docs2/src/fallback/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone, Bpaf)] 4 | #[bpaf(options)] 5 | #[allow(dead_code)] 6 | pub struct Options { 7 | /// Number of jobs 8 | #[bpaf(argument("JOBS"), fallback(42), display_fallback)] 9 | jobs: usize, 10 | } 11 | -------------------------------------------------------------------------------- /docs2/src/fallback_with/cases.md: -------------------------------------------------------------------------------- 1 | `fallback_with` changes parser to fallback to a value that comes from a potentially failing 2 | computation when argument is not specified 3 | 4 | > 5 | 6 | If value is present - fallback value is ignored 7 | 8 | > --version 10 9 | 10 | Parsing errors are preserved and presented to the user 11 | 12 | > --version ten 13 | 14 | `bpaf` encases parsers with fallback value of some sort in usage with `[]` 15 | 16 | > --help 17 | -------------------------------------------------------------------------------- /docs2/src/fallback_with/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | fn try_to_get_version() -> Result { 4 | Ok(42) 5 | } 6 | 7 | #[derive(Debug, Clone)] 8 | pub struct Options { 9 | version: usize, 10 | } 11 | 12 | pub fn options() -> OptionParser { 13 | let version = long("version") 14 | .help("Specify protocol version") 15 | .argument("VERS") 16 | .fallback_with(try_to_get_version) 17 | .display_fallback(); 18 | construct!(Options { version }).to_options() 19 | } 20 | -------------------------------------------------------------------------------- /docs2/src/fallback_with/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | fn try_to_get_version() -> Result { 4 | Ok(42) 5 | } 6 | 7 | #[derive(Debug, Clone, Bpaf)] 8 | #[bpaf(options)] 9 | pub struct Options { 10 | #[bpaf(argument("VERS"), fallback_with(try_to_get_version), display_fallback)] 11 | /// Specify protocol version 12 | version: usize, 13 | } 14 | -------------------------------------------------------------------------------- /docs2/src/find/cases.md: -------------------------------------------------------------------------------- 1 | Usually `find` takes a path where to look, the rest is optional 2 | 3 | > src tests 4 | 5 | In addition to paths `find` can take some more options, typically unusual: username, note a 6 | single dash with a long name: 7 | 8 | > -user bob 9 | 10 | 11 | Permissions, in an unusual format: 12 | 13 | > -mode /x 14 | 15 | And the most interesting one is `-exec` which takes multiple arbitrary parameters terminated 16 | by `;` (in shell you have to escape it as `\\;`) 17 | 18 | > -exec cat -A '{}' \; 19 | 20 | As usuall you can mix them and order doesn't matter 21 | 22 | > src -mode -r -user bob -exec rustc '{}' \; 23 | 24 | While `bpaf` takes some effort to render the help even for custom stuff - you can always 25 | bypass it by hiding options and substituting your own with custom `header`/`footer`. 26 | 27 | > --help 28 | -------------------------------------------------------------------------------- /docs2/src/flag/cases.md: -------------------------------------------------------------------------------- 1 | In `--help` output `bpaf` shows flags with no meta variable attached 2 | 3 | > --help 4 | 5 | Presense of a long name is decoded into `Yes` 6 | > --decision 7 | 8 | Absense is `No` 9 | > 10 | -------------------------------------------------------------------------------- /docs2/src/flag/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | // 5 | #[allow(dead_code)] 6 | pub struct Options { 7 | decision: Decision, 8 | } 9 | 10 | #[derive(Debug, Clone)] 11 | pub enum Decision { 12 | Yes, 13 | No, 14 | } 15 | 16 | fn parse_decision() -> impl Parser { 17 | long("decision") 18 | .help("Positive decision") 19 | .flag(Decision::Yes, Decision::No) 20 | } 21 | 22 | pub fn options() -> OptionParser { 23 | let decision = parse_decision(); 24 | construct!(Options { decision }).to_options() 25 | } 26 | -------------------------------------------------------------------------------- /docs2/src/flag/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone, Bpaf)] 4 | #[bpaf(options)] 5 | pub struct Options { 6 | /// Positive decision 7 | #[bpaf(flag(Decision::Yes, Decision::No))] 8 | decision: Decision, 9 | } 10 | 11 | #[derive(Debug, Clone)] 12 | pub enum Decision { 13 | Yes, 14 | No, 15 | } 16 | -------------------------------------------------------------------------------- /docs2/src/format_fallback/cases.md: -------------------------------------------------------------------------------- 1 | `fallback` changes parser to fallback to a default value used when argument is not specified 2 | 3 | > 4 | 5 | If value is present - fallback value is ignored 6 | 7 | > --log-file output.txt 8 | 9 | Parsing errors are preserved and presented to the user 10 | 11 | > --log-file / 12 | 13 | With [`display_fallback`](ParseFallback::display_fallback), 14 | [`debug_fallback`](ParseFallback::debug_fallback), and 15 | [`format_fallback`](ParseFallback::format_fallback), you can make it so the default value 16 | is visible in the `--help` output. 17 | 18 | > --help 19 | -------------------------------------------------------------------------------- /docs2/src/format_fallback/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | use std::{fmt::Display as _, path::PathBuf}; 4 | #[derive(Debug, Clone)] 5 | pub struct Options { 6 | log_file: PathBuf, 7 | } 8 | 9 | pub fn options() -> OptionParser { 10 | let log_file = long("log-file") 11 | .help("Path to log file") 12 | .argument::("FILE") 13 | .guard( 14 | |log_file| !log_file.is_dir(), 15 | "The log file can't be a directory", 16 | ) 17 | .fallback(PathBuf::from("logfile.txt")) 18 | .format_fallback(|path, f| path.display().fmt(f)); 19 | construct!(Options { log_file }).to_options() 20 | } 21 | -------------------------------------------------------------------------------- /docs2/src/format_fallback/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | use std::{fmt::Display as _, path::PathBuf}; 4 | #[derive(Debug, Clone, Bpaf)] 5 | #[bpaf(options)] 6 | #[allow(dead_code)] 7 | pub struct Options { 8 | /// Path to log file 9 | #[bpaf( 10 | argument("FILE"), 11 | guard(|log_file| !log_file.is_dir(), "The log file can't be a directory"), 12 | fallback(PathBuf::from("logfile.txt")), 13 | format_fallback(|path, f| path.display().fmt(f)), 14 | )] 15 | log_file: PathBuf, 16 | } 17 | -------------------------------------------------------------------------------- /docs2/src/format_fallback_with/cases.md: -------------------------------------------------------------------------------- 1 | `fallback_with` changes parser to fallback to a value that comes from a potentially failing 2 | computation when argument is not specified 3 | 4 | > 5 | 6 | If value is present - fallback value is ignored 7 | 8 | > --log-file output.txt 9 | 10 | Parsing errors are preserved and presented to the user 11 | 12 | > --log-file / 13 | 14 | `bpaf` encases parsers with fallback value of some sort in usage with `[]` 15 | 16 | > --help 17 | -------------------------------------------------------------------------------- /docs2/src/format_fallback_with/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | use std::{fmt::Display as _, path::PathBuf}; 4 | fn try_to_get_log_file() -> Result { 5 | Ok(PathBuf::from("logfile.txt")) 6 | } 7 | 8 | #[derive(Debug, Clone)] 9 | pub struct Options { 10 | log_file: PathBuf, 11 | } 12 | 13 | pub fn options() -> OptionParser { 14 | let log_file = long("log-file") 15 | .help("Path to log file") 16 | .argument::("FILE") 17 | .guard( 18 | |log_file| !log_file.is_dir(), 19 | "The log file can't be a directory", 20 | ) 21 | .fallback_with(try_to_get_log_file) 22 | .format_fallback(|path, f| path.display().fmt(f)); 23 | construct!(Options { log_file }).to_options() 24 | } 25 | -------------------------------------------------------------------------------- /docs2/src/format_fallback_with/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | use std::{fmt::Display as _, path::PathBuf}; 4 | fn try_to_get_log_file() -> Result { 5 | Ok(PathBuf::from("logfile.txt")) 6 | } 7 | 8 | #[derive(Debug, Clone, Bpaf)] 9 | #[bpaf(options)] 10 | pub struct Options { 11 | /// Path to log file 12 | #[bpaf( 13 | argument("FILE"), 14 | guard(|log_file| !log_file.is_dir(), "The log file can't be a directory"), 15 | fallback_with(try_to_get_log_file), 16 | format_fallback(|path, f| path.display().fmt(f)), 17 | )] 18 | log_file: PathBuf, 19 | } 20 | -------------------------------------------------------------------------------- /docs2/src/group_help/cases.md: -------------------------------------------------------------------------------- 1 | `group_help` adds extra decoration for the inner group in `--help` message 2 | 3 | > --help 4 | 5 | And doesn't change the parsing behavior in any way 6 | 7 | > --argument 32 --width 20 --height 13 8 | -------------------------------------------------------------------------------- /docs2/src/group_help/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | pub struct Rectangle { 5 | width: u32, 6 | height: u32, 7 | } 8 | 9 | // 10 | #[allow(dead_code)] 11 | #[derive(Debug, Clone)] 12 | pub struct Options { 13 | argument: u32, 14 | rectangle: Rectangle, 15 | } 16 | 17 | pub fn options() -> OptionParser { 18 | let argument = long("argument") 19 | .help("important argument") 20 | .argument("ARG") 21 | .fallback(30); 22 | 23 | let width = long("width") 24 | .help("Width of the rectangle") 25 | .argument("W") 26 | .fallback(10); 27 | let height = long("height") 28 | .help("Height of the rectangle") 29 | .argument("H") 30 | .fallback(10); 31 | let rectangle = construct!(Rectangle { width, height }).group_help("Takes a rectangle"); 32 | 33 | construct!(Options { 34 | argument, 35 | rectangle 36 | }) 37 | .to_options() 38 | } 39 | -------------------------------------------------------------------------------- /docs2/src/group_help/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone, Bpaf)] 4 | pub struct Rectangle { 5 | /// Width of the rectangle 6 | #[bpaf(argument("W"), fallback(10))] 7 | width: u32, 8 | /// Height of the rectangle 9 | #[bpaf(argument("H"), fallback(10))] 10 | height: u32, 11 | } 12 | 13 | #[derive(Debug, Clone, Bpaf)] 14 | #[bpaf(options)] 15 | pub struct Options { 16 | /// important argument 17 | #[bpaf(fallback(30))] 18 | argument: u32, 19 | /// secret switch 20 | #[bpaf(external, group_help("Takes a rectangle"))] 21 | rectangle: Rectangle, 22 | } 23 | -------------------------------------------------------------------------------- /docs2/src/guard/cases.md: -------------------------------------------------------------------------------- 1 | `guard` don't make any changes to generated `--help` message 2 | 3 | > --help 4 | 5 | You can use guard to set boundary limits or perform other checks on parsed values. 6 | Parser accepts numbers below 10 7 | 8 | > --number 5 9 | 10 | And fails with the error message on higher values: 11 | 12 | > --number 11 13 | 14 | 15 | But if function inside the parser fails - user will get the error back unless it's handled 16 | in some way 17 | 18 | > --number ten 19 | -------------------------------------------------------------------------------- /docs2/src/guard/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | pub struct Options { 5 | number: u32, 6 | } 7 | 8 | pub fn options() -> OptionParser { 9 | let number = long("number").argument::("N").guard( 10 | |n| *n <= 10, 11 | "Values greater than 10 are only available in the DLC pack!", 12 | ); 13 | construct!(Options { number }).to_options() 14 | } 15 | -------------------------------------------------------------------------------- /docs2/src/guard/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | fn dlc_check(number: &u32) -> bool { 4 | *number <= 10 5 | } 6 | 7 | const DLC_NEEDED: &str = "Values greater than 10 are only available in the DLC pack!"; 8 | 9 | #[derive(Debug, Clone, Bpaf)] 10 | #[bpaf(options)] 11 | pub struct Options { 12 | #[bpaf(argument("N"), guard(dlc_check, DLC_NEEDED))] 13 | number: u32, 14 | } 15 | -------------------------------------------------------------------------------- /docs2/src/help/cases.md: -------------------------------------------------------------------------------- 1 | > --help 2 | -------------------------------------------------------------------------------- /docs2/src/help/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::{doc::*, *}; 3 | #[derive(Debug, Clone)] 4 | pub struct Options { 5 | number: u32, 6 | } 7 | 8 | pub fn options() -> OptionParser { 9 | let number = long("number") 10 | .help( 11 | &[ 12 | ("Very", Style::Emphasis), 13 | (" important argument", Style::Text), 14 | ][..], 15 | ) 16 | .argument::("N"); 17 | construct!(Options { number }).to_options() 18 | } 19 | -------------------------------------------------------------------------------- /docs2/src/help/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::{doc::*, *}; 3 | 4 | const ARG: &[(&str, Style)] = &[ 5 | ("Very", Style::Emphasis), 6 | (" important argument", Style::Text), 7 | ]; 8 | 9 | #[derive(Debug, Clone, Bpaf)] 10 | #[bpaf(options)] 11 | pub struct Options { 12 | #[bpaf(argument("N"), help(ARG))] 13 | number: u32, 14 | } 15 | -------------------------------------------------------------------------------- /docs2/src/hide/cases.md: -------------------------------------------------------------------------------- 1 | `hide` removes the inner parser from any help or autocompletion logic 2 | 3 | > --help 4 | 5 | But doesn't change the parsing behavior in any way otherwise 6 | 7 | > --argument 32 8 | 9 | > --argument 42 --switch 10 | -------------------------------------------------------------------------------- /docs2/src/hide/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | pub struct Options { 5 | argument: u32, 6 | switch: bool, 7 | } 8 | 9 | pub fn options() -> OptionParser { 10 | let argument = long("argument") 11 | .help("important argument") 12 | .argument("ARG") 13 | .fallback(30); 14 | let switch = long("switch").help("secret switch").switch().hide(); 15 | construct!(Options { argument, switch }).to_options() 16 | } 17 | -------------------------------------------------------------------------------- /docs2/src/hide/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone, Bpaf)] 4 | #[bpaf(options)] 5 | pub struct Options { 6 | /// important argument 7 | #[bpaf(fallback(30))] 8 | argument: u32, 9 | /// secret switch 10 | #[bpaf(hide)] 11 | switch: bool, 12 | } 13 | -------------------------------------------------------------------------------- /docs2/src/hide_usage/cases.md: -------------------------------------------------------------------------------- 1 | `hide_usage` hides the inner parser from the generated usage line, but not from the rest of the help or completion 2 | 3 | > --help 4 | 5 | But doesn’t change the parsing behavior in any way otherwise 6 | 7 | > --argument 32 8 | 9 | > --argument 32 --switch 10 | -------------------------------------------------------------------------------- /docs2/src/hide_usage/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | // 4 | #[allow(dead_code)] 5 | #[derive(Debug, Clone)] 6 | pub struct Options { 7 | argument: u32, 8 | switch: bool, 9 | } 10 | 11 | pub fn options() -> OptionParser { 12 | let argument = long("argument") 13 | .help("important argument") 14 | .argument("ARG") 15 | .fallback(30); 16 | let switch = long("switch") 17 | .help("not that important switch") 18 | .switch() 19 | .hide_usage(); 20 | construct!(Options { argument, switch }).to_options() 21 | } 22 | -------------------------------------------------------------------------------- /docs2/src/hide_usage/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[allow(dead_code)] 4 | #[derive(Debug, Clone, Bpaf)] 5 | #[bpaf(options)] 6 | pub struct Options { 7 | /// important argument 8 | #[bpaf(fallback(30))] 9 | argument: u32, 10 | /// not that important switch 11 | #[bpaf(hide_usage)] 12 | switch: bool, 13 | } 14 | -------------------------------------------------------------------------------- /docs2/src/intro/cases.md: -------------------------------------------------------------------------------- 1 | With everything in place users should be able to pass their arguments 2 | 3 | > "Hello world" 4 | 5 | As well as read the help message generated by the library 6 | 7 | > --help 8 | -------------------------------------------------------------------------------- /docs2/src/intro/combine.rs: -------------------------------------------------------------------------------- 1 | use bpaf::*; 2 | 3 | #[derive(Debug, Clone)] 4 | pub struct Options { 5 | message: String, 6 | } 7 | 8 | pub fn options() -> OptionParser { 9 | let message = positional("MESSAGE").help("Message to print in a big friendly letters"); 10 | construct!(Options { message }).to_options() 11 | } 12 | -------------------------------------------------------------------------------- /docs2/src/intro/derive.rs: -------------------------------------------------------------------------------- 1 | use bpaf::*; 2 | 3 | #[derive(Debug, Clone, Bpaf)] 4 | #[bpaf(options)] 5 | pub struct Options { 6 | /// Message to print in a big friendly letters 7 | #[bpaf(positional("MESSAGE"))] 8 | message: String, 9 | } 10 | -------------------------------------------------------------------------------- /docs2/src/last/cases.md: -------------------------------------------------------------------------------- 1 | In `--help` message `last` shows that inner parser can run multiple times 2 | 3 | > --help 4 | 5 | 6 | `style` takes one of several possible values and `last` lets user to pass it several times 7 | 8 | > --intel 9 | > --intel --att 10 | > --intel --att --intel 11 | 12 | same goes with `report` 13 | 14 | > --intel --detailed 15 | > --att --detailed --minimal 16 | -------------------------------------------------------------------------------- /docs2/src/last/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | pub enum Style { 5 | Intel, 6 | Att, 7 | Llvm, 8 | } 9 | 10 | #[derive(Debug, Clone)] 11 | pub enum Report { 12 | /// Include defailed report 13 | Detailed, 14 | /// Include minimal report 15 | Minimal, 16 | /// No preferences 17 | Undecided, 18 | } 19 | 20 | #[derive(Debug, Clone)] 21 | pub struct Options { 22 | style: Style, 23 | report: Report, 24 | } 25 | 26 | pub fn options() -> OptionParser { 27 | let intel = long("intel") 28 | .help("Show assembly using Intel style") 29 | .req_flag(Style::Intel); 30 | let att = long("att") 31 | .help("Show assembly using AT&T style") 32 | .req_flag(Style::Att); 33 | let llvm = long("llvm").help("Show llvm-ir").req_flag(Style::Llvm); 34 | let style = construct!([intel, att, llvm]).last(); 35 | 36 | let detailed = long("detailed") 37 | .help("Include detailed report") 38 | .req_flag(Report::Detailed); 39 | let minimal = long("minimal") 40 | .help("Include minimal report") 41 | .req_flag(Report::Minimal); 42 | let report = construct!([detailed, minimal]) 43 | .last() 44 | .fallback(Report::Undecided); 45 | 46 | construct!(Options { style, report }).to_options() 47 | } 48 | -------------------------------------------------------------------------------- /docs2/src/last/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone, Bpaf)] 4 | #[bpaf(last)] 5 | pub enum Style { 6 | /// Show assembly using Intel style 7 | Intel, 8 | /// Show assembly using AT&T style 9 | Att, 10 | /// Show llvm-ir 11 | Llvm, 12 | } 13 | 14 | #[derive(Debug, Clone, Bpaf)] 15 | #[bpaf(last, fallback(Report::Undecided))] 16 | pub enum Report { 17 | /// Include detailed report 18 | Detailed, 19 | /// Include minimal report 20 | Minimal, 21 | #[bpaf(skip)] 22 | /// No preferences 23 | Undecided, 24 | } 25 | 26 | #[derive(Debug, Clone, Bpaf)] 27 | #[bpaf(options)] 28 | pub struct Options { 29 | // external here uses explicit reference to function `style` 30 | // generated above 31 | #[bpaf(external(style))] 32 | style: Style, 33 | // here reference is implicit and derived from field name: `report` 34 | #[bpaf(external)] 35 | report: Report, 36 | } 37 | -------------------------------------------------------------------------------- /docs2/src/many/cases.md: -------------------------------------------------------------------------------- 1 | In usage lines `many` items are indicated with `...` 2 | > --help 3 | 4 | Run inner parser as many times as possible collecting all the new results 5 | First `false` is collected from a switch even if it is not consuming anything 6 | 7 | > --argument 10 --argument 20 8 | 9 | If there's no matching parameters - it would produce an empty vector. Note, in case of 10 | [`switch`](NamedArg::switch) parser or other parsers that can succeed without consuming anything 11 | it would capture that value so `many` captures the first one of those. 12 | You can use [`req_flag`](NamedArg::req_flag) to avoid that. 13 | 14 | > 15 | 16 | For parsers that can succeed without consuming anything such as `flag` or `switch` - `many` 17 | only collects values as long as they produce something 18 | 19 | > --switch --switch 20 | -------------------------------------------------------------------------------- /docs2/src/many/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | pub struct Options { 5 | argument: Vec, 6 | switches: Vec, 7 | } 8 | 9 | pub fn options() -> OptionParser { 10 | let argument = long("argument") 11 | .help("important argument") 12 | .argument("ARG") 13 | .many(); 14 | let switches = long("switch").help("some switch").switch().many(); 15 | construct!(Options { argument, switches }).to_options() 16 | } 17 | -------------------------------------------------------------------------------- /docs2/src/many/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone, Bpaf)] 4 | #[bpaf(options)] 5 | pub struct Options { 6 | /// important argument 7 | argument: Vec, 8 | /// some switch 9 | #[bpaf(long("switch"), switch)] 10 | switches: Vec, 11 | } 12 | -------------------------------------------------------------------------------- /docs2/src/many_catch/cases.md: -------------------------------------------------------------------------------- 1 | Despite parser producing a funky value - help looks like you would expect from a parser that 2 | takes two values 3 | 4 | > --help 5 | 6 | When executed with no parameters it produces four `[]` values - all parsers succeed by the 7 | nature of them being [`many`](Parser::many) 8 | 9 | > 10 | 11 | When executed with expected parameters fields with `usize` get their values 12 | 13 | > --height 100 --width 100 --height 12 --width 44 14 | 15 | With incorrect value for `--height` parameter inner part of `height` parser fails, `many` 16 | combined with `catch` handles this failure and produces `[]` without consuming value from the 17 | command line. Parser `height_str` runs next and consumes the value as a string 18 | 19 | > --height ten --height twenty 20 | 21 | In case of wrong `--width` - parser `width` fails, parser for `many` sees this as a 22 | "value is present but not correct" and propagates the error outside, execution never reaches 23 | `width_str` parser 24 | 25 | > --width ten 26 | -------------------------------------------------------------------------------- /docs2/src/many_catch/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | pub struct Options { 5 | height: Vec, 6 | height_str: Vec, 7 | width: Vec, 8 | width_str: Vec, 9 | } 10 | 11 | pub fn options() -> OptionParser { 12 | // contains catch 13 | let height = long("height") 14 | .help("Height of a rectangle") 15 | .argument::("PX") 16 | .many() 17 | .catch(); 18 | 19 | let height_str = long("height").argument::("PX").many().hide(); 20 | 21 | // contains no catch 22 | let width = long("width") 23 | .help("Width of a rectangle") 24 | .argument::("PX") 25 | .many(); 26 | 27 | let width_str = long("width").argument::("PX").many().hide(); 28 | 29 | construct!(Options { 30 | height, 31 | height_str, 32 | width, 33 | width_str 34 | }) 35 | .to_options() 36 | } 37 | -------------------------------------------------------------------------------- /docs2/src/many_catch/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone, Bpaf)] 4 | #[bpaf(options)] 5 | pub struct Options { 6 | #[bpaf(long, argument("PX"), many, catch)] 7 | /// Height of a rectangle 8 | height: Vec, 9 | 10 | #[bpaf(long("height"), argument("PX"), many, hide)] 11 | height_str: Vec, 12 | 13 | #[bpaf(long, argument("PX"), many)] 14 | /// Width of a rectangle 15 | width: Vec, 16 | 17 | #[bpaf(long("width"), argument("PX"), many, hide)] 18 | width_str: Vec, 19 | } 20 | -------------------------------------------------------------------------------- /docs2/src/map/cases.md: -------------------------------------------------------------------------------- 1 | `map` don't make any changes to generated `--help` message 2 | 3 | 4 | You can use `map` to apply arbitrary pure transformation to any input. 5 | Here `--number` takes a numerical value and doubles it 6 | 7 | > --number 10 8 | 9 | But if function inside the parser fails - user will get the error back unless it's handled 10 | in some way. In fact here execution never reaches `map` function - 11 | [`argument`](NamedArg::argument) tries to parse `ten` as a number, fails and reports the error 12 | 13 | > --number ten 14 | -------------------------------------------------------------------------------- /docs2/src/map/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | // 5 | #[allow(dead_code)] 6 | pub struct Options { 7 | number: u32, 8 | } 9 | pub fn options() -> OptionParser { 10 | let number = long("number").argument::("N").map(|x| x * 2); 11 | construct!(Options { number }).to_options() 12 | } 13 | -------------------------------------------------------------------------------- /docs2/src/map/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | fn twice_the_num(n: u32) -> u32 { 4 | n * 2 5 | } 6 | 7 | #[derive(Debug, Clone, Bpaf)] 8 | #[bpaf(options)] 9 | // 10 | #[allow(dead_code)] 11 | pub struct Options { 12 | #[bpaf(argument::("N"), map(twice_the_num))] 13 | number: u32, 14 | } 15 | -------------------------------------------------------------------------------- /docs2/src/named_arg_combine/cases.md: -------------------------------------------------------------------------------- 1 | `--help` output will contain first short and first long names that are present and won't have 2 | anything about hidden aliases. 3 | 4 | > --help 5 | 6 | `--detailed` is a hidden alias and still works despite not being present in `--help` output 7 | above 8 | 9 | > -o -s 2 --detailed 10 | 11 | And hidden means actually hidden. While error message can suggest to fix a typo to make it a 12 | valid _visible_ argument 13 | 14 | > -o best.txt -s 10 --verbos 15 | 16 | It will not do so for hidden aliases 17 | 18 | > -o best.txt -s 10 --detaile 19 | 20 | 21 | In this example names `-o` and `--output` can be parsed by two parsers - `to_file` and 22 | `to_console`, first one succeeds only if `-o` is followed by a non option name, `best.txt`. 23 | 24 | > -o best.txt --size 10 25 | 26 | If such name is not present - parser will try to consume one without, producing `ToConsole` 27 | variant. 28 | 29 | > -o -s 42 30 | 31 | If neither is present - it fails - parser for `output` expects one of its branches to succeed 32 | 33 | > -s 330 34 | 35 | But this can be fixed with [`optional`](Parser::optional) (not included in this example). 36 | -------------------------------------------------------------------------------- /docs2/src/named_arg_combine/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | // 4 | use std::path::PathBuf; 5 | #[derive(Debug, Clone)] 6 | pub enum Output { 7 | ToFile(PathBuf), 8 | ToConsole, 9 | } 10 | pub fn options() -> OptionParser<(usize, Output, bool)> { 11 | // In most cases you don't keep `NamedArg` around long enough 12 | // to assign it a name 13 | let size = short('s') 14 | .long("size") 15 | .help("Maximum size to process") 16 | .argument("SIZE"); 17 | 18 | // but it can be useful if you want to have several arguments 19 | // sharing exact set of names - for example a switch (req_flag) 20 | // and an argument; 21 | let output = short('o').long("output"); 22 | 23 | let to_file = output 24 | .clone() 25 | .help("Save output to file") 26 | .argument("PATH") 27 | .map(Output::ToFile); 28 | let to_console = output 29 | .help("Print output to console") 30 | .req_flag(Output::ToConsole); 31 | 32 | // when combining multiple parsers that can conflict with each other 33 | // it's a good idea to put more general first: 34 | let output = construct!([to_file, to_console]); 35 | 36 | let verbose = short('v') 37 | .long("verbose") 38 | .long("detailed") 39 | .help("Produce a detailed report") 40 | .switch(); 41 | 42 | construct!(size, output, verbose).to_options() 43 | } 44 | -------------------------------------------------------------------------------- /docs2/src/named_arg_derive/cases.md: -------------------------------------------------------------------------------- 1 | `--help` output will contain first short and first long names that are present and won't have 2 | anything about hidden aliases. 3 | 4 | > --help 5 | 6 | `--essential` is a hidden alias and still works despite not being present in `--help` output 7 | above 8 | 9 | > --database default --essential 10 | 11 | And hidden means actually hidden. While error message can suggest to fix a typo to make it a 12 | valid _visible_ argument 13 | 14 | > --database default --quie 15 | 16 | It will not do so for hidden aliases 17 | 18 | > --database default --essentia 19 | -------------------------------------------------------------------------------- /docs2/src/numeric_prefix/cases.md: -------------------------------------------------------------------------------- 1 | If `bpaf` can parse first positional argument as number - it becomes a numeric prefix 2 | 3 | > 10 eat 4 | 5 | Otherwise it gets ignored 6 | 7 | > "just eat" 8 | 9 | 10 | If validation passes but second argument is missing - in this example there's no fallback 11 | 12 | > 10 13 | 14 | Help should show that the prefix is optional 15 | 16 | > --help 17 | -------------------------------------------------------------------------------- /docs2/src/optional/cases.md: -------------------------------------------------------------------------------- 1 | `bpaf` encases optional arguments in usage with `[]` 2 | 3 | > --help 4 | 5 | Missing arguments are turned into None 6 | 7 | > 8 | 9 | Present values are `Some` 10 | 11 | > --version 10 12 | 13 | As usual you can specify both 14 | 15 | > --version 10 --feature feat 16 | -------------------------------------------------------------------------------- /docs2/src/optional/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | pub struct Options { 5 | version: Option, 6 | feature: Option, 7 | } 8 | pub fn options() -> OptionParser { 9 | let version = long("version").argument("VERS").optional(); 10 | let feature = long("feature").argument("FEAT").optional(); 11 | construct!(Options { version, feature }).to_options() 12 | } 13 | -------------------------------------------------------------------------------- /docs2/src/optional/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone, Bpaf)] 4 | #[bpaf(options)] 5 | pub struct Options { 6 | #[bpaf(argument("VERS"))] 7 | version: Option, 8 | #[bpaf(argument("FEAT"))] 9 | feature: Option, 10 | } 11 | -------------------------------------------------------------------------------- /docs2/src/optional_catch/cases.md: -------------------------------------------------------------------------------- 1 | Despite parser producing a funky value - help looks like you would expect from a parser that 2 | takes two values 3 | 4 | > --help 5 | 6 | When executed with no parameters it produces four `None` values - all parsers succeed by the 7 | nature of them being [`optional`](Parser::optional) 8 | 9 | > 10 | 11 | When executed with expected parameters fields with `usize` get their values 12 | 13 | > --height 100 --width 100 14 | 15 | With incorrect value for `--height` parameter inner part of `height` parser fails, `optional` 16 | combined with `catch` handles this failure and produces `None` without consuming value from the 17 | command line. Parser `height_str` runs next and consumes the value as a string 18 | 19 | > --height ten 20 | 21 | In case of wrong `--width` - parser `width` fails, parser for `optional` sees this as a 22 | "value is present but not correct" and propagates the error outside, execution never reaches 23 | `width_str` parser 24 | 25 | > --width ten 26 | -------------------------------------------------------------------------------- /docs2/src/optional_catch/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | pub struct Options { 5 | height: Option, 6 | height_str: Option, 7 | width: Option, 8 | width_str: Option, 9 | } 10 | 11 | pub fn options() -> OptionParser { 12 | // contains catch 13 | let height = long("height") 14 | .help("Height of a rectangle") 15 | .argument::("PX") 16 | .optional() 17 | .catch(); 18 | 19 | let height_str = long("height").argument::("PX").optional().hide(); 20 | 21 | // contains no catch 22 | let width = long("width") 23 | .help("Width of a rectangle") 24 | .argument::("PX") 25 | .optional(); 26 | 27 | let width_str = long("width").argument::("PX").optional().hide(); 28 | 29 | construct!(Options { 30 | height, 31 | height_str, 32 | width, 33 | width_str 34 | }) 35 | .to_options() 36 | } 37 | -------------------------------------------------------------------------------- /docs2/src/optional_catch/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone, Bpaf)] 4 | #[bpaf(options)] 5 | pub struct Options { 6 | #[bpaf(long, argument("PX"), optional, catch)] 7 | /// Height of a rectangle 8 | height: Option, 9 | 10 | #[bpaf(long("height"), argument("PX"), optional, hide)] 11 | height_str: Option, 12 | 13 | #[bpaf(long, argument("PX"), optional)] 14 | /// Width of a rectangle 15 | width: Option, 16 | 17 | #[bpaf(long("width"), argument("PX"), optional, hide)] 18 | width_str: Option, 19 | } 20 | -------------------------------------------------------------------------------- /docs2/src/parse/cases.md: -------------------------------------------------------------------------------- 1 | `parse` don't make any changes to generated `--help` message 2 | 3 | > --help 4 | 5 | You can use `parse` to apply arbitrary failing transformation to any input. 6 | For example here `--number` takes a numerical value and doubles it 7 | 8 | > --number 10 9 | 10 | But if function inside the parser fails - user will get the error back unless it's handled 11 | in some other way 12 | 13 | > --number ten 14 | -------------------------------------------------------------------------------- /docs2/src/parse/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | // 4 | use std::{num::ParseIntError, str::FromStr}; 5 | #[derive(Debug, Clone)] 6 | pub struct Options { 7 | number: u32, 8 | } 9 | 10 | pub fn options() -> OptionParser { 11 | let number = long("number") 12 | .argument::("N") 13 | // normally you'd use argument:: to get a numeric 14 | // value and `map` to double it 15 | .parse::<_, _, ParseIntError>(|s| Ok(u32::from_str(&s)? * 2)); 16 | construct!(Options { number }).to_options() 17 | } 18 | -------------------------------------------------------------------------------- /docs2/src/parse/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | // 4 | use std::{num::ParseIntError, str::FromStr}; 5 | fn twice_the_num(s: String) -> Result { 6 | Ok(u32::from_str(&s)? * 2) 7 | } 8 | 9 | #[derive(Debug, Clone, Bpaf)] 10 | #[bpaf(options)] 11 | pub struct Options { 12 | #[bpaf(argument::("N"), parse(twice_the_num))] 13 | number: u32, 14 | } 15 | -------------------------------------------------------------------------------- /docs2/src/positional/cases.md: -------------------------------------------------------------------------------- 1 | Positional items show up in a separate group of arguments if they contain a help message, 2 | otherwise they will show up only in **Usage** part. 3 | 4 | > --help 5 | 6 | You can mix positional items with regular items 7 | 8 | > --verbose bpaf 9 | 10 | And since `bpaf` API expects to have non positional items consumed before positional ones - you 11 | can use them in a different order. In this example `bpaf` corresponds to a `crate_name` field and 12 | `--verbose` -- to `verbose`. 13 | 14 | > bpaf --verbose 15 | 16 | In previous examples optional field `feature` was missing, this one contains it. 17 | 18 | > bpaf autocomplete 19 | 20 | Users can use `--` to tell `bpaf` to treat remaining items as positionals - this might be 21 | required to handle unusual items. 22 | 23 | > bpaf -- --verbose 24 | > -- bpaf --verbose 25 | 26 | Without using `--` `bpaf` would only accept items that don't start with `-` as positional. 27 | 28 | > --detailed 29 | > --verbose 30 | 31 | You can use [`any`] to work around this restriction. 32 | -------------------------------------------------------------------------------- /docs2/src/positional/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | pub struct Options { 5 | verbose: bool, 6 | crate_name: String, 7 | feature_name: Option, 8 | } 9 | 10 | pub fn options() -> OptionParser { 11 | let verbose = short('v') 12 | .long("verbose") 13 | .help("Display detailed information") 14 | .switch(); 15 | 16 | let crate_name = positional("CRATE").help("Crate name to use"); 17 | 18 | let feature_name = positional("FEATURE") 19 | .help("Display information about this feature") 20 | .optional(); 21 | 22 | construct!(Options { 23 | verbose, 24 | // You must place positional items and commands after 25 | // all other parsers 26 | crate_name, 27 | feature_name 28 | }) 29 | .to_options() 30 | } 31 | -------------------------------------------------------------------------------- /docs2/src/positional/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone, Bpaf)] 4 | #[bpaf(options)] 5 | pub struct Options { 6 | /// Display detailed information 7 | #[bpaf(short, long)] 8 | verbose: bool, 9 | 10 | // You must place positional items and commands after 11 | // all other parsers 12 | #[bpaf(positional("CRATE"))] 13 | /// Crate name to use 14 | crate_name: String, 15 | 16 | #[bpaf(positional("FEATURE"))] 17 | /// Display information about this feature 18 | feature_name: Option, 19 | } 20 | -------------------------------------------------------------------------------- /docs2/src/positional_strict/cases.md: -------------------------------------------------------------------------------- 1 | Usage line for a cargo-run like app that takes an app name and possibly many strictly 2 | positional child arguments can look like this: 3 | 4 | > --help 5 | 6 | Here any argument passed before double dash goes to the parser itself 7 | 8 | > --bin dd --verbose 9 | 10 | Anything after it - collected into strict arguments 11 | 12 | > --bin dd -- --verbose 13 | -------------------------------------------------------------------------------- /docs2/src/positional_strict/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | pub struct Options { 5 | verbose: bool, 6 | binary: String, 7 | args: Vec, 8 | } 9 | 10 | pub fn options() -> OptionParser { 11 | let verbose = short('v') 12 | .long("verbose") 13 | .help("Produce detailed report") 14 | .switch(); 15 | let binary = long("bin").help("Binary to execute").argument("BIN"); 16 | let args = positional("ARG") 17 | .help("Arguments for the binary") 18 | .strict() 19 | .many(); 20 | construct!(Options { 21 | verbose, 22 | binary, 23 | args 24 | }) 25 | .to_options() 26 | } 27 | -------------------------------------------------------------------------------- /docs2/src/positional_strict/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone, Bpaf)] 4 | #[bpaf(options)] 5 | pub struct Options { 6 | #[bpaf(short, long)] 7 | /// Produce detailed report 8 | verbose: bool, 9 | #[bpaf(long("bin"), argument("BIN"))] 10 | /// Binary to execute 11 | binary: String, 12 | #[bpaf(positional("ARG"), strict, many)] 13 | /// Arguments for the binary 14 | args: Vec, 15 | } 16 | -------------------------------------------------------------------------------- /docs2/src/pure/cases.md: -------------------------------------------------------------------------------- 1 | `pure` does not show up in `--help` message 2 | 3 | > --help 4 | 5 | And there's no way to alter the value from the command line 6 | 7 | > --name Bob 8 | 9 | Any attempts to do so would result in an error :) 10 | 11 | > --money 100000 --name Hackerman 12 | -------------------------------------------------------------------------------- /docs2/src/pure/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | pub struct Options { 5 | name: String, 6 | money: u32, 7 | } 8 | 9 | pub fn options() -> OptionParser { 10 | // User can customise a name 11 | let name = long("name").help("Use a custom user name").argument("NAME"); 12 | // but not starting amount of money 13 | let money = pure(330); 14 | construct!(Options { name, money }).to_options() 15 | } 16 | -------------------------------------------------------------------------------- /docs2/src/pure/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone, Bpaf)] 4 | #[bpaf(options)] 5 | pub struct Options { 6 | #[bpaf(argument("NAME"))] 7 | /// Use a custom user name 8 | name: String, 9 | #[bpaf(pure(330))] 10 | money: u32, 11 | } 12 | -------------------------------------------------------------------------------- /docs2/src/pure_with/cases.md: -------------------------------------------------------------------------------- 1 | `pure` does not show up in `--help` message 2 | 3 | > --help 4 | 5 | And there's no way to alter the value from the command line 6 | 7 | > --name Bob 8 | 9 | Any attempts to do so would result in an error :) 10 | 11 | > --money 100000 --name Hackerman 12 | -------------------------------------------------------------------------------- /docs2/src/pure_with/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | pub struct Options { 5 | name: String, 6 | money: u32, 7 | } 8 | 9 | fn starting_money() -> Result { 10 | Ok(330) 11 | } 12 | 13 | pub fn options() -> OptionParser { 14 | // User can customise a name 15 | let name = long("name").help("Use a custom user name").argument("NAME"); 16 | // but not starting amount of money 17 | let money = pure_with(starting_money); 18 | construct!(Options { name, money }).to_options() 19 | } 20 | -------------------------------------------------------------------------------- /docs2/src/pure_with/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone, Bpaf)] 4 | #[bpaf(options)] 5 | pub struct Options { 6 | #[bpaf(argument("NAME"))] 7 | /// Use a custom user name 8 | name: String, 9 | #[bpaf(pure_with(starting_money))] 10 | money: u32, 11 | } 12 | 13 | fn starting_money() -> Result { 14 | Ok(330) 15 | } 16 | -------------------------------------------------------------------------------- /docs2/src/req_flag/cases.md: -------------------------------------------------------------------------------- 1 | In `--help` message `req_flag` look similarly to [`switch`](NamedArg::switch) and 2 | [`flag`](NamedArg::flag) 3 | 4 | > --help 5 | 6 | Example contains two parsers that fails without any input: `agree` requires passing `--agree` 7 | 8 | > 9 | 10 | While `style` takes one of several possible values 11 | 12 | > --agree 13 | 14 | It is possible to alter the behavior using [`fallback`](Parser::fallback) or 15 | [`hide`](Parser::hide). 16 | 17 | > --agree --intel 18 | 19 | While parser for `style` takes any posted output - it won't take multiple of them at once 20 | (unless other combinators such as [`many`](Parser::many) permit it) or [`last`](Parser::last). 21 | 22 | > --agree --att --llvm 23 | -------------------------------------------------------------------------------- /docs2/src/req_flag/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | pub enum Style { 5 | Intel, 6 | Att, 7 | Llvm, 8 | } 9 | 10 | #[derive(Debug, Clone)] 11 | pub enum Report { 12 | /// Include defailed report 13 | Detailed, 14 | /// Include minimal report 15 | Minimal, 16 | /// No preferences 17 | Undecided, 18 | } 19 | 20 | #[derive(Debug, Clone)] 21 | pub struct Options { 22 | agree: (), 23 | style: Style, 24 | report: Report, 25 | } 26 | 27 | pub fn options() -> OptionParser { 28 | let agree = long("agree") 29 | .help("You must agree to perform the action") 30 | .req_flag(()); 31 | 32 | let intel = long("intel") 33 | .help("Show assembly using Intel style") 34 | .req_flag(Style::Intel); 35 | let att = long("att") 36 | .help("Show assembly using AT&T style") 37 | .req_flag(Style::Att); 38 | let llvm = long("llvm").help("Show llvm-ir").req_flag(Style::Llvm); 39 | let style = construct!([intel, att, llvm]); 40 | 41 | let detailed = long("detailed") 42 | .help("Include detailed report") 43 | .req_flag(Report::Detailed); 44 | let minimal = long("minimal") 45 | .help("Include minimal report") 46 | .req_flag(Report::Minimal); 47 | let report = construct!([detailed, minimal]).fallback(Report::Undecided); 48 | 49 | construct!(Options { 50 | agree, 51 | style, 52 | report 53 | }) 54 | .to_options() 55 | } 56 | -------------------------------------------------------------------------------- /docs2/src/req_flag/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone, Bpaf)] 4 | pub enum Style { 5 | /// Show assembly using Intel style 6 | Intel, 7 | /// Show assembly using AT&T style 8 | Att, 9 | /// Show llvm-ir 10 | Llvm, 11 | } 12 | 13 | #[derive(Debug, Clone, Bpaf)] 14 | #[bpaf(fallback(Report::Undecided))] 15 | pub enum Report { 16 | /// Include detailed report 17 | Detailed, 18 | /// Include minimal report 19 | Minimal, 20 | #[bpaf(skip)] 21 | /// No preferences 22 | Undecided, 23 | } 24 | 25 | #[derive(Debug, Clone, Bpaf)] 26 | #[bpaf(options)] 27 | pub struct Options { 28 | /// You must agree to perform the action 29 | agree: (), 30 | // external here uses explicit reference to function `style` 31 | // generated above 32 | #[bpaf(external(style))] 33 | style: Style, 34 | // here reference is implicit and derived from field name: `report` 35 | #[bpaf(external)] 36 | report: Report, 37 | } 38 | -------------------------------------------------------------------------------- /docs2/src/short_long_env/cases.md: -------------------------------------------------------------------------------- 1 | As usual switch is optional, arguments are required 2 | 3 | > -a 42 -u Bobert 4 | 5 | 6 | Help displays only visible aliases (and a current value for env arguments) 7 | 8 | > --help 9 | 10 | But you can still use hidden aliases, both short and long 11 | 12 | > --also-switch --also-arg 330 --user Bobert 13 | 14 | And unless there's `many` or similar modifiers having multiple aliases doesn't mean 15 | you can specify them multiple times: 16 | 17 | > -A 42 -a 330 -u Bobert 18 | 19 | Also hidden aliases are really hidden and only meant to do backward compatibility stuff, they 20 | won't show up anywhere else in completions or error messages 21 | 22 | > -a 42 -A 330 -u Bobert 23 | -------------------------------------------------------------------------------- /docs2/src/short_long_env/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | // 5 | #[allow(dead_code)] 6 | pub struct Options { 7 | switch: bool, 8 | arg: usize, 9 | username: String, 10 | } 11 | 12 | pub fn options() -> OptionParser { 13 | let switch = short('s') // first `short` creates a builder 14 | .short('S') // second switch is a hidden alias 15 | .long("switch") // visible long name 16 | .long("also-switch") // hidden alias 17 | .help("Switch with many names") 18 | .switch(); // `switch` finalizes the builder 19 | 20 | let arg = long("argument") // long is also a builder 21 | .short('a') 22 | .short('A') 23 | .long("also-arg") 24 | .help("Argument with names") 25 | .argument::("ARG"); 26 | 27 | let username = long("user") 28 | .short('u') 29 | .env("USER1") 30 | .help("Custom user name") 31 | .argument::("USER"); 32 | 33 | construct!(Options { 34 | switch, 35 | arg, 36 | username 37 | }) 38 | .to_options() 39 | } 40 | -------------------------------------------------------------------------------- /docs2/src/short_long_env/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone, Bpaf)] 4 | // 5 | #[allow(dead_code)] 6 | #[bpaf(options)] 7 | pub struct Options { 8 | #[bpaf(short, long, short('S'), long("also-switch"))] 9 | /// Switch with many names 10 | switch: bool, 11 | #[bpaf(short, long("argument"), short('A'), long("also-arg"))] 12 | /// Argument with names 13 | arg: usize, 14 | #[bpaf(short, long("user"), env("USER1"), argument("USER"))] 15 | /// Custom user name 16 | username: String, 17 | } 18 | -------------------------------------------------------------------------------- /docs2/src/some/cases.md: -------------------------------------------------------------------------------- 1 | In usage lines `some` items are indicated with `...` 2 | 3 | > --help 4 | 5 | Run inner parser as many times as possible collecting all the new results, but unlike 6 | `many` needs to collect at least one element to succeed 7 | 8 | > --argument 10 --argument 20 --switch 9 | 10 | With not enough parameters to satisfy both parsers at least once - it fails 11 | 12 | > 13 | 14 | both parsers need to succeed to create a struct 15 | 16 | > --argument 10 17 | 18 | For parsers that can succeed without consuming anything such as `flag` or `switch` - `some` 19 | only collects values as long as they produce something 20 | 21 | > --switch --argument 10 22 | -------------------------------------------------------------------------------- /docs2/src/some/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | // 4 | #[allow(dead_code)] 5 | #[derive(Debug, Clone)] 6 | pub struct Options { 7 | argument: Vec, 8 | switches: Vec, 9 | } 10 | 11 | pub fn options() -> OptionParser { 12 | let argument = long("argument") 13 | .help("important argument") 14 | .argument("ARG") 15 | .some("want at least one argument"); 16 | let switches = long("switch") 17 | .help("some switch") 18 | .req_flag(true) 19 | .some("want at least one switch"); 20 | construct!(Options { argument, switches }).to_options() 21 | } 22 | -------------------------------------------------------------------------------- /docs2/src/some/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | // 4 | #[allow(dead_code)] 5 | #[derive(Debug, Clone, Bpaf)] 6 | #[bpaf(options)] 7 | pub struct Options { 8 | /// important argument 9 | #[bpaf(argument("ARG"), some("want at least one argument"))] 10 | argument: Vec, 11 | /// some switch 12 | #[bpaf(long("switch"), req_flag(true), some("want at least one switch"))] 13 | switches: Vec, 14 | } 15 | -------------------------------------------------------------------------------- /docs2/src/some_catch/cases.md: -------------------------------------------------------------------------------- 1 | Despite parser producing a funky value - help looks like you would expect from a parser that 2 | takes two values 3 | 4 | > --help 5 | 6 | When executed with no parameters parser fails because `some` requires you to specify at least 7 | one matching parameter 8 | 9 | > 10 | 11 | When executed with expected parameters fields with `usize` get their values 12 | 13 | > --height 100 --width 100 --height 12 --width 44 14 | 15 | With incorrect value for `--height` parameter inner part of `height` parser fails, `some` 16 | combined with `catch` handles this failure and produces `[]` without consuming value from the 17 | command line. Parser `height_str` runs next and consumes the value as a string 18 | 19 | > --height 10 --height twenty --width 33 20 | 21 | In case of wrong `--width` - parser `width` fails, parser for `some` sees this as a 22 | "value is present but not correct" and propagates the error outside, execution never reaches 23 | `width_str` parser 24 | 25 | > --height 10 --width 33 --width ten 26 | -------------------------------------------------------------------------------- /docs2/src/some_catch/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | pub struct Options { 5 | height: Vec, 6 | height_str: Vec, 7 | width: Vec, 8 | width_str: Vec, 9 | } 10 | 11 | pub fn options() -> OptionParser { 12 | // contains catch 13 | let height = long("height") 14 | .help("Height of a rectangle") 15 | .argument::("PX") 16 | .some("You must specify some heights") 17 | .catch(); 18 | 19 | let height_str = long("height").argument::("PX").many().hide(); 20 | 21 | // contains no catch 22 | let width = long("width") 23 | .help("Width of a rectangle") 24 | .argument::("PX") 25 | .some("You must specify some widths"); 26 | 27 | let width_str = long("width").argument::("PX").many().hide(); 28 | 29 | construct!(Options { 30 | height, 31 | height_str, 32 | width, 33 | width_str 34 | }) 35 | .to_options() 36 | } 37 | -------------------------------------------------------------------------------- /docs2/src/some_catch/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone, Bpaf)] 4 | #[bpaf(options)] 5 | pub struct Options { 6 | #[bpaf(long, argument("PX"), some("You must specify some heights"), catch)] 7 | /// Height of a rectangle 8 | height: Vec, 9 | 10 | #[bpaf(long("height"), argument("PX"), many, hide)] 11 | height_str: Vec, 12 | 13 | #[bpaf(long, argument("PX"), some("You must specify some widths"))] 14 | /// Width of a rectangle 15 | width: Vec, 16 | 17 | #[bpaf(long("width"), argument("PX"), many, hide)] 18 | width_str: Vec, 19 | } 20 | -------------------------------------------------------------------------------- /docs2/src/switch/cases.md: -------------------------------------------------------------------------------- 1 | In `--help` output `bpaf` shows switches as usual flags with no meta variable attached 2 | 3 | > --help 4 | 5 | Both `switch` and `flag` succeed if value is not present, `switch` returns `false`, `flag` returns 6 | second value. 7 | 8 | > 9 | 10 | When value is present - `switch` returns `true`, `flag` returns first value. 11 | 12 | > --verbose --no-default-features --detailed 13 | 14 | Like with most parsrs unless specified `switch` and `flag` consume at most one item from the 15 | command line: 16 | 17 | > --no-default-features --no-default-features 18 | -------------------------------------------------------------------------------- /docs2/src/switch/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | pub struct Options { 5 | verbose: bool, 6 | release: bool, 7 | default_features: bool, 8 | } 9 | 10 | pub fn options() -> OptionParser { 11 | let verbose = short('v') 12 | .long("verbose") 13 | .help("Produce verbose output") 14 | .switch(); 15 | let release = long("release") 16 | .help("Build artifacts in release mode") 17 | .flag(true, false); 18 | let default_features = long("no-default-features") 19 | .help("Do not activate default features") 20 | // default_features uses opposite values, 21 | // producing `true` when value is absent 22 | .flag(false, true); 23 | 24 | construct!(Options { 25 | verbose, 26 | release, 27 | default_features, 28 | }) 29 | .to_options() 30 | } 31 | -------------------------------------------------------------------------------- /docs2/src/switch/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone, Bpaf)] 4 | #[bpaf(options)] 5 | pub struct Options { 6 | /// Produce verbose output 7 | // bpaf uses `switch` for `bool` fields in named 8 | // structs unless consumer attribute is present. 9 | // But it is also possible to give it explicit 10 | // consumer annotation to serve as a reminder: 11 | // #[bpaf(short, long, switch)] 12 | #[bpaf(short, long)] 13 | verbose: bool, 14 | 15 | #[bpaf(flag(true, false))] 16 | /// Build artifacts in release mode 17 | release: bool, 18 | 19 | /// Do not activate default features 20 | // default_features uses opposite values, 21 | // producing `true` when value is absent 22 | #[bpaf(long("no-default-features"), flag(false, true))] 23 | default_features: bool, 24 | } 25 | -------------------------------------------------------------------------------- /docs2/src/switch_help/cases.md: -------------------------------------------------------------------------------- 1 | When `--help` used once it renders shoter version of the help information 2 | 3 | > --help 4 | 5 | When used twice - it renders full version. Documentation generator uses full 6 | version as well 7 | 8 | > --help --help 9 | 10 | Presence or absense of a help message should not affect the parser's output 11 | 12 | > --name Bob output.txt 13 | -------------------------------------------------------------------------------- /docs2/src/switch_help/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | pub struct Options { 5 | verbose: bool, 6 | name: String, 7 | output: Option, 8 | } 9 | 10 | pub fn options() -> OptionParser { 11 | let verbose = short('v') 12 | .long("verbose") 13 | .help( 14 | "\ 15 | Output detailed help information, you can specify it multiple times 16 | 17 | when used once it outputs basic diagnostic info, 18 | when used twice or three times - it includes extra debugging.", 19 | // ^ note extra spaces before "when" that preserve the linebreaks 20 | ) 21 | .switch(); 22 | let name = long("name") 23 | .help("Use this as a task name") 24 | .argument("NAME"); 25 | 26 | let output = positional("OUTPUT") 27 | .help("Save output to a file") 28 | .optional(); 29 | 30 | construct!(Options { 31 | verbose, 32 | name, 33 | output 34 | }) 35 | .to_options() 36 | } 37 | -------------------------------------------------------------------------------- /docs2/src/switch_help/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone, Bpaf)] 4 | #[bpaf(options)] 5 | pub struct Options { 6 | #[bpaf(short, long)] 7 | /// Output detailed help information, you can specify it multiple times 8 | /// 9 | /// when used once it outputs basic diagnostic info, 10 | /// when used twice or three times - it includes extra debugging. 11 | // ^ note extra spaces before when that preserve the linebreaks 12 | verbose: bool, 13 | 14 | #[bpaf(argument("NAME"))] 15 | /// Use this as a task name 16 | name: String, 17 | 18 | #[bpaf(positional("OUTPUT"))] 19 | /// Save output to a file 20 | output: Option, 21 | } 22 | -------------------------------------------------------------------------------- /docs2/src/to_options/cases.md: -------------------------------------------------------------------------------- 1 | In addition to all the arguments specified by user `bpaf` adds a few more. One of them is 2 | `--help`: 3 | 4 | > --help 5 | 6 | The other one is `--version` - passing a string literal or something like 7 | `env!("CARGO_PKG_VERSION")` to get version from `cargo` directly usually works 8 | 9 | > --version 10 | 11 | Other than that `bpaf` tries its best to provide a helpful error messages 12 | 13 | > 14 | 15 | And if all parsers are satisfied [`run`](OptionParser::run) produces the result 16 | 17 | > -i 10 18 | -------------------------------------------------------------------------------- /docs2/src/to_options/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | pub struct Options { 5 | argument: u32, 6 | } 7 | 8 | pub fn options() -> OptionParser { 9 | let argument = short('i').argument::("ARG"); 10 | construct!(Options { argument }) 11 | .to_options() 12 | .version("3.1415") 13 | .descr("This is a short description") 14 | .header("It can contain multiple blocks, this block goes before options") 15 | .footer("This one goes after") 16 | } 17 | -------------------------------------------------------------------------------- /docs2/src/to_options/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone, Bpaf)] 4 | #[bpaf(options, version("3.1415"))] 5 | /// This is a short description 6 | /// 7 | /// 8 | /// It can contain multiple blocks, this block goes before options 9 | /// 10 | /// 11 | /// This one goes after 12 | pub struct Options { 13 | #[bpaf(short('i'))] 14 | argument: u32, 15 | } 16 | -------------------------------------------------------------------------------- /docs2/src/usage/cases.md: -------------------------------------------------------------------------------- 1 | Method `usage` lets you to override the whole usage line 2 | 3 | > --help 4 | 5 | It doesn't alter parser's behavior otherwise 6 | 7 | > 8 | > -r --binary test 9 | -------------------------------------------------------------------------------- /docs2/src/usage/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | pub struct Options { 5 | release: bool, 6 | binary: String, 7 | } 8 | 9 | pub fn options() -> OptionParser { 10 | let release = short('r') 11 | .long("release") 12 | .help("Perform actions in release mode") 13 | .switch(); 14 | 15 | let binary = short('b') 16 | .long("binary") 17 | .help("Use this binary") 18 | .argument("BIN"); 19 | 20 | construct!(Options { release, binary }) 21 | .to_options() 22 | .usage("Usage: my_program [--release] [--binary=BIN] ...") 23 | } 24 | -------------------------------------------------------------------------------- /docs2/src/usage/derive.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone, Bpaf)] 4 | #[bpaf(options, usage("Usage: my_program [--release] [--binary=BIN] ..."))] 5 | pub struct Options { 6 | #[bpaf(short, long)] 7 | /// Perform actions in release mode 8 | release: bool, 9 | #[bpaf(short, long, argument("BIN"))] 10 | /// Use this binary 11 | binary: String, 12 | } 13 | -------------------------------------------------------------------------------- /docs2/src/with_group_help/cases.md: -------------------------------------------------------------------------------- 1 | `with_group_help` lets you write longer description for group of options that can also refer to 2 | those options. Similar to [`group_help`](Parser::group_help) encased optios are separated from 3 | the rest by a blank line. 4 | 5 | Invoking help with a single `--help` flag renders shot(er) version of the help message 6 | that contanis only the first paragraph for each block: 7 | 8 | > --help 9 | 10 | Invoking help with double `--help --help` flag renders the full help message with all the 11 | descriptions added 12 | 13 | > --help --help 14 | 15 | Other than rendering the help message that there's no interactions with other parsers 16 | 17 | > --width 120 --height 11 18 | 19 | > --argument 12 20 | -------------------------------------------------------------------------------- /docs2/src/with_group_help/combine.rs: -------------------------------------------------------------------------------- 1 | use bpaf::doc::*; 2 | use bpaf::*; 3 | // 4 | #[allow(dead_code)] 5 | #[derive(Debug, Clone)] 6 | pub struct Rectangle { 7 | width: u32, 8 | height: u32, 9 | } 10 | 11 | // 12 | #[allow(dead_code)] 13 | #[derive(Debug, Clone)] 14 | pub struct Options { 15 | argument: u32, 16 | rectangle: Rectangle, 17 | } 18 | 19 | fn generate_rectangle_help(meta: MetaInfo) -> Doc { 20 | let mut buf = Doc::default(); 21 | buf.text("The app takes a rectangle defined by width and height\n\nYou can customize the screen size using "); 22 | buf.meta(meta, true); 23 | buf.text(" parameters"); 24 | buf 25 | } 26 | 27 | pub fn options() -> OptionParser { 28 | let argument = long("argument") 29 | .help("important argument") 30 | .argument("ARG") 31 | .fallback(30); 32 | let width = long("width") 33 | .help("Width of the rectangle") 34 | .argument("W") 35 | .fallback(10); 36 | let height = long("height") 37 | .help("Height of the rectangle") 38 | .argument("H") 39 | .fallback(10); 40 | let rectangle = 41 | construct!(Rectangle { width, height }).with_group_help(generate_rectangle_help); 42 | 43 | construct!(Options { 44 | argument, 45 | rectangle 46 | }) 47 | .to_options() 48 | } 49 | -------------------------------------------------------------------------------- /docs2/src/with_usage/cases.md: -------------------------------------------------------------------------------- 1 | `with_usage` lets you to place some custom text around generated usage line 2 | 3 | > --help 4 | 5 | It doesn't alter parser's behavior otherwise 6 | 7 | > 8 | > -r --binary test 9 | -------------------------------------------------------------------------------- /docs2/src/with_usage/combine.rs: -------------------------------------------------------------------------------- 1 | // 2 | use bpaf::*; 3 | #[derive(Debug, Clone)] 4 | pub struct Options { 5 | release: bool, 6 | binary: String, 7 | } 8 | 9 | pub fn options() -> OptionParser { 10 | let release = short('r') 11 | .long("release") 12 | .help("Perform actions in release mode") 13 | .switch(); 14 | 15 | let binary = short('b') 16 | .long("binary") 17 | .help("Use this binary") 18 | .argument("BIN"); 19 | 20 | construct!(Options { release, binary }) 21 | .to_options() 22 | .with_usage(|u| { 23 | let mut doc = Doc::default(); 24 | doc.emphasis("Usage: "); 25 | doc.literal("my_program"); 26 | doc.text(" "); 27 | doc.doc(&u); 28 | doc 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /docs2/src/xorg/cases.md: -------------------------------------------------------------------------------- 1 | `xorg` takes parameters in a few different ways, notably as a long name starting with plus or 2 | minus with different defaults 3 | 4 | > -xinerama +backing 5 | 6 | But also as `+ext name` and `-ext name` to enable or disable an extensions 7 | 8 | > --turbo +ext banana -ext apple 9 | 10 | While `bpaf` takes some effort to render the help even for custom stuff - you can always 11 | bypass it by hiding options and substituting your own with custom `header`/`footer`. 12 | 13 | > --help 14 | -------------------------------------------------------------------------------- /documentation/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "documentation" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /documentation/_documentation/_1_tutorials/_0_types_of_arguments/_0_switch/index.md: -------------------------------------------------------------------------------- 1 | #### Options, switches or flags 2 | 3 | Options or flags usually starts with a dash, a single dash for short options and a double dash for 4 | long one. Several short options can usually be squashed together with a single dash in front of 5 | them to save on typing: `-vvv` can be parsed the same as `-v -v -v`. Options don't have any 6 | other information apart from being there or not. Relative position usually does not matter and 7 | `--alpha --beta` should parse the same as `--beta --alpha`. 8 | 9 |
10 |
11 | $ cargo --help
12 | $ ls -la
13 | $ ls --time --reverse
14 | 
15 |
16 | 17 | #![cfg_attr(not(doctest), doc = include_str!("docs2/switch.md"))] 18 | 19 | For more detailed info see [`NamedArg::switch`] and 20 | [`NamedArg::flag`] 21 | -------------------------------------------------------------------------------- /documentation/_documentation/_1_tutorials/_0_types_of_arguments/_1_argument/index.md: -------------------------------------------------------------------------------- 1 | #### Option arguments or arguments 2 | 3 | Option arguments are similar to regular options but they come with an extra value attached. 4 | Value can be separated by a space, `=` or directly adjacent to a short name. Same as with 5 | options - their relative position usually doesn't matter. 6 | 7 |
8 |
 9 | $ cargo build --package bpaf
10 | $ cargo test -j2
11 | $ cargo check --bin=megapotato
12 | 
13 |
14 | 15 | In the generated help message or documentation they come with a placeholder metavariable, 16 | usually a short, all-caps word describing what the value means: `NAME`, `AGE`, `SPEC`, and `CODE` 17 | are all valid examples. 18 | 19 | #![cfg_attr(not(doctest), doc = include_str!("docs2/argument.md"))] 20 | 21 | For more detailed info see [`NamedArg::argument`] 22 | -------------------------------------------------------------------------------- /documentation/_documentation/_1_tutorials/_0_types_of_arguments/_2_positional/index.md: -------------------------------------------------------------------------------- 1 | #### Operands or positional items 2 | 3 | Operands are usually items that are present on a command line and not prefixed by a short or 4 | long name. They are usually used to represent the most important part of the operation: 5 | `cat Cargo.toml` - display THIS file, `rm -rf target` - remove THIS folder and so on. 6 | 7 |
8 |
 9 | $ cat /etc/passwd
10 | $ rm -rf target
11 | $ man gcc
12 | 
13 |
14 | 15 | #![cfg_attr(not(doctest), doc = include_str!("docs2/positional.md"))] 16 | 17 | For more detailed info see [`positional`](crate::positional) and 18 | -------------------------------------------------------------------------------- /documentation/_documentation/_1_tutorials/_0_types_of_arguments/_3_command/index.md: -------------------------------------------------------------------------------- 1 | #### Commands or subcommands 2 | 3 | Commands are similar to positional items, but instead of representing an item they start 4 | a whole new parser, usually with its help and other arguments. Commands allow a single 5 | application to perform multiple different functions. The command parser will be able to parse all 6 | the command line options to the right of the command name 7 | 8 |
9 |
10 | $ cargo build --release
11 | $ cargo clippy
12 | $ cargo asm --intel --everything
13 | 
14 |
15 | 16 | #![cfg_attr(not(doctest), doc = include_str!("docs2/command.md"))] 17 | 18 | For more detailed info see [`OptionParser::command`] 19 | -------------------------------------------------------------------------------- /documentation/_documentation/_1_tutorials/_0_types_of_arguments/_4_exotic/index.md: -------------------------------------------------------------------------------- 1 | #### Exotic schemas 2 | 3 | While modern software tends to use just the options listed above you can still encounter 4 | programs created before those options became the norm and they use something completely different, 5 | let me give a few examples, see [the parsing cookbook](crate::_documentation::_2_howto) 6 | about actually parsing them 7 | 8 | `su` takes an option that consists of a single dash `-` 9 | 10 |
11 | $ su -
12 | 
13 | 14 | `find` considers everything between `--exec` and `;` to be a single item. 15 | this example calls `ls -l` on every file `find` finds. 16 | 17 |
18 | $ find /etc --exec ls -l '{}' \;
19 | 
20 | 21 | `Xorg` and related tools use flag-like items that start with a single `+` to enable a 22 | feature and with `-` to disable it. 23 | 24 |
25 | $ xorg -backing +xinerama
26 | 
27 | 28 | `dd` takes several key-value pairs, this would create a 100M file 29 |
30 | $ dd if=/dev/zero of=dummy.bin bs=1M count=100
31 | 
32 | 33 | Most of the command line arguments in Turbo C++ 3.0 start with `/`. For example, option 34 | `/x` tells it to use all available extended memory, while `/x[=n]` limits it to n kilobytes 35 |
36 | C:\PROJECT>TC /x=200
37 | 
38 | -------------------------------------------------------------------------------- /documentation/_documentation/_1_tutorials/_0_types_of_arguments/index.md: -------------------------------------------------------------------------------- 1 | #### Types of arguments 2 | common types of line options and conventions 3 | 4 | This chapter serves as an introduction to available command line options and tries to set the 5 | terminology. If you are familiar with command line argument parsers in general - feel free to 6 | skip it. 7 | 8 | If you ever used any software from a command line (say `cargo`) you used command line options. 9 | Let's recap how you might run tests for a crate in your rust project: 10 | 11 |
12 |
13 | $ cargo test -p my_project --verbose
14 | 
15 |
16 | 17 | `cargo` here is an executable name, everything to the right of it separated by spaces are the 18 | options. 19 | 20 | Nowadays programs share mostly similar conventions about what a command line argument is, it 21 | wasn't the case before though. Let's cover the basic types. 22 | -------------------------------------------------------------------------------- /documentation/_documentation/_1_tutorials/_1_combinatoric_api/_0_simple_parser/_0_switch/index.md: -------------------------------------------------------------------------------- 1 | #### Switch parser 2 | 3 | Let's start with the simplest possible one - a simple switch that gets parsed into a `bool`. 4 | 5 | First of all - the switch needs a name - you can start with [`short`] or [`long`] and add more 6 | names if you want: `long("simple")` or `short('s').long("simple")`. This gives something with 7 | the type [`NamedArg`]: 8 | 9 | ```rust 10 | # use bpaf::*; 11 | use bpaf::parsers::NamedArg; 12 | fn simple_switch() -> NamedArg { 13 | short('s').long("simple") 14 | } 15 | ``` 16 | 17 | From `NamedArg` you make a switch parser by calling [`NamedArg::switch`]. Usually, you do it 18 | right away without assigning `NamedArg` to a variable. 19 | 20 | ```rust 21 | # use bpaf::*; 22 | fn simple_switch() -> impl Parser { 23 | short('s').long("simple").switch() 24 | } 25 | ``` 26 | 27 | The switch parser we just made implements trait [`Parser`] and to run it you convert it to [`OptionParser`] with 28 | [`Parser::to_options`] and run it with [`OptionParser::run`] 29 | 30 | Full example with some sample inputs and outputs: 31 | #![cfg_attr(not(doctest), doc = include_str!("docs2/compose_basic_switch.md"))] 32 | 33 | 34 | With [`NamedArg::help`] you can attach a help message that will be used in `--help` output. 35 | -------------------------------------------------------------------------------- /documentation/_documentation/_1_tutorials/_1_combinatoric_api/_0_simple_parser/_1_argument/index.md: -------------------------------------------------------------------------------- 1 | #### Argument parser 2 | 3 | Next in complexity would be a parser to consume a named argument, such as `-p my_crate`. Same 4 | as with the switch parser it starts from a `NamedArg` but the next method is [`NamedArg::argument`]. 5 | This method takes a metavariable name - a short description that will be used in the `--help` 6 | output. `rustc` also needs to know the parameter type you are trying to parse, there are 7 | several ways to do it: 8 | 9 | ```rust 10 | # use bpaf::*; 11 | # use std::path::PathBuf; 12 | fn simple_argument_1() -> impl Parser { 13 | // rustc figures out the type from returned value 14 | long("number").argument("NUM") 15 | } 16 | 17 | fn simple_argument_2() -> impl Parser { 18 | // type is specified explicitly with turbofish 19 | long("name").argument::("NAME") 20 | } 21 | 22 | fn file_parser() -> OptionParser { 23 | // OptionParser is a type for finalized parser, at this point you can 24 | // start adding extra information to the `--help` message 25 | long("file").argument::("FILE").to_options() 26 | } 27 | ``` 28 | 29 | You can use any type for as long as it implements [`FromStr`]. To parse items that don't 30 | implement it you can first parse a `String` or `OsString` and then use [`Parser::parse`], see 31 | [the next chapter](super::super::_1_chaining) on how to do that. 32 | 33 | Full example with some sample inputs and outputs: 34 | #![cfg_attr(not(doctest), doc = include_str!("docs2/compose_basic_argument.md"))] 35 | -------------------------------------------------------------------------------- /documentation/_documentation/_1_tutorials/_1_combinatoric_api/_0_simple_parser/_2_positional/index.md: -------------------------------------------------------------------------------- 1 | #### Positional item parser 2 | 3 | And the last simple option type is a parser for positional items. Since there's no name you use 4 | the [`positional`] function directly. Similar to [`NamedArg::argument`] this method takes 5 | a metavariable name and a type parameter in some form. You can also attach the help message 6 | thanks to [`ParsePositional::help`] 7 | 8 | Full example: 9 | #![cfg_attr(not(doctest), doc = include_str!("docs2/compose_basic_positional.md"))] 10 | -------------------------------------------------------------------------------- /documentation/_documentation/_1_tutorials/_1_combinatoric_api/_0_simple_parser/index.md: -------------------------------------------------------------------------------- 1 | #### Making a simple parser 2 | 3 | In this chapter we'll go over making a few simple parsers. 4 | -------------------------------------------------------------------------------- /documentation/_documentation/_1_tutorials/_1_combinatoric_api/_3_subcommands/index.md: -------------------------------------------------------------------------------- 1 | #### Subcommand parsers 2 | 3 | To make a parser for a subcommand you make an `OptionParser` for that subcommand first as if it 4 | was the only thing your application would parse then turn it into a regular [`Parser`] 5 | you can further compose with [`OptionParser::command`]. 6 | 7 | This gives [`ParseCommand`] back, you can add aliases or tweak the help message if you want to. 8 | 9 | #![cfg_attr(not(doctest), doc = include_str!("docs2/compose_basic_command.md"))] 10 | -------------------------------------------------------------------------------- /documentation/_documentation/_1_tutorials/_1_combinatoric_api/_4_decorating/index.md: -------------------------------------------------------------------------------- 1 | #### Improving the user experience 2 | 3 | Once you have the final parser done there are still a few ways you can improve user experience. 4 | [`OptionParser`] comes equipped with a few methods that let you set version number, 5 | description, help message header and footer and so on. 6 | 7 | #![cfg_attr(not(doctest), doc = include_str!("docs2/compose_basic_to_options.md"))] 8 | 9 | There are a few other things you can do: 10 | 11 | - group some of the primitive parsers into logical blocks for `--help` message with 12 | [`Parser::group_help`] 13 | - add tests to make sure important combinations are handled the way they are supposed to 14 | after any future refactors with [`OptionParser::run_inner`] 15 | - add a test to make sure that bpaf internal invariants are satisfied with 16 | [`OptionParser::check_invariants`] 17 | - generate user documentation in manpage and markdown formats with 18 | [`OptionParser::render_manpage`] and [`OptionParser::render_markdown`] 19 | -------------------------------------------------------------------------------- /documentation/_documentation/_1_tutorials/_1_combinatoric_api/index.md: -------------------------------------------------------------------------------- 1 | #### Combinatoric API 2 | Parse arguments without using proc macros 3 | 4 | When making a parser in the Combinatoric style API you usually go through those steps 5 | 6 | 1. Design data type your application will receive 7 | 2. Design command line options user will have to pass 8 | 3. Create a set of simple parsers 9 | 4. Combine and transform simple parsers to create the final data type 10 | 5. Transform the resulting [`Parser`] into [`OptionParser`] and run it 11 | 12 | Let's go through some of them in more detail: 13 | -------------------------------------------------------------------------------- /documentation/_documentation/_1_tutorials/_2_derive_api/_0_intro/index.md: -------------------------------------------------------------------------------- 1 | #### Getting started with derive macro 2 | 3 | Let's take a look at a simple example 4 | 5 | #![cfg_attr(not(doctest), doc = include_str!("docs2/derive_basic_intro.md"))] 6 | 7 | `bpaf` is trying hard to guess what you are trying to achieve just from the types so it will 8 | pick up types, doc comments, presence or absence of names, but it is possible to customize all 9 | of it, add custom transformations, validations and more. 10 | -------------------------------------------------------------------------------- /documentation/_documentation/_1_tutorials/_2_derive_api/_1_custom_names/index.md: -------------------------------------------------------------------------------- 1 | #### Customizing flag and argument names 2 | 3 | By default names for flag names are taken directly from the field names so usually you don't 4 | have to do anything about it, but you can change it with annotations on the fields themselves: 5 | 6 | #![cfg_attr(not(doctest), doc = include_str!("docs2/derive_basic_custom_name.md"))] 7 | 8 | Rules for picking the name are: 9 | 10 | 1. With no annotations field name longer than a single character becomes a long name, 11 | single character name becomes a short name 12 | 2. Adding either `long` or `short` disables item 1, so adding `short` disables the long name 13 | 3. `long` or `short` annotation without a parameter derives a value from a field name 14 | 4. `long` or `short` with a parameter uses that instead 15 | 5. You can have multiple `long` and `short` annotations, the first of each type becomes a 16 | visible name, remaining are used as hidden aliases 17 | 18 | And if you decide to add names - they should go to the left side of the annotation list 19 | -------------------------------------------------------------------------------- /documentation/_documentation/_1_tutorials/_2_derive_api/_2_custom_consumers/index.md: -------------------------------------------------------------------------------- 1 | #### Customizing the consumers 2 | 3 | By default, `bpaf` picks parsers depending on a field type according to those rules: 4 | 5 | 1. `bool` fields are converted into switches: [`NamedArg::switch`](crate::parsers::NamedArg::switch) 6 | 2. `()` (unit) fields, unit variants of an enum or unit structs themselves are handled as 7 | [`NamedArg::req_flag`](crate::parsers::NamedArg::req_flag) and thus users must always specify 8 | them for the parser to succeed 9 | 3. All other types with no `Vec`/`Option` are parsed using [`FromStr`](std::str::FromStr), but 10 | smartly, so non-utf8 `PathBuf`/`OsString` are working as expected. 11 | 4. For values wrapped in `Option` or `Vec` bpaf derives the inner parser and then applies 12 | applies logic from [`Parser::optional`] and [`Parser::many`] respectively. 13 | 14 | You can change it with annotations like `switch`, `argument` or `positional` 15 | 16 | 17 | #![cfg_attr(not(doctest), doc = include_str!("docs2/derive_basic_custom_consumer.md"))] 18 | 19 | With arguments that consume a value you can specify its type using turbofish-line syntax 20 | 21 | 22 | ```no_run 23 | # use bpaf::*; 24 | #[derive(Debug, Clone, Bpaf)] 25 | #[bpaf(options)] 26 | pub struct Options { 27 | /// A custom argument 28 | #[bpaf(positional::("LENGTH"))] 29 | argument: usize, 30 | } 31 | 32 | fn main() { 33 | let opts = options().run(); 34 | println!("{:?}", opts) 35 | } 36 | ``` 37 | -------------------------------------------------------------------------------- /documentation/_documentation/_1_tutorials/_2_derive_api/_3_postpr/index.md: -------------------------------------------------------------------------------- 1 | #### Transforming parsed values 2 | 3 | Once the field has a consumer you can start applying transformations from the [`Parser`] trait. 4 | Annotation share the same names and follow the same composition rules as in Combinatoric API. 5 | 6 | 7 | #![cfg_attr(not(doctest), doc = include_str!("docs2/derive_basic_postpr.md"))] 8 | 9 | -------------------------------------------------------------------------------- /documentation/_documentation/_1_tutorials/_2_derive_api/_4_enums_and_structs/index.md: -------------------------------------------------------------------------------- 1 | #### Parsing structs and enums 2 | 3 | To produce a struct bpaf needs for all the field parsers to succeed. If you are planning to use 4 | it for some other purpose as well and want to skip them during parsing you can use [`pure`] to 5 | fill in values in member fields and `#[bpaf(skip)]` on enum variants you want to ignore, see 6 | combinatoric example in [`Parser::last`]. 7 | 8 | If you use `#[derive(Bpaf)]` on an enum parser will produce a variant for which all the parsers 9 | succeed. 10 | 11 | #![cfg_attr(not(doctest), doc = include_str!("docs2/derive_basic_enum.md"))] 12 | -------------------------------------------------------------------------------- /documentation/_documentation/_1_tutorials/_2_derive_api/_5_generate/index.md: -------------------------------------------------------------------------------- 1 | #### What gets generated 2 | 3 | Usually calling derive macro on a type generates code to derive a trait implementation for this 4 | type. With bpaf it's slightly different. It instead generates a function with a name that 5 | depends on the name of the type and gives either a composable parser (`Parser`) or option parser 6 | (`OptionParser`) back. 7 | 8 | You can customize the function name with `generate` annotation at the top level: 9 | 10 | ```no_run 11 | # use bpaf::*; 12 | #[derive(Debug, Clone, Bpaf)] 13 | #[bpaf(options, generate(my_options))] 14 | pub struct Options { 15 | /// A simple switch 16 | switch: bool 17 | } 18 | 19 | 20 | fn main() { 21 | let opts = my_options().run(); 22 | println!("{:?}", opts); 23 | } 24 | ``` 25 | -------------------------------------------------------------------------------- /documentation/_documentation/_1_tutorials/_2_derive_api/_6_nesting/index.md: -------------------------------------------------------------------------------- 1 | #### Making nested parsers 2 | 3 | Up to this point, we've been looking at cases where fields of a structure are all simple 4 | parsers, possibly wrapped in `Option` or `Vec`, but it is also possible to nest derived parsers 5 | too: 6 | 7 | #![cfg_attr(not(doctest), doc = include_str!("docs2/derive_basic_nesting.md"))] 8 | 9 | 10 | `external` takes an optional function name and will call that function to make the parser for 11 | the field. You can chain more transformations after the `external` and if the name is absent - 12 | `bpaf` would use the field name instead, so you can also write the example above as 13 | 14 | 15 | ```rust 16 | # use bpaf::*; 17 | #[derive(Debug, Clone, Bpaf)] 18 | pub enum Format { 19 | /// Produce output in HTML format 20 | Html, 21 | /// Produce output in Markdown format 22 | Markdown, 23 | /// Produce output in manpage format 24 | Manpage, 25 | } 26 | 27 | #[derive(Debug, Clone, Bpaf)] 28 | #[bpaf(options)] 29 | pub struct Options { 30 | /// File to process 31 | input: String, 32 | #[bpaf(external)] 33 | format: Format, 34 | } 35 | ``` 36 | -------------------------------------------------------------------------------- /documentation/_documentation/_1_tutorials/_2_derive_api/_7_commands/index.md: -------------------------------------------------------------------------------- 1 | #### Parsing subcommands 2 | 3 | The easiest way to define a group of subcommands is to have them inside the same enum with variant 4 | constructors annotated with `#[bpaf(command("name"))]` with or without the name 5 | 6 | 7 | #![cfg_attr(not(doctest), doc = include_str!("docs2/derive_basic_commands.md"))] 8 | -------------------------------------------------------------------------------- /documentation/_documentation/_1_tutorials/_2_derive_api/_8_cargo/index.md: -------------------------------------------------------------------------------- 1 | #### Making a cargo command 2 | 3 | To make a cargo command you should pass its name as a parameter to `options`. In this example, 4 | `bpaf` will parse extra parameter cargo passes and you will be able to use it either directly 5 | with `cargo run` from the repository, running it by `cargo-asm` name or with `cargo asm` name. 6 | 7 | ```no_run 8 | # use bpaf::*; 9 | #[derive(Debug, Clone, Bpaf)] 10 | #[bpaf(options("asm"))] 11 | pub struct Options { 12 | /// A simple switch 13 | switch: bool 14 | } 15 | 16 | 17 | fn main() { 18 | let opts = options().run(); 19 | println!("{:?}", opts); 20 | } 21 | ``` 22 | -------------------------------------------------------------------------------- /documentation/_documentation/_1_tutorials/_2_derive_api/index.md: -------------------------------------------------------------------------------- 1 | #### Derive API tutorial 2 | Create a parser by defining a structure 3 | 4 | 5 | When making a parser using Derive API you should go through approximately following steps: 6 | 7 | 1. Design data type your application will receive 8 | 2. Design command line options user will have to pass 9 | 3. Add `#[derive(Bpaf, Debug, Clone)]` on top of your type or types 10 | 4. Add `#[bpaf(xxx)]` annotations on types and fields 11 | 5. And `#[bpaf(options)]` to the top type 12 | 6. Run the resulting parser 13 | 14 | 15 | Let’s go through some of them in more detail: 16 | -------------------------------------------------------------------------------- /documentation/_documentation/_1_tutorials/index.md: -------------------------------------------------------------------------------- 1 | #### Tutorials 2 | practical, learning oriented guides 3 | -------------------------------------------------------------------------------- /documentation/_documentation/_2_howto/_0_testing/index.md: -------------------------------------------------------------------------------- 1 | #### Testing your parsers 2 | 3 | You can test values your parser produces and expected output 4 | 5 | ```no_run 6 | # use bpaf::*; 7 | #[derive(Debug, Clone, Bpaf)] 8 | #[bpaf(options)] 9 | pub struct Options { 10 | pub user: String 11 | } 12 | 13 | #[test] 14 | fn test_my_options() { 15 | let help = options() 16 | .run_inner(&["--help"]) 17 | .unwrap_err() 18 | .unwrap_stdout(); 19 | let expected_help = "\ 20 | Usage --user=ARG 21 | 22 | "; 23 | 24 | assert_eq!(help, expected_help); 25 | } 26 | 27 | #[test] 28 | fn test_value() { 29 | let value = options() 30 | .run_inner(&["--user", "Bob"]) 31 | .unwrap(); 32 | assert_eq!(value.user, "Bob"); 33 | } 34 | ``` 35 | 36 | [`OptionParser::run_inner`] takes [`Args`] or anything that can be converted to it, in most 37 | cases using a static slice with strings is enough. 38 | 39 | Easiest way to consume [`ParseFailure`] for testing purposes is with 40 | [`ParseFailure::unwrap_stderr`] and [`ParseFailure::unwrap_stdout`] - result will lack any colors 41 | even with them enabled which makes testing easier. 42 | 43 | Successful result parse produces a value, "failed" parse produces stdout or stderr outputs - 44 | stdout to print help message or version number and stderr to print the error message. 45 | -------------------------------------------------------------------------------- /documentation/_documentation/_2_howto/index.md: -------------------------------------------------------------------------------- 1 | #### HOWTO - practical, oriented to solving problems guides 2 | -------------------------------------------------------------------------------- /documentation/_documentation/_3_cookbook/_00_find/index.md: -------------------------------------------------------------------------------- 1 | #### `find(1)`: `find -exec commands -flags terminated by \;` 2 | 3 | An Example for `find` shows how to implement 3 different unusual options: 4 | 5 | - an option with a long name but a single dash as a prefix: `-user bob` 6 | - an option that captures everything until the next fixed character 7 | - an option that takes a set of characters: `-mode -rw`, `mode /rw` 8 | 9 | In all cases, long name with a single dash is implemented by the [`literal`] with 10 | [`ParseAny::anywhere`](crate::parsers::ParseAny::anywhere) with some items made `adjacent` to it. 11 | 12 | To parse `-user bob` this is simply literal `-user` adjacent to a positional item with `map` to 13 | focus on the interesting part. 14 | 15 | For `-exec more things here ;` this is a combination of literal `-exec`, followed by `many` 16 | items that are not `;` parsed positionally with `any` followed by `;` - again with `any`, but 17 | `literal` works too. 18 | 19 | And lastly to parse mode - after the tag, we accept `any` to be able to handle a combination of 20 | modes that may or may not start with `-` and use [`Parser::parse`] to parse them or fail. 21 | 22 | All special cases are made optional with [`Parser::optional`], but [`Parser::fallback`] also 23 | works. 24 | 25 | #![cfg_attr(not(doctest), doc = include_str!("docs2/find.md"))] 26 | -------------------------------------------------------------------------------- /documentation/_documentation/_3_cookbook/_01_dd/index.md: -------------------------------------------------------------------------------- 1 | #### `dd(1)`: `dd if=/dev/zero of=/dev/null bs=1000` 2 | 3 | This example implements syntax similar to `dd` command. The main idea is to implement something to 4 | make it simple to make parsers for `PREFIX=SUFFIX`, where prefix is fixed for each parser - for 5 | example `if=` or `of=` and suffix is parsed with usual [`FromStr`](std::str::FromStr) trait. 6 | 7 | The function `tag` serves this purpose. It performs the following steps: 8 | 9 | - consume any item that starts with a prefix at any argument position with [`any`] and 10 | [`ParseAny::anywhere`] 11 | - attaches help message and custom metadata to make `--help` friendlier 12 | - parses suffix with [`Parser::parse`] 13 | 14 | The rest of the parser simply uses `tag` to parse a few of `dd` arguments 15 | 16 | #![cfg_attr(not(doctest), doc = include_str!("docs2/dd.md"))] 17 | -------------------------------------------------------------------------------- /documentation/_documentation/_3_cookbook/_02_xorg/index.md: -------------------------------------------------------------------------------- 1 | #### `Xorg(1)`: `Xorg +xinerama +extension name` 2 | 3 | This example implements syntax similar to used by `Xorg` or similar programs. As usual with 4 | strange examples [`any`] serves an important role. 5 | 6 | The example implements the following parsers: 7 | 8 | - enable or disable an extension using `+ext name` and `-ext name` like syntax 9 | - enable or disable specific extensions with syntax like `-xinerama` or `+backing` 10 | 11 | Both parsers use [`any`] with [`ParseAny::anywhere`] 12 | 13 | 14 | #![cfg_attr(not(doctest), doc = include_str!("docs2/xorg.md"))] 15 | -------------------------------------------------------------------------------- /documentation/_documentation/_3_cookbook/_03_command_chaining/index.md: -------------------------------------------------------------------------------- 1 | #### Command chaining 2 | Lets you do things like `setup.py sdist bdist`: [command chaining](https://click.palletsprojects.com/en/7.x/commands/#multi-command-chaining) 3 | 4 | With [`adjacent`](crate::parsers::ParseCommand::adjacent) 5 | `bpaf` allows you to have several commands side by side instead of being nested. 6 | 7 | #![cfg_attr(not(doctest), doc = include_str!("docs2/adjacent_command.md"))] 8 | -------------------------------------------------------------------------------- /documentation/_documentation/_3_cookbook/_04_multi_value/index.md: -------------------------------------------------------------------------------- 1 | #### Multi-value arguments: `--foo ARG1 ARG2 ARG3` 2 | 3 | By default arguments take at most one value, you can create multi value options by using 4 | [`adjacent`](crate::parsers::ParseCon::adjacent) modifier 5 | 6 | #![cfg_attr(not(doctest), doc = include_str!("docs2/adjacent_struct_0.md"))] 7 | -------------------------------------------------------------------------------- /documentation/_documentation/_3_cookbook/_05_struct_groups/index.md: -------------------------------------------------------------------------------- 1 | #### Structure groups: `--foo --foo-1 ARG1 --foo-2 ARG2 --foo-3 ARG3` 2 | 3 | Groups of options that can be specified multiple times. All such groups should be kept without 4 | overwriting previous one. 5 | 6 | ```console 7 | $ prometheus_sensors_exporter \ 8 | \ 9 | `# 2 physical sensors located on physycial different i2c bus or address` \ 10 | --sensor \ 11 | --sensor-device=tmp102 \ 12 | --sensor-name="temperature_tmp102_outdoor" \ 13 | --sensor-i2c-bus=0 \ 14 | --sensor-i2c-address=0x48 \ 15 | --sensor \ 16 | --sensor-device=tmp102 \ 17 | --sensor-name="temperature_tmp102_indoor" \ 18 | --sensor-i2c-bus=1 \ 19 | --sensor-i2c-address=0x49 \ 20 | ``` 21 | 22 | #![cfg_attr(not(doctest), doc = include_str!("docs2/adjacent_struct_1.md"))] 23 | -------------------------------------------------------------------------------- /documentation/_documentation/_3_cookbook/_06_multi_flag/index.md: -------------------------------------------------------------------------------- 1 | #### Multi-value arguments with optional flags: `--foo ARG1 --flag --inner ARG2` 2 | 3 | So you can parse things while parsing things. Not sure why you might need this, but you can 4 | :) 5 | 6 | #![cfg_attr(not(doctest), doc = include_str!("docs2/adjacent_struct_4.md"))] 7 | -------------------------------------------------------------------------------- /documentation/_documentation/_3_cookbook/_07_skip_positional/index.md: -------------------------------------------------------------------------------- 1 | #### Skipping optional positional items if parsing or validation fails 2 | 3 | Combinations like [`Parser::optional`] and 4 | [`ParseOptional::catch`](crate::parsers::ParseOptional::catch) allow to try to parse something 5 | and then handle the error as if pase attempt never existed 6 | 7 | #![cfg_attr(not(doctest), doc = include_str!("docs2/numeric_prefix.md"))] 8 | -------------------------------------------------------------------------------- /documentation/_documentation/_3_cookbook/_08_cargo_helper/index.md: -------------------------------------------------------------------------------- 1 | #### Implementing cargo commands 2 | 3 | With [`cargo_helper`](crate::batteries::cargo_helper) you can use your application as a `cargo` command. 4 | You will need to enable `batteries` feature while importing `bpaf`. 5 | 6 | #![cfg_attr(not(doctest), doc = include_str!("docs2/cargo_helper.md"))] 7 | -------------------------------------------------------------------------------- /documentation/_documentation/_3_cookbook/_09_numeric_flags/index.md: -------------------------------------------------------------------------------- 1 | #### Numeric flags - compression levels like in zip 2 | 3 | While you can add flags in a usual way for compression levels using `short(1)`, `short(2)`, etc 4 | combined with `req_flag`, you can also parse all of then using [`any`] 5 | 6 | ```no_run 7 | use bpaf::{doc::Style, *}; 8 | 9 | fn compression() -> impl Parser { 10 | any::("COMP", |x: isize| { 11 | if (-9..=-1).contains(&x) { 12 | Some(x.abs().try_into().unwrap()) 13 | } else { 14 | None 15 | } 16 | }) 17 | .metavar(&[ 18 | ("-1", Style::Literal), 19 | (" to ", Style::Text), 20 | ("-9", Style::Literal), 21 | ]) 22 | .help("Compression level") 23 | .anywhere() 24 | } 25 | 26 | fn main() { 27 | let opts = compression().to_options().run(); 28 | 29 | println!("{:?}", opts); 30 | } 31 | ``` 32 | -------------------------------------------------------------------------------- /documentation/_documentation/_3_cookbook/index.md: -------------------------------------------------------------------------------- 1 | #### Parsing cookbook 2 | How to parse less frequent combinations 3 | 4 | While `bpaf`'s design tries to cover the most common use cases, mostly 5 | [posix conventions](https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/basedefs/V1_chap12.html), 6 | it can also handle some more unusual requirements. It might come at the cost of having to write 7 | more code, more confusing error messages or worse performance, but it will get the job done. 8 | -------------------------------------------------------------------------------- /documentation/_documentation/index.md: -------------------------------------------------------------------------------- 1 | #### Project documentation 2 | 3 | See [official website](https://pacak.github.io/bpaf/bpaf/_documentation/index.html) for more up to date version. 4 | -------------------------------------------------------------------------------- /documentation/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use documentation::*; 4 | 5 | fn main() -> Result<()> { 6 | let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("_documentation"); 7 | 8 | let mut res = String::new(); 9 | let doc = Entry { 10 | module: "_documentation".to_owned(), 11 | title: title(&root)?, 12 | path: root, 13 | }; 14 | walk(&mut res, 0, &doc.path, None, Some(&doc), (None, None))?; 15 | 16 | let target = PathBuf::from(env!("CARGO_MANIFEST_DIR")) 17 | .parent() 18 | .unwrap() 19 | .join("src/_documentation.rs"); 20 | write_updated(res, target)?; 21 | Ok(()) 22 | } 23 | -------------------------------------------------------------------------------- /examples/at_least_two.rs: -------------------------------------------------------------------------------- 1 | //! How to require presence of at least N values, 2 | //! 3 | //! This program accepts "-f -f -f" or "-fffff" but not "-f" 4 | 5 | use bpaf::*; 6 | 7 | fn main() { 8 | let opt = short('f') 9 | .req_flag(()) 10 | .many() 11 | .guard(|x| x.len() >= 2, "at least two arguments are required") 12 | .to_options() 13 | .run(); 14 | 15 | println!("{:?}", opt); 16 | } 17 | -------------------------------------------------------------------------------- /examples/cargo-cmd.rs: -------------------------------------------------------------------------------- 1 | //! When implementing a cargo subcommand parser needs to be able to skip the first argument which 2 | //! is always the same as the executable name. For this example executable name is `cargo-cmd` so 3 | //! first argument would be `cmd`. A way to support both cases - when it's present and it's absent 4 | //! would be to use a combination of `literal`, `optional` and `hide`. 5 | //! `bpaf` also provides a helper `cargo_helper` that does exactly this. 6 | 7 | use bpaf::*; 8 | 9 | #[derive(Debug, Clone)] 10 | #[allow(dead_code)] 11 | struct Opts { 12 | width: usize, 13 | height: usize, 14 | } 15 | 16 | fn main() { 17 | // defining a parser in a usual way 18 | let width = short('w').argument::("WIDTH").fallback(10); 19 | let height = short('h').argument::("HEIGHT").fallback(10); 20 | let parser = construct!(Opts { width, height }); 21 | 22 | let cmd = literal("cmd").optional().hide(); 23 | let combined_parser = construct!(cmd, parser).map(|x| x.1); 24 | 25 | let opts = combined_parser.to_options().run(); 26 | 27 | println!("{:?}", opts); 28 | } 29 | -------------------------------------------------------------------------------- /examples/cat.rs: -------------------------------------------------------------------------------- 1 | //! You can open files as part of parsing process too, might not be the best idea though 2 | //! because depending on a context `bpaf` might need to evaluate some parsers multiple times. 3 | //! 4 | //! Main motivation for this example is that you can open a file as part of the argument parsing 5 | //! and give a reader directly to user. In practice to replicate `cat`'s behavior you'd accept 6 | //! multiple files with `many` and open them one by one in your code. 7 | 8 | use bpaf::*; 9 | use std::{ 10 | ffi::OsString, 11 | fs::File, 12 | io::{stdin, BufRead, BufReader, Read}, 13 | }; 14 | 15 | fn main() { 16 | let file = positional::("FILE") 17 | .help("File name to concatenate, with no FILE or when FILE is -, read standard input") 18 | .optional() 19 | .parse::<_, Box, std::io::Error>(|path| { 20 | Ok(if let Some(path) = path { 21 | if path == "-" { 22 | Box::new(stdin()) 23 | } else { 24 | Box::new(File::open(path)?) 25 | } 26 | } else { 27 | Box::new(stdin()) 28 | }) 29 | }) 30 | .to_options() 31 | .descr("Concatenate a file to standard output") 32 | .run(); 33 | 34 | let reader = BufReader::new(file); 35 | 36 | for line in reader.lines() { 37 | println!("{}", line.unwrap()); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /examples/compression.rs: -------------------------------------------------------------------------------- 1 | //! Numeric flags similar to zip compression levels, accepts -1 to -9, produces usize 2 | 3 | use bpaf::{doc::Style, *}; 4 | 5 | fn compression() -> impl Parser { 6 | any::("COMP", |x: isize| { 7 | if (-9..=-1).contains(&x) { 8 | Some(x.abs().try_into().unwrap()) 9 | } else { 10 | None 11 | } 12 | }) 13 | .metavar(&[ 14 | ("-1", Style::Literal), 15 | (" to ", Style::Text), 16 | ("-9", Style::Literal), 17 | ]) 18 | .help("Compression level") 19 | .anywhere() 20 | } 21 | 22 | fn main() { 23 | let opts = compression().to_options().run(); 24 | 25 | println!("{:?}", opts); 26 | } 27 | -------------------------------------------------------------------------------- /examples/csample.rs: -------------------------------------------------------------------------------- 1 | //! This example shows dynamic shell completion features 2 | 3 | fn complete_calculator(input: &String) -> Vec<(&'static str, Option<&'static str>)> { 4 | let items = ["alpha", "beta", "banana", "cat", "durian"]; 5 | items 6 | .iter() 7 | .filter(|item| item.starts_with(input)) 8 | .map(|item| (*item, None)) 9 | .collect::>() 10 | } 11 | 12 | fn main() { 13 | use bpaf::*; 14 | 15 | let a = short('a').long("avocado").help("Use avocado").switch(); 16 | let b = short('b').long("banana").help("Use banana").switch(); 17 | let bb = long("bananananana").help("I'm Batman").switch(); 18 | let c = long("calculator") 19 | .help("calculator expression") 20 | .argument::("EXPR") 21 | .complete(complete_calculator); 22 | let parser = construct!(a, b, bb, c) 23 | .to_options() 24 | .descr("Dynamic autocomplete example") 25 | .footer( 26 | "\ 27 | bpaf supports dynamic autocompletion for a few shells, make sure your binary is in $PATH 28 | and try using one of those this output should go into a file that depends on your shell: 29 | $ csample --bpaf-complete-style-bash 30 | $ csample --bpaf-complete-style-zsh 31 | $ csample --bpaf-complete-style-fish 32 | $ csample --bpaf-complete-style-elvish", 33 | ); 34 | 35 | println!("{:?}", parser.fallback_to_usage().run()); 36 | } 37 | -------------------------------------------------------------------------------- /examples/customize_help.rs: -------------------------------------------------------------------------------- 1 | //! `--help` output customizations 2 | //! 3 | //! help header, help footer, a short description and a custom usage line 4 | use bpaf::*; 5 | 6 | fn main() { 7 | let opt = short('d') 8 | .help("Release the dragon") 9 | .switch() 10 | .to_options() 11 | .descr("I am a program and I do things") 12 | .header("Sometimes they even work.") 13 | .footer("Beware `-d`, dragons be here") 14 | .with_usage(|doc| { 15 | let mut u = Doc::default(); 16 | u.emphasis("You can call it with following flags:"); 17 | u.text(" "); 18 | u.doc(&doc); 19 | u 20 | }) 21 | .run(); 22 | 23 | println!("{:?}", opt); 24 | } 25 | -------------------------------------------------------------------------------- /examples/derive-smart-pointer.rs: -------------------------------------------------------------------------------- 1 | //! With minimal changes you can parse directly into smart pointers. 2 | //! 3 | //! While Bpaf derive macro knows nothing about Box or Arc it lets you 4 | //! to parse into them by overriding the parsing sequence. 5 | //! 6 | //! Here `name` first parsed into a regular `String` then converted 7 | //! into `Box` using `map`. 8 | //! 9 | //! Similarly a set of coins is parsed into a regular vector and later converted into Arc still 10 | //! inside the parser 11 | 12 | #![allow(dead_code)] 13 | 14 | use bpaf::*; 15 | use std::sync::Arc; 16 | 17 | #[derive(Debug, Clone, Bpaf)] 18 | #[bpaf(options)] 19 | pub struct Options { 20 | #[bpaf(argument::("NAME"), map(Box::from))] 21 | /// Adventurer's name 22 | name: Box, 23 | 24 | #[bpaf(positional::("COIN"), many, map(Arc::from))] 25 | /// A set of coins 26 | coins: Arc<[usize]>, 27 | } 28 | 29 | fn main() { 30 | let xs = options().fallback_to_usage().run(); 31 | println!("{xs:?}"); 32 | } 33 | -------------------------------------------------------------------------------- /examples/derive.rs: -------------------------------------------------------------------------------- 1 | //! pretty basic derive example with external function 2 | 3 | use bpaf::{short, Bpaf, Parser}; 4 | use std::path::PathBuf; 5 | 6 | #[derive(Debug, Clone, Bpaf)] 7 | #[bpaf(options, version)] 8 | #[allow(dead_code)] 9 | struct Opts { 10 | /// Activate debug mode 11 | #[bpaf(short, long)] 12 | debug: bool, 13 | /// this comment is ignored 14 | #[bpaf(external(verbose))] 15 | verbose: usize, 16 | /// Set speed 17 | #[bpaf(argument("SPEED"), fallback(42.0))] 18 | speed: f64, 19 | /// Output file 20 | output: PathBuf, 21 | 22 | #[bpaf(guard(positive, "must be positive"), fallback(1))] 23 | nb_cars: u32, 24 | files_to_process: Vec, 25 | } 26 | 27 | fn verbose() -> impl Parser { 28 | // number of occurrences of the v/verbose flag capped at 3 29 | short('v') 30 | .long("verbose") 31 | .help("Increase the verbosity\nYou can specify it up to 3 times\neither as -v -v -v or as -vvv") 32 | .req_flag(()) 33 | .many() 34 | .map(|xs| xs.len()) 35 | .guard(|&x| x <= 3, "It doesn't get any more verbose than this") 36 | } 37 | 38 | fn positive(input: &u32) -> bool { 39 | *input > 0 40 | } 41 | 42 | fn main() { 43 | println!("{:#?}", opts().run()); 44 | } 45 | -------------------------------------------------------------------------------- /examples/derive_rudo.rs: -------------------------------------------------------------------------------- 1 | /// parser inspired by https://github.com/hood/rudo/blob/e448942b752c56dd2be2e2bb5026ced45e215ed6/src/main.rs 2 | /// 3 | use bpaf::*; 4 | 5 | #[derive(Debug, Clone, Bpaf)] 6 | #[allow(dead_code)] 7 | #[bpaf(options)] 8 | struct Options { 9 | /// help 10 | #[bpaf(external, fallback(Action::List))] 11 | action: Action, 12 | } 13 | 14 | #[derive(Debug, Clone, Bpaf)] 15 | enum Action { 16 | /// Add a new TODO item 17 | #[bpaf(command)] 18 | Add(String), 19 | 20 | /// Mark nth item as done 21 | #[bpaf(command)] 22 | Mark(usize), 23 | 24 | /// Read nth item 25 | #[bpaf(command)] 26 | Read(usize), 27 | 28 | /// Lists everything 29 | // name argument for command is optional 30 | #[bpaf(command("list"))] 31 | List, 32 | } 33 | 34 | fn main() { 35 | println!("{:?}", options().run()); 36 | } 37 | -------------------------------------------------------------------------------- /examples/derive_this_or_that.rs: -------------------------------------------------------------------------------- 1 | //! deriving for tri-state enabled/disabled/undecided switch, combinatoric version would use 2 | //! combination of `req_flag` and `construct!([on, off])` 3 | 4 | #![allow(dead_code)] 5 | use bpaf::Bpaf; 6 | 7 | // By default bpaf tries to parse booleans as flags, do something smart 8 | // about Strings and file names and handles Option/Vec. 9 | // Everything else is handled as a textual named argument. 10 | #[derive(Debug, Clone, Bpaf)] 11 | #[bpaf(options, fallback(Opts::Undecided))] 12 | enum Opts { 13 | /// enabled 14 | On, 15 | /// disabled 16 | Off, 17 | /// undecined 18 | #[bpaf(skip)] 19 | Undecided, 20 | } 21 | 22 | fn main() { 23 | println!("{:?}", opts().run()) 24 | } 25 | -------------------------------------------------------------------------------- /examples/enum_in_args.rs: -------------------------------------------------------------------------------- 1 | //! parsing argument value into enum. You can use crate `strum`'s `EnumString` for this purposes as well 2 | //! 3 | use std::str::FromStr; 4 | 5 | use bpaf::*; 6 | 7 | #[derive(Debug, Clone)] 8 | enum Baz { 9 | Foo, 10 | Bar, 11 | FooBar, 12 | } 13 | 14 | impl FromStr for Baz { 15 | type Err = String; 16 | fn from_str(s: &str) -> Result 17 | where 18 | Self: Sized, 19 | { 20 | match s { 21 | "foo" => Ok(Baz::Foo), 22 | "bar" => Ok(Baz::Bar), 23 | "foobar" => Ok(Baz::FooBar), 24 | _ => Err("Expected foo|bar|foobar".to_string()), 25 | } 26 | } 27 | } 28 | fn main() { 29 | let opt = long("baz") 30 | .short('b') 31 | .help("choose between foo, bar or foobar") 32 | .argument::("CMD") 33 | .to_options() 34 | .run(); 35 | 36 | println!("{:#?}", opt); 37 | } 38 | -------------------------------------------------------------------------------- /examples/enum_tuple.rs: -------------------------------------------------------------------------------- 1 | //! How to extract subcommands' args into external structs. 2 | 3 | use bpaf::*; 4 | 5 | #[derive(Debug, Clone)] 6 | pub struct Foo { 7 | pub bar: Option, 8 | } 9 | 10 | #[derive(Debug, Clone)] 11 | pub enum Command { 12 | Foo(Foo), 13 | } 14 | 15 | fn main() { 16 | let bar = short('b') 17 | .long("bar") 18 | .help("some bar command") 19 | .argument::("BAR") 20 | .optional(); 21 | 22 | let bar_cmd = construct!(Foo { bar }) 23 | .to_options() 24 | .descr("This command will try to do foo given a bar argument"); 25 | 26 | let opt = bar_cmd 27 | .command("foo") 28 | .help("command for doing foo") 29 | .map(Command::Foo) 30 | .to_options() 31 | .run(); 32 | 33 | println!("{:#?}", opt); 34 | } 35 | -------------------------------------------------------------------------------- /examples/env_logger.rs: -------------------------------------------------------------------------------- 1 | //! convert verbosty level from count of -v flags into enum from a logger crate of your choice 2 | 3 | use bpaf::*; 4 | 5 | // generally you'd use this from the log crate itself 6 | #[derive(Debug, Copy, Clone)] 7 | pub enum LevelFilter { 8 | Off, 9 | Error, 10 | Warn, 11 | Info, 12 | Debug, 13 | Trace, 14 | } 15 | 16 | #[derive(Bpaf)] 17 | #[bpaf(options)] 18 | #[allow(dead_code)] 19 | #[derive(Debug, Clone)] 20 | struct Options { 21 | #[bpaf(external)] 22 | verbose: LevelFilter, 23 | /// number of potatoes 24 | #[bpaf(fallback(3))] 25 | potato: usize, 26 | } 27 | 28 | fn verbose() -> impl Parser { 29 | short('v') 30 | .help("Verbosity level, use multiple times for more verbosity") 31 | .req_flag(()) 32 | .count() 33 | .map(|l| { 34 | use LevelFilter::*; 35 | [Off, Error, Warn, Info, Debug, Trace][l.clamp(0, 5)] 36 | }) 37 | } 38 | 39 | fn main() { 40 | println!("{:#?}", options().run()); 41 | } 42 | -------------------------------------------------------------------------------- /examples/env_variable.rs: -------------------------------------------------------------------------------- 1 | //! Consume a named argument with fallback to environment variable 2 | 3 | use bpaf::*; 4 | 5 | #[allow(dead_code)] 6 | #[derive(Clone, Debug)] 7 | struct Opts { 8 | pub key: String, 9 | } 10 | 11 | pub fn main() { 12 | let key = long("key") 13 | .env("ACCESS_KEY") 14 | .help("access key to use") 15 | .argument::("KEY"); 16 | 17 | let opts = construct!(Opts { key }).to_options().run(); 18 | 19 | println!("{:?}", opts); 20 | } 21 | -------------------------------------------------------------------------------- /examples/ex_positional.rs: -------------------------------------------------------------------------------- 1 | //! Non derive version for positional arguments 2 | use bpaf::*; 3 | use std::path::PathBuf; 4 | 5 | #[allow(dead_code)] 6 | #[derive(Debug, Clone)] 7 | struct Options { 8 | value: u32, 9 | files: Vec, 10 | } 11 | 12 | fn main() { 13 | let value = long("value") 14 | .help("Mysterious value") 15 | .argument::("VAL") 16 | .fallback(42); 17 | let files = positional::("FILE").many(); 18 | let opts = construct!(Options { value, files }).to_options().run(); 19 | 20 | println!("{:#?}", opts); 21 | } 22 | -------------------------------------------------------------------------------- /examples/fallback_command.rs: -------------------------------------------------------------------------------- 1 | //! Parse some commands manually or collect anything else as 2 | //! for manual parsing 3 | use bpaf::*; 4 | use std::ffi::OsString; 5 | 6 | #[derive(Debug, Clone, Bpaf)] 7 | #[bpaf(options)] 8 | #[allow(dead_code)] 9 | enum Commands { 10 | #[bpaf(command)] 11 | Build { 12 | /// Optimization level 13 | opt: u32, 14 | }, 15 | Fallback(#[bpaf(external(fallback), hide)] Fallback), 16 | } 17 | 18 | #[derive(Debug, Clone, Bpaf)] 19 | #[allow(dead_code)] 20 | struct Fallback { 21 | #[bpaf(positional("COMMAND"))] 22 | name: String, 23 | 24 | #[bpaf(any("ARG", Some))] 25 | args: Vec, 26 | } 27 | 28 | fn main() { 29 | let opts = commands().run(); 30 | println!("{:?}", opts); 31 | } 32 | -------------------------------------------------------------------------------- /examples/filenames.rs: -------------------------------------------------------------------------------- 1 | //! This example shows how to use shell completion to ask for 2 | //! a file with one of two extensions. If you want to specify just one 3 | //! extension having it as something like "*.rs" is good enough 4 | 5 | use bpaf::{positional, Parser, ShellComp}; 6 | use std::path::PathBuf; 7 | 8 | fn main() { 9 | let parser = positional::("FILE") 10 | .complete_shell(ShellComp::File { 11 | mask: Some("*.(md|toml)"), 12 | }) 13 | .many() 14 | .to_options(); 15 | 16 | let r = parser.run(); 17 | println!("{:?}", r); 18 | } 19 | -------------------------------------------------------------------------------- /examples/flatten.rs: -------------------------------------------------------------------------------- 1 | //! All the flags don't have to live in the same structure, this example uses non derive version. 2 | //! with derive API you would use `external` annotation 3 | 4 | use bpaf::*; 5 | 6 | #[allow(dead_code)] 7 | #[derive(Debug, Clone)] 8 | struct Cmdline { 9 | /// switch verbosity on 10 | verbose: bool, 11 | daemon_opts: DaemonOpts, 12 | } 13 | 14 | #[allow(dead_code)] 15 | #[derive(Debug, Clone)] 16 | struct DaemonOpts { 17 | /// daemon user 18 | user: String, 19 | 20 | /// daemon group 21 | group: String, 22 | } 23 | 24 | fn main() { 25 | let verbose = short('v').help("switch verbosity on").switch(); 26 | let user = short('u').help("daemon user").argument::("USER"); 27 | let group = short('g').help("daemon group").argument::("GROUP"); 28 | let daemon_opts = construct!(DaemonOpts { user, group }); 29 | let opt = construct!(Cmdline { 30 | verbose, 31 | daemon_opts 32 | }) 33 | .to_options() 34 | .run(); 35 | println!("{:?}", opt); 36 | } 37 | -------------------------------------------------------------------------------- /examples/many_comma_separated_args.rs: -------------------------------------------------------------------------------- 1 | /// To parse comma separated values it's easier to treat them as strings 2 | use bpaf::*; 3 | use std::str::FromStr; 4 | 5 | // --ports 1,2,3 --ports 4,5 => [1,2,3,4,5] 6 | fn args() -> impl Parser> { 7 | long("ports") 8 | .help("Comma separated list of ports") 9 | .argument::("PORTS") 10 | .parse(|s| { 11 | s.split(',') 12 | .map(u16::from_str) 13 | .collect::, _>>() 14 | }) 15 | .many() 16 | .map(|nested| nested.into_iter().flatten().collect()) 17 | } 18 | 19 | fn main() { 20 | println!("{:?}", args().to_options().run()); 21 | } 22 | -------------------------------------------------------------------------------- /examples/many_comma_separated_args_derive.rs: -------------------------------------------------------------------------------- 1 | /// To parse comma separated values it's easier to treat them as strings 2 | use bpaf::*; 3 | use std::{num::ParseIntError, str::FromStr}; 4 | 5 | fn split_and_parse(s: String) -> Result, ParseIntError> { 6 | s.split(',') 7 | .map(u16::from_str) 8 | .collect::, _>>() 9 | } 10 | 11 | fn flatten_vec(vv: Vec>) -> Vec { 12 | vv.into_iter().flatten().collect() 13 | } 14 | 15 | #[derive(Debug, Clone, Bpaf)] 16 | #[allow(dead_code)] 17 | struct Opts { 18 | #[bpaf( 19 | long, 20 | argument::("PORTS"), 21 | parse(split_and_parse), 22 | many, 23 | map(flatten_vec) 24 | )] 25 | /// Comma separated list of ports 26 | ports: Vec, 27 | } 28 | 29 | fn main() { 30 | println!("{:?}", opts().to_options().run()); 31 | } 32 | -------------------------------------------------------------------------------- /examples/multiple_fallback.rs: -------------------------------------------------------------------------------- 1 | //! Multi level fallback example: 2 | //! 3 | //! Fallback to one of several values 4 | //! - the command line argument 5 | //! - the environmental variable 6 | //! - the config file 7 | //! - the hard-coded default 8 | 9 | #![allow(dead_code)] 10 | use bpaf::*; 11 | 12 | #[derive(Clone, Debug)] 13 | struct Config { 14 | field1: u32, 15 | field2: u64, 16 | } 17 | 18 | /// Here this is a constant but it can be OnceCell from `once_cell` 19 | /// that reads the config and deals with substituting missing falues with defaults 20 | const DEFAULT_CONFIG: Config = Config { 21 | field1: 42, 22 | field2: 10, 23 | }; 24 | 25 | pub fn main() { 26 | let field1 = long("field1") 27 | .env("FIELD1") 28 | .help("Field 1") 29 | .argument::("ARG") 30 | .fallback(DEFAULT_CONFIG.field1); 31 | let field2 = long("field2") 32 | .env("FIELD2") 33 | .help("Field 2") 34 | .argument::("ARG") 35 | .fallback(DEFAULT_CONFIG.field2); 36 | 37 | let opts = construct!(Config { field1, field2 }).to_options().run(); 38 | 39 | // At this point if you get opts - it should be taken from one of 40 | // - the command line argument 41 | // - the environmental variable 42 | // - the config file 43 | // - the hard-coded default (from config parser) 44 | println!("{:?}", opts); 45 | } 46 | -------------------------------------------------------------------------------- /examples/negative.rs: -------------------------------------------------------------------------------- 1 | //! Parsing negative numbers from the command line. `bpaf` won't try to do anything special here, 2 | //! user will have to escape minus sign either with `=` or -- 3 | use bpaf::*; 4 | 5 | fn main() { 6 | let age = long("age").argument::("AGE"); 7 | let msg = "\ 8 | To pass a value that starts with a dash requres one one of two special syntaxes: 9 | 10 | This will pass '-1' to '--age' handler and leave remaining arguments as is 11 | --age=-1 12 | This will transform everything after '--' into non flags, '--age' will handle '-1' 13 | and positional handlers will be able to handle the rest. 14 | --age -- -1"; 15 | let num = age.to_options().descr(msg).run(); 16 | println!("age: {}", num); 17 | } 18 | -------------------------------------------------------------------------------- /examples/no_import.rs: -------------------------------------------------------------------------------- 1 | //! You don't need to import bpaf in order to use it, it will look ugly though 2 | 3 | #[allow(dead_code)] 4 | #[derive(Debug, Clone)] 5 | struct Out { 6 | debug: bool, 7 | speed: f64, 8 | } 9 | 10 | fn main() { 11 | // A flag, true if used in the command line. Can be required, this one is optional 12 | let debug = bpaf::short('d') 13 | .long("debug") 14 | .help("Activate debug mode") 15 | .switch(); 16 | 17 | // an argument, parsed and with default value 18 | let speed = bpaf::Parser::fallback( 19 | bpaf::short('s') 20 | .long("speed") 21 | .help("Set speed") 22 | .argument::("SPEED"), 23 | 42.0, 24 | ); 25 | 26 | // packing things in a struct assumes parser for each field is in scope. 27 | let opt = bpaf::Parser::to_options(bpaf::construct!(Out { debug, speed })).run(); 28 | 29 | println!("{:#?}", opt); 30 | } 31 | -------------------------------------------------------------------------------- /examples/numeric_prefix.rs: -------------------------------------------------------------------------------- 1 | /// You can parse multiple positional elements with earlier being optional as well 2 | /// This example takes two - optional numeric prefix and a command name: 3 | /// 4 | /// > numeric_prefix 8 work 5 | /// Options { prefix: Some(8), command: "work" } 6 | /// 7 | /// > numeric_prefix sleep 8 | /// Options { prefix: None, command: "sleep" } 9 | /// 10 | /// Generated usage reflects that: 11 | /// Usage: numeric_prefix [PREFIX] COMMAND 12 | use bpaf::*; 13 | 14 | #[derive(Debug, Clone)] 15 | #[allow(dead_code)] 16 | pub struct Options { 17 | prefix: Option, 18 | command: String, 19 | } 20 | 21 | pub fn options() -> OptionParser { 22 | let prefix = positional::("PREFIX") 23 | .help("Optional numeric command prefix") 24 | .optional() 25 | .catch(); 26 | let command = positional::("COMMAND").help("Required command name"); 27 | 28 | construct!(Options { prefix, command }).to_options() 29 | } 30 | 31 | fn main() { 32 | println!("{:#?}", options().run()); 33 | } 34 | -------------------------------------------------------------------------------- /examples/positional_derive.rs: -------------------------------------------------------------------------------- 1 | //! Derive for struct with named values that uses positional argument 2 | 3 | use bpaf::*; 4 | use std::path::PathBuf; 5 | 6 | #[allow(dead_code)] 7 | #[derive(Debug, Clone, Bpaf)] 8 | #[bpaf(options)] 9 | struct Options { 10 | /// Mysterious value 11 | #[bpaf(argument("VAL"), fallback(42))] 12 | value: u32, 13 | #[bpaf(positional("FILE"))] 14 | files: Vec, 15 | } 16 | 17 | fn main() { 18 | let opts = options().run(); 19 | println!("{:#?}", opts); 20 | } 21 | -------------------------------------------------------------------------------- /examples/rectangle.rs: -------------------------------------------------------------------------------- 1 | //! A simple combinatoric parser with extra metainformation attached, see --help 2 | 3 | use bpaf::*; 4 | 5 | #[allow(dead_code)] 6 | #[derive(Debug, Copy, Clone)] 7 | struct Out { 8 | rect: Option, 9 | verbose: bool, 10 | } 11 | 12 | #[allow(dead_code)] 13 | #[derive(Debug, Copy, Clone)] 14 | struct Rect { 15 | width: usize, 16 | height: usize, 17 | } 18 | 19 | fn main() { 20 | let width = short('w') 21 | .long("width") 22 | .help("Width of the rectangle") 23 | .argument::("PX"); 24 | 25 | let height = short('h') 26 | .long("height") 27 | .help("Height of the rectangle") 28 | .argument::("PX"); 29 | 30 | let rect = construct!(Rect { width, height }) 31 | .group_help("Rectangle is defined by width and height in meters") 32 | .optional(); 33 | 34 | let verbose = short('v') 35 | .long("verbose") 36 | .help("Print computation steps") 37 | .switch(); 38 | 39 | let opt = construct!(Out { verbose, rect }) 40 | .to_options() 41 | .descr("This program calculates rectangle's area") 42 | .header("vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv") 43 | .footer("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^") 44 | .run(); 45 | println!("{:#?}", opt); 46 | } 47 | -------------------------------------------------------------------------------- /examples/shared_args.rs: -------------------------------------------------------------------------------- 1 | //! It is possible to have shared args returned alongside a subcommand 2 | 3 | use bpaf::*; 4 | 5 | #[derive(Debug, Clone, Bpaf)] 6 | #[allow(dead_code)] 7 | struct Action { 8 | verbose: bool, 9 | number: u32, 10 | } 11 | 12 | #[derive(Debug, Clone, Bpaf)] 13 | #[allow(dead_code)] 14 | struct Build { 15 | verbose: bool, 16 | } 17 | 18 | #[derive(Debug, Clone)] 19 | enum Command { 20 | Action(Action), 21 | Build(Build), 22 | } 23 | 24 | fn shared() -> impl Parser> { 25 | positional("ARG").many() 26 | } 27 | 28 | fn parse_command() -> impl Parser<(Command, Vec)> { 29 | let action = action().map(Command::Action); 30 | let action = construct!(action, shared()).to_options().command("action"); 31 | let build = build().map(Command::Build); 32 | let build = construct!(build, shared()).to_options().command("build"); 33 | construct!([action, build]) 34 | } 35 | 36 | fn main() { 37 | let opts = parse_command().to_options().run(); 38 | 39 | println!("{:?}", opts); 40 | } 41 | -------------------------------------------------------------------------------- /examples/very_custom_usage.rs: -------------------------------------------------------------------------------- 1 | //! A way to customize usage for a nested command 2 | //! 3 | //! Usually you would go with generated usage or by overriding it using `usage` attribute 4 | //! to the top level or command level bpaf annotation. By taking advantage of command being just a 5 | //! set of options with it's own help message and a custom prefix you can override the usage with 6 | //! an arbitrary string, including one generated at runtime by doing something like this: 7 | 8 | use bpaf::*; 9 | 10 | // this defines top level set of options and refers to an external parser `cmd_usage` 11 | // At this point cmd_usage can be any parser that produces Cmd 12 | #[derive(Debug, Clone, Bpaf)] 13 | #[bpaf(options)] 14 | #[allow(dead_code)] 15 | enum Opts { 16 | Cmd(#[bpaf(external(cmd_usage))] Cmd), 17 | } 18 | 19 | // bpaf defines command as something with its own help message that can be accessed with a 20 | // positional command name - inside of the command there is an OptionParser, this struct 21 | // defines the parser we are going to use later 22 | #[derive(Debug, Clone, Bpaf)] 23 | #[bpaf(options)] 24 | #[allow(dead_code)] 25 | struct Cmd { 26 | opt: bool, 27 | } 28 | 29 | // At this point we have OptionParser and we want to turn that into a regular parser 30 | // with custom usage string - for that we are using two functions from combinatoric api: 31 | // `usage` and `command` 32 | fn cmd_usage() -> impl Parser { 33 | cmd().usage("A very custom usage goes here").command("cmd") 34 | } 35 | 36 | fn main() { 37 | println!("{:?}", opts().run()); 38 | } 39 | -------------------------------------------------------------------------------- /legacy/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "legacy" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | bpaf = { path = "../", features = ["derive", "autocomplete", "docgen"] } 10 | bpaf_derive = { path = "../bpaf_derive" } 11 | 12 | [workspace] 13 | -------------------------------------------------------------------------------- /src/_unusual.rs: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/docs2/compose_basic_switch.md: -------------------------------------------------------------------------------- 1 | 2 | ```no_run 3 | use bpaf::*; 4 | 5 | pub fn options() -> OptionParser { 6 | let simple = short('s').long("simple").switch(); 7 | simple.to_options() 8 | } 9 | 10 | fn main() { 11 | println!("{:?}", options().run()) 12 | } 13 | ``` 14 | 15 |
Output 16 | 17 | If you run the app with no parameters - switch will parse as `false` 18 | 19 | 20 |
21 | $ app
22 | false 23 |
24 | 25 | 26 | Both short and long names produce true 27 | 28 | 29 |
30 | $ app -s
31 | true 32 |
33 | 34 | 35 |
36 | $ app --simple
37 | true 38 |
39 | 40 | 41 | In addition to the switch you defined `bpaf` generates switch for user help 42 | 43 | 44 |
45 | $ app --help
46 |

Usage: app [-s]

47 | Available options:
-s, --simple
48 |
-h, --help
49 |
Prints help information
50 |
51 |

52 | 64 |
65 | 66 |
-------------------------------------------------------------------------------- /src/from_os_str.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | any::{Any, TypeId}, 3 | ffi::OsString, 4 | path::PathBuf, 5 | str::FromStr, 6 | }; 7 | 8 | pub(crate) fn parse_os_str(os: OsString) -> Result 9 | where 10 | T: FromStr + 'static, 11 | ::Err: std::fmt::Display, 12 | { 13 | if TypeId::of::() == TypeId::of::() { 14 | let anybox: Box = Box::new(os); 15 | Ok(*(anybox.downcast::().unwrap())) 16 | } else if TypeId::of::() == TypeId::of::() { 17 | let anybox: Box = Box::new(PathBuf::from(os)); 18 | Ok(*(anybox.downcast::().unwrap())) 19 | } else { 20 | match os.to_str() { 21 | Some(s) => T::from_str(s).map_err(|e| e.to_string()), 22 | None => Err(format!("{} is not a valid utf8", os.to_string_lossy())), 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tarp.sh: -------------------------------------------------------------------------------- 1 | cargo tarpaulin --target-dir tarp \ 2 | --workspace \ 3 | --all-features \ 4 | --exclude-files tests/*.rs \ 5 | --exclude-files docs/*/*.rs \ 6 | --exclude-files docs/*/*/*.rs \ 7 | --exclude-files docs/build.rs \ 8 | --exclude-files bpaf_cauwugo/src/*.rs \ 9 | --exclude-files bpaf_derive/src/*_tests.rs \ 10 | --exclude-files src/tests.rs 11 | -------------------------------------------------------------------------------- /tests/batteries.rs: -------------------------------------------------------------------------------- 1 | use bpaf::batteries::toggle_flag; 2 | use bpaf::*; 3 | 4 | #[test] 5 | fn test_toggle_flag() { 6 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 7 | enum Flag { 8 | Y, 9 | N, 10 | } 11 | 12 | let parser = toggle_flag(short('y'), Flag::Y, short('n'), Flag::N).to_options(); 13 | 14 | let r = parser.run_inner(&[]).unwrap(); 15 | assert_eq!(r, None); 16 | 17 | let r = parser.run_inner(&["-y", "-y", "-n"]).unwrap(); 18 | assert_eq!(r, Some(Flag::N)); 19 | 20 | let r = parser.run_inner(&["-y", "-y", "-n", "-y"]).unwrap(); 21 | assert_eq!(r, Some(Flag::Y)); 22 | } 23 | -------------------------------------------------------------------------------- /tests/markdown.md: -------------------------------------------------------------------------------- 1 | # simple 2 | 3 | I am a program and I do things 4 | 5 | **Usage**: **`simple`** \[**`-d`**\] **`--user`**=_`USER`_ 6 | 7 | Sometimes they even work. 8 | 9 | **Available options:** 10 | - **`-d`**, **`--kraken`** — 11 | Unleash the kraken 12 | - **` --user`**=_`USER`_ — 13 | Log in as this user 14 | 15 | Uses environment variable **`USER`** 16 | - **`-h`**, **`--help`** — 17 | Prints help information 18 | 19 | 20 | 21 | Beware `-d`, dragons be here 22 | -------------------------------------------------------------------------------- /tests/polymorph.rs: -------------------------------------------------------------------------------- 1 | use std::str::FromStr; 2 | 3 | use bpaf::*; 4 | 5 | #[test] 6 | fn generic_argument_field() { 7 | #[derive(Debug, Clone, Eq, PartialEq)] 8 | struct Poly { 9 | field: T, 10 | } 11 | 12 | fn poly(name: &'static str) -> impl Parser> 13 | where 14 | T: FromStr + 'static, 15 | ::Err: std::fmt::Display, 16 | { 17 | let field = long(name).argument("ARG"); 18 | construct!(Poly { field }) 19 | } 20 | 21 | let a = poly::("usize").optional(); 22 | let b = poly::("u32").optional(); 23 | let parser = construct!(a, b).to_options(); 24 | 25 | let r = parser.run_inner(&["--usize", "12"]).unwrap(); 26 | assert_eq!(r, (Some(Poly { field: 12 }), None)); 27 | 28 | let r = parser.run_inner(&["--u32", "12"]).unwrap(); 29 | assert_eq!(r, (None, Some(Poly { field: 12 }))); 30 | 31 | let r = parser.run_inner(&["--u32", "12", "--usize", "24"]).unwrap(); 32 | assert_eq!(r, (Some(Poly { field: 24 }), Some(Poly { field: 12 }))); 33 | } 34 | -------------------------------------------------------------------------------- /tests/simple.1: -------------------------------------------------------------------------------- 1 | .ie \n(.g .ds Aq \(aq 2 | .el .ds Aq ' 3 | .TH simple 1 Aug\ 2022 Michael\ Baykov\ asdf 4 | .SH NAME 5 | \fRsimple \- \fP\fRI am a program and I do things\fP 6 | .SH SYNOPSIS 7 | \fBsimple\fP\fR \fP\fR[\fP\fB\-d\fP\fR] \fP\fB\-\-user\fP\fR=\fP\fIUSER\fP 8 | .PP 9 | \fRSometimes they even work.\fP 10 | .PP 11 | .SS AVAILABLE\ OPTIONS: 12 | .TP 13 | \fB\-d\fP\fR, \fP\fB\-\-kraken\fP 14 | \fRUnleash the kraken\fP 15 | .PP 16 | .TP 17 | \fB \-\-user\fP\fR=\fP\fIUSER\fP 18 | \fRLog in as this user\fP 19 | .PP 20 | .TP 21 | \fRUses environment variable \fP\fBUSER\fP 22 | .PP 23 | .TP 24 | \fB\-h\fP\fR, \fP\fB\-\-help\fP 25 | \fRPrints help information\fP 26 | .PP 27 | .PP 28 | \fRBeware `\-d`, dragons be here\fP --------------------------------------------------------------------------------