Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rc 0.6.0 #63

Merged
merged 8 commits into from
Sep 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bpaf"
version = "0.5.7"
version = "0.6.0"
edition = "2021"
categories = ["command-line-interface"]
description = "A simple Command Line Argument Parser with parser combinators"
Expand All @@ -14,7 +14,7 @@ exclude = [".github/workflows", "tarp.sh"]


[dependencies]
bpaf_derive = { path = "./bpaf_derive", version = "0.2.2", optional = true }
bpaf_derive = { path = "./bpaf_derive", version = "=0.3.0", optional = true }

[dev-dependencies]
bpaf = { path = ".", features = ["derive", "extradocs", "autocomplete"] }
Expand Down
55 changes: 34 additions & 21 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,39 @@
# Change Log
## bpaf [0.6.0] - unreleased
- removed `positional_os()` and `argument_os()`
- removed `from_str`
- internal refactors
- documentation refactor: examples with input/output
- `any` - a positional way to capture anything
- `adjacent` restriction
- `catch` inside `ParseOption`, `ParseMany` and `ParseSome`
- usage line refactor, cosmetic mostly
- include \n in --version, cosmetic

Migration guide:
1. replace `positional_os("FOO")` with `positional::<Type>("FOO")` or use `positional` in derive API
2. replace `argument_os("FOO")` with `argument::<Type>("FOO")` or use `argument` in derive API
3. replace any uses of `from_str::<T>` with turbofish on the nearest `positional` or
`argument`. You can use `map` to apply `FromStr` parser if `String` gets created inside your parsers

