gilt
Rich terminal formatting for Rust -- a port of Python's rich library.
gilt brings beautiful terminal output to Rust with styles, tables, trees, syntax highlighting, progress bars, and more -- all rendered as ANSI escape sequences.
Quick Start
[]
= "0.11"
use *;
Migrating from 0.10.x to 0.11.0
v0.11.0 collapses the Color struct into a 5-variant enum and converts
Segment.style from a public field to a method. Both changes are
mechanical at call sites; the type-level changes shrink Color from
~40 B + heap String to ~4 B inline Copy.
Color: struct → enum
// Before:
Color
// After:
Standard
// or
parse?
Field accessors are now methods:
| Before | After |
|---|---|
color.name |
color.name() → Cow<'_, str> |
color.color_type |
color.kind() → ColorType |
color.number |
color.number() → Option<u8> |
color.triplet |
color.triplet() → Option<ColorTriplet> |
Pattern-matching uses the variants directly:
// Before:
match color.color_type
// After:
match color
Segment.style: field → method
// Before: // After:
seg.style.is_some seg.style.is_some
seg.style.clone seg.style.cloned
seg.style.as_ref seg.style
if let Some = seg.style if let Some = seg.style
seg.style = Some seg.set_style
seg.style.clone.unwrap_or_else seg.style_owned
For struct-literal construction (Segment { style: Some(s), … }), use
Segment::new(text, Some(s), control) or Segment::styled(text, s).
Behavior changes (rare)
- EightBit colors lose named round-trip:
Color::parse("yellow4")? .name()now returns"color(106)"instead of"yellow4". Only the 16 standard ANSI colors get canonical names. Segment::style_owned()collapses None and Some(null): both map toStyle::null(). Usestyle()directly to distinguish.
What didn't change
Everything else: Console::print, the Renderable trait, markup parsing,
all widget APIs (Panel, Tree, Table, Progress, Live, Status),
Style::parse / Style::combine / Style::render, color parsing,
themes, layout, Text API. If you only consume widgets and never
construct Color or Segment directly, you likely don't need to
change anything.
Why no L2 interner activation?
The original v0.11.0 design included a per-Segment style interner that
would swap Segment.style storage to a 4-byte StyleId. After L1
shrunk Color and made Style cheap to clone, activating the interner
empirically regressed console_render/table_100_rows from 600 µs to
884 µs (+47%) — the per-construction Mutex<HashMap> lock acquisition
exceeded the savings. See CHANGELOG [0.11.0] for the bench table.
The StyleInterner and StyleId types remain in gilt::style_interner
as dormant scaffolding for potential future use (e.g., a lock-free
DashMap-backed redesign).
Recent releases
| Version | Highlights |
|---|---|
| v0.11.0 | L1 Color enum collapse (~10× smaller, Copy); Segment.style field → method; dormant StyleInterner types |
| v0.10.3 | T8 lock-free Live writers — +21,000× under writer+renderer contention |
| v0.10.2 | Live::update_renderable: &self; threaded contention bench |
| v0.10.1 | Text::char_len cache (~1 ns cached) |
| v0.10.0 | Rich v15.0.0 sync — TTY env overrides, 4-tuple Syntax.padding, Text::from_ansi newline preservation |
| v0.8.0 | Thread-safe Console, LRU style/color caching, iterator .progress() |
| v0.7.0 | Builder standardization (with_ prefix everywhere), comprehensive rustdoc |
| v0.6.0 | Soundness fix in live_render, hardened API, expanded prelude |
See CHANGELOG.md for details and Migrating from 0.10.x to 0.11.0 above.
Features
Core Widgets
- Text -- Rich text with markup, styles, wrapping, alignment
- Table -- Unicode box-drawing tables with column alignment and row striping
- Panel -- Bordered content panels with titles
- Tree -- Hierarchical tree display with guide lines
- Columns -- Multi-column layout
- Layout -- Flexible split-pane layouts
Terminal Features
- Syntax -- Code highlighting via syntect (150+ languages)
- Markdown -- Terminal-rendered Markdown
- JSON -- Pretty-printed JSON with highlighting
- Progress -- Multi-bar progress display with ETA, speed, spinner
- Live -- Live-updating terminal display
- Status -- Spinner with status message
Rust-Native Extensions
- Gradients -- True-color RGB gradient text
- Sparkline -- Inline Unicode bar charts
- Canvas -- Braille dot-matrix drawing with line, rect, circle primitives
- Diff -- Unified and side-by-side text diffs with colored output
- Figlet -- Large ASCII art text rendering
- CsvTable -- CSV to rich Table conversion
- Stylize trait --
"hello".bold().red()method chaining - Iterator progress --
iter.progress()adapter #[derive(Table, Panel, Tree, Columns, Rule, Inspect, Renderable)]-- Auto-generate widgets from structs- Environment detection --
NO_COLOR,FORCE_COLOR,CLICOLORsupport - Inspect -- Debug any value with rich formatting
- Accessibility -- WCAG 2.1 contrast checking,
REDUCE_MOTIONdetection - Extended underlines -- Curly, dotted, dashed, double styles with color
- anstyle interop -- Bidirectional conversion with
anstyletypes
Integrations
- miette -- Diagnostic reporting with gilt styling
- eyre -- Error reporting with gilt styling
- tracing -- Log subscriber with colored output
- anstyle -- Convert between gilt and anstyle
Color/Styletypes
Feature Gates
All four heavy dependencies are default-on. Disable them for minimal builds:
# Full (default) -- includes json, markdown, syntax, interactive
= "0.11"
# Minimal -- no heavy deps
= { = "0.11", = false }
# Pick what you need
= { = "0.11", = false, = ["json", "syntax"] }
| Feature | Default | Description |
|---|---|---|
json |
yes | Pretty-printed JSON (serde, serde_json) |
markdown |
yes | Terminal Markdown rendering (pulldown-cmark) |
syntax |
yes | Syntax highlighting (syntect) |
interactive |
yes | Password prompts, select/multi-select (rpassword) |
tracing |
no | tracing subscriber with gilt formatting |
derive |
no | #[derive(Table, Panel, Tree, ...)] proc macros (7 derives) |
miette |
no | miette::ReportHandler implementation |
eyre |
no | eyre::EyreHandler implementation |
csv |
no | CSV file reading via csv crate (built-in parser always available) |
anstyle |
no | Bidirectional From conversions with anstyle types |
Examples
# Showcase (runs all major widgets)
# Core widgets
# Rust-native features
# Feature-gated examples
See the examples/ directory for all 104 examples.
Global Console
// Print with markup
print_text;
// Print JSON
print_json;
// Inspect any Debug value
inspect;
Performance
gilt includes a criterion benchmark suite (~80 benchmarks) covering text rendering, style application, table layout, segment operations, threaded contention, and more:
Recent perf wins from the v0.10.x → v0.11.0 cycle:
| Bench | Before | After | Notes |
|---|---|---|---|
console_render/table_100_rows |
1.119 ms | 600 µs | -46%, T1/T2/T3/T9 + L1 Color enum |
console_render/table_10_rows |
90.6 µs | 50 µs | -45% |
text_operations/len_repeated_cached |
O(n) | ~1 ns | T11 Text::char_len AtomicUsize cache |
live_threaded/update_plus_render/1 |
43 ops/s | 904 K ops/s | +21,000×, T8 ArcSwap lock-free writers |
cell_len (ASCII) |
65.9 ns | 4.9 ns | -93%, ASCII fast path |
Minimum Supported Rust Version
gilt requires Rust 1.82.0 or later (for std::sync::LazyLock).
License
MIT