Rust derive-based argument parsing optimized for code size

Overview

Argh

Argh is an opinionated Derive-based argument parser optimized for code size

crates.io license docs.rs Argh

Derive-based argument parsing optimized for code size and conformance to the Fuchsia commandline tools specification

The public API of this library consists primarily of the FromArgs derive and the from_env function, which can be used to produce a top-level FromArgs type from the current program's commandline arguments.

Basic Example

use argh::FromArgs;

#[derive(FromArgs)]
/// Reach new heights.
struct GoUp {
    /// whether or not to jump
    #[argh(switch, short = 'j')]
    jump: bool,

    /// how high to go
    #[argh(option)]
    height: usize,

    /// an optional nickname for the pilot
    #[argh(option)]
    pilot_nickname: Option<String>,
}

fn main() {
    let up: GoUp = argh::from_env();
}

./some_bin --help will then output the following:

Usage: cmdname [-j] --height <height> [--pilot-nickname <pilot-nickname>]

Reach new heights.

Options:
  -j, --jump        whether or not to jump
  --height          how high to go
  --pilot-nickname  an optional nickname for the pilot
  --help            display usage information

The resulting program can then be used in any of these ways:

  • ./some_bin --height 5
  • ./some_bin -j --height 5
  • ./some_bin --jump --height 5 --pilot-nickname Wes

Switches, like jump, are optional and will be set to true if provided.

Options, like height and pilot_nickname, can be either required, optional, or repeating, depending on whether they are contained in an Option or a Vec. Default values can be provided using the #[argh(default = "<your_code_here>")] attribute, and in this case an option is treated as optional.

use argh::FromArgs;

fn default_height() -> usize {
    5
}

#[derive(FromArgs)]
/// Reach new heights.
struct GoUp {
    /// an optional nickname for the pilot
    #[argh(option)]
    pilot_nickname: Option<String>,

    /// an optional height
    #[argh(option, default = "default_height()")]
    height: usize,

    /// an optional direction which is "up" by default
    #[argh(option, default = "String::from(\"only up\")")]
    direction: String,
}

fn main() {
    let up: GoUp = argh::from_env();
}

Custom option types can be deserialized so long as they implement the FromArgValue trait (automatically implemented for all FromStr types). If more customized parsing is required, you can supply a custom fn(&str) -> Result<T, String> using the from_str_fn attribute:

use argh::FromArgs;

#[derive(FromArgs)]
/// Goofy thing.
struct FiveStruct {
    /// always five
    #[argh(option, from_str_fn(always_five))]
    five: usize,
}

fn always_five(_value: &str) -> Result<usize, String> {
    Ok(5)
}

Positional arguments can be declared using #[argh(positional)]. These arguments will be parsed in order of their declaration in the structure:

use argh::FromArgs;

#[derive(FromArgs, PartialEq, Debug)]
/// A command with positional arguments.
struct WithPositional {
    #[argh(positional)]
    first: String,
}

The last positional argument may include a default, or be wrapped in Option or Vec to indicate an optional or repeating positional argument.

Subcommands are also supported. To use a subcommand, declare a separate FromArgs type for each subcommand as well as an enum that cases over each command:

use argh::FromArgs;

#[derive(FromArgs, PartialEq, Debug)]
/// Top-level command.
struct TopLevel {
    #[argh(subcommand)]
    nested: MySubCommandEnum,
}

#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand)]
enum MySubCommandEnum {
    One(SubCommandOne),
    Two(SubCommandTwo),
}

#[derive(FromArgs, PartialEq, Debug)]
/// First subcommand.
#[argh(subcommand, name = "one")]
struct SubCommandOne {
    #[argh(option)]
    /// how many x
    x: usize,
}

#[derive(FromArgs, PartialEq, Debug)]
/// Second subcommand.
#[argh(subcommand, name = "two")]
struct SubCommandTwo {
    #[argh(switch)]
    /// whether to fooey
    fooey: bool,
}

NOTE: This is not an officially supported Google product.

