A fork of google/argh with environment variable fallback for options.
This fork exists because upstream argh only reads configuration from the command line. That works well for local CLI tools, but in Kubernetes you usually configure workloads through environment variables—env, envFrom, ConfigMaps, and Secrets in the pod spec—not long argument lists in the container command. With env fallback, the same FromArgs struct can serve both interactive use (flags on the CLI) and cluster deployment (values injected as env vars), without a separate config layer.
This crate is API-compatible with argh. By default the Rust import is argh_env, but you can keep using argh via a Cargo dependency alias (see Migrating from argh).
Options can read from the environment when not passed on the command line:
#[argh(option, env = "MY_VAR")]
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.
You can depend on argh-env directly, or keep the argh dependency name in Cargo.toml:
# Cargo.toml — keep the dependency key as `argh`
argh = { package = "argh-env", version = "0.1" }With that alias, existing use argh::FromArgs imports continue to work unchanged.
Alternatively, depend on the crate by its published name:
# Cargo.toml
argh-env = "0.1.0" # was: argh = "0.1.20"use argh_env::FromArgs; // was: use argh::FromArgs
// #[argh(...)] attributes unchangeduse argh_env::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_env::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, 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.
You can specify an environment variable fallback using the env attribute.
If the argument is not provided via the command line, argh_env will look for the
environment variable. Command-line values always take precedence over environment
variables.
This is especially useful in Kubernetes: set options via the pod manifest and keep
CLI flags for local development and debugging. For example, a Deployment can set
DATABASE_URL while you still pass --database-url when running the binary on your
machine.
use argh_env::FromArgs;
#[derive(FromArgs)]
/// Reach new heights.
struct GoUp {
/// an optional nickname for the pilot
#[argh(option, env = "GO_UP_PILOT")]
pilot_nickname: Option<String>,
/// an optional height
#[argh(option, env = "GO_UP_HEIGHT")]
height: usize,
}
fn main() {
let up: GoUp = argh_env::from_env();
}use argh_env::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_env::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_env::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_env::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_env::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", short = 't')]
struct SubCommandTwo {
#[argh(switch)]
/// whether to fooey
fooey: bool,
}You can define a complex help output that includes an Examples section.
Use a {command_name} placeholder.
#[derive(FromArgs, Debug)]
#[argh(
description = "{command_name} is a tool to reach new heights.\n\n\
Start exploring new heights:\n\n\
\u{00A0} {command_name} --height 5 jump\n\
",
example = "\
{command_name} --height 5\n\
{command_name} --height 5 j\n\
{command_name} --height 5 --pilot-nickname Wes jump"
)]
pub struct CliArgs {
/// how high to go
#[argh(option)]
height: usize,
/// an optional nickname for the pilot
#[argh(option)]
pilot_nickname: Option<String>,
/// command to execute
#[argh(subcommand)]
pub command: Command,
}Output:
Usage: goup --height <height> [--pilot-nickname <pilot-nickname>] <command> [<args>]
goup is a tool to reach new heights.
Start exploring new heights:
goup --height 5 jump
Options:
--height how high to go
--pilot-nickname an optional nickname for the pilot
--help, help display usage information
Commands:
jump j whether or not to jump
Examples:
goup --height 5
goup --height 5 j
goup --height 5 --pilot-nickname Wes jump
The argh_env::FromArgs derive macro can be debugged with the cargo-expand crate.
See argh/examples/simple_example.rs for the example struct we wish to expand.
First, install cargo-expand by running cargo install cargo-expand. Note this requires the nightly build of Rust.
Once installed, run cargo expand --package argh-env and you can see the expanded code.
This is an independent fork of google/argh, maintained separately from Google. The original project is not an officially supported Google product. This fork is licensed under the same BSD-3-Clause license as upstream.