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}