Comments
  • Remove letter-case restriction for attribute descriptions

    Remove letter-case restriction for attribute descriptions

    Removes the unnecessary restriction for descriptions to start with a lowercase letter.

    Descriptions should not be required to start with a lowercase letter. This is not a standard.

    Examples

    struct Config {
        #[argh(switch)]
        /// Doggos are noisy. Dog goes WOOF
        bark: bool,
    }
    
    opened by ghost 11
  • [WIP] Automatic print of a default value

    [WIP] Automatic print of a default value

    Disruptive change: default should be a string, not a code. Reason: options can already be parsed from a string, but not vice versa Pros: easy patch, easier defaults, no need to implement Display for opt Cons: invalid default value panics in runtime, no backward compatibility

    opened by fadeevab 5
  • Suggestion: `#[argh(rename =

    Suggestion: `#[argh(rename = "name")]`

    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")].

    enhancement good first issue 
    opened by Boscop 4
  • Update package docs from README (about default args)

    Update package docs from README (about default args)

    The previous commit to README doesn't update a package's documentation. It's essensially to upgrade package docs which is a main reference for developers.

    opened by fadeevab 4
  • Raw `Result` in redaction macro

    Raw `Result` in redaction macro

    https://github.com/google/argh/blob/329890897aac903864c6b3e1672d8ce32a3ef5c3/argh_derive/src/lib.rs#L437-L438

    (Introduced in https://github.com/google/argh/pull/91)

    This raw Result can cause type confusion where the user's type Result alias is used instead of std::result::Result, causing compilation failure.

    error[E0107]: this type alias takes 1 type argument but 2 type arguments were supplied
       --> main.rs:205:10
        |
    205 | #[derive(FromArgs, PartialEq, Debug)]
        |          ^^^^^^^^- help: remove this type argument
        |          |
        |          expected 1 type argument
        |
    note: type alias defined here, with 1 type parameter: `T`
       --> main.rs:23:6
        |
    23  | type Result<T> = std::result::Result<T, error::Error>;
        |      ^^^^^^ -
        = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
    

    Other places in argh use std::result::Result, and if I change that one spot to std::result::Result as well, argh and my application once again test fine.

    opened by tjkirch 3
  • Enable Fuchsia safe command line tool analytics

    Enable Fuchsia safe command line tool analytics

    This PR allows Fuchsia DX tools to collect analytics without collecting any user passed data.

    Any tool that wants to collect analytics can re-parse the args with an extra flag, "--dump_args_passed" to retrieve a string of the command options suitable for posting to analytics.

    opened by BobEvans 3
  • Fix the source of the docs.rs badge link

    Fix the source of the docs.rs badge link

    issue:

    The current docs.rs badge image link points to https://docs.rs/com/badge.svg instead of https://docs.rs/argh/badge.svg

    fix:

    Modify the readme so the above badge image url points to the correct crate ('argh' instead of 'com').

    opened by foresterre 3
  • [Question] What is a feasibility of automatic help about default options?

    [Question] What is a feasibility of automatic help about default options?

    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!

    opened by fadeevab 3
  • Why change from license to license-file?

    Why change from license to license-file?

    I saw 329890897aac903864c6b3e1672d8ce32a3ef5c3 "Use license-file to point at license" and it strikes me as a step backward, or rather at least removing the license attribute does. According to the documentation (emphasis added):

    If a package is using a nonstandard license, then the license-file field may be specified in lieu of the license field.

    But this package is not using a nonstandard license. That doesn't seem to mean that we cannot use license-file instead, but doing so means that we have to verify that LICENSE contains a standard license, and in some cases consider any variation.

    Can both license and license-file be used instead? And if not both, can the change be reverted so that we're not losing this important license information?

    opened by ajorg-aws 2
  • `--help` after `--` is consumed by argh

    `--help` after `--` is consumed by argh

    Source:

    // argh_test/src/main.rs
    use argh::FromArgs;
    
    /// Test application
    #[derive(Debug, FromArgs)]
    struct Args {
        /// command to execute
        #[argh(positional)]
        cmd: Vec<String>,
    }
    
    fn main() {
        println!("{:?}", argh::from_env::<Args>());
    }
    

    Expected behavior

    $ ./target/debug/argh_test a b c
    Args { cmd: ["a", "b", "c"] }
    
    $ ./target/debug/argh_test a b c --help
    Usage: argh_test [<cmd...>]
    
    Test application
    
    Options:
      --help            display usage information
    
    $ ./target/debug/argh_test -- a b c --help
    Args { cmd: ["a", "b", "c", "--help"] }
    

    Actual behavior

    # OK
    $ ./target/debug/argh_test a b c
    Args { cmd: ["a", "b", "c"] }
    
    # OK
    $ ./target/debug/argh_test a b c --help
    Usage: argh_test [<cmd...>]
    
    Test application
    
    Options:
      --help            display usage information
    
    # Unexpected! --help is consumed by argh
    $ ./target/debug/argh_test -- a b c --help
    Usage: argh_test [<cmd...>]
    
    Test application
    
    Options:
      --help            display usage information
    
    
    opened by gifnksm 2
  • added arg_name attribute, repeatable options are now documented

    added arg_name attribute, repeatable options are now documented

    repeated #[argh(option] arguments are now shown in the help header appropriately:

    #[argh(option)]
    /// option
    opt: Vec<String>,
    
    • old output: [--opt <opt>]
    • new output: [--opt <opt...>]

    Option names can now be overridden with the arg_name = "string" attribute:

    #[argh(option, arg_name = "opt_arg")]
    /// option
    opt: String,
    
    • produces: --opt <opt_arg>
    opened by mkatychev 2
  • Negative switches

    Negative switches

    My project has a use case for negative switch flags. E.g:

    --action // Sets args.action to true --noaction // Sets args.action to false

    Yes, we could add the two flags ourselves, but then we'd have to handle the parsing logic and possibility of conflicts manually, and we'd have to implement it for every switch or else be inconsistent in our UX.

    I would rather see a negative flag added as part of the macro for the switch type automatically, and have integrated checks to ensure the positive and negative aren't both included.

    opened by doughertyda 0
  • add an attribute on option argument to let `from_str_fn` can return `Vec<T>` or `Option<T>` as-is

    add an attribute on option argument to let `from_str_fn` can return `Vec` or `Option` as-is

    In some cases I don't expect Vec<T> to mean that the argument can be repeated, or Option<T> to mean that the argument is optional. For example an argument expects a comma-separated list of integers, which I would expect to be parsed by from_str_fn as Vec<i32>:

    #[derive(FromArgs)]
    /// some description
    struct Args {
        /// some description
        #[argh(option, from_str_fn(parse_list))]
        list: Vec<i32>,
    }
    
    fn parse_list(s: &str) -> Result<Vec<i32>, String> {
        s.split(",").map(|n| n.parse()).collect::<Result<Vec<_>, _>>().map_err(|err| format!("invaild number {}", err))
    }
    

    The compiler raised error:

    mismatched types
    expected enum `Result<i32, _>`
       found enum `Result<Vec<i32>, _>`
    

    I had to create a newtype:

    #[derive(FromArgs)]
    /// some description
    struct Args {
        /// some description
        #[argh(option, from_str_fn(parse_list))]
        list: List,
    }
    
    struct List(pub Vec<i32>);
    
    fn parse_list(s: &str) -> Result<List, String> {
        match s.split(",").map(|n| n.parse()).collect::<Result<Vec<_>, _>>() {
            Ok(list) => Ok(List(list)),
            Err(err) => Err(format!("invaild number {}", err)),
        }
    }
    

    If an attribute can be provided to make from_str_fn return Vec<T> or Option<T> as-is, the newtype will not be needed.

    opened by stackinspector 7
  • Feature Request: choices

    Feature Request: choices

    Similar to the click arg parse library from Python, it would be nice if argh had a similar feature. I am new to rust, so I can't suggest a good interface, but perhaps something like:

    #[derive(FromArgs, Debug)]
    struct Args {
        #[argh(choice, choices = vec!["foo", "bar", "baz"])]
        thing_type: String
    }
    

    but an enum might be a better option:

    #[derive(Debug)]
    enum Choice {
        Foo, Bar, Baz
    }
    
    #[derive(FromArgs, Debug)]
    struct Args {
        #[argh(choice, choices = Choice)]
        thing_type: Choice
    }
    

    Perhaps this is possible already, but I couldn't figure out how to do so given the example and readme. Happy to close this if it is already possible.

    Thanks!

    opened by bbstilson 2
  • Help trait for exposing help information as JSON

    Help trait for exposing help information as JSON

    This is a refactor that requires all structs that implement FromArgs to also derive the argh::Help trait if JSON encoded help is needed.

    Added tests, still working on testing in an application.

    opened by claywilkinson 1
  • `Complete` subcommand to list all subcommands for a given argument.

    `Complete` subcommand to list all subcommands for a given argument.

    Adding a subcommand for all commands, similar to help, which is responsible for generating all the subcommands for a given command.

    This function can be used to assist with auto-completion in the shell by providing the set of possible subcommands available at the cursor.

    opened by naudzghebre 1
Owner
Google
Google ❤️ Open Source
Google
A full featured, fast Command Line Argument Parser for Rust

clap Command Line Argument Parser for Rust It is a simple-to-use, efficient, and full-featured library for parsing command line arguments and subcomma

null 10.4k Jan 10, 2023
Docopt for Rust (command line argument parser).

THIS CRATE IS UNMAINTAINED This crate is unlikely to see significant future evolution. The primary reason to choose this crate for a new project is if

null 743 Jan 1, 2023
argmax is a library that allows Rust applications to avoid Argument list too long errors (E2BIG) by providing a std::process::Command wrapper with a

argmax argmax is a library that allows Rust applications to avoid Argument list too long errors (E2BIG) by providing a std::process::Command wrapper w

David Peter 22 Nov 20, 2022
A full featured, fast Command Line Argument Parser for Rust

clap Command Line Argument Parser for Rust It is a simple-to-use, efficient, and full-featured library for parsing command line arguments and subcomma

Ed Page 0 Jun 16, 2022
A simple, lightweight and extensible command line argument parser for rust codebases

A simple, lightweight and extensible command line argument parser for rust codebases. This crate aims to provide you with an easy-to-use and extensibl

Victor Ndaba 20 Nov 12, 2022
A minimal argument parser

Pieces An argument parser built with control in mind. Parsing The results you get are dependent on what order you parse in. If you want to say only pa

ibx34 3 Sep 30, 2021
Supporting code for the paper "Optimized Homomorphic Evaluation of Boolean Functions" submitted to Eurocrypt 2024

This repository contains the code related to the paper Optimized Homomorphic Evaluation of Boolean Functions. The folder search_algorithm contains the

CryptoExperts 3 Oct 23, 2023
Rusty Shellcode Reflective DLL Injection (sRDI) - A small reflective loader in Rust 4KB in size for generating position-independent code (PIC) in Rust.

Shellcode Reflective DLL Injection (sRDI) Shellcode reflective DLL injection (sRDI) is a process injection technique that allows us to convert a given

null 242 Jul 5, 2023
A lightweight async Web crawler in Rust, optimized for concurrent scraping while respecting `robots.txt` rules.

??️ crawly A lightweight and efficient web crawler in Rust, optimized for concurrent scraping while respecting robots.txt rules. ?? Features Concurren

CrystalSoft 5 Aug 29, 2023
This library provides a convenient derive macro for the standard library's std::error::Error trait.

derive(Error) This library provides a convenient derive macro for the standard library's std::error::Error trait. [dependencies] therror = "1.0" Compi

Sebastian Thiel 5 Oct 23, 2023
Derive forms from structs.

leptos_form: Derive leptos forms from rust structs Documentation Docs GitHub repository Cargo package Minimum supported Rust version: 1.75.0 or later

null 10 Nov 25, 2023
Interpreted, optimized, JITed and compiled implementations of the Brainfuck lang.

Interpreted, Optimized, JITed and Compiled Brainfuck implementations This repo contains a series of brainfuck implementations based on Eli Bendersky b

Rodrigo Batista de Moraes 5 Jan 6, 2023
Solutions for exact and optimized best housing chains in BDO using popjumppush and MIP.

Work in progress. About This project is an implementation of the pop_jump_push algorithm. It uses graph data from the MMORPG Black Desert Online's tow

Thell 'Bo' Fowler 3 May 2, 2023
Cloud-optimized GeoTIFF ... Parallel I/O 🦀

cog3pio Cloud-optimized GeoTIFF ... Parallel I/O Yet another attempt at creating a GeoTIFF reader, in Rust, with Python bindings. Installation Rust ca

Wei Ji 9 Mar 4, 2024
⚡🚀 Content Delivery Network written in Rustlang, optimized for speed and latency.

Supported Formats HTML Javscript Css Image PNG JPG JPEG GIF SVG Video MP4 WEBM FLV Audio OGG ACC MP3 Archives ZIP RAR Feeds & Data JSON YAML XML Docum

Noname 3 Apr 9, 2024
A small Rust library that let's you get position and size of the active window on Windows and MacOS

active-win-pos-rs A small Rust library that let's you get position and size of the active window on Windows and MacOS Build % git clone https://github

Dmitry Malkov 21 Jan 6, 2023
Not the fastest terminal colors library. Don't even ask about size.

TROLOLORS Not the fastest terminal colors library. Don't even ask about size. Why? Don't even try to use it. But maybe you need to say to your boss th

Dmitriy Kovalenko 15 Oct 27, 2021
httm prints the size, date and corresponding locations of available unique versions of files residing on ZFS snapshots

httm prints the size, date and corresponding locations of available unique versions of files residing on ZFS snapshots, as well as allowing their interactive viewing and restoration.

null 837 Dec 30, 2022
Tight Model format is a lossy 3D model format focused on reducing file size as much as posible without decreasing visual quality of the viewed model or read speeds.

What is Tight Model Format The main goal of the tmf project is to provide a way to save 3D game assets compressed in such a way, that there are no not

null 59 Mar 6, 2023