Coverage Report

Created: 2026-01-13 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/tabled-0.20.0/src/tables/compact.rs
Line
Count
Source
1
//! This module contains a [`CompactTable`] table.
2
3
use core::{cmp::max, fmt};
4
5
use crate::{
6
    grid::{
7
        colors::NoColors,
8
        config::{AlignmentHorizontal, CompactConfig, Indent, Sides},
9
        dimension::{ConstDimension, ConstSize, Dimension},
10
        records::{
11
            into_records::{LimitColumns, LimitRows},
12
            IntoRecords, IterRecords,
13
        },
14
        util::string::get_line_width,
15
        CompactGrid,
16
    },
17
    settings::{style::Style, TableOption},
18
};
19
20
/// A table which consumes an [`IntoRecords`] iterator.
21
/// It assumes that the content has only single line.
22
///
23
/// In contrast to [`Table`] [`CompactTable`] does no allocations but it consumes an iterator.
24
/// It's useful when you don't want to re/allocate a buffer for your data.
25
///
26
/// # Example
27
///
28
/// It works smoothly with arrays.
29
///
30
#[cfg_attr(feature = "std", doc = "```")]
31
#[cfg_attr(not(feature = "std"), doc = "```ignore")]
32
/// use tabled::{settings::Style, tables::CompactTable};
33
///
34
/// let data = [
35
///     ["FreeBSD", "1993", "William and Lynne Jolitz", "?"],
36
///     ["OpenBSD", "1995", "Theo de Raadt", ""],
37
///     ["HardenedBSD", "2014", "Oliver Pinter and Shawn Webb", ""],
38
/// ];
39
///
40
/// let table = CompactTable::from(data)
41
///     .with(Style::psql())
42
///     .to_string();
43
///
44
/// assert_eq!(
45
///     table,
46
///     concat!(
47
///         " FreeBSD     | 1993 | William and Lynne Jolitz     | ? \n",
48
///         " OpenBSD     | 1995 | Theo de Raadt                |   \n",
49
///         " HardenedBSD | 2014 | Oliver Pinter and Shawn Webb |   ",
50
///     )
51
/// );
52
/// ```
53
///
54
/// But it's default creation requires to be given an estimated cell width, and the amount of columns.
55
///
56
#[cfg_attr(feature = "std", doc = "```")]
57
#[cfg_attr(not(feature = "std"), doc = "```ignore")]
58
/// use tabled::{settings::Style, tables::CompactTable};
59
///
60
/// let data = [
61
///     ["FreeBSD", "1993", "William and Lynne Jolitz", "?"],
62
///     ["OpenBSD", "1995", "Theo de Raadt", ""],
63
///     ["HardenedBSD", "2014", "Oliver Pinter and Shawn Webb", ""],
64
/// ];
65
///
66
/// // See what will happen if the given width is too narrow
67
///
68
/// let table = CompactTable::new(&data)
69
///     .columns(4)
70
///     .width(5)
71
///     .with(Style::ascii())
72
///     .to_string();
73
///
74
/// assert_eq!(
75
///     table,
76
///     "+-----+-----+-----+-----+\n\
77
///      | FreeBSD | 1993 | William and Lynne Jolitz | ?   |\n\
78
///      |-----+-----+-----+-----|\n\
79
///      | OpenBSD | 1995 | Theo de Raadt |     |\n\
80
///      |-----+-----+-----+-----|\n\
81
///      | HardenedBSD | 2014 | Oliver Pinter and Shawn Webb |     |\n\
82
///      +-----+-----+-----+-----+"
83
/// );
84
/// ```
85
///
86
/// [`Table`]: crate::Table
87
#[derive(Debug, Clone)]
88
pub struct CompactTable<I, D> {
89
    records: I,
90
    cfg: CompactConfig,
91
    dims: D,
92
    count_columns: usize,
93
    count_rows: Option<usize>,
94
}
95
96
impl<I> CompactTable<I, ConstDimension<0, 0>> {
97
    /// Creates a new [`CompactTable`] structure with a width dimension for all columns.
98
0
    pub const fn new(iter: I) -> Self
99
0
    where
100
0
        I: IntoRecords,
101
    {
102
0
        Self {
103
0
            records: iter,
104
0
            cfg: create_config(),
105
0
            count_columns: 0,
106
0
            count_rows: None,
107
0
            dims: ConstDimension::new(ConstSize::Value(2), ConstSize::Value(1)),
108
0
        }
109
0
    }
110
}
111
112
impl<I, const ROWS: usize, const COLS: usize> CompactTable<I, ConstDimension<COLS, ROWS>> {
113
    /// Set a height for each row.
114
0
    pub fn height<S: Into<ConstSize<COUNT_ROWS>>, const COUNT_ROWS: usize>(
115
0
        self,
116
0
        size: S,
117
0
    ) -> CompactTable<I, ConstDimension<COLS, COUNT_ROWS>> {
118
0
        let (width, _) = self.dims.into();
119
0
        CompactTable {
120
0
            dims: ConstDimension::new(width, size.into()),
121
0
            records: self.records,
122
0
            cfg: self.cfg,
123
0
            count_columns: self.count_columns,
124
0
            count_rows: self.count_rows,
125
0
        }
126
0
    }
127
128
    /// Set a width for each column.
129
0
    pub fn width<S: Into<ConstSize<COUNT_COLUMNS>>, const COUNT_COLUMNS: usize>(
130
0
        self,
131
0
        size: S,
132
0
    ) -> CompactTable<I, ConstDimension<COUNT_COLUMNS, ROWS>> {
133
0
        let (_, height) = self.dims.into();
134
0
        CompactTable {
135
0
            dims: ConstDimension::new(size.into(), height),
136
0
            records: self.records,
137
0
            cfg: self.cfg,
138
0
            count_columns: self.count_columns,
139
0
            count_rows: self.count_rows,
140
0
        }
141
0
    }
142
}
143
144
impl<I, D> CompactTable<I, D> {
145
    /// Creates a new [`CompactTable`] structure with a known dimension.
146
    ///
147
    /// Notice that the function wont call [`Estimate`].
148
    ///
149
    /// [`Estimate`]: crate::grid::dimension::Estimate
150
0
    pub fn with_dimension(iter: I, dimension: D) -> Self
151
0
    where
152
0
        I: IntoRecords,
153
    {
154
0
        Self {
155
0
            records: iter,
156
0
            dims: dimension,
157
0
            cfg: create_config(),
158
0
            count_columns: 0,
159
0
            count_rows: None,
160
0
        }
161
0
    }
162
163
    /// With is a generic function which applies options to the [`CompactTable`].
164
0
    pub fn with<O>(mut self, option: O) -> Self
165
0
    where
166
0
        for<'a> O: TableOption<IterRecords<&'a I>, CompactConfig, D>,
167
    {
168
0
        let mut records = IterRecords::new(&self.records, self.count_columns, self.count_rows);
169
0
        option.change(&mut records, &mut self.cfg, &mut self.dims);
170
171
0
        self
172
0
    }
173
174
    /// Limit a number of rows.
175
0
    pub const fn rows(mut self, count_rows: usize) -> Self {
176
0
        self.count_rows = Some(count_rows);
177
0
        self
178
0
    }
179
180
    /// Limit a number of columns.
181
0
    pub const fn columns(mut self, count: usize) -> Self {
182
0
        self.count_columns = count;
183
0
        self
184
0
    }
185
186
    /// Returns a table config.
187
0
    pub fn get_config(&self) -> &CompactConfig {
188
0
        &self.cfg
189
0
    }
190
191
    /// Returns a table config.
192
0
    pub fn get_config_mut(&mut self) -> &mut CompactConfig {
193
0
        &mut self.cfg
194
0
    }
195
196
    /// Format table into [fmt::Write]er.
197
0
    pub fn fmt<W>(self, writer: W) -> fmt::Result
198
0
    where
199
0
        I: IntoRecords,
200
0
        I::Cell: AsRef<str>,
201
0
        D: Dimension,
202
0
        W: fmt::Write,
203
    {
204
0
        build_grid(
205
0
            writer,
206
0
            self.records,
207
0
            self.dims,
208
0
            self.cfg,
209
0
            self.count_columns,
210
0
            self.count_rows,
211
        )
212
0
    }
213
214
    /// Format table into a writer.
215
    #[cfg(feature = "std")]
216
    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
217
0
    pub fn build<W>(self, writer: W) -> std::io::Result<()>
218
0
    where
219
0
        I: IntoRecords,
220
0
        I::Cell: AsRef<str>,
221
0
        D: Dimension,
222
0
        W: std::io::Write,
223
    {
224
0
        let writer = crate::util::utf8_writer::UTF8Writer::new(writer);
225
0
        self.fmt(writer).map_err(std::io::Error::other)
226
0
    }
227
228
    /// Build a string.
229
    ///
230
    /// We can't implement [`std::string::ToString`] cause it does takes `&self` reference.
231
    #[allow(clippy::inherent_to_string)]
232
    #[cfg(feature = "std")]
233
    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
234
0
    pub fn to_string(self) -> String
235
0
    where
236
0
        I: IntoRecords,
237
0
        I::Cell: AsRef<str>,
238
0
        D: Dimension,
239
    {
240
0
        let mut buf = String::new();
241
0
        self.fmt(&mut buf)
242
0
            .expect("it's expected to be ok according to doc");
243
244
0
        buf
245
0
    }
246
}
247
248
impl<T, const ROWS: usize, const COLS: usize> From<[[T; COLS]; ROWS]>
249
    for CompactTable<[[T; COLS]; ROWS], ConstDimension<COLS, ROWS>>