You shouldn't be using those names directly, but there are some structure renames:
1. `Positional` -> `ParsePositional`
2. `BuildArgument` -> ParseArgument`
3. `Command` -> `ParseCommand`

# What's new in 0.6.0
- `adjacent` restriction to parse things in a tighter context
- `catch` for `many`, `some` and `optional` to handle parse errors
- `any` positional like, to consume pretty much anything from a command line
- improved documentation with more detailed examples
- cosmetic improvements
- a sneaky reminder to use `to_options` on `Parser` before trying to run it
- a way to make boxed parsers with single item `construct!` macro for making dynamic parsers
- a horrible way to reduce Yoda-talk coding by placing primitive definitions inside the `construct!` macro

With new additions you should be able to parse pretty much anything and then some more :)

pacak marked this conversation as resolved.
Show resolved Hide resolved
# Migration guide 0.5.x -> 0.6.x
pacak marked this conversation as resolved.
Show resolved Hide resolved

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One way would be to rename OptionParser into Parser - now that Parser is a trait they can coexist, and with hidden deprecated .run() in Parser trait to serve as a reminder - it should be harder to confuse those two I think. to_options is still a thing, with OptionParser renamed it can probably be changed to done/finish/make or add_info/with_info/and_info?

I would recommend against Parser and Parser as that can be confusing even if they do operate in different namespaces.

As for to_options:

  • done, et all implies it is built but it isn't
  • add_info focuses purely on adding context but there is more going on

I would focus on the core of what it is. It is taking the lower level parsers and wrapping / building / turning them into a full application / user-facing / std::env::args parser.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You also go though OptionParser when implementing commands and subcommands, making tests for your code or parsing things from sources other than std::env::args_os. I think of it as a parser with some extra meta information attached. Interfacing with the lower level is done inside the run method only - and that's the part that needs to be pluggable in order to support windows style flags FWIW.

1. Replace any uses of `positional_os` and `argument_os` with `positional` and `argument` plus turbofish:
```diff
-let file = positional_os("FILE").help("File to use").map(PathBuf::from);
+let file = positional::<PathBuf>("FILE").help("File to use");
```
2. Replace any uses of `from_str` with either turbofish on the consumer or with `parse`, if `String` is generated inside the parser:
```diff
-let number = short('n').argument("N").from_str::<usize>();
+let number = short('n').argument::<usize>("N");
```
You can still use it for your own types if you implement `FromOsStr`, alternatively `parse` still works:
```diff
-let my = long("my-type").argument("MAGIC").from_str::<MyType>();
+let my = long("my-type").argument::<String>("MAGIC").parse(|s| MyType::from_str(s));
```
3. You shouldn't be using those names directly in your code, but there are some renames
- `Positional` -> `ParsePositional`
- `BuildArgument` -> `ParseArgument`
- `Command` -> `ParseCommand`
- `Named` -> `NamedArg`
Comment on lines +33 to +36
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why Parse prefixes for the first three but not for NamedArg?

Personally, I would do

  • PositionalArg
  • Argument
  • Command
  • NamedArg

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why Parse prefixes for the first three but not for NamedArg?

First three are actually parsers that can extract something, NamedArg needs .flag, .switch or .argument to become one. At the moment any primitive parser starts with Parse so naming is consistent.

For Command - when messing with passing arguments to cargo I found it confusing when there's several things named Command sitting in one namespace: one from bpaf and one from std::process::Command. Having distinct names helps to reduce this kind of confusion. I guess I'll poke my coworkers as well for the input.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I renamed clap::App to clap::Command back in 3.1.0 and no one has brought up concerns over naming conflicts. Granted, it was only deprecated and in clap 3.2 we made deprecations opt-in. We'll see when clap v4 comes out.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well. For that to be reported you need a bunch of factors:

  1. One needs to try to pass some stuff to a child process which might be a problem in general: Partially parse args, capturing unknown args into a vec, rather than error clap-rs/clap#1404
  2. To do this in the same module as the argument parsing - I'm lazy and it's a prototype.
  3. Be actually bothered enough to report. I probably wouldn't be.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(1) seems irrelevant. You just need to deal with Command in the same mod. This can happen because of that, because of how cargo run -- works, or just because

(2) depends on if you do use clap::Command. Personally, I don't. I only like using use for traits.


## bpaf [0.5.7] - 2022-09-04
- bugfix with zsh autocomplete #46
Expand Down
2 changes: 1 addition & 1 deletion bpaf_cauwugo/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
bpaf = { version = "0.5.5", path = "../", features = ["derive", "autocomplete"] }
bpaf = { version = "0.6.0", path = "../", features = ["derive", "autocomplete"] }
pacak marked this conversation as resolved.
Show resolved Hide resolved
cargo_metadata = "0.15.0"
once_cell = "1.13.1"

Expand Down
2 changes: 1 addition & 1 deletion bpaf_derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bpaf_derive"
version = "0.2.2"
version = "0.3.0"
edition = "2021"
categories = ["command-line-interface"]
description = "Derive macros for bpaf Command Line Argument Parser"
Expand Down
25 changes: 25 additions & 0 deletions docs/src/cargo_helper/cases.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
? Let's say the goal is to parse an argument and a switch:
> --argument 15
OK
Options { argument: 15, switch: false }

? But when used as a `cargo` subcommand, cargo will also pass the command name, this example
? uses _wrong_ subcommand name to bypass the helper and show how it would look without it
> wrong --argument 15
Stderr
No such command: `wrong`, did you mean `-s`?

? When used with the right command - helper simply consumes it
> pretty --argument 42 -s
OK
Options { argument: 42, switch: true }

? And it doesn't show up in `--help` so not to confuse users
> --help
Stdout
Usage: --argument ARG [-s]

Available options:
--argument <ARG> An argument
-s A switch
-h, --help Prints help information
19 changes: 19 additions & 0 deletions docs/src/cargo_helper/combine.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
use bpaf::*;
//
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct Options {
argument: usize,
switch: bool,
}

pub fn options() -> OptionParser<Options> {
let argument = long("argument")
.help("An argument")
.argument::<usize>("ARG");
let switch = short('s').help("A switch").switch();
let options = construct!(Options { argument, switch });

cargo_helper("pretty", options).to_options()
}
13 changes: 13 additions & 0 deletions docs/src/cargo_helper/derive.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
use bpaf::*;
//
#[allow(dead_code)]
#[derive(Debug, Clone, Bpaf)]
#[bpaf(options("pretty"))]
pub struct Options {
/// An argument
argument: usize,
/// A switch
#[bpaf(short)]
switch: bool,
}
19 changes: 11 additions & 8 deletions examples/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ struct Out {

fn main() {
// A flag, true if used in the command line. Can be required, this one is optional
let debug = short('d')
.long("debug")
.help("Activate debug mode")
.switch();

let debug = short('d') // start with a short name
.long("debug") // also add a long name
.help("Activate debug mode") // and a help message to use
.switch(); // turn this into a switch

// number of occurrences of the v/verbose flag capped at 3 with an error here but you can also
// use `max` inside `map`
Expand All @@ -35,22 +36,23 @@ fn main() {
let speed = short('s')
.long("speed")
.help("Set speed")
.argument("SPEED")
.argument::<f64>("SPEED") // you can specify a type to parse
.fallback(42.0);

let output = short('o')
.long("output")
.help("output file")
.argument::<PathBuf>("OUTPUT");
.argument("OUTPUT"); // but it's optional when rustc can derive it

// no magical name transmogrifications in combinatoric API
// no magical name transmogrifications in combinatoric API,
let nb_cars = short('n').long("nb-cars").argument("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::<PathBuf>("FILE");
.argument("FILE");
let files_to_process = file_to_proces.many();

// packing things in a struct assumes parser for each field is in scope.
Expand All @@ -63,6 +65,7 @@ fn main() {
files_to_process
})
.to_options()
.descr("This is a description")
.run();

println!("{:#?}", opt);
Expand Down
21 changes: 16 additions & 5 deletions examples/cat.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
//! You can open files as part of parsing process too, might not be the best idea though
//! because depending on a context `bpaf` might need to evaluate some parsers multiple times
//! because depending on a context `bpaf` might need to evaluate some parsers multiple times.
//!
//! Main motivation for this example is that you can open a file as part of the argument parsing
//! and give a reader directly to user. In practice to replicate `cat`'s behavior you'd accept
//! multiple files with `many` and open them one by one in your code.

use bpaf::*;
use std::{
Expand All @@ -10,19 +14,26 @@ use std::{

fn main() {
let file = positional::<OsString>("FILE")
.help("File name to concatenate, with no FILE or when FILE is -, read standard input")
.optional()
.parse::<_, Box<dyn Read>, std::io::Error>(|path| {
Ok(if path == "-" {
Box::new(stdin())
Ok(if let Some(path) = path {
if path == "-" {
Box::new(stdin())
} else {
Box::new(File::open(path)?)
}
} else {
Box::new(File::open(path)?)
Box::new(stdin())
})
})
.to_options()
.descr("Concatenate a file to standard output")
.run();

let reader = BufReader::new(file);

for line in reader.lines() {
println!("{:?}", line.unwrap());
println!("{}", line.unwrap());
}
}
32 changes: 0 additions & 32 deletions examples/custom_usage.rs

This file was deleted.

6 changes: 3 additions & 3 deletions examples/after_help.rs → examples/customize_help.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
//! How to append a prefix or suffix to the help message generated.
//! `--help` output customizations
//!
//! You can also override usage line if you don't like the generated one
//! help header, help footer, a short description and a custom usage line
use bpaf::*;

fn main() {
let opt = short('d')
.help("Release the dragon")
.switch()
.to_options()
// help metadata
.descr("I am a program and I do things")
.header("Sometimes they even work.")
.footer("Beware `-d`, dragons be here")
.usage("You can call it with following flags: {usage}")
.run();

println!("{:?}", opt);
Expand Down
32 changes: 0 additions & 32 deletions examples/simple.rs

This file was deleted.

21 changes: 0 additions & 21 deletions examples/very_basic.rs

This file was deleted.

Loading