
Crabular
A high-performance ASCII table library for Rust with zero dependencies.
Features
- Multiple table styles - Classic, Modern (Unicode), Minimal, Compact, Markdown
- Flexible alignment - Left, Center, Right per-cell and per-column
- Vertical alignment - Top, Middle, Bottom for multi-line cells
- Width constraints - Fixed, Min, Max, Proportional, Wrap
- Multi-line cells - Automatic word wrapping with configurable widths
- Cell spanning - Colspan support for merged cells
- Sorting - Sort by column (alphabetic or numeric, ascending or descending)
- Filtering - Filter rows by exact match, predicate, or substring
- Builder API - Fluent interface for table construction
- Zero dependencies - No external crates required
- Safe Rust -
#![forbid(unsafe_code)] - High performance - Zero-allocation Display trait, allocation pooling for repeated renders
Performance
Crabular v0.2.0+ includes Rust 1.93 optimizations for significant performance improvements:
Zero-Allocation Display
use Table;
let table = new
.header
.row;
// Zero-allocation printing (20-40% faster)
println!;
// For comparison, this allocates:
println!;
Allocation Pooling for Repeated Renders
use ;
let mut buffer = Vecwith_capacity;
for item in 0..10
Benefits: 30-50% faster for repeated renders (pagination, filtering UI)
Installation
Add to your Cargo.toml:
[]
= "0.5"
Quick Start
use ;
let mut table = new;
table.set_style;
table.set_headers;
table.add_row;
table.add_row;
println!;
Output:
┌────────┬─────┬────────────┐
│ Name │ Age │ City │
├────────┼─────┼────────────┤
│ Kelana │ 30 │ Berlin │
│ Kata │ 25 │ Yogyakarta │
└────────┴─────┴────────────┘
Builder API
For a more fluent experience, use TableBuilder:
use ;
let output = new
.style
.header
.constrain
.constrain
.align
.rows
.render;
print!; // Or use .print() directly with std feature
Truncation
Limit cell content length with truncation:
use ;
let output = new
.style
.header
.truncate // Truncate to 20 characters with "..." suffix
.rows
.render;
print!;
Output:
┌─────┬────────────┬───────────────────────┬───────┐
│ ID │ Name │ Description │ Score │
├─────┼────────────┼───────────────────────┼───────┤
│ 1 │ Kata │ A very long descr... │ 95.5 │
│ 2 │ Kelana │ Short desc │ 87.2 │
│ 3 │ Squidward │ Another extremely... │ 92.0 │
└─────┴────────────┴───────────────────────┴───────┘
Note: Truncation is applied lazily during row insertion, so there's zero overhead when not used.
Table Styles
use TableStyle;
// Available styles:
let _ = Classic; // +---+---+ with | and -
let _ = Modern; // Unicode box-drawing characters
let _ = Minimal; // Header separator only
let _ = Compact; // No outer borders
let _ = Markdown; // GitHub-flavored markdown tables
Classic
+-----------------+-----+---------------+
| Name | Age | City |
+-----------------+-----+---------------+
| Kelana | 30 | Berlin |
| Kata | 25 | Yogyakarta |
| Cherry Blossom | 35 | Bikini Bottom |
+-----------------+-----+---------------+
Modern
┌─────────────────┬─────┬───────────────┐
│ Name │ Age │ City │
├─────────────────┼─────┼───────────────┤
│ Kelana │ 30 │ Berlin │
│ Kata │ 25 │ Yogyakarta │
│ Cherry Blossom │ 35 │ Bikini Bottom │
└─────────────────┴─────┴───────────────┘
Minimal
Name Age City
──────────────────────────────────────────
Kelana 30 Berlin
Kata 25 Yogyakarta
Cherry Blossom 35 Bikini Bottom
Compact
│ Name │ Age │ City │
──────────────────┼──────┼────────────────
│ Kelana │ 30 │ Berlin │
│ Kata │ 25 │ Yogyakarta │
│ Cherry Blossom │ 35 │ Bikini Bottom │
Markdown
| Name | Age | City |
|----------------|-----|---------------|
| Kelana | 30 | Berlin |
| Kata | 25 | Yogyakarta |
| Cherry Blossom | 35 | Bikini Bottom |
Width Constraints
Control column widths with various constraints:
use ;
let mut table = new;
// Fixed width (exactly N characters)
table.constrain;
// Minimum width (at least N characters)
table.constrain;
// Maximum width (at most N characters, truncates if needed)
table.constrain;
// Proportional (percentage of available width)
table.constrain;
// Wrap (word wrap at N characters)
table.constrain;
Alignment
use ;
let mut table = new;
// Set column alignment
table.align;
table.align;
table.align;
// Per-row alignment via Row::with_alignment
let row = with_alignment;
table.add_row;
Vertical Alignment
For multi-line cells:
use ;
let mut table = new;
table.valign; // Default
table.valign;
table.valign;
Cell Spanning (Colspan)
Create cells that span multiple columns:
use ;
let mut table = new;
table.set_headers;
let mut row = new;
let mut merged = new;
merged.set_span; // This cell spans 2 columns
row.push;
row.push;
table.add_row;
Note: Standard Markdown does not support colspan. When using
TableStyle::Markdownwith spanned cells, the output will render visually but won't be valid Markdown table syntax.
Sorting
Sort table rows by any column:
use ;
let mut table = new;
table.add_row;
table.add_row;
// Alphabetic sorting
table.sort; // Ascending by column 0
table.sort_desc; // Descending by column 0
// Numeric sorting
table.sort_num; // Ascending numeric by column 1
table.sort_num_desc; // Descending numeric by column 1
// Custom sorting - compare by first column content
table.sort_by;
Filtering
Filter rows based on conditions:
use ;
let mut table = new;
table.add_row;
table.add_row;
table.add_row;
// Exact match - keeps rows where column 1 equals "Active"
table.filter_eq;
// Substring match - keeps rows where column 0 contains "Kelana"
// table.filter_has(0, "Kelana");
// Custom predicate on column - keeps rows where column 2 > 50
// table.filter_col(2, |val| val.parse::<i32>().unwrap_or(0) > 50);
// Full row predicate - keeps rows with more than 2 cells
let filtered = table.filtered;
let _ = filtered;
Column Operations
use ;
let mut table = new;
table.set_headers;
table.add_row;
table.add_row;
// Add column at the end (first value is header, rest are row values)
table.add_column;
// Insert column at position (first value is header, rest are row values)
table.insert_column;
// Remove column
table.remove_column;
CLI Tool
A separate CLI tool is available at crabular-cli:
# Install
# From CSV file (default: first row is header)
# Truncate long cell content to 20 characters
# Treat all rows as data (no header)
# Skip first row, treat remaining as data
# From stdin
|
# From JSON (supports nested objects)
|
# Different styles
CLI Options
| Option | Description |
|---|---|
-i, --input <FILE> |
Input file path (use - for stdin) |
-o, --output <FILE> |
Output file path |
-s, --style <STYLE> |
Table style: classic, modern, minimal, compact, markdown |
--format <FORMAT> |
Input format: csv, tsv, ssv, json, jsonl |
-S, --separator <CHAR> |
Field separator (default: auto-detect) |
--truncate N |
Truncate cell content to N characters with "..." suffix |
--no-header |
Treat all rows as data (no header row) |
--skip-header |
Skip first row, treat remaining as data |
API Reference
Table
| Method | Description |
|---|---|
new() |
Create empty table |
set_headers(row) |
Set header row |
add_row(row) |
Add data row |
truncate(limit) |
Set max cell content length |
render() |
Render to string |
print() |
Print to stdout |
set_style(style) |
Set table style |
align(col, alignment) |
Set column alignment |
valign(alignment) |
Set vertical alignment |
constrain(constraint) |
Add width constraint |
sort(col) |
Sort ascending |
sort_desc(col) |
Sort descending |
sort_num(col) |
Sort numeric ascending |
sort_num_desc(col) |
Sort numeric descending |
filter_eq(col, value) |
Filter by exact match |
filter_has(col, substr) |
Filter by substring |
filter_col(col, pred) |
Filter by predicate |
TableBuilder
| Method | Description |
|---|---|
new() |
Create new builder |
style(style) |
Set table style |
header(cells) |
Set header row |
row(cells) |
Add data row |
rows(data) |
Add multiple rows |
truncate(limit) |
Set max cell content length |
align(col, alignment) |
Set column alignment |
valign(alignment) |
Set vertical alignment |
constrain(col, constraint) |
Set column constraint |
padding(padding) |
Set cell padding |
build() |
Build table |
render() |
Build and render |
print() |
Build and print |
License
MIT License - see LICENSE for details.