text_grid/
cell.rs

1use crate::{Cells, CellsFormatter};
2
3use self::HorizontalAlignment::*;
4use std::{cmp::min, fmt::*};
5
6/// Cell`s style.
7#[derive(Clone, Copy, Default)]
8
9pub struct CellStyle {
10    pub(crate) align_h: Option<HorizontalAlignment>,
11}
12impl CellStyle {
13    pub fn new() -> Self {
14        Self::default()
15    }
16
17    /// Returns the styles in which the empty style has been replaced with the specified style.
18    ///
19    /// Judgment as to whether the style is empty or not is done for each individual element.
20    pub fn or(self, style: CellStyle) -> CellStyle {
21        CellStyle {
22            align_h: self.align_h.or(style.align_h),
23        }
24    }
25
26    pub fn align_h(self, value: HorizontalAlignment) -> Self {
27        CellStyle {
28            align_h: Some(value),
29        }
30    }
31}
32
33/// Horizontal alignments for cell's content.
34#[derive(Clone, Copy)]
35pub enum HorizontalAlignment {
36    Left,
37    Center,
38    Right,
39}
40
41/// A data structure that can be formatted into a cell.
42///
43/// Normally, [`cell()`] or [`cell!`](crate::cell!) is used to create a value that implements `RawCell`.
44///
45/// If you implement `RawCell` for a type, you should also implement [`Cells`] for convenience.
46///
47/// ```
48/// use text_grid::*;
49/// struct X(String);
50///
51/// impl RawCell for X {
52///     fn fmt(&self, s: &mut String) {
53///         s.push_str(&self.0);
54///     }
55///     fn style(&self) -> CellStyle {
56///         CellStyle::new().align_h(HorizontalAlignment::Right)
57///     }
58/// }
59/// impl Cells for X {
60///     fn fmt(f: &mut CellsFormatter<Self>) {
61///         f.content(Cell::new);
62///     }
63/// }
64/// ```
65pub trait RawCell {
66    /// Output the cell text to given buffer.
67    fn fmt(&self, s: &mut String);
68
69    /// Return cell's style.
70    fn style(&self) -> CellStyle {
71        CellStyle::default()
72    }
73
74    fn style_for_body(&self) -> CellStyle {
75        CellStyle::default()
76    }
77}
78impl RawCell for () {
79    fn fmt(&self, _: &mut String) {}
80}
81impl<T: ?Sized + RawCell> RawCell for &T {
82    fn fmt(&self, s: &mut String) {
83        T::fmt(*self, s)
84    }
85    fn style(&self) -> CellStyle {
86        T::style(*self)
87    }
88    fn style_for_body(&self) -> CellStyle {
89        T::style_for_body(*self)
90    }
91}
92impl<T: ?Sized + RawCell> RawCell for &mut T {
93    fn fmt(&self, s: &mut String) {
94        T::fmt(*self, s)
95    }
96    fn style(&self) -> CellStyle {
97        T::style(*self)
98    }
99    fn style_for_body(&self) -> CellStyle {
100        T::style_for_body(*self)
101    }
102}
103impl<T: RawCell> RawCell for Option<T> {
104    fn fmt(&self, s: &mut String) {
105        if let Some(value) = self {
106            value.fmt(s);
107        }
108    }
109    fn style(&self) -> CellStyle {
110        if let Some(value) = self {
111            value.style()
112        } else {
113            CellStyle::default()
114        }
115    }
116    fn style_for_body(&self) -> CellStyle {
117        if let Some(value) = self {
118            value.style_for_body()
119        } else {
120            CellStyle::default()
121        }
122    }
123}
124impl<T: RawCell, E: RawCell> RawCell for std::result::Result<T, E> {
125    fn fmt(&self, s: &mut String) {
126        match self {
127            Ok(value) => value.fmt(s),
128            Err(value) => value.fmt(s),
129        }
130    }
131    fn style(&self) -> CellStyle {
132        match self {
133            Ok(value) => value.style(),
134            Err(value) => value.style(),
135        }
136    }
137    fn style_for_body(&self) -> CellStyle {
138        match self {
139            Ok(value) => value.style_for_body(),
140            Err(value) => value.style_for_body(),
141        }
142    }
143}
144
145struct DisplayCellSource<T: Display>(T);
146impl<T: Display> RawCell for DisplayCellSource<T> {
147    fn fmt(&self, s: &mut String) {
148        write!(s, "{}", self.0).unwrap()
149    }
150}
151
152/// Create [`Cell`] from [`Display`].
153///
154/// The returned value owns the value passed in.
155/// Therefore, the returned value can not be move out of the lifetime of the passed value.
156///
157/// ```ignore
158/// use text_grid::*;
159///
160/// fn f_error() -> Cell<impl RawCell> {
161///     let s = String::from("ABC");
162///     cell(&s) // Error : returns a value referencing data owned by the current function
163/// }
164/// ```
165///
166/// ```
167/// use text_grid::*;
168///
169/// fn f_ok() -> Cell<impl RawCell> {
170///     let s = String::from("ABC");
171///     cell(s) // OK
172/// }
173/// ```
174pub fn cell(value: impl Display) -> Cell<impl RawCell> {
175    Cell::new(DisplayCellSource(value))
176}
177
178struct FmtFnCellSource<F>(F);
179impl<F: Fn(&mut String) -> Result> RawCell for FmtFnCellSource<F> {
180    fn fmt(&self, s: &mut String) {
181        (self.0)(s).unwrap()
182    }
183}
184
185/// Create [`Cell`] from closure that call [`std::write!`] macro.
186///
187/// # Examples
188///
189/// ```
190/// use text_grid::*;
191/// use std::fmt::Write;
192///
193/// let s = String::from("ABC");
194/// let cell_a = cell_by(|f| write!(f, "{}", &s));
195/// let cell_b = cell_by(|f| write!(f, "{}", &s));
196/// ```
197pub fn cell_by<F: Fn(&mut String) -> Result>(f: F) -> Cell<impl RawCell> {
198    Cell::new(FmtFnCellSource(f))
199}
200
201/// Create [`Cell`] via runtime expression interpolation, as in [`format!`].
202///
203/// Use the `format!` syntax to create [`Cell`]. See [`std::fmt`] for more information.
204///
205/// # Examples
206/// ```
207/// use text_grid::*;
208/// struct RowData {
209///     a: f64,
210///     b: f64,
211/// }
212/// impl Cells for RowData {
213///     fn fmt(f: &mut CellsFormatter<Self>) {
214///         f.column("a", |s| cell!("{:.2}", s.a).right());
215///         f.column("b", |s| cell!("{:.3}", s.b).right());
216///     }
217/// }
218///
219/// let rows = [
220///    RowData { a: 1.10, b: 1.11 },
221///    RowData { a: 1.00, b: 0.10 },
222/// ];
223/// let g = to_grid(rows);
224///
225/// assert_eq!(format!("\n{g}"), r#"
226///   a   |   b   |
227/// ------|-------|
228///  1.10 | 1.110 |
229///  1.00 | 0.100 |
230/// "#);
231/// ```
232///
233/// # Arguments ownership
234///
235/// This macro consumes the variable used in the argument.
236/// ```ignore
237/// use text_grid::*;
238///
239/// let s = String::from("ABC");
240/// let cell_a = cell!("{}", &s); // `s` moved into `cell_a` here
241/// let cell_b = cell!("{}", &s); // ERROR : `s` used here after move
242/// ```
243///
244/// To avoid consume variables, use only variables that implements `Copy` .
245///
246/// ```
247/// use text_grid::*;
248///
249/// let s = String::from("ABC");
250/// let s = &s; // immutable reference implements `Copy`.
251/// let cell_a = cell!("{}", s);
252/// let cell_b = cell!("{}", s); // OK
253/// // Return value owns the reference.
254/// // Therefore, the returned value can not be move out of the lifetime of the reference.
255/// ```
256///
257/// or use [`cell_by`] and `write!`.
258///
259/// ```
260/// use text_grid::*;
261/// use std::fmt::Write;
262///
263/// let s = String::from("ABC");
264/// let cell_a = cell_by(|f| write!(f, "{}", &s));
265/// let cell_b = cell_by(|f| write!(f, "{}", &s));
266/// // Return value owns the reference.
267/// // Therefore, the returned value can not be move out of the lifetime of the reference.
268/// ```
269///
270/// or use [`cell()`] and `format!`.
271///
272/// ```
273/// use text_grid::*;
274///
275/// let s = String::from("ABC");
276/// let cell_a = cell(format!("{}", &s));
277/// let cell_b = cell(format!("{}", &s));
278/// // Return value owns formatted string.
279/// // Therefore, the returned value can move anywhere.
280/// ```
281#[macro_export]
282macro_rules! cell {
283    ($ ( $ arg : tt ) *) => { {
284            use std::fmt::Write;
285            $crate::cell_by(move |f| write!(f, $($arg)*))
286        }
287    };
288}
289
290/// Implementation of [`RawCell`] that can specify styles.
291pub struct Cell<T> {
292    source: T,
293    style: CellStyle,
294}
295impl<T: RawCell> RawCell for Cell<T> {
296    fn fmt(&self, s: &mut String) {
297        self.source.fmt(s)
298    }
299    fn style(&self) -> CellStyle {
300        self.style
301    }
302}
303impl<T: RawCell> Cells for Cell<T> {
304    fn fmt(f: &mut CellsFormatter<Self>) {
305        f.content_cell(|s| s);
306    }
307}
308
309impl<T: RawCell> Cell<T> {
310    /// Create a new `Cell` with specified [`RawCell`].
311    pub fn new(source: T) -> Self {
312        let style = source.style();
313        Cell { source, style }
314    }
315
316    /// Return the cell with horizontal alignment set to the left.
317    pub fn left(self) -> Self {
318        self.with_align_h(Left)
319    }
320
321    /// Return the cell with horizontal alignment set to the right.
322    pub fn right(self) -> Self {
323        self.with_align_h(Right)
324    }
325
326    /// Return the cell with horizontal alignment set to the center.
327    pub fn center(self) -> Self {
328        self.with_align_h(Center)
329    }
330
331    /// Return the cell with aligned baseline.
332    ///
333    /// ```rust
334    /// use text_grid::*;
335    ///
336    /// struct Source(&'static str);
337    ///
338    /// impl Cells for Source {
339    ///     fn fmt(f: &mut CellsFormatter<Self>) {
340    ///         f.column("default", |x| &x.0);
341    ///         f.column("baseline", |x| cell(&x.0).baseline("-"));
342    ///     }
343    /// }
344    /// let rows = [
345    ///     Source("1-2345"),
346    ///     Source("1234-5"),
347    ///     Source("12345"),
348    /// ];
349    /// let g = to_grid(rows);
350    ///
351    /// assert_eq!(format!("\n{g}"), r#"
352    ///  default |  baseline  |
353    /// ---------|------------|
354    ///  1-2345  |     1-2345 |
355    ///  1234-5  |  1234-5    |
356    ///  12345   | 12345      |
357    /// "#);
358    /// ```
359    pub fn baseline(self, baseline: &str) -> impl Cells {
360        let mut value = String::new();
361        self.fmt(&mut value);
362        BaselineAlignedCell::new(value, baseline)
363    }
364
365    /// Return the cell with an empty style replaced by the specified style.
366    ///
367    /// Judgment as to whether the style is empty or not is done for each individual element.
368    pub fn with_base_style(self, style: CellStyle) -> Self {
369        Cell {
370            source: self.source,
371            style: self.style.or(style),
372        }
373    }
374    fn with_align_h(self, align_h: HorizontalAlignment) -> Self {
375        Cell {
376            source: self.source,
377            style: CellStyle {
378                align_h: Some(align_h),
379            },
380        }
381    }
382}
383impl Cell<String> {
384    /// Create a new `Cell` with empty string and empty style.
385    pub fn empty() -> Self {
386        Self::new(String::new())
387    }
388}
389
390macro_rules! impl_cell_source {
391    ($t:ty, $align:expr ) => {
392        impl RawCell for $t {
393            fn fmt(&self, s: &mut String) {
394                write!(s, "{}", self).unwrap()
395            }
396            fn style_for_body(&self) -> CellStyle {
397                CellStyle {
398                    align_h: Some($align),
399                }
400            }
401        }
402        impl Cells for $t {
403            fn fmt(f: &mut CellsFormatter<Self>) {
404                f.content_cell(|x| x);
405            }
406        }
407    };
408}
409
410impl_cell_source!(u8, Right);
411impl_cell_source!(i8, Right);
412impl_cell_source!(u16, Right);
413impl_cell_source!(i16, Right);
414impl_cell_source!(u32, Right);
415impl_cell_source!(i32, Right);
416impl_cell_source!(u64, Right);
417impl_cell_source!(i64, Right);
418impl_cell_source!(u128, Right);
419impl_cell_source!(i128, Right);
420impl_cell_source!(isize, Right);
421impl_cell_source!(usize, Right);
422impl_cell_source!(String, Left);
423impl_cell_source!(str, Left);
424impl_cell_source!(char, Center);
425impl_cell_source!(bool, Center);
426
427/// A cell with aligned baseline.
428///
429/// Use [`Cell::baseline`] to create an instance of this type.
430struct BaselineAlignedCell {
431    value: String,
432    baseline_offset: usize,
433}
434impl BaselineAlignedCell {
435    fn new(value: String, baseline: &str) -> Self {
436        let baseline_offset = value.find(baseline).unwrap_or(value.len());
437        Self {
438            value,
439            baseline_offset,
440        }
441    }
442    fn left(&self) -> &str {
443        &self.value[..self.baseline_offset]
444    }
445    fn right(&self) -> &str {
446        &self.value[self.baseline_offset..]
447    }
448}
449
450impl Cells for BaselineAlignedCell {
451    fn fmt(f: &mut CellsFormatter<Self>) {
452        f.content(|this| cell(this.left()).right());
453        f.content(|this| cell(this.right()).left());
454    }
455}
456
457impl Cells for f32 {
458    fn fmt(f: &mut CellsFormatter<Self>) {
459        f.content(|this| cell(this).baseline("."))
460    }
461}
462
463impl Cells for f64 {
464    fn fmt(f: &mut CellsFormatter<Self>) {
465        f.content(|this| cell(this).baseline("."))
466    }
467}
468
469/// Create [`Cells`] for float numbers via runtime expression interpolation.
470///
471/// # Examples
472///
473/// ```
474/// use text_grid::*;
475/// let s = cells_schema::<f64>(|f| {
476///     f.column("",      |x| cell!("{x:e}"));
477///     f.column("e",     |x| cells_f!("{x:e}"));
478///     f.column(".2e",   |x| cells_f!("{x:.2e}"));
479///     f.column("E",     |x| cells_f!("{x:E}"));
480///     f.column("debug", |x| cells_f!("{x:?}"));
481/// });
482///
483/// let g = to_grid_with_schema(vec![1.0, 0.95, 123.45, 0.000001, 1.0e-20, 10000000000.0], s);
484/// assert_eq!(format!("\n{g}"), OUTPUT);
485///
486/// const OUTPUT: &str = r"
487///           |      e       |    .2e     |      E       |        debug         |
488/// ----------|--------------|------------|--------------|----------------------|
489///  1e0      | 1      e   0 | 1.00 e   0 | 1      E   0 |           1.0        |
490///  9.5e-1   | 9.5    e  -1 | 9.50 e  -1 | 9.5    E  -1 |           0.95       |
491///  1.2345e2 | 1.2345 e   2 | 1.23 e   2 | 1.2345 E   2 |         123.45       |
492///  1e-6     | 1      e  -6 | 1.00 e  -6 | 1      E  -6 |           1    e  -6 |
493///  1e-20    | 1      e -20 | 1.00 e -20 | 1      E -20 |           1    e -20 |
494///  1e10     | 1      e  10 | 1.00 e  10 | 1      E  10 | 10000000000.0        |
495/// ";
496/// ```
497#[macro_export]
498macro_rules! cells_f {
499    ($($t:tt)*) => {
500        $crate::cells_f(format!($($t)*))
501    };
502}
503
504/// Create [`Cells`] for float numbers from [`Display`].
505///
506/// Format in the same way as [`cells_f!`] macro.
507pub fn cells_f(value: impl Display) -> impl Cells {
508    ExpCells::new(value.to_string())
509}
510struct ExpCells {
511    value: String,
512    offset_dot: usize,
513    offset_e: usize,
514}
515impl ExpCells {
516    fn new(value: String) -> Self {
517        let offset_e = value.rfind(['e', 'E']).unwrap_or(value.len());
518        let offset_dot = min(value.find('.').unwrap_or(value.len()), offset_e);
519        Self {
520            value,
521            offset_dot,
522            offset_e,
523        }
524    }
525}
526
527impl Cells for ExpCells {
528    fn fmt(f: &mut CellsFormatter<Self>) {
529        f.stretch()
530            .content(|x| cell(&x.value[..x.offset_dot]).right());
531        f.content(|x| {
532            if x.offset_dot < x.offset_e {
533                &x.value[x.offset_dot..x.offset_e]
534            } else {
535                ""
536            }
537        });
538        f.content(|x| {
539            Cell::new(if x.offset_e < x.value.len() {
540                Ok(cell!(" {} ", &x.value[x.offset_e..x.offset_e + 1]))
541            } else {
542                Err(())
543            })
544        });
545        f.content(|x| cell(&x.value[min(x.offset_e + 1, x.value.len())..]).right());
546    }
547}