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