Test your data. Render your view.
Standout is a CLI framework for Rust that enforces separation between logic and presentation. Your handlers return structs, not strings—making CLI logic as testable as any other code.
CLI code that mixes logic with println! statements is impossible to unit test:
fn list_command(show_all: bool) {
let todos = storage::list().unwrap();
println!("Your Todos:");
for todo in todos.iter() {
if show_all || todo.status == Status::Pending {
println!(" {} {}", if todo.done { "[x]" } else { "[ ]" }, todo.title);
}
}
}With Standout, handlers return data. The framework handles rendering:
fn list_handler(matches: &ArgMatches, _ctx: &CommandContext) -> HandlerResult<TodoResult> {
let show_all = matches.get_flag("all");
let todos = storage::list()?
.into_iter()
.filter(|t| show_all || t.status == Status::Pending)
.collect();
Ok(Output::Render(TodoResult { todos }))
}
#[test]
fn test_list_filters_completed() {
let result = list_handler(&matches, &ctx).unwrap();
assert!(result.todos.iter().all(|t| t.status == Status::Pending));
}Because your logic returns a struct, you test the struct. No stdout capture, no regex, no brittleness.
- Testable by design — Handlers return data; framework handles rendering
- Multiple output modes — Rich terminal, JSON, YAML, CSV from the same handler
- MiniJinja templates — Familiar syntax with partials, filters, and hot reload
- CSS/YAML styling — Semantic styles with light/dark mode support
- Tabular layouts — Declarative columns with alignment, truncation, wrapping
- Clap integration — Automatic dispatch via derive macros
- Incremental adoption — Migrate one command at a time
cargo add standoutuse standout::cli::{App, Dispatch, CommandContext, HandlerResult, Output};
use standout::{embed_templates, embed_styles};
#[derive(Subcommand, Dispatch)]
#[dispatch(handlers = handlers)]
pub enum Commands {
List,
}
let app = App::builder()
.commands(Commands::dispatch_config())
.templates(embed_templates!("src/templates"))
.styles(embed_styles!("src/styles"))
.build()?;
app.run(Cli::command(), std::env::args());myapp list # Rich terminal output
myapp list --output json # JSON for scriptingYou can find comprehensive documentation in our book: standout.magik.works
- Introduction to Standout — Start here
- Rendering System — Templates and styles
- Tabular Layouts — Tables and alignment
- All Topics — Complete reference
Contributions welcome. Use the issue tracker for bugs and feature requests.
MIT