Sap
🌿 A small, simple and sweet argument parser for Rust
Install / Use
/learn @tailwags/SapREADME
🌿 Sap
A small, simple and sweet argument parser for Rust
Sap is a minimal, zero-dependency Unix command-line argument parser for Rust. It exposes an iterator-based API that handles GNU-style options and gives you full control over how each argument is consumed.
Features
- GNU-style option parsing: short (
-a), long (--verbose), and combined options (-abc) - Value handling: options with values via
--name=valueor as a separate following argument - POSIX compliance:
--separator and-(stdin) are handled correctly - Zero dependencies: no external crates
- Iterator-based: works with any iterator yielding
ArgLikeitems (&str,String,OsStr, etc.), so you can parse args from the environment, aVec, or a test fixture without conversion - Error handling: errors carry the offending argument and the parser transitions to a defined poisoned state
Quick Start
Add Sap to your Cargo.toml:
[dependencies]
sap = "0.2.0"
Usage
Basic Example
use sap::{Parser, Argument};
// Parse from command line arguments
let mut parser = Parser::from_env().unwrap();
while let Some(arg) = parser.forward().unwrap() {
match arg {
Argument::Short('v') => println!("Verbose mode enabled"),
Argument::Long("help") => println!("Help requested"),
Argument::Long("file") => {
if let Some(filename) = parser.value()? {
println!("Processing file: {}", filename);
}
}
Argument::Value(val) => println!("Positional argument: {}", val),
Argument::Stdio => println!("Reading from stdin"),
}
}
Parsing Custom Arguments
use sap::{Parser, Argument};
// Parse from any iterator of string-like values
let mut parser = Parser::from_arbitrary(["myprogram", "-v", "--file=input.txt"]).unwrap();
while let Some(arg) = parser.forward().unwrap() {
match arg {
Argument::Short('v') => println!("Verbose mode enabled"),
Argument::Long("file") => {
if let Some(filename) = parser.value()? {
println!("Processing file: {}", filename);
}
}
Argument::Value(val) => println!("Positional argument: {}", val),
_ => {}
}
}
Argument Types
Sap recognizes four types of arguments:
Argument::Short(char)- Short options like-v,-x, and combined ones like-abcArgument::Long(&str)- Long options like--verbose,--file, including values like--file=foo.txtArgument::Value(Cow<str>)- Positional arguments and operandsArgument::Stdio- The special-argument (stdin/stdout)
Complete Example
Here's an example showing a typical CLI application:
use sap::{Parser, Argument, Result};
fn main() -> Result<()> {
let mut parser = Parser::from_env()?;
let mut verbose = false;
let mut output_file = None;
let mut input_files = Vec::new();
while let Some(arg) = parser.forward()? {
match arg {
Argument::Short('v') | Argument::Long("verbose") => {
verbose = true;
}
Argument::Short('h') | Argument::Long("help") => {
print_help(parser.name());
return Ok(());
}
Argument::Short('o') | Argument::Long("output") => {
output_file = parser.value()?;
if output_file.is_none() {
eprintln!("Error: --output requires a value");
std::process::exit(1);
}
}
Argument::Value(file) => {
input_files.push(file.into_owned());
}
Argument::Stdio => {
input_files.push("-".to_string());
}
unknown => {
eprintln!("Error: {}", unknown.unexpected());
std::process::exit(1);
}
}
}
if verbose {
println!("Verbose mode enabled");
if let Some(ref output) = output_file {
println!("Output file: {}", output);
}
println!("Input files: {:?}", input_files);
}
Ok(())
}
fn print_help(program_name: &str) {
println!("Usage: {} [OPTIONS] [FILES...]", program_name);
println!("Options:");
println!(" -v, --verbose Enable verbose output");
println!(" -o, --output Specify output file");
println!(" -h, --help Show this help message");
}
Real-World Examples
puppyutils is a collection of Unix utilities written in Rust that uses sap as its argument parser. Sap was originally built for that project, so the source is a good reference for real usage.
treesap: derive-macro interface
treesap is a companion crate that lets you
declare your CLI arguments as a plain Rust struct instead of writing a manual
parsing loop. It is built on top of sap and exposes a #[derive(Parser)]
macro that generates a parse() method for you.
treesap is a work in progress. Not all field types and attributes are implemented yet. See the treesap README for the current status.
[dependencies]
sap = "0.2.0"
treesap = "0.1.0"
use treesap::Parser;
#[derive(Debug, Parser)]
struct Args {
verbose: bool,
dry_run: bool,
}
fn main() -> sap::Result<()> {
let args = Args::parse()?;
if args.verbose {
println!("verbose mode enabled");
}
if args.dry_run {
println!("dry-run: no changes will be made");
}
Ok(())
}
Acknowledgments
Special thanks to Esther who wrote the original parser design for this library <3
License
This project is licensed under the Apache-2.0 License. For more information, please see the LICENSE file.
Related Skills
himalaya
339.5kCLI to manage emails via IMAP/SMTP. Use `himalaya` to list, read, write, reply, forward, search, and organize emails from the terminal. Supports multiple accounts and message composition with MML (MIME Meta Language).
node-connect
339.5kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
83.9kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
coding-agent
339.5kDelegate coding tasks to Codex, Claude Code, or Pi agents via background process
