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}