Clarence
Build powerful CLI tools backed by HTTP APIs in Rust.
use clarence::Clarence;
fn main() {
Clarence::builder()
.name("mycli")
.base_url("https://api.example.com")
.run();
}
mycli users list --active
Features
- Automatic HTTP routing - Commands map to API endpoints
- Built-in authentication - Store tokens, use in headers
- Multi-environment support - Optional dev, staging, prod configs
- Git context - Send repository info (working directory) as HTTP headers
- Custom handlers - Override default behavior
- JSON CLI actions - Let your API control the CLI (print, download, execute, storage)
Quick Start
cargo add clarence
Basic HTTP CLI
use clarence::Clarence;
fn main() {
Clarence::builder()
.name("mycli")
.base_url("https://api.example.com")
.help("My CLI tool")
.version(env!("CARGO_PKG_VERSION"))
.run();
}
Usage:
mycli deploy staging --force true
With Authentication
use clarence::{Clarence, Context, Handler, Result, StorageRef};
fn main() {
Clarence::builder()
.name("mycli")
.base_url("https://api.example.com")
.header("Authorization", StorageRef::get("token"))
.on("login", Handler::custom(login))
.fallback(Handler::http().build())
.run();
}
fn login(ctx: &mut Context) -> Result<i32> {
let token = ctx.args.get_flag("token")
.ok_or("Usage: mycli login --token YOUR_TOKEN")?;
ctx.store.set("token", format!("Bearer {}", token))?;
ctx.end().success_with("Logged in successfully")
}
Usage:
mycli login --token abc123
mycli users list
Custom Handlers
use clarence::{Clarence, Context, Handler, Method, Result};
fn main() {
Clarence::builder()
.name("mycli")
.base_url("https://api.example.com")
.on("login", Handler::custom(login))
.on("deploy", Handler::http()
.method(Method::Post)
.build())
.run();
}
fn login(ctx: &mut Context) -> Result<i32> {
let user = ctx.args.positional.first().unwrap();
println!("Logging in as {}", user);
ctx.end().success()
}
Multi-Environment
use clarence::{Clarence, Environment, StorageRef};
fn main() {
Clarence::builder()
.name("mycli")
.environments(
Environment::builder()
.flag("env")
.default("dev")
.add("prod", "https://api.prod.com", |env| {
env.header("X-Env", "production")
})
.add("dev", "http://localhost:3000", |_| {})
.build()
)
.run();
}
Usage:
mycli deploy mycli deploy --env prod
Git Context
Send git repository information as HTTP headers:
use clarence::{Clarence, Handler, Method};
fn main() {
Clarence::builder()
.name("mycli")
.base_url("https://api.example.com")
.send_git_context() .run();
Clarence::builder()
.name("mycli")
.base_url("https://api.example.com")
.on("deploy", Handler::http()
.method(Method::Post)
.send_git_context() .build())
.run();
Handler::custom(|ctx| {
if ctx.git.is_git_repo {
println!("Branch: {:?}", ctx.git.branch);
println!("Repo: {:?}", ctx.git.repo_name);
}
ctx.end().success()
})
}
HTTP headers sent:
x-cli-git: true (or false)
x-cli-git-repo-url: https://github.com/user/repo.git
x-cli-git-repo-name: repo
x-cli-git-branch: main
JSON CLI Actions
Your API can control CLI behavior returning cli_actions:
{
"cli_actions": [
{ "type": "print", "text": "✓ Deployed to production" },
{ "type": "execute", "command": "ls" },
{ "type": "download", "url": "https://...", "path": "./deploy.log" },
{ "type": "storage_set", "key": "last_deploy", "value": "prod" },
{ "type": "storage_unset", "key": "last_deploy" }
]
}
Enable in handler:
use clarence::{Handler, JsonCliActionsParser};
Handler::http()
.parser(
JsonCliActionsParser::new()
.allow_print()
.allow_execute() .allow_download()
.allow_storage_set()
.allow_storage_unset()
)
.build()
API Reference
Builder
Clarence::builder()
.name("mycli") .base_url("https://api.example.com") .header("key", "value") .header("key", StorageRef::get("k")) .send_git_context() .help("Help text") .version("1.0.0") .on("command", handler) .fallback(handler) .environments(config) .run()
Handlers
Handler::custom(|ctx| { ... })
Handler::http()
.method(Method::Post) .endpoint("/path") .header("key", "value") .parser(parser) .send_git_context() .build()
Context
fn handler(ctx: &mut Context) -> Result<i32> {
ctx.args.command ctx.args.positional ctx.args.flags ctx.args.get_flag("key") ctx.args.has_flag("key")
ctx.store.set("key", value)?
ctx.store.get("key") ctx.store.unset("key")?
ctx.git.is_git_repo ctx.git.repo_url ctx.git.repo_name ctx.git.branch
ctx.end().success() ctx.end().success_with("Done!") ctx.end().error() ctx.end().error_with("Failed!") ctx.end().code(42)
Ok(0) }
Examples
Run examples with:
cargo run --example complete -- --help
cargo run --example github -- login --token YOUR_TOKEN
cargo run --example environments -- deploy --env prod
Testing
cargo test
License
MIT
Repository
https://github.com/osscorplabs/clarence
Authors