Something went wrong. Try again.
A small command line app to create pretty unicode/ascii tables.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990// Copyright © 2026 David Caldwell <david@porkrind.org>
use clap::{CommandFactory, Parser,ValueEnum, error::ErrorKind};
#[derive(Parser,Debug)]#[command(version, about, long_about = None, allow_hyphen_values = true)]struct Cli { /// Cell data, one cell per argument. /// /// Each row (including the first) must start with '--row' or '--header'. /// Example: /// table --header "column 1" "column 2" "column 3" --row a b c --row c d "and e" /// produces /// ┌──────────┬──────────┬──────────┐ /// │ column 1 │ column 2 │ column 3 │ /// ╞══════════╪══════════╪══════════╡ /// │ a │ b │ c │ /// ├──────────┼──────────┼──────────┤ /// │ c │ d │ and e │ /// └──────────┴──────────┴──────────┘ #[arg(trailing_var_arg = true, verbatim_doc_comment)] data: Vec<String>,
/// Sets a custom table style #[arg(short, long, value_name = "STYLE")] style: Option<Style>,}
#[derive(Clone,ValueEnum,Debug)]enum Style { Unicode, Round, Double, DoubleFrame, Wide, WideFrame, WideHeader, Block, Braile, ASCII,}
impl Style { fn preset(&self) -> &'static str { match self { Style::Unicode => "││──╞═╪╡│─┼├┤┬┴┌┐└┘", Style::Round => "││──╞═╪╡│─┼├┤┬┴╭╮╰╯", Style::Double => "║║══╠═╬╣║═╬╠╣╦╩╔╗╚╝", Style::DoubleFrame => "║║══╠═╪╣│─┼╟╢╤╧╔╗╚╝", Style::Wide => "┃┃━━┣━╋┫┃━╋┣┫┳┻┏┓┗┛", Style::WideFrame => "┃┃━━┣━┿┫│─┼┠┨┯┷┏┓┗┛", Style::WideHeader => "││──┝━┿┥│─┼├┤┬┴┌┐└┘", Style::Block => "▌▐▀▄▙▄▙▟▌▄▙▙▟▛▙▛▜▙▟", Style::Braile => "⡇⢸⠒⠒⡷⠶⡷⢾⡇⠒⡗⡗⢺⡖⠓⡖⢲⠓⠚", Style::ASCII => comfy_table::presets::ASCII_FULL, } }}
fn main() -> Result<(), Box<dyn std::error::Error>> { let cli = Cli::parse();
let header = match cli.data.first().as_deref().map(|s| s.as_str()) { None => { Cli::command().error(ErrorKind::MissingRequiredArgument, "No data provided").exit(); } Some("--header") => true, Some("--row") => false, Some(arg) => { Cli::command().error(ErrorKind::InvalidValue, format!("The first data argument must be '--row' or '--header', not '{arg}'")).exit(); } };
let table_data: Vec<&[String]> = cli.data[1..].split(|v| v == "--row").collect(); let mut table = comfy_table::Table::new(); match cli.style { None => table.load_preset(Style::Unicode.preset()), Some(style) => table.load_preset(style.preset()), }; if header { table.set_header(table_data[0]); table.add_rows(table_data.into_iter().skip(1)); } else { table.add_rows(table_data); }
println!("{table}");
Ok(())}