standout_render/tabular/mod.rs
1//! Unicode-aware column formatting for terminal tables.
2//!
3//! This module provides utilities for aligned, column-based terminal output
4//! that correctly handles Unicode (CJK characters count as 2 columns) and
5//! preserves ANSI escape codes without counting them toward width.
6//!
7//! ## TabularFormatter vs Template Filters
8//!
9//! Two approaches, choose based on your needs:
10//!
11//! | Approach | Use When |
12//! |----------|----------|
13//! | Template filters (`col`, `pad_left`) | Simple tables, column widths known at template time |
14//! | TabularFormatter | Dynamic widths, CSV export, complex specs with data extraction |
15//!
16//! Template filters are simpler for most cases. Use TabularFormatter when you
17//! need width resolution from actual data or structured CSV export.
18//!
19//! ## Template Filters (Declarative)
20//!
21//! Filters are available in all Standout templates:
22//!
23//! ```jinja
24//! {% for entry in entries %}
25//! {{ entry.hash | col(7) }} {{ entry.author | col(12) }} {{ entry.message | col(50) }}
26//! {% endfor %}
27//! ```
28//!
29//! | Filter | Usage |
30//! |--------|-------|
31//! | `col` | `{{ value \| col(10) }}` or `{{ value \| col(10, align='right', truncate='middle') }}` |
32//! | `pad_left` | `{{ value \| pad_left(10) }}` |
33//! | `pad_right` | `{{ value \| pad_right(10) }}` |
34//! | `pad_center` | `{{ value \| pad_center(10) }}` |
35//! | `truncate_at` | `{{ value \| truncate_at(10, 'middle', '...') }}` |
36//! | `display_width` | `{{ value \| display_width }}` |
37//!
38//! ## TabularFormatter (Imperative)
39//!
40//! For programmatic control and CSV export:
41//!
42//! ```rust
43//! use standout_render::tabular::{FlatDataSpec, Column, Width, Align, TabularFormatter};
44//!
45//! let spec = FlatDataSpec::builder()
46//! .column(Column::new(Width::Fixed(8)))
47//! .column(Column::new(Width::Fill))
48//! .column(Column::new(Width::Fixed(10)).align(Align::Right))
49//! .separator(" ")
50//! .build();
51//!
52//! let formatter = TabularFormatter::new(&spec, 80);
53//! let row = formatter.format_row(&["abc123", "path/to/file.rs", "pending"]);
54//! ```
55//!
56//! ## Width Strategies
57//!
58//! - [`Width::Fixed(n)`] - Exactly n display columns
59//! - [`Width::Bounded { min, max }`] - Auto-size within bounds based on content
60//! - [`Width::Fill`] - Expand to fill remaining space
61//!
62//! ## Truncation Modes
63//!
64//! - [`TruncateAt::End`] - Keep start: "Hello W…"
65//! - [`TruncateAt::Start`] - Keep end: "…o World"
66//! - [`TruncateAt::Middle`] - Keep both: "Hel…orld" (useful for paths)
67//!
68//! ## Utility Functions
69//!
70//! ```rust
71//! use standout_render::tabular::{display_width, truncate_end, pad_right, wrap};
72//!
73//! let text = "Hello World";
74//! let truncated = truncate_end(text, 8, "…"); // "Hello W…"
75//! let padded = pad_right(&truncated, 10); // "Hello W… "
76//! assert_eq!(display_width(&padded), 10);
77//!
78//! // Word-wrap long text
79//! let lines = wrap("hello world foo bar", 11);
80//! assert_eq!(lines, vec!["hello world", "foo bar"]);
81//! ```
82//!
83//! # Column Width Strategies
84//!
85//! - [`Width::Fixed(n)`] - Exactly n display columns
86//! - [`Width::Bounded { min, max }`] - Auto-calculate from content within bounds
87//! - [`Width::Fill`] - Expand to fill remaining space (one per table)
88//!
89//! # Truncation Modes
90//!
91//! - [`TruncateAt::End`] - Keep start, truncate end: "Hello W…"
92//! - [`TruncateAt::Start`] - Keep end, truncate start: "…o World"
93//! - [`TruncateAt::Middle`] - Keep both ends: "Hel…orld"
94//!
95//! # Template Filters
96//!
97//! | Filter | Usage |
98//! |--------|-------|
99//! | `col` | `{{ value \| col(10) }}` or `{{ value \| col(10, align='right', truncate='middle') }}` |
100//! | `pad_left` | `{{ value \| pad_left(10) }}` |
101//! | `pad_right` | `{{ value \| pad_right(10) }}` |
102//! | `pad_center` | `{{ value \| pad_center(10) }}` |
103//! | `truncate_at` | `{{ value \| truncate_at(10, 'middle', '...') }}` |
104//! | `display_width` | `{{ value \| display_width }}` |
105
106mod decorator;
107pub mod filters;
108mod formatter;
109mod resolve;
110mod traits;
111mod types;
112mod util;
113
114// Re-export types
115pub use decorator::{BorderStyle, Table};
116pub use formatter::{CellOutput, TabularFormatter};
117pub use resolve::ResolvedWidths;
118pub use traits::{Tabular, TabularFieldDisplay, TabularFieldOption, TabularRow};
119
120// Note: Tabular and TabularRow derive macros are re-exported from the main `standout` crate
121// when the "macros" feature is enabled.
122pub use types::{
123 Align, Anchor, Col, Column, ColumnBuilder, Decorations, FlatDataSpec, FlatDataSpecBuilder,
124 Overflow, TabularSpec, TabularSpecBuilder, TruncateAt, Width,
125};
126
127// Re-export utility functions
128pub use util::{
129 display_width, pad_center, pad_left, pad_right, truncate_end, truncate_middle, truncate_start,
130 wrap, wrap_indent,
131};