text_grid/
cells_formatter.rs

1use crate::cell::*;
2use crate::Cells;
3
4/// Used to define columns.
5///
6/// - Use [`column`](Self::column) to create column.
7/// - Use [`column_with`](Self::column_with) to create multi level header.
8/// - Use [`content`](Self::content) to create shared header columns.
9pub struct CellsFormatter<'a, 'b, T: ?Sized> {
10    w: &'a mut dyn CellsWrite,
11    d: Option<&'b T>,
12    stretch: bool,
13}
14
15impl<'a, 'b, T: ?Sized> CellsFormatter<'a, 'b, T> {
16    pub(crate) fn new(w: &'a mut dyn CellsWrite, d: Option<&'b T>) -> Self {
17        Self {
18            w,
19            d,
20            stretch: false,
21        }
22    }
23
24    /// Define column group. Used to create multi row header.
25    ///
26    /// - header : Column group header's cell. If horizontal alignment is not specified, it is set to the center.
27    /// - f : A function to define columns in the group.
28    ///
29    /// # Examples
30    ///
31    /// ```
32    /// use text_grid::*;
33    /// struct RowData {
34    ///     a: u32,
35    ///     b_1: u32,
36    ///     b_2: u32,
37    /// }
38    /// impl Cells for RowData {
39    ///     fn fmt(f: &mut CellsFormatter<Self>) {
40    ///         f.column("a", |s| s.a);
41    ///         f.column_with("b", |f| {
42    ///             f.column("1", |s| s.b_1);
43    ///             f.column("2", |s| s.b_2);
44    ///         });
45    ///     }
46    /// }
47    ///
48    /// let rows = [
49    ///     RowData { a: 300,b_1: 10, b_2: 20 },
50    ///     RowData { a: 300,b_1: 1, b_2: 500 },
51    /// ];
52    /// let g = to_grid(rows);
53    /// assert_eq!(format!("\n{g}"), r#"
54    ///   a  |    b     |
55    /// -----|----------|
56    ///      | 1  |  2  |
57    /// -----|----|-----|
58    ///  300 | 10 |  20 |
59    ///  300 |  1 | 500 |
60    /// "#);
61    /// ```
62    pub fn column_with(
63        &mut self,
64        header: impl RawCell,
65        f: impl FnOnce(&mut CellsFormatter<'_, 'b, T>),
66    ) {
67        self.w.column_start(&header);
68        f(self);
69        self.w.column_end(&header);
70    }
71
72    /// Define column content. Used to create shared header column.
73    ///
74    /// - f : A function to obtain cells.
75    ///
76    /// # Examples
77    ///
78    /// ```
79    /// use text_grid::*;
80    /// struct RowData {
81    ///     a: u32,
82    ///     b_1: u32,
83    ///     b_2: u32,
84    /// }
85    /// impl Cells for RowData {
86    ///     fn fmt(f: &mut CellsFormatter<Self>) {
87    ///         f.column("a", |s| s.a);
88    ///         f.column_with("b", |f| {
89    ///             f.content(|s| s.b_1);
90    ///             f.content(|_| " ");
91    ///             f.content(|s| s.b_2);
92    ///         });
93    ///     }
94    /// }
95    ///
96    /// let rows = [
97    ///     RowData { a: 300, b_1: 10, b_2: 20 },
98    ///     RowData { a: 300, b_1: 1, b_2: 500 },
99    /// ];
100    /// let g = to_grid(rows);
101    /// assert_eq!(format!("\n{g}"), r#"
102    ///   a  |   b    |
103    /// -----|--------|
104    ///  300 | 10  20 |
105    ///  300 |  1 500 |
106    /// "#);
107    /// ```
108    pub fn content<U: Cells>(&mut self, f: impl FnOnce(&'b T) -> U) {
109        self.map_with(f, U::fmt)
110    }
111
112    /// Define column content.
113    ///
114    /// - f : A function to obtain cell.
115    pub(crate) fn content_cell<U: RawCell>(&mut self, f: impl FnOnce(&'b T) -> U) {
116        self.w.content(
117            self.d.map(f).as_ref().map(|x| x as &dyn RawCell),
118            self.stretch,
119        );
120    }
121
122    /// Define column.
123    ///
124    /// - header : Column header's cell. If horizontal alignment is not specified, it is set to the center.
125    /// - f : A function to obtain cell.
126    ///
127    /// # Examples
128    ///
129    /// ```
130    /// use text_grid::*;
131    /// struct RowData {
132    ///     a: u32,
133    ///     b: u32,
134    /// }
135    /// impl Cells for RowData {
136    ///     fn fmt(f: &mut CellsFormatter<Self>) {
137    ///         f.column("a", |s| s.a);
138    ///         f.column("b", |s| s.b);
139    ///     }
140    /// }
141    ///
142    /// let rows = [
143    ///     RowData { a: 300, b: 1 },
144    ///     RowData { a: 2, b: 200 },
145    /// ];
146    /// let g = to_grid(rows);
147    /// assert_eq!(format!("\n{g}"), r#"
148    ///   a  |  b  |
149    /// -----|-----|
150    ///  300 |   1 |
151    ///    2 | 200 |
152    /// "#);
153    /// ```
154    pub fn column<U: Cells>(&mut self, header: impl RawCell, f: impl FnOnce(&'b T) -> U) {
155        self.column_with(header, |cf| cf.content(f));
156    }
157
158    /// Creates a [`CellsFormatter`] whose source value was converted.
159    ///
160    /// If you want to convert to an owned value instead of a reference, use [`map_with`](Self::map_with) instead.
161    pub fn map<U: ?Sized>(&mut self, m: impl FnOnce(&T) -> &U) -> CellsFormatter<'_, 'b, U> {
162        CellsFormatter {
163            w: self.w,
164            d: self.d.map(m),
165            stretch: self.stretch,
166        }
167    }
168
169    /// Creates a [`CellsFormatter`] whose source value was converted.
170    ///
171    /// Unlike [`map`](Self::map), it can be converted to an owned value.
172    pub fn map_with<U>(
173        &mut self,
174        m: impl FnOnce(&'b T) -> U,
175        f: impl FnOnce(&mut CellsFormatter<U>),
176    ) {
177        f(&mut CellsFormatter {
178            w: self.w,
179            d: self.d.map(m).as_ref(),
180            stretch: self.stretch,
181        });
182    }
183
184    /// Creates a [`CellsFormatter`] that outputs the body cell only when the source value satisfies the condition.
185    pub fn filter(&mut self, f: impl FnOnce(&T) -> bool) -> CellsFormatter<T> {
186        CellsFormatter {
187            w: self.w,
188            d: self.d.filter(|data| f(data)),
189            stretch: self.stretch,
190        }
191    }
192
193    /// Creates a [`CellsFormatter`] that both filters and maps.
194    pub fn filter_map<U>(&mut self, f: impl FnOnce(&T) -> Option<&U>) -> CellsFormatter<'_, 'b, U> {
195        CellsFormatter {
196            w: self.w,
197            d: self.d.and_then(f),
198            stretch: self.stretch,
199        }
200    }
201
202    /// Creates a [`CellsFormatter`] that both filters and maps.
203    pub fn filter_map_with<U>(
204        &mut self,
205        f: impl FnOnce(&'b T) -> Option<U>,
206        some: impl FnOnce(&mut CellsFormatter<U>),
207    ) {
208        some(&mut CellsFormatter {
209            w: self.w,
210            d: self.d.and_then(f).as_ref(),
211            stretch: self.stretch,
212        });
213    }
214
215    pub fn try_map_with<O, E: RawCell>(
216        &mut self,
217        f: impl FnOnce(&'b T) -> std::result::Result<O, E>,
218        ok: impl FnOnce(&mut CellsFormatter<O>),
219    ) {
220        let d = self.d.map(f);
221        if let Some(Err(e)) = &d {
222            self.w.content_start(e);
223        }
224        ok(&mut CellsFormatter {
225            w: self.w,
226            d: d.as_ref().and_then(|x| x.as_ref().ok()),
227            stretch: self.stretch,
228        });
229        if let Some(Err(e)) = &d {
230            self.w.content_end(e);
231        }
232    }
233
234    /// Return `CellsFormatter` that generates the columns to be stretched preferentially.
235    ///
236    /// See [`ColumnStyle::stretch`](crate::ColumnStyle::stretch) for details.
237    pub fn stretch(&mut self) -> CellsFormatter<'_, 'b, T> {
238        CellsFormatter {
239            w: self.w,
240            d: self.d,
241            stretch: true,
242        }
243    }
244
245    /// Apply `f` to self.
246    pub fn with(&mut self, f: impl Fn(&mut Self)) {
247        f(self);
248    }
249}
250impl<'a, 'b, T: ?Sized> CellsFormatter<'a, 'b, &T> {
251    pub fn unref(&mut self) -> CellsFormatter<T> {
252        CellsFormatter {
253            w: self.w,
254            d: self.d.map(|x| &**x),
255            stretch: self.stretch,
256        }
257    }
258}
259impl<'a, 'b, T: ?Sized> CellsFormatter<'a, 'b, &mut T> {
260    pub fn unref(&mut self) -> CellsFormatter<T> {
261        CellsFormatter {
262            w: self.w,
263            d: self.d.map(|x| &**x),
264            stretch: self.stretch,
265        }
266    }
267}
268
269pub(crate) trait CellsWrite {
270    /// Called once for each cell.
271    /// In the case of merged cells, it is also called for each unmerged cells.
272    ///
273    /// `cell`: Cell's value. If `None`, it is merged cells.
274    fn content(&mut self, cell: Option<&dyn RawCell>, stretch: bool);
275
276    /// Called when merged cells start.
277    fn content_start(&mut self, cell: &dyn RawCell);
278
279    /// Called when merged cells end.
280    fn content_end(&mut self, cell: &dyn RawCell);
281
282    /// Called at the start of cells separated by ruled lines.
283    fn column_start(&mut self, header: &dyn RawCell);
284
285    /// Called at the end of cells separated by ruled lines.
286    fn column_end(&mut self, header: &dyn RawCell);
287}