pub trait FromOsStr {
    type Out;

    fn from_os_str(s: OsString) -> Result<Self::Out, String>
    where
        Self: Sized
; }
Expand description

Like FromStr but parses OsString instead

bpaf implements it for most of the things in std lib supported by FromStr. you can implement it for your types to be able to use them in turbofish directly for positional, argument and any.

Alternatively you can use FromUtf8 type tag to parse any type that implements FromStr

Combinatoric usage
#[derive(Debug, Clone)]
pub struct Options {
    coin: Coin,
    file: PathBuf,
    name: Option<String>,
}

/// A custom datatype that doesn't implement [`FromOsStr`] but implements [`FromStr`]
#[derive(Debug, Clone, Copy)]
enum Coin {
    Heads,
    Tails,
}
impl FromStr for Coin {
    type Err = String;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "heads" => Ok(Coin::Heads),
            "tails" => Ok(Coin::Tails),
            _ => Err(format!("Expected 'heads' or 'tails', got '{}'", s)),
        }
    }
}

pub fn options() -> OptionParser<Options> {
    let file = positional::<PathBuf>("FILE").help("File to use");
    // sometimes you can get away with not specifying type in positional's turbofish
    let coin = long("coin")
        .help("Coin toss results")
        .argument::<FromUtf8<Coin>>("COIN")
        .fallback(Coin::Heads);
    let name = positional::<String>("NAME")
        .help("Name to look for")
        .optional();
    construct!(Options { coin, file, name }).to_options()
}
Derive usage
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
    /// Coin toss results
    #[bpaf(argument::<FromUtf8<Coin>>("COIN"), fallback(Coin::Heads))]
    coin: Coin,

    /// File to use
    #[bpaf(positional::<PathBuf>("FILE"))]
    file: PathBuf,
    /// Name to look for
    #[bpaf(positional("NAME"))]
    name: Option<String>,
}

/// A custom datatype that doesn't implement [`FromOsStr`] but implements [`FromStr`]
#[derive(Debug, Clone, Copy)]
enum Coin {
    Heads,
    Tails,
}
impl FromStr for Coin {
    type Err = String;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "heads" => Ok(Coin::Heads),
            "tails" => Ok(Coin::Tails),
            _ => Err(format!("Expected 'heads' or 'tails', got '{}'", s)),
        }
    }
}
Examples

Positionals are consumed left to right, one at a time, no skipping unless the value is optional

% app main.rs
Options { coin: Heads, file: "main.rs", name: None }

Both positionals are present

% app main.rs hello
Options { coin: Heads, file: "main.rs", name: Some("hello") }

To parse items without having to write FromOsStr instance you can use FromUtf8 helper type?

% app main.rs --coin tails
Options { coin: Tails, file: "main.rs", name: None }

Only name is optional in this example, not specifying file is a failure

% app 
Expected <FILE>, pass --help for usage information

And usage information

% app --help
Usage: [--coin COIN] <FILE> [<NAME>]

Available positional items:
    <FILE>  File to use
    <NAME>  Name to look for

Available options:
        --coin <COIN>  Coin toss results
    -h, --help         Prints help information

Required Associated Types

Mostly a hack to allow parsing types that implement inside FromStr but aren’t contained inside stdlib, see FromUtf8.

Required Methods

Parse OsString or fail

Errors

Returns error message along with a lossy representation of the original string on failure

Implementations on Foreign Types

Implementors