tabled/features/formatting.rs
1//! This module contains settings for render strategy of papergrid.
2//!
3//! [`TrimStrategy`] and [`AlignmentStrategy`] allows to set [`Alignment`] settings.
4//!
5//! [`TabSize`] sets a default tab size.
6//!
7//! [`Alignment`]: crate::Alignment
8
9use papergrid::{
10 records::{Records, RecordsMut},
11 Entity,
12};
13
14use crate::{CellOption, Table, TableOption};
15
16/// Set a tab size.
17///
18/// The size is used in order to calculate width correctly.
19///
20/// Default value is 4 (basically 1 '\t' equals 4 spaces).
21///
22/// IMPORTANT: The tab character might be not present in output,
23/// it might be replaced by spaces.
24#[derive(Debug, Default, Clone)]
25pub struct TabSize(pub usize);
26
27impl<R> TableOption<R> for TabSize
28where
29 R: Records + RecordsMut<String>,
30{
31 fn change(&mut self, table: &mut Table<R>) {
32 table.get_config_mut().set_tab_width(self.0);
33 table.update_records();
34 table.destroy_width_cache();
35 }
36}
37
38/// `AlignmentStrategy` is a responsible for a flow how we apply an alignment.
39/// It mostly matters for multiline strings.
40///
41/// # Examples
42///
43/// ```
44/// use tabled::{
45/// Table, Style, Modify, Alignment, object::Segment,
46/// formatting::{AlignmentStrategy, TrimStrategy}
47/// };
48///
49/// // sample_from: https://opensource.adobe.com/Spry/samples/data_region/JSONDataSetSample.html
50/// let json = r#"
51/// {
52/// "id": "0001",
53/// "type": "donut",
54/// "name": "Cake",
55/// "ppu": 0.55,
56/// "batters": {
57/// "batter": [
58/// { "id": "1001", "type": "Regular" },
59/// { "id": "1002", "type": "Chocolate" },
60/// ]
61/// },
62/// "topping": [
63/// { "id": "5001", "type": "None" },
64/// { "id": "5006", "type": "Chocolate with Sprinkles" },
65/// { "id": "5003", "type": "Chocolate" },
66/// { "id": "5004", "type": "Maple" }
67/// ]
68/// }"#;
69///
70/// let mut table = Table::new(&[json]);
71/// table
72/// .with(Style::modern())
73/// .with(Modify::new(Segment::all()).with(Alignment::right()))
74/// .with(Modify::new(Segment::all()).with(TrimStrategy::None));
75///
76/// println!("{}", table);
77///
78/// assert_eq!(
79/// format!("\n{}", table),
80/// r#"
81/// ┌───────────────────────────────────────────────────────────────┐
82/// │ &str │
83/// ├───────────────────────────────────────────────────────────────┤
84/// │ │
85/// │ { │
86/// │ "id": "0001", │
87/// │ "type": "donut", │
88/// │ "name": "Cake", │
89/// │ "ppu": 0.55, │
90/// │ "batters": { │
91/// │ "batter": [ │
92/// │ { "id": "1001", "type": "Regular" }, │
93/// │ { "id": "1002", "type": "Chocolate" }, │
94/// │ ] │
95/// │ }, │
96/// │ "topping": [ │
97/// │ { "id": "5001", "type": "None" }, │
98/// │ { "id": "5006", "type": "Chocolate with Sprinkles" }, │
99/// │ { "id": "5003", "type": "Chocolate" }, │
100/// │ { "id": "5004", "type": "Maple" } │
101/// │ ] │
102/// │ } │
103/// └───────────────────────────────────────────────────────────────┘"#);
104///
105/// table
106/// .with(Modify::new(Segment::all()).with(AlignmentStrategy::PerCell))
107/// .with(Modify::new(Segment::all()).with(TrimStrategy::Horizontal));
108///
109/// assert_eq!(
110/// format!("\n{}", table),
111/// r#"
112/// ┌───────────────────────────────────────────────────────────────┐
113/// │ &str │
114/// ├───────────────────────────────────────────────────────────────┤
115/// │ │
116/// │ { │
117/// │ "id": "0001", │
118/// │ "type": "donut", │
119/// │ "name": "Cake", │
120/// │ "ppu": 0.55, │
121/// │ "batters": { │
122/// │ "batter": [ │
123/// │ { "id": "1001", "type": "Regular" }, │
124/// │ { "id": "1002", "type": "Chocolate" }, │
125/// │ ] │
126/// │ }, │
127/// │ "topping": [ │
128/// │ { "id": "5001", "type": "None" }, │
129/// │ { "id": "5006", "type": "Chocolate with Sprinkles" }, │
130/// │ { "id": "5003", "type": "Chocolate" }, │
131/// │ { "id": "5004", "type": "Maple" } │
132/// │ ] │
133/// │ } │
134/// └───────────────────────────────────────────────────────────────┘"#);
135///
136/// table.with(Modify::new(Segment::all()).with(AlignmentStrategy::PerLine));
137///
138/// assert_eq!(
139/// format!("\n{}", table),
140/// r#"
141/// ┌───────────────────────────────────────────────────────────────┐
142/// │ &str │
143/// ├───────────────────────────────────────────────────────────────┤
144/// │ │
145/// │ { │
146/// │ "id": "0001", │
147/// │ "type": "donut", │
148/// │ "name": "Cake", │
149/// │ "ppu": 0.55, │
150/// │ "batters": { │
151/// │ "batter": [ │
152/// │ { "id": "1001", "type": "Regular" }, │
153/// │ { "id": "1002", "type": "Chocolate" }, │
154/// │ ] │
155/// │ }, │
156/// │ "topping": [ │
157/// │ { "id": "5001", "type": "None" }, │
158/// │ { "id": "5006", "type": "Chocolate with Sprinkles" }, │
159/// │ { "id": "5003", "type": "Chocolate" }, │
160/// │ { "id": "5004", "type": "Maple" } │
161/// │ ] │
162/// │ } │
163/// └───────────────────────────────────────────────────────────────┘"#);
164/// ```
165#[derive(Debug, Clone)]
166pub enum AlignmentStrategy {
167 /// Apply alignment for cell content as a whole.
168 PerCell,
169 /// Apply alignment for each line of a cell content as a whole.
170 PerLine,
171}
172
173impl<R> CellOption<R> for AlignmentStrategy {
174 fn change_cell(&mut self, table: &mut Table<R>, entity: Entity) {
175 let mut formatting = *table.get_config().get_formatting(entity);
176 match &self {
177 AlignmentStrategy::PerCell => formatting.allow_lines_alignement = false,
178 AlignmentStrategy::PerLine => formatting.allow_lines_alignement = true,
179 }
180
181 table.get_config_mut().set_formatting(entity, formatting);
182 }
183}
184
185/// `TrimStrategy` determines if it's allowed to use empty space while doing [`Alignment`].
186///
187/// # Examples
188///
189/// ```
190/// use tabled::{
191/// Table, Style, Modify, Alignment, object::Segment,
192/// formatting::{TrimStrategy, AlignmentStrategy}
193/// };
194///
195/// let mut table = Table::new(&[" Hello World"]);
196/// table
197/// .with(Style::modern())
198/// .with(
199/// Modify::new(Segment::all())
200/// .with(Alignment::left())
201/// .with(TrimStrategy::Horizontal)
202/// );
203///
204/// // Note that nothing was changed exactly.
205///
206/// assert_eq!(
207/// table.to_string(),
208/// "┌────────────────┐\n\
209/// │ &str │\n\
210/// ├────────────────┤\n\
211/// │ Hello World │\n\
212/// └────────────────┘"
213/// );
214///
215/// // To trim lines you would need also set [`AlignmentStrategy`].
216/// table.with(Modify::new(Segment::all()).with(AlignmentStrategy::PerLine));
217///
218/// assert_eq!(
219/// table.to_string(),
220/// "┌────────────────┐\n\
221/// │ &str │\n\
222/// ├────────────────┤\n\
223/// │ Hello World │\n\
224/// └────────────────┘"
225/// );
226///
227/// let mut table = Table::new(&[" \n\n\n Hello World"]);
228/// table
229/// .with(Style::modern())
230/// .with(
231/// Modify::new(Segment::all())
232/// .with(Alignment::center())
233/// .with(Alignment::top())
234/// .with(TrimStrategy::Vertical)
235/// );
236///
237/// assert_eq!(
238/// table.to_string(),
239/// "┌─────────────────┐\n\
240/// │ &str │\n\
241/// ├─────────────────┤\n\
242/// │ Hello World │\n\
243/// │ │\n\
244/// │ │\n\
245/// │ │\n\
246/// └─────────────────┘"
247/// );
248/// ```
249///
250/// [`Alignment`]: crate::Alignment
251#[derive(Debug, Clone)]
252pub enum TrimStrategy {
253 /// Allow vertical trim.
254 Vertical,
255 /// Allow horizontal trim.
256 Horizontal,
257 /// Allow horizontal and vertical trim.
258 Both,
259 /// Doesn't allow any trim.
260 None,
261}
262
263impl<R> CellOption<R> for TrimStrategy {
264 fn change_cell(&mut self, table: &mut Table<R>, entity: Entity) {
265 let mut formatting = *table.get_config().get_formatting(entity);
266
267 // todo: could be changed to be a struct an enum like consts in `impl` block.
268 match self {
269 TrimStrategy::Vertical => {
270 formatting.vertical_trim = true;
271 }
272 TrimStrategy::Horizontal => {
273 formatting.horizontal_trim = true;
274 }
275 TrimStrategy::Both => {
276 formatting.vertical_trim = true;
277 formatting.horizontal_trim = true;
278 }
279 TrimStrategy::None => {
280 formatting.vertical_trim = false;
281 formatting.horizontal_trim = false;
282 }
283 }
284
285 table.get_config_mut().set_formatting(entity, formatting);
286 }
287}