google / argh Goto Github PK
View Code? Open in Web Editor NEWRust derive-based argument parsing optimized for code size
License: BSD 3-Clause "New" or "Revised" License
Rust derive-based argument parsing optimized for code size
License: BSD 3-Clause "New" or "Revised" License
I tried:
#[derive(argh::FromArgs)]
#[argh(description = "...")]
struct Args {
#[argh(positional)]
n: usize,
}
When I ran cargo run invalid-argument
, it printed:
Error parsing positional argument 'n' with value 'invalid-argument: invalid digit found in string
A single quote was missing. It should be Error parsing positional argument 'n' with value 'invalid-argument': invalid digit found in string
, just like error messages for non-positional arguments:
Line 429 in 8d9a82f
Line 448 in 8d9a82f
arg
.Is it possible to have arbitrarily nested subcommands? I have an example where it doesn't seem to be working, but maybe I'm holding it wrong :)
The example is like:
#[derive(Debug, FromArgs)]
pub struct Args {
#[argh(subcommand)]
command: Command,
}
#[derive(Debug, FromArgs)]
#[argh(subcommand)]
pub enum Command {
Foo(Foo),
Bar(Bar),
}
#[derive(Debug, FromArgs)]
#[argh(subcommand, name = "foo")]
pub enum Foo {
Baz(Baz),
Quux(Quux),
}
#[derive(Debug, FromArgs)]
#[argh(subcommand, name = "baz")]
pub struct Baz {
/// the baz
#[argh(option)]
baz: String,
}
#[derive(Debug, FromArgs)]
#[argh(subcommand, name = "quux")]
pub struct Quux {
/// the quux
#[argh(option)]
quux: String,
}
#[derive(Debug, FromArgs)]
#[argh(subcommand, name = "bar")]
pub enum Bar {
Who(Who),
What(What),
}
pub struct Who {
/// the Who
#[argh(option]
who: String,
}
#[derive(Debug, FromArgs)]
#[argh(subcommand, name = "what")]
pub struct What {
/// the What
#[argh(option)]
what: String,
}
The error I get is along the lines:
error: Unused `argh` attribute on `#![derive(FromArgs)]` enum. Such `enum`s can only be used to dispatch to subcommands, and should only contain the #[argh(subcommand)] attribute.
--> copy/src/bin/main.rs:43:27
|
43 | #[argh(subcommand, name = "foo")]
| ^^^^^^^^^
error: Unused `argh` attribute on `#![derive(FromArgs)]` enum. Such `enum`s can only be used to dispatch to subcommands, and should only contain the #[argh(subcommand)] attribute.
--> copy/src/bin/main.rs:69:27
|
69 | #[argh(subcommand, name = "bar")]
| ^^^^^^^^
error[E0277]: the trait bound `Bar: SubCommand` is not satisfied
--> copy/src/bin/main.rs:34:17
|
34 | #[derive(Debug, FromArgs)]
| ^^^^^^^^ the trait `SubCommand` is not implemented for `Bar`
|
= note: required by `COMMAND`
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: the trait bound `Foo: SubCommand` is not satisfied
--> copy/src/bin/main.rs:34:17
|
34 | #[derive(Debug, FromArgs)]
| ^^^^^^^^ the trait `SubCommand` is not implemented for `Foo`
|
= note: required by `COMMAND`
= note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
Thanks for the help :)
Can it be used for implementing programs like xargs
or nice
where it is important to pass around command line unmodified, even if it contains invalid UTF-8?
A convenient feature of clap is the support for generating shell completion scripts from build.rs (i.e. during build of the application, thus without overhead on the application).
I don't know if there's already something available and I didn't find it or if it's still something which should be added.
If an addition is needed, would you welcome some PR ?
Hello,
I was working on a crate and found inconsistent newlines when argh prints help for a binary when using different forms of doc comments.
When fields within a FromArgs
struct are documented using ///
style doc comments, and consecutive lines also begin with ///
every newline and break in the doc comment is ignored when the --help
string is printed. If instead the fields are documented using /** */
style doc block comments, newlines within that block are preserved into the output of --help
.
I have provided some sample code which manages to reproduce it (at least on my end) and should provide help with triaging this.
OS | Cargo Version | Rustup default |
---|---|---|
Windows 10 | cargo 1.43.0-nightly (bda50510d 2020-03-02) | nightly-x86_64-pc-windows-msvc (default) |
#[derive(Debug, argh::FromArgs)]
/// Demonstrating the disparity between /** */ doc comments
/// and /// doc comments
struct FailToDocument {
#[argh(switch)]
/// this doc comment will print itself correctly
/// when wrapped using multiple newlines + ///
/// at the begining
working_example: bool,
#[argh(switch)]
/** this doc comment will fail to print itself correctly
when wrapped using multiple newlines + /** */ at the
begining */
failing_example: bool,
}
fn main() {
dbg!(argh::from_env::<FailToDocument>());
}
Shell | Fish Shell on Windows Subsystem for Linux |
---|
/mnt/z/dev/stf-rs$ cargo.exe run -- --help
Finished dev [unoptimized + debuginfo] target(s) in 0.02s
Running `target\debug\stf.exe --help`
Usage: target\debug\stf.exe [--working-example] [--failing-example]
Demonstrating the disparity between /** */ doc comments and /// doc comments
Options:
--working-example this doc comment will print itself correctly when wrapped
using multiple newlines + /// at the begining
--failing-example this doc comment will fail to print itself correctly
when
wrapped using multiple newlines + /** */ at the
begining
--help display usage information
Shell | Powershell |
---|
PS Z:\dev\stf-rs> cargo run -- --help
Finished dev [unoptimized + debuginfo] target(s) in 0.02s
Running `target\debug\stf.exe --help`
Usage: target\debug\stf.exe [--working-example] [--failing-example]
Demonstrating the disparity between /** */ doc comments and /// doc comments
Options:
--working-example this doc comment will print itself correctly when wrapped
using multiple newlines + /// at the begining
--failing-example this doc comment will fail to print itself correctly
when
wrapped using multiple newlines + /** */ at the
begining
--help display usage information
Currently, passing -h
doesn't work:
Unrecognized argument: -h
Passing -h
should work the same as --help
, unless one of the options/switches uses short = 'h'
.
And in the Usage output, it would then show
-h, --help display usage information
instead of just
--help display usage information
Please show default values of options in --help
output :)
(E.g. like clap)
Is it possible to make a default subcommand with argh
? I want something like this:
#[derive(FromArgs, PartialEq, Debug)]
/// Top-level command.
struct TopLevel {
#[argh(subcommand)]
nested: Sub,
}
#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand)]
enum Sub {
#[argh(default)] // <-----
One(SubCommandOne),
Two(SubCommandTwo),
}
/* one and two subcommands*/
With
cmd one ...
resulting in Sub::One(..)
cmd two ...
resulting in Sub::Two(..)
cmd ...
resulting in Sub::One(..)
(the default)#[derive(FromArgs, Debug)]
/// tool
pub struct Config {
#[argh(option)]
/// run forever. Exit with Ctrl-C.
pub r#loop: bool,
}
$ tool --loop
Required options not provided:
--r-loop
Obviously, here the flag should be loop
not r-loop
:)
There might be a reason why default
requires an expression-as-a-string, but I can't think of one. The reference has examples of macros doing lots of funky things inside attributes.
It would be nice if someone could write, for example,
#[derive(FromArgs)]
struct GoUp {
#[argh(option, default = default_height())]
height: usize,
#[argh(option, default = String::from("only up"))]
direction: String,
}
The second example in particular is much nicer than what we have to write today,
#[argh(option, default = "String::from(\"only up\")")]
direction: String,
We could even wrap the expression with Into::into()
in the expansion so it gets even shorter..
#[argh(option, default = "only up")]
direction: String,
The only problem is that this is definitely a breaking change.
One possible soft migration is to add a new attribute default_expr
which has these semantics.
Is there analogue of #[structopt(flatten)]
in Argh?
For example, in GoUp
README example, how do I extract jump
and height
into a separate struct without chaning CLI?
When the help shows the usage, it shows the full path of a command. Instead, show the basename of the command (just the actually command's name).
structopt supports "flattening", which allows one struct to be embedded in another "transparently". This is useful as documented in structopt, but also useful when two commands have the same arguments and the author does not wish to duplicate the code.
I tried this:
#[derive(Clone, argh::FromArgs)]
#[argh(description = "...")]
struct Options {
#[argh(option, description = "...")]
bind_to: SocketAddr,
#[argh(positional)]
dir: PathBuf,
}
When I ran the program with no argument, it printed:
Required positional arguments not provided:
dirRequired options not provided:
--bind-to
The second line of the output was unreadable. I expected the output to be:
Required positional arguments not provided:
dir
Required options not provided:
--bind-to
It seems that
Lines 536 to 538 in 507087f
Lines 527 to 529 in 507087f
An example directory of common usage patterns
I have a command that ends up running another program with some CLI arguments. I have a struct like:
#[derive(Debug, FromArgs)]
struct Args {
#[argh(switch, short = 'v')]
verbose: bool,
#[argh(positional)]
program: String,
#[argh(positional)]
args: Vec<String>,
}
argh
allows options and switches to be parsed even after position arguments are found, which leads the following behavior:
-v program arg1 arg2 arg3
is parsed as {verbose: true, program: "program", args: ["arg1", "arg2", "arg3"]}
program --option-for-the-program
doesn't parse, because --option-for-the-program
isn't a valid argument for Args
.
I would expect it to parse as {verbose: false, program: "program", args: ["--option-for-the-program"]}
.
program arg1 arg2 -v arg3
bizarrely parses as {verbose: true, program: "Program", args: ["arg1", "arg2", "arg3"]}
.
I would expect it to parse args
as ["arg1", "arg2", "-v", "arg3"]
.
Similar to above, program arg1 arg2 -p arg3
fails to parse because -p
is treated as a switch for the top level command.
A fix for all of this would be to stop parsing options/switches once we hit a positional argument, and definitely don't allow options/switches to come in the middle of a Vec
positional argument.
The issue with this fix is that some people might want to run their commands like tar my_dir/ -f output.tar
instead of tar -f output.tar my_dir/
-- this would break that.
One could definitely argue that my use case is weird and not particularly suited for argh
. However, consider a more reasonable (albeit slightly contrived) example:
#[derive(Debug, FromArgs)]
/// Sums some numbers.
struct Sum {
/// the numbers
#[argh(positional)]
numbers: Vec<i64>,
}
This returns the correct results for sum 1 2 3 4 5
, but breaks if you give it sum 4 7 -8 3 5
(erroring with "Unrecognized argument: -8"), because of the same issue.
I have been using argh to develop a new cargo command. Cargo passes the actual command being executed as the first command line argument. In order to capture (and ignore) that argument, I have an extra positional argument in the structure, followed by the real positional arguments. However, this first argument shows up in the command line help and is quite confusing as users should not actually enter it. It would be good to have a flag that omits a positional argument (and possibly others) from the generated help text.
#60 marks all options following --
as positional arguments, but there's no way to tell the difference between arguments before and after the dashes.
My use case is that I have a cargo wrapper, and I want to pass all arguments after --
verbatim to cargo, but parse arguments before --
differently.
Please automatically add --version
to print program version: env!("CARGO_PKG_VERSION")
.
Also I think it'd make sense if --help
prints the version as well.
#[derive(FromArgs, PartialEq, Debug)]
/// Top-level command.
struct TopLevel {
#[argh(subcommand)]
nested: MySubCommandEnum,
/// number of connections
#[argh(option, short = 'c', default = "1")]
connections: usize,
}
#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand)]
enum MySubCommandEnum {
Pub(PublishConfig),
Sub(SubConfig),
}
Here in we have 2 sub commands for a base command.
in main.rs
let base = argh::from_env()
After that I want to filter out he nested command and use one appropriately.
let sub_command = tt.nested;
println!("{:?}", sub_command);
match sub_command {
MySubCommandEnum::Pub => {
println!("We have to do Publish");
},
MySubCommandEnum::Sub => {
println!("We have to subs");
},
}
When I try to do something like this, I get following error expected unit struct, unit variant or constant, found tuple variant
MySubCommandEnum::Pubdid you mean
MySubCommandEnum::Pub`
What am I missing? Thanks in advance.
Hi!
I was thinking about automatic print of the defaults to the help message, like kingpin
and clap
do ("-f, --file file [default: ~/path/to]"). After messing around argh
code I realized that it would be not really trivial to make a PR... or trivial? What is your ideology about printing defaults to help?
Of course, the easy way is just to manually add them to the description, still...
Thank you!
default
may only be specified on#[argh(option)]
or#[argh(subcommand)]
fields"
But the code checks for positional
or option
. Error message should be fixed to include #[argh(positional)]
. Preferrably default subcommands should be implemented as well (with effect similar to optional subcommands).
Currently EarlyExit
output is written to stdout, but stderr is more appropriate for diagnostics and any user-directed messages that should never end up in a pipeline.
Two dashes ('--') will indicate the end of argument options. All subsequent values are given to the tool as-is. For example, with "Usage: foo [-a] ", the command line "foo -- -a" may interpret -a as a file name rather than a switch. Further, "foo -a -- -a" enables the switch -a (the first -a, before the --) and passes the literal text -a (the second -a).
This option is supported in various tools. Would be useful to support here too.
Hi,
is there a way to express a flag that might have a value?
This doesn't work:
#[derive(FromArgs)]
struct Opt {
#[argh(option, short='f')]
/// my flag
flag: Option<Option<u32>>
}
Most command line tools send --help
and --version
output to stdout, but send parsing failures to stderr. argh
now sends both of these 'early exits' to stdout.
It should be possible to use an enum
as the possible value of an option, without wrapping it in an Option
when and if the enum
implements Default
.
Current example :
#[derive(argh::FromArgs)]
struct Args {
/// output format
#[argh(option)]
format: Option<OutputFormat>,
}
let args: Args = argh::from_env();
let format = args.format.unwrap_or_default();
Using the Default trait :
#[derive(argh::FromArgs)]
struct Args {
/// output format
#[argh(option)]
format: OutputFormat, // enum should implement Default
}
This will need to evolve, this proposal is just a starting point based on history and practical concerns for invested stakeholders.
We should identify some key stakeholders to take part in decision making with regard to the above, and at a later time establish how that group membership evolves over time. The first implementation of the group will be discretionary.
Practical matters:
I would really like to be able to do the following:
prog <general_args..> cmd1 <cmd1_args..> cmd2 <cmd2_args..>
Because my program needs to have a CLI to run commands seq sequentially, without duplicating all the fields in each subcommand: Currently the cli for that looks like:
prog <general_args..> cmd1 <cmd1_args..> --cmd2 <cmd2_args..>
Which makes everything really awkward, especially because I have to duplicate the fields and support chaining and calling of cmd2
directly. And now I want to be able to chain arbitrary number of commands, this gets very hairy!
So please, can we allow multiple subcommands? :)
Is there a way to display the help message when an unknown flag is passed?
I love argh
for its simplicity, compile performance and leanness, and I find myself migrating more and more projects to it.
Just now it became evident that despite supporting PathBuf
in struct fields, it will always assume valid UTF-8 in the arguments it parses. This might be an assumption that holds in Fuchsia, but may make it impossible to use for general purpose linux tools that consume paths in arguments.
I wonder if there is any chance to allow parsing PathBuf
fields without making assumptions about their encoding?
Thank you
PS: This question was triggered by this post on reddit/r/rust.
use argh::FromArgs;
use std::path::PathBuf;
#[derive(FromArgs, Debug)]
#[argh(description = "Prints xrefs to specified string address")]
struct Arguments {
/// path to target binary file
#[argh(positional)]
filename: PathBuf,
/// todo
#[argh(option, from_str_fn(always_five))]
offset: usize,
/// todo
#[argh(option, from_str_fn(always_five))]
length: usize,
}
fn always_five(_value: &str) -> Result<usize, String> {
Ok(5)
}
fn main() {
let p: Arguments = argh::from_env();
dbg!(p);
}
$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.03s
Running `target/debug/xstrings`
Required positional arguments not provided:
filenameRequired options not provided:
--offset
--length
Problem: Often, fields that are option members have long (descriptive) names that make sense in the source code, but are too long to type for cli options.
The attribute short = 'x'
can only take single chars, so that can't be used to shorten them.
Solution: Adding a way to rename cli option members using #[argh(rename = "name")]
.
Similar to #[serde(rename = "name")]
.
It is recommended not to track Cargo.lock
in Rust libraries, and to track it for binaries only. argh is a library. Shouldn't we add it to .gitignore
?
Is there a way to specify that a positional arg need at least one value?
#[derive(FromArgs)]
/// MyCMD
struct Opt {
#[argh(positional)]
/// script files to run
test_file: Vec<PathBuf>,
}
gives me:
Usage: mycmd.exe [<test_file...>]
Which suggests the tool can work without any input when it needs at least one.
In some cases (i.e. if an argument is the correct type but maybe isn't exactly desirable), it'd be nice to short-circuit the app and manually print the usage. I didn't see a way to do this in the docs.
I'm using 0.1.3
.
/// ...
#[derive(argh::FromArgs)]
struct Opts {
#[argh(positional)]
dbpath: PathBuf,
#[argh(subcommand)]
cmd: Cmd,
}
/// Outout entire content of the database to text file, stdout (suitable for migrations)
#[derive(argh::FromArgs)]
#[argh(subcommand, name = "export")]
struct Export {}
/// Import entire content of the database from text file, stdin (suitable for migrations)
#[derive(argh::FromArgs)]
#[argh(subcommand, name = "import")]
struct Import {}
#[derive(argh::FromArgs)]
#[argh(subcommand)]
enum Cmd {
Export(Export),
Import(Import),
}
Required positional arguments not provided:
dbpathOne of the following subcommands must be present:
help
export
import
If Argh is "optimized for code size", then it only makes sense to show a comparison with other existing parsers, similar to the chart in the README for pico-args.
(Though, ideally, also with a quick run-down of how the features and APIs compare.)
The pico-args chart covers bare Clap, Clap+structopt, pico-args, and gumdrop, though argparse also shows up reasonably high in search results.
Currently the usage prints positional args in snake_case, e.g.:
Usage: D:\foo.exe [<project_file>] [-t <time-offset>] [<command>] [<args>]
It should also print positional args in kebab-case for consistency (project-file
in this case).
I'm having a weird issue with the generated help message: the command is with it's absolute path when I run ./target/debug/mycmd --help
What am I doing wrong?
./target/debug/mycmd --help
Usage: ./target/debug/mycmd [<test_file...>] -a <address> [-p <port>]
MyCMD
Options:
-a, --address address
-p, --port port
--help display usage information
#[derive(FromArgs)]
/// MyCMD
struct Opt {
#[argh(option, short = 'a')]
/// address
address: String,
#[argh(option, short = 'p')]
/// port
port: Option<u32>,
#[argh(positional)]
/// script files to run
test_file: Vec<PathBuf>,
}
It's not obvious from the readme that from_env() does not take input from environment variables. This may be confusing for first time readers of this documentation. Note that the 'env' term has popular usage to mean environment variables in the 'env', env_logger, and dotenv crates.
I think the documentation is great, so this isn't intended as critical, but rather an attempt to highlight the challenge for newcomers to rust, who may struggle to understand the meaning of various documentation.
There are some changes to the library (as well as increasing usage!) and I'd like to prevent size regressions. We should check binary output on a slew of targets/configs on every commit
Running `/work/pump --help`
Usage: /work/pump <topic> [<fields...>] [-w]
pump
Options:
-w, --wait-for-reply
wait for reply
--help display usage information
/// pump
#[derive(Debug, FromArgs)]
struct Args {
/// topic of message to send
#[argh(positional)]
topic: String,
...
}
Shouldn't positional arguments like topic and fields also have their description displayed in a section such as Options
? If not, how would the user know the meaning of topic
and fields
? Thanks
It should be an error to have multiple options/switches with same short name.
Currently it compiles without even a warning! E.g. if you have two options using the same short letter, or an option and a switch etc.
While initially written for Fuchsia. this is a generally useful library. References could be confusing
The spec at https://fuchsia.dev/fuchsia-src/concepts/api/cli doesn't specifically say that --arg=value
is disallowed, and yet argh disallows it.
While this may well be intended behavior, consider that it is a very confusing behavior for flags nowadays, where quite a few flag parsers assume --arg value
and --arg=value
can both work. The latter works better in contexts where whitespaces can add
confusion.
Currently, you can specify 'zero or more' options with #[argh(positional)] opt: Vec<_>
, but there is no way to say 'one or more options'.
Is it possible to allow switch chaining like -vv
for verbosity?
With this
/// verbosity level
#[argh(switch, short = 'v')]
verbosity: u8,
it only allows -v -v
, not -v 2
or -vv
.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.