Column Formatting
Each column has flags that control how its data is rendered.
Alignment
Columns default to left-aligned. Set right or center alignment on the column,
and all cells in that column will follow. Individual cells can override the
column’s alignment via cell.alignment_set().
use cols::{Column, Alignment};
Column::new("SIZE").right(true); // right-align
Column::new("STATUS").center(true); // center-align
Column::new("PRICE").align(Alignment::Right); // equivalent to .right(true)
Right alignment
use cols::{Table, Column, print_table};
fn main() {
let mut t = Table::new();
t.termwidth_set(40);
t.add_column(Column::new("LEFT").width_fixed(10));
t.add_column(Column::new("RIGHT").width_fixed(10).right(true));
let l1 = t.new_line(None);
t.line_mut(l1).data_set(0, "a").data_set(1, "1");
let l2 = t.new_line(None);
t.line_mut(l2).data_set(0, "bb").data_set(1, "22");
let l3 = t.new_line(None);
t.line_mut(l3).data_set(0, "ccc").data_set(1, "333");
print_table(&t, &mut std::io::stdout().lock()).unwrap();
}
LEFT RIGHT
a 1
bb 22
ccc 333
Truncation
Truncate data that exceeds the column width:
use cols::{Table, Column, TermForce, print_table};
fn main() {
let mut t = Table::new();
t.termforce_set(TermForce::Always);
t.termwidth_set(20);
t.headings_set(false);
t.add_column(Column::new("A").width_fixed(8).truncate(true));
t.add_column(Column::new("B").width_fixed(8));
let row = t.new_line(None);
t.line_mut(row)
.data_set(0, "this-is-very-long")
.data_set(1, "short");
print_table(&t, &mut std::io::stdout().lock()).unwrap();
}
this-is-ver short
Wrapping
Wrap long data across multiple output lines:
use cols::{Table, Column, TermForce, print_table};
fn main() {
let mut t = Table::new();
t.termwidth_set(30);
t.termforce_set(TermForce::Always);
t.add_column(Column::new("ID").width_fixed(4));
t.add_column(Column::new("TEXT").wrap(true));
let l1 = t.new_line(None);
t.line_mut(l1).data_set(0, "1").data_set(1, "Short text");
let l2 = t.new_line(None);
t.line_mut(l2)
.data_set(0, "2")
.data_set(1, "A longer description that wraps");
print_table(&t, &mut std::io::stdout().lock()).unwrap();
}
ID TEXT
1 Short text
2 A longer description that
wraps
The table’s wrap_set(false) overrides this flag globally.
Word wrap
By default, wrapping splits at character boundaries. Use word_wrap(true)
to break at word boundaries instead. Words wider than the column fall back
to character-level wrapping.
use cols::{Table, Column, TermForce, print_table};
fn main() {
let mut t = Table::new();
t.termwidth_set(30);
t.termforce_set(TermForce::Always);
t.add_column(Column::new("ID").width_fixed(4));
t.add_column(Column::new("TEXT").word_wrap(true));
let l1 = t.new_line(None);
t.line_mut(l1).data_set(0, "1").data_set(1, "Short text");
let l2 = t.new_line(None);
t.line_mut(l2)
.data_set(0, "2")
.data_set(1, "A longer description that wraps at word boundaries");
let l3 = t.new_line(None);
t.line_mut(l3)
.data_set(0, "3")
.data_set(1, "Superlongwordthatexceedsthecolumnwidth here");
print_table(&t, &mut std::io::stdout().lock()).unwrap();
}
ID TEXT
1 Short text
2 A longer description
that wraps at word
boundaries
3 Superlongwordthatexceedst
hecolumnwidth here
Hidden columns
Store data without displaying it. Useful for sorting or filtering by a column that shouldn’t appear in the output:
use cols::{Table, Column, print_table};
fn main() {
let mut t = Table::new();
t.termwidth_set(40);
t.add_column(Column::new("A").width_fixed(5));
t.add_column(Column::new("B").width_fixed(5).hidden(true));
t.add_column(Column::new("C").width_fixed(5));
let l = t.new_line(None);
t.line_mut(l)
.data_set(0, "visible")
.data_set(1, "hidden")
.data_set(2, "also visible");
print_table(&t, &mut std::io::stdout().lock()).unwrap();
}
A C
visible also visible
Strict width
Prevent the layout engine from shrinking a column below its hint:
Column::new("ID").width_fixed(6).strict_width(true)
Normally the layout engine may shrink columns to fit the terminal. strict_width exempts a column from this.
No extremes
Exclude the single largest value when calculating column width:
Column::new("PATH").no_extremes(true)
If one row has a very long value, it won’t force the entire column wide. The outlier value is truncated or overflows instead of dominating the layout.
Encoding
By default, non-printable control characters are encoded as \xHH:
DATA
hello\x01world\x1b[31m
To pass through specific characters, set safechars on the column:
Column::new("DATA").safechars("\t") // tabs pass through, other control chars encoded
Or disable encoding entirely on the table:
table.encoding_set(false);
Header repeat
For long tables, you can repeat the header row at regular intervals so
it stays visible when scrolling. The interval is based on termheight —
headers repeat every termheight - 1 data lines.
use cols::{Table, Column, TermForce, print_table};
fn main() {
let mut t = Table::new();
t.termwidth_set(30);
t.termforce_set(TermForce::Always);
t.termheight_set(5);
t.header_repeat_set(true);
t.add_column(Column::new("NAME").width_fixed(10));
t.add_column(Column::new("VALUE").width_fixed(10).right(true));
for i in 1..=12 {
let row = t.new_line(None);
t.line_mut(row)
.data_set(0, &format!("item-{i}"))
.data_set(1, &format!("{}", i * 10));
}
print_table(&t, &mut std::io::stdout().lock()).unwrap();
}
NAME VALUE
item-1 10
item-2 20
item-3 30
item-4 40
NAME VALUE
item-5 50
item-6 60
item-7 70
item-8 80
NAME VALUE
item-9 90
item-10 100
item-11 110
item-12 120
This works in both flat and tree modes.