pico-args
An extremely lightweight and simple argument parser. Features minimal functionality and fast processing.
Framework
pico-args
Overview
pico-args is an extremely lightweight and simple argument parser. It features minimal functionality and fast processing, implemented with zero dependencies. It's suitable for simple CLI tools or embedded systems, chosen when lightweight nature is prioritized.
Details
pico-args is a minimal command-line argument parser developed by RazrFalcon. As the name "pico" suggests, it's designed for ultra-small size and high-speed processing, with no dependencies other than the standard library. By focusing only on basic argument parsing without providing complex features, it achieves lightweight nature and high performance.
Key Features
- Ultra-lightweight: Very small footprint and minimal dependencies
- Fast Processing: High-speed argument parsing with minimal overhead
- Simple API: Intuitive and easy-to-understand API design
- Zero Dependencies: No dependencies other than the standard library
- Type Safety: Safe argument processing leveraging Rust's type system
- Custom Parsers: Enables implementation of custom value conversion functions
- Error Handling: Proper error handling with clear error types
Pros and Cons
Pros
- Minimal Footprint: Very lightweight with minimal resource usage
- High Speed: Extremely low overhead for argument parsing
- Simple: Low learning cost, can start using immediately
- No Dependencies: Independence without relying on external libraries
- Embedded-friendly: Suitable for resource-constrained environments
Cons
- Limited Features: Advanced CLI features (help generation, subcommands) not provided
- Manual Implementation: Help messages and complex structures must be manually implemented
- Not for Large Scale: Unsuitable for complex CLI applications
- Ecosystem: Limited integration features with other CLI libraries
Key Links
Example Usage
use pico_args;
use std::path::PathBuf;
#[derive(Debug)]
struct Config {
// Required numeric parameter
count: u32,
// Optional string parameter
name: Option<String>,
// Parameter with default value
timeout: u64,
// Input file path
input: PathBuf,
// Output file path (optional)
output: Option<PathBuf>,
// Flag
verbose: bool,
}
fn main() {
let config = match parse_args() {
Ok(config) => config,
Err(e) => {
eprintln!("Argument parsing error: {}", e);
std::process::exit(1);
}
};
println!("Config: {:#?}", config);
if config.verbose {
println!("Verbose mode enabled");
}
println!("Input file: {:?}", config.input);
if let Some(output) = &config.output {
println!("Output file: {:?}", output);
}
}
fn parse_args() -> Result<Config, pico_args::Error> {
let mut pargs = pico_args::Arguments::from_env();
// Check for help flag
if pargs.contains(["-h", "--help"]) {
print_help();
std::process::exit(0);
}
// Parse each argument
let config = Config {
// Required parameter (using custom parser)
count: pargs.value_from_fn("--count", |s| {
s.parse::<u32>().map_err(|_| "number required")
})?,
// Optional parameter
name: pargs.opt_value_from_str("--name")?,
// Parameter with default value
timeout: pargs.opt_value_from_str("--timeout")?.unwrap_or(30),
// Flag (whether it exists)
verbose: pargs.contains(["-v", "--verbose"]),
// Required positional argument (input file)
input: pargs.free_from_str()?,
// Optional positional argument (output file)
output: pargs.opt_free_from_str()?,
};
// Check for unused arguments
let remaining = pargs.finish();
if !remaining.is_empty() {
eprintln!("Warning: unrecognized arguments: {:?}", remaining);
}
Ok(config)
}
fn print_help() {
const HELP: &str = "\
Usage: myapp [OPTIONS] <input-file> [output-file]
Options:
--count COUNT Number of processes (required)
--name NAME Process name (optional)
--timeout SECONDS Timeout in seconds (default: 30)
-v, --verbose Enable verbose output
-h, --help Show this help
Arguments:
<input-file> Input file to process
[output-file] Output file destination (optional)
Examples:
myapp --count 10 --verbose input.txt output.txt
myapp --count 5 --name \"test\" input.txt
";
print!("{}", HELP);
}
// Advanced usage example: custom parsers and complex types
fn advanced_example() -> Result<(), pico_args::Error> {
let mut pargs = pico_args::Arguments::from_env();
// Parse size with custom parser (e.g., "10MB", "1GB")
let size: u64 = pargs.value_from_fn("--size", |s| {
parse_size(s).ok_or("invalid size format")
})?;
// Get multiple values (comma-separated)
let tags: Vec<String> = pargs.opt_value_from_fn("--tags", |s| {
Ok(s.split(',').map(|s| s.trim().to_string()).collect())
})?.unwrap_or_default();
println!("Size: {} bytes", size);
println!("Tags: {:?}", tags);
Ok(())
}
fn parse_size(s: &str) -> Option<u64> {
let s = s.to_lowercase();
if let Some(s) = s.strip_suffix("gb") {
s.parse::<u64>().ok().map(|n| n * 1_000_000_000)
} else if let Some(s) = s.strip_suffix("mb") {
s.parse::<u64>().ok().map(|n| n * 1_000_000)
} else if let Some(s) = s.strip_suffix("kb") {
s.parse::<u64>().ok().map(|n| n * 1_000)
} else {
s.parse().ok()
}
}