250
where
251
    T: AsRef<str>,
252
{
253
0
    fn from(mat: [[T; COLS]; ROWS]) -> Self {
254
0
        let mut width = [0; COLS];
255
0
        for row in mat.iter() {
256
0
            for (col, text) in row.iter().enumerate() {
257
0
                let text = text.as_ref();
258
0
                let text_width = get_line_width(text);
259
0
                width[col] = max(width[col], text_width);
260
0
            }
261
        }
262
263
        // add padding
264
0
        for w in &mut width {
265
0
            *w += 2;
266
0
        }
267
268
0
        let dims = ConstDimension::new(ConstSize::List(width), ConstSize::Value(1));
269
0
        Self::with_dimension(mat, dims).columns(COLS).rows(ROWS)
270
0
    }
271
}
272
273
0
fn build_grid<W, I, D>(
274
0
    writer: W,
275
0
    records: I,
276
0
    dims: D,
277
0
    config: CompactConfig,
278
0
    cols: usize,
279
0
    rows: Option<usize>,
280
0
) -> fmt::Result
281
0
where
282
0
    W: fmt::Write,
283
0
    I: IntoRecords,
284
0
    I::Cell: AsRef<str>,
285
0
    D: Dimension,
286
{
287
0
    match rows {
288
0
        Some(limit) => {
289
0
            let records = LimitRows::new(records, limit);
290
0
            let records = LimitColumns::new(records, cols);
291
0
            let records = IterRecords::new(records, cols, rows);
292
0
            CompactGrid::new(records, config, dims, NoColors).build(writer)
293
        }
294
        None => {
295
0
            let records = LimitColumns::new(records, cols);
296
0
            let records = IterRecords::new(records, cols, rows);
297
0
            CompactGrid::new(records, config, dims, NoColors).build(writer)
298
        }
299
    }
300
0
}
301
302
0
const fn create_config() -> CompactConfig {
303
0
    CompactConfig::new()
304
0
        .set_padding(Sides::new(
305
0
            Indent::spaced(1),
306
0
            Indent::spaced(1),
307
0
            Indent::zero(),
308
0
            Indent::zero(),
309
        ))
310
0
        .set_alignment_horizontal(AlignmentHorizontal::Left)
311
0
        .set_borders(Style::ascii().get_borders())
312
0
}
313
314
impl<R, D> TableOption<R, CompactConfig, D> for CompactConfig {
315
0
    fn change(self, _: &mut R, cfg: &mut CompactConfig, _: &mut D) {
316
0
        *cfg = self;
317
0
    }
318
}