examples/derive_show_asm.rs

```no_run
//! Parsing snippet from cargo-show-asm
//! Derive + typed fallback + external both with and without name

use bpaf::{construct, long, Bpaf, Parser, ShellComp};
use std::{convert::Infallible, path::PathBuf};

#[derive(Clone, Debug, Bpaf)]
#[bpaf(options("asm"))] // derives cargo helper for cargo-asm
#[allow(clippy::struct_excessive_bools)]
pub struct Options {
    #[bpaf(external(parse_manifest_path))]
    pub manifest_path: PathBuf,
    /// Custom target directory for generated artifacts
    #[bpaf(argument("DIR"))]
    pub target_dir: Option<PathBuf>,
    /// Package to use if ambigous
    #[bpaf(long, short, argument("SPEC"))]
    pub package: Option<String>,
    #[bpaf(external, optional)]
    pub focus: Option<Focus>,
    /// Produce a build plan instead of actually building
    pub dry: bool,
    /// Requires Cargo.lock and cache are up to date
    pub frozen: bool,
    /// Requires Cargo.lock is up to date
    pub locked: bool,
    /// Run without accessing the network
    pub offline: bool,
    #[bpaf(external)]
    pub format: Format,
    #[bpaf(external, fallback(Syntax::Intel))]
    pub syntax: Syntax,
    #[bpaf(external)]
    pub selected_function: SelectedFunction,
}

#[derive(Debug, Clone, Bpaf)]
/// Item to pick from the output
pub struct SelectedFunction {
    /// Complete or partial function name to filter
    #[bpaf(positional("FUNCTION"))]
    pub function: Option<String>,
    /// Select nth item from a filtered list
    #[bpaf(positional("INDEX"), fallback(0))]
    pub nth: usize,
}

fn parse_manifest_path() -> impl Parser<PathBuf> {
    long("manifest-path")
        .help("Path to Cargo.toml")
        .argument::<PathBuf>("PATH")
        .complete_shell(ShellComp::File {
            mask: Some("*.toml"),
        })
        .parse(|p| {
            // cargo-metadata wants to see
            if p.is_absolute() {
                Ok(p)
            } else {
                std::env::current_dir()
                    .map(|d| d.join(p))
                    .and_then(|full_path| full_path.canonicalize())
            }
        })
        .fallback_with(|| std::env::current_dir().map(|x| x.join("Cargo.toml")))
}

#[derive(Debug, Clone, Bpaf)]
/// How to render output
pub struct Format {
    /// Print interleaved Rust code
    pub rust: bool,

    #[bpaf(external(color_detection))]
    pub color: bool,

    /// include full demangled name instead of just prefix
    pub full_name: bool,
}

#[derive(Debug, Clone, Bpaf)]
/// Pick output type
///
/// included help
///
///
/// Extended help
pub enum Syntax {
    /// Generate assembly using Intel style
    Intel,
    /// Generate assembly using AT&T style
    Att,
}

fn color_detection() -> impl Parser<bool> {
    let yes = long("color")
        .help("Enable color highlighting")
        .req_flag(true);
    let no = long("no-color")
        .help("Disable color highlighting")
        .req_flag(false);
    construct!([yes, no]).fallback_with::<_, Infallible>(|| {
        // we can call for supports-color crate here
        Ok(true)
    })
}

fn comp_examples(prefix: &String) -> Vec<(String, Option<String>)> {
    // in the actual app we can ask cargo-metadata for this info
    let examples = ["derive_show_asm", "coreutils", "comonad"];
    examples
        .iter()
        .filter_map(|e| {
            if e.starts_with(prefix) {
                Some((e.to_string(), None))
            } else {
                None
            }
        })
        .collect()
}

#[derive(Debug, Clone, Bpaf)]
/// Select artifact to use for analysis
///
/// Only one is valid
pub enum Focus {
    /// Show results from library code
    Lib,

    Test(
        /// Show results from a test
        #[bpaf(long("test"), argument("TEST"))]
        String,
    ),

    Bench(
        /// Show results from a benchmark
        #[bpaf(long("bench"), argument("BENCH"))]
        String,
    ),

    Example(
        /// Show results from an example
        #[bpaf(long("example"), argument("EXAMPLE"), complete(comp_examples))]
        String,
    ),

    Bin(
        /// Show results from a binary
        #[bpaf(long("bin"), argument("BIN"))]
        String,
    ),
}

fn main() {
    println!("{:#?}", options().run());
}

```


Output

Example defines this parser

$ app --help

Usage: app [--manifest-path=PATH] [--target-dir=DIR] [-p=SPEC] [--lib | --test=TEST | --bench=BENCH | --example=EXAMPLE | --bin=BIN] [--dry] [--frozen] [--locked] [--offline] [--rust] [--color | --no-color] [--full-name] [--intel | --att] [FUNCTION] [INDEX]

Select artifact to use for analysis
 Only one is valid

    --lib

Show results from library code

    --test=TEST

Show results from a test

    --bench=BENCH

Show results from a benchmark

    --example=EXAMPLE

Show results from an example

    --bin=BIN

Show results from a binary

How to render output

    --rust

Print interleaved Rust code

    --color

Enable color highlighting

    --no-color

Disable color highlighting

    --full-name

include full demangled name instead of just prefix

Pick output type
 included help

    --intel

Generate assembly using Intel style

    --att

Generate assembly using AT&T style

Item to pick from the output

FUNCTION

Complete or partial function name to filter

INDEX

Select nth item from a filtered list

Available options:

    --manifest-path=PATH

Path to Cargo.toml

    --target-dir=DIR

Custom target directory for generated artifacts

-p, --package=SPEC

Package to use if ambigous

    --dry

Produce a build plan instead of actually building

    --frozen

Requires Cargo.lock and cache are up to date

    --locked

Requires Cargo.lock is up to date

    --offline

Run without accessing the network

-h, --help

Prints help information

By default completion system lists all possible cases

% derive_show_asm \t
% derive_show_asm
--manifest-path=PATH     -- Path to Cargo.toml
--target-dir=DIR         -- Custom target directory for generated artifacts
--package=SPEC           -- Package to use if ambigous
--dry                    -- Produce a build plan instead of actually building
--frozen                 -- Requires Cargo.lock and cache are up to date
--locked                 -- Requires Cargo.lock is up to date
--offline                -- Run without accessing the network
Select artifact to use for analysis
--lib                    -- Show results from library code
--test=TEST              -- Show results from a test
--bench=BENCH            -- Show results from a benchmark
--example=EXAMPLE        -- Show results from an example
--bin=BIN                -- Show results from a binary
How to render output
--rust                   -- Print interleaved Rust code
--color                  -- Enable color highlighting
--no-color               -- Disable color highlighting
--full-name              -- include full demangled name instead of just prefix
Pick output type
--intel                  -- Generate assembly using Intel style
--att                    -- Generate assembly using AT&T style
Item to pick from the output
FUNCTION: Complete or partial function name to filter

But when user tries to complete example name - it only lists examples produced by
`comp_examples` function

% derive_show_asm --example \t
% derive_show_asm --example
Select artifact to use for analysis
EXAMPLE: Show results from an example
derive_show_asm
coreutils
comonad

And completes the full name when user gives enough information

% derive_show_asm --example cor\t
% derive_show_asm --example coreutils

