Skip to content

Commit

Permalink
Require explicit type annotation for positional, argument and any
Browse files Browse the repository at this point in the history
  • Loading branch information
pacak committed Sep 23, 2022
1 parent a2e8ab3 commit 94b5351
Show file tree
Hide file tree
Showing 33 changed files with 299 additions and 90 deletions.
4 changes: 3 additions & 1 deletion docs/src/argument/combine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ pub struct Options {

pub fn options() -> OptionParser<Options> {
let value = long("value").argument::<isize>("ARG").fallback(100);
let shorty = short('s').argument::<u64>("ARG");
// You can use FromUtf8 type tag to parse things that only implement `FromStr`, but not `FromOsStr`
// `u64` implements both and only used as an example
let shorty = short('s').argument::<FromUtf8<u64>>("ARG");
construct!(Options { value, shorty }).to_options()
}
4 changes: 3 additions & 1 deletion docs/src/argument/derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ use bpaf::*;
pub struct Options {
#[bpaf(fallback(100))]
value: isize,
#[bpaf(short)]
// You can use FromUtf8 type tag to parse things that only implement FromStr, but not FromOsStr
// `u64` implements both and only used as an example
#[bpaf(short, argument::<FromUtf8<u64>>("ARG"))]
shorty: u64,
}
10 changes: 8 additions & 2 deletions docs/src/boxed/combine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,14 @@ pub enum Command {
}

pub fn options() -> OptionParser<Command> {
let a = positional("A").map(Command::A).to_options().command("a");
let b = positional("B").map(Command::B).to_options().command("b");
let a = positional::<String>("A")
.map(Command::A)
.to_options()
.command("a");
let b = positional::<String>("B")
.map(Command::B)
.to_options()
.command("b");
let sneaky = false;
let a = if sneaky {
construct!(a)
Expand Down
2 changes: 1 addition & 1 deletion docs/src/command/combine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ fn cmd() -> impl Parser<Cmd> {
let flag = long("flag")
.help("This flag is specific to command")
.switch();
let arg = long("arg").argument("ARG");
let arg = long("arg").argument::<usize>("ARG");
construct!(Cmd { flag, arg })
.to_options()
.descr("Command to do something")
Expand Down
15 changes: 11 additions & 4 deletions docs/src/positional/cases.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
? Positionals are consumed left to right, one at a time, no skipping unless the value is optional
> main.rs
OK
Options { file: "main.rs", name: None }
Options { coin: Heads, file: "main.rs", name: None }

? Both positionals are present
> main.rs hello
OK
Options { file: "main.rs", name: Some("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?
> main.rs --coin tails
OK
Options { coin: Tails, file: "main.rs", name: None }

? Only `name` is optional in this example, not specifying `file` is a failure
>
Expand All @@ -16,11 +22,12 @@ Expected <FILE>, pass --help for usage information
? And usage information
> --help
Stdout
Usage: <FILE> [<NAME>]
Usage: [--coin COIN] <FILE> [<NAME>]

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

Available options:
-h, --help Prints help information
--coin <COIN> Coin toss results
-h, --help Prints help information
30 changes: 27 additions & 3 deletions docs/src/positional/combine.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,42 @@
//
use std::path::PathBuf;
use std::{path::PathBuf, str::FromStr};
//
use bpaf::*;
#[derive(Debug, Clone)]
//
#[allow(dead_code)]
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 name = positional("NAME").help("Name to look for").optional();
construct!(Options { file, name }).to_options()
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()
}
23 changes: 22 additions & 1 deletion docs/src/positional/derive.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,37 @@
//
use std::path::PathBuf;
use std::{path::PathBuf, str::FromStr};
//
use bpaf::*;
#[derive(Debug, Clone, Bpaf)]
//
#[allow(dead_code)]
#[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)),
}
}
}
2 changes: 1 addition & 1 deletion docs/src/short_long_env/combine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ pub fn options() -> OptionParser<Options> {
.short('u')
.env("USER1")
.help("Custom user name")
.argument("USER");
.argument::<String>("USER");

construct!(Options {
switch,
Expand Down
6 changes: 3 additions & 3 deletions examples/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,17 @@ fn opts() -> OptionParser<Out> {
let output = short('o')
.long("output")
.help("output file")
.argument("OUTPUT"); // but it's optional when rustc can derive it
.argument::<PathBuf>("OUTPUT"); // but it's optional when rustc can derive it

// no magical name transmogrifications in combinatoric API,
let nb_cars = short('n').long("nb-cars").argument("N");
let nb_cars = short('n').long("nb-cars").argument::<u32>("N");

// a parser that consumes one argument
// you can build the inner parser in one go or as multiple steps giving each step a name
let file_to_proces = short('f')
.long("file")
.help("File to process")
.argument("FILE");
.argument::<PathBuf>("FILE");
let files_to_process = file_to_proces.many();

// packing things in a struct assumes parser for each field is in scope.
Expand Down
2 changes: 1 addition & 1 deletion examples/confusing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ enum PreCommand {
fn main() {
let token = long("token")
.help("Token used for complex commands")
.argument("TOKEN")
.argument::<String>("TOKEN")
.optional();

// start with defining 3 commands: simple, complex1 and complex2
Expand Down
2 changes: 1 addition & 1 deletion examples/csample.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ fn main() {
let bb = long("bananananana").help("I'm Batman").switch();
let c = long("calculator")
.help("calculator expression")
.argument("EXPR")
.argument::<String>("EXPR")
.complete(complete_calculator);
let parser = construct!(a, b, bb, c)
.to_options()
Expand Down
1 change: 1 addition & 0 deletions examples/enum_in_args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ enum Baz {
}

impl FromOsStr for Baz {
type Out = Self;
fn from_os_str(s: std::ffi::OsString) -> Result<Self, String>
where
Self: Sized,
Expand Down
2 changes: 1 addition & 1 deletion examples/enum_tuple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ fn main() {
let bar = short('b')
.long("bar")
.help("some bar command")
.argument("BAR")
.argument::<String>("BAR")
.optional();

let bar_cmd = construct!(Foo { bar })
Expand Down
2 changes: 1 addition & 1 deletion examples/env_variable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub fn main() {
let key = long("key")
.env("ACCESS_KEY")
.help("access key to use")
.argument("KEY");
.argument::<String>("KEY");

let opts = construct!(Opts { key }).to_options().run();

Expand Down
4 changes: 2 additions & 2 deletions examples/flatten.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ struct DaemonOpts {

fn main() {
let verbose = short('v').help("switch verbosity on").switch();
let user = short('u').help("daemon user").argument("USER");
let group = short('g').help("daemon group").argument("GROUP");
let user = short('u').help("daemon user").argument::<String>("USER");
let group = short('g').help("daemon group").argument::<String>("GROUP");
let daemon_opts = construct!(DaemonOpts { user, group });
let opt = construct!(Cmdline {
verbose,
Expand Down
8 changes: 5 additions & 3 deletions examples/git.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
//! Note, this "fetch" command uses fallback to inner description to get the help message, "add"
//! uses explicit override with the same value.
use std::path::PathBuf;

use bpaf::*;

#[allow(dead_code)]
Expand All @@ -17,14 +19,14 @@ enum Opt {
Add {
interactive: bool,
all: bool,
files: Vec<String>,
files: Vec<PathBuf>,
},
}

fn main() {
let dry_run = long("dry_run").switch();
let all = long("all").switch();
let repository = positional("SRC").fallback("origin".to_string());
let repository = positional::<String>("SRC").fallback("origin".to_string());
let fetch = construct!(Opt::Fetch {
dry_run,
all,
Expand All @@ -37,7 +39,7 @@ fn main() {

let interactive = short('i').switch();
let all = long("all").switch();
let files = positional("FILE").many();
let files = positional::<PathBuf>("FILE").many();
let add = construct!(Opt::Add {
interactive,
all,
Expand Down
4 changes: 2 additions & 2 deletions examples/multiple_fallback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ pub fn main() {
let field1 = long("field1")
.env("FIELD1")
.help("Field 1")
.argument("ARG")
.argument::<u32>("ARG")
.fallback(DEFAULT_CONFIG.field1);
let field2 = long("field2")
.env("FIELD2")
.help("Field 2")
.argument("ARG")
.argument::<u64>("ARG")
.fallback(DEFAULT_CONFIG.field2);

let opts = construct!(Config { field1, field2 }).to_options().run();
Expand Down
2 changes: 1 addition & 1 deletion examples/no_import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ fn main() {
bpaf::short('s')
.long("speed")
.help("Set speed")
.argument("SPEED"),
.argument::<f64>("SPEED"),
42.0,
);

Expand Down
4 changes: 2 additions & 2 deletions examples/rectangle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ fn main() {
let width = short('w')
.long("width")
.help("Width of the rectangle")
.argument("PX");
.argument::<usize>("PX");

let height = short('h')
.long("height")
.help("Height of the rectangle")
.argument("PX");
.argument::<usize>("PX");

let rect = construct!(Rect { width, height })
.group_help("Rectangle is defined by width and height in meters")
Expand Down
4 changes: 2 additions & 2 deletions examples/sensors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ struct Opts {

fn opts() -> Opts {
let sensor = long("sensor").req_flag(());
let device = long("sensor-device").argument("DEVICE");
let name = long("sensor-name").argument("NAME");
let device = long("sensor-device").argument::<String>("DEVICE");
let name = long("sensor-name").argument::<String>("NAME");

// from_str needs to be replaced with `parse` that can deal with hex digits
let bus_id = long("sensor-i2c-bus").argument::<usize>("BUS");
Expand Down
2 changes: 1 addition & 1 deletion examples/top_to_bottom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ fn output() -> impl Parser<PathBuf> {

// no magical name transmogrifications.
fn nb_cars() -> impl Parser<u32> {
short('n').long("nb-cars").argument("N")
short('n').long("nb-cars").argument::<u32>("N")
}

fn files_to_process() -> impl Parser<Vec<PathBuf>> {
Expand Down
8 changes: 5 additions & 3 deletions examples/travel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,23 @@ fn main() {
.help("speed in MPH")
.argument::<f64>("SPEED")
.map(|x| x * 1.6);
let kph = long("kph").help("Speed in KPH").argument("SPEED");
let kph = long("kph").help("Speed in KPH").argument::<f64>("SPEED");

// speed is either kph or mph, conversion to mph is handled by the parser
let speed = construct!([mph, kph]);

// parsers for distances, both are converted to the same units
let km = long("km").help("Distance in KM").argument("KMs");
let km = long("km").help("Distance in KM").argument::<f64>("KMs");
let mi = long("mi")
.help("distance in miles")
.argument::<f64>("MILES")
.map(|x| x * 1.6);
let dist = construct!([mi, km]);

// time, presumably in seconds
let time = long("time").help("Travel time in hours").argument("TIME");
let time = long("time")
.help("Travel time in hours")
.argument::<f64>("TIME");

// parsed time is trivially converted to time segment
let segment_time = time.map(Segment::Time);
Expand Down
8 changes: 6 additions & 2 deletions src/docs/argument.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ pub struct Options {
pub fn options() -> OptionParser<Options> {
let value = long("value").argument::<isize>("ARG").fallback(100);
let shorty = short('s').argument::<u64>("ARG");
// You can use FromUtf8 type tag to parse things that only implement `FromStr`, but not `FromOsStr`
// `u64` implements both and only used as an example
let shorty = short('s').argument::<FromUtf8<u64>>("ARG");
construct!(Options { value, shorty }).to_options()
}
```
Expand All @@ -29,7 +31,9 @@ pub fn options() -> OptionParser<Options> {
pub struct Options {
#[bpaf(fallback(100))]
value: isize,
#[bpaf(short)]
// You can use FromUtf8 type tag to parse things that only implement FromStr, but not FromOsStr
// `u64` implements both and only used as an example
#[bpaf(short, argument::<FromUtf8<u64>>("ARG"))]
shorty: u64,
}
```
Expand Down
Loading

0 comments on commit 94b5351

Please sign in to comment.