Value Resolution
Every parameter in clish follows a consistent resolution pipeline. This page explains how values flow from the command line through environment variables and defaults to your function.
The resolution pipeline
For any parameter that has env and/or default configured, values are resolved in this order:
CLI argument --> Environment variable --> Default value --> Error
Each step is checked in sequence. The first source that provides a value wins.
Step 1: Command-line argument
If the user passes the argument on the command line, that value is used immediately. No other steps are checked.
myapp deploy --env production # "production" is used, env var and default ignored
Step 2: Environment variable
If the CLI argument is not provided and the parameter has an env key, clish checks the environment variable:
#[command(param(env, env = "DEPLOY_ENV"))]
fn deploy(env: Named<String>) { ... }
export DEPLOY_ENV=staging
myapp deploy # "staging" is used from $DEPLOY_ENV
If the environment variable is not set, the pipeline continues to step 3.
Step 3: Default value
If neither the CLI argument nor the environment variable provided a value and the parameter has a default key, the default is used:
#[command(param(port, default = "8080"))]
fn serve(port: Named<u16>) { ... }
myapp serve # port = 8080 (default)
myapp serve --port 3000 # port = 3000 (CLI wins)
Step 4: Error
If the parameter is required (not wrapped in Option) and no value was found from any source, clish prints a missing value error:
error: missing value for option --env
|
1 | myapp deploy
| ^^^^
|
= note: --env expects a value
Optional parameters (Named<Option<T>>) skip the error step and resolve to None.
Combining env and default
You can use both env and default on the same parameter:
#[command(
param(host, env = "DEPLOY_HOST", default = "localhost"),
)]
fn deploy(host: Pos<String>) { ... }
The resolution order is still CLI > env > default > error:
myapp deploy # host = "localhost" (default)
export DEPLOY_HOST=prod.example.com
myapp deploy # host = "prod.example.com" (env wins over default)
myapp deploy myserver # host = "myserver" (CLI wins over everything)
Resolution for different parameter types
Required named options (Named<T>)
| Source available | Result |
|---|---|
| CLI argument | Uses CLI value |
| No CLI, env set | Uses env value |
| No CLI, no env, default set | Uses default |
| No CLI, no env, no default | Error: missing value |
Optional named options (Named<Option<T>>)
| Source available | Result |
|---|---|
| CLI argument | Some(CLI value) |
| No CLI, env set | Some(env value) |
| No CLI, no env, default set | Some(default) |
| No CLI, no env, no default | None |
Repeatable named options (Named<Vec<T>>)
| Source available | Result |
|---|---|
| CLI argument(s) | Vec of CLI values |
| No CLI, env set | Vec with env value |
| No CLI, no env, default set | Vec with default |
| No CLI, no env, no default | Empty Vec |
Required positionals (Pos<T>)
Positional parameters do not support env or default in the same way as named options. They are always required and must be provided on the command line. If missing, clish prints a missing argument error.
Optional positionals (Pos<Option<T>>)
If the positional is not provided, it resolves to None. Environment variables and defaults are not typically used with optional positionals.
Variadic positionals (Pos<Vec<T>>)
Collects zero or more values from the command line. Always resolves to a Vec (possibly empty).
Environment variable parsing
When an environment variable is used as a fallback, its string value is parsed the same way as a CLI argument. This means type parsing applies:
#[command(param(port, env = "SERVER_PORT"))]
fn serve(port: Named<u16>) { ... }
export SERVER_PORT=abc
myapp serve
# error: invalid value 'abc': expected u16
Practical patterns
Development defaults with production overrides
#[command(
param(host, env = "APP_HOST", default = "localhost"),
param(port, env = "APP_PORT", default = "3000"),
param(debug, env = "APP_DEBUG"),
)]
fn serve(host: Named<String>, port: Named<u16>, debug: bool) { ... }
In development, defaults apply. In production, set environment variables.
Required with env fallback
#[command(
param(api_key, env = "API_KEY"),
)]
fn fetch(api_key: Named<String>) { ... }
The user can pass --api-key directly, or set $API_KEY. If neither is provided, an error is shown.
Optional with sensible default
#[command(
param(format, default = "json", choices = ["json", "yaml", "toml"]),
)]
fn export(format: Named<String>) { ... }
Defaults to JSON, but the user can override with --format yaml.
Next step
Now that you understand how values are resolved, learn about Validation to see how clish enforces choices, conflicts, and prerequisites.