tabled/settings/format/
format_content.rs

1use crate::{
2    grid::{
3        config::{Entity, Position},
4        records::{ExactRecords, PeekableRecords, Records, RecordsMut},
5    },
6    settings::{CellOption, TableOption},
7};
8
9/// A lambda which formats cell content.
10#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
11pub struct FormatContent<F> {
12    f: F,
13    multiline: bool,
14}
15
16impl<F> FormatContent<F> {
17    pub(crate) fn new(f: F) -> Self {
18        Self {
19            f,
20            multiline: false,
21        }
22    }
23}
24
25impl<F> FormatContent<F> {
26    /// Multiline a helper function for changing multiline content of cell.
27    /// Using this formatting applied for all rows not to a string as a whole.
28    ///
29    /// ```rust,no_run
30    /// use tabled::{Table, settings::{Format, object::Segment, Modify}};
31    ///
32    /// let data: Vec<&'static str> = Vec::new();
33    /// let table = Table::new(&data)
34    ///     .with(Modify::new(Segment::all()).with(Format::content(|s| s.to_string()).multiline()))
35    ///     .to_string();
36    /// ```
37    pub fn multiline(mut self) -> Self {
38        self.multiline = true;
39        self
40    }
41}
42
43impl<F, R, D, C> TableOption<R, C, D> for FormatContent<F>
44where
45    F: FnMut(&str) -> String + Clone,
46    R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
47{
48    fn change(self, records: &mut R, cfg: &mut C, _: &mut D) {
49        CellOption::change(self, records, cfg, Entity::Global);
50    }
51}
52
53impl<F, R, C> CellOption<R, C> for FormatContent<F>
54where
55    F: FnMut(&str) -> String + Clone,
56    R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
57{
58    fn change(mut self, records: &mut R, _: &mut C, entity: Entity) {
59        let count_rows = records.count_rows();
60        let count_cols = records.count_columns();
61        let max_pos = Position::new(count_rows, count_cols);
62
63        for pos in entity.iter(count_rows, count_cols) {
64            if !max_pos.has_coverage(pos) {
65                continue;
66            }
67
68            let text = records.get_text(pos);
69
70            let new_text = if self.multiline {
71                multiline(self.f.clone())(text)
72            } else {
73                (self.f)(text)
74            };
75
76            records.set(pos, new_text);
77        }
78    }
79}
80
81fn multiline<F: FnMut(&str) -> String>(mut f: F) -> impl FnMut(&str) -> String {
82    move |s: &str| {
83        let mut v = Vec::new();
84        for line in s.lines() {
85            v.push(f(line));
86        }
87
88        v.join("\n")
89    }
90}