Coverage Report

Created: 2026-01-25 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/papergrid-0.17.0/src/grid/compact.rs
Line
Count
Source
1
//! The module contains a [`CompactGrid`] structure,
2
//! which is a relatively strict grid.
3
4
use core::{
5
    borrow::Borrow,
6
    fmt::{self, Display, Write},
7
};
8
9
use crate::{
10
    ansi::{ANSIFmt, ANSIStr},
11
    colors::Colors,
12
    config::{AlignmentHorizontal, Borders, HorizontalLine, Indent, Sides},
13
    dimension::Dimension,
14
    records::{IntoRecords, Records},
15
    util::string::get_line_width,
16
};
17
18
use crate::config::compact::CompactConfig;
19
20
type ANSIString = ANSIStr<'static>;
21
22
/// Grid provides a set of methods for building a text-based table.
23
#[derive(Debug, Clone)]
24
pub struct CompactGrid<R, D, G, C> {
25
    records: R,
26
    config: G,
27
    dimension: D,
28
    colors: C,
29
}
30
31
impl<R, D, G, C> CompactGrid<R, D, G, C> {
32
    /// The new method creates a grid instance with default styles.
33
0
    pub fn new(records: R, config: G, dimension: D, colors: C) -> Self {
34
0
        CompactGrid {
35
0
            records,
36
0
            config,
37
0
            dimension,
38
0
            colors,
39
0
        }
40
0
    }
41
}
42
43
impl<R, D, G, C> CompactGrid<R, D, G, C> {
44
    /// Sets colors map.
45
0
    pub fn with_colors<Colors>(self, colors: Colors) -> CompactGrid<R, D, G, Colors> {
46
0
        CompactGrid {
47
0
            records: self.records,
48
0
            config: self.config,
49
0
            dimension: self.dimension,
50
0
            colors,
51
0
        }
52
0
    }
53
54
    /// Builds a table.
55
0
    pub fn build<F>(self, mut f: F) -> fmt::Result
56
0
    where
57
0
        R: Records,
58
0
        <R::Iter as IntoRecords>::Cell: AsRef<str>,
59
0
        D: Dimension,
60
0
        C: Colors,
61
0
        G: Borrow<CompactConfig>,
62
0
        F: Write,
63
    {
64
0
        if self.records.count_columns() == 0 {
65
0
            return Ok(());
66
0
        }
67
68
0
        let config = self.config.borrow();
69
0
        print_grid(&mut f, self.records, config, &self.dimension, &self.colors)
70
0
    }
71
72
    /// Builds a table into string.
73
    ///
74
    /// Notice that it consumes self.
75
    #[cfg(feature = "std")]
76
    #[allow(clippy::inherent_to_string)]
77
0
    pub fn to_string(self) -> String
78
0
    where
79
0
        R: Records,
80
0
        <R::Iter as IntoRecords>::Cell: AsRef<str>,
81
0
        D: Dimension,
82
0
        G: Borrow<CompactConfig>,
83
0
        C: Colors,
84
    {
85
0
        let mut buf = String::new();
86
0
        self.build(&mut buf).expect("It's guaranteed to never happen otherwise it's considered an stdlib error or impl error");
87
0
        buf
88
0
    }
89
}
90
91
impl<R, D, G, C> Display for CompactGrid<R, D, G, C>
92
where
93
    for<'a> &'a R: Records,
94
    for<'a> <<&'a R as Records>::Iter as IntoRecords>::Cell: AsRef<str>,
95
    D: Dimension,
96
    G: Borrow<CompactConfig>,
97
    C: Colors,
98
{
99
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100
0
        let records = &self.records;
101
0
        let config = self.config.borrow();
102
103
0
        print_grid(f, records, config, &self.dimension, &self.colors)
104
0
    }
105
}
106
107
#[derive(Debug, Clone)]
108
struct RowConfig<D, C> {
109
    margin: Sides<ColoredIndent>,
110
    pad: Sides<ColoredIndent>,
111
    verticals: HorizontalLine<ColoredIndent>,
112
    alignment: AlignmentHorizontal,
113
    dims: D,
114
    colors: C,
115
    count_columns: usize,
116
}
117
118
impl<D, C> RowConfig<D, C> {
119
0
    fn new(cfg: &CompactConfig, dims: D, colors: C, count_columns: usize) -> Self {
120
0
        let borders_chars = cfg.get_borders();
121
0
        let borders_colors = cfg.get_borders_color();
122
0
        let verticals = create_vertical_borders(borders_chars, borders_colors);
123
0
        let margin = create_margin(cfg);
124
0
        let pad = create_padding(cfg);
125
0
        let alignment = cfg.get_alignment_horizontal();
126
127
0
        Self {
128
0
            margin,
129
0
            pad,
130
0
            alignment,
131
0
            verticals,
132
0
            colors,
133
0
            dims,
134
0
            count_columns,
135
0
        }
136
0
    }
137
}
138
139
#[derive(Debug, Clone)]
140
struct RowIter<I> {
141
    iter: I,
142
    row: usize,
143
}
144
145
impl<I> RowIter<I> {
146
0
    fn new(iter: I, row: usize) -> Self {
147
0
        Self { iter, row }
148
0
    }
149
}
150
151
0
fn print_grid<F, R, D, C>(
152
0
    f: &mut F,
153
0
    records: R,
154
0
    cfg: &CompactConfig,
155
0
    dims: &D,
156
0
    colors: &C,
157
0
) -> fmt::Result
158
0
where
159
0
    F: Write,
160
0
    R: Records,
161
0
    <R::Iter as IntoRecords>::Cell: AsRef<str>,
162
0
    D: Dimension,
163
0
    C: Colors,
164
{
165
0
    let count_columns = records.count_columns();
166
0
    let count_rows = records.hint_count_rows();
167
168
0
    if count_columns == 0 || matches!(count_rows, Some(0)) {
169
0
        return Ok(());
170
0
    }
171
172
0
    let mut records = records.iter_rows().into_iter();
173
0
    let records_first = match records.next() {
174
0
        Some(row) => row,
175
0
        None => return Ok(()),
176
    };
177
178
0
    let wtotal = total_width(cfg, dims, count_columns);
179
180
0
    let borders_chars = cfg.get_borders();
181
0
    let borders_colors = cfg.get_borders_color();
182
183
0
    let horizontal_borders = create_horizontal(borders_chars);
184
0
    let horizontal_colors = create_horizontal_colors(borders_colors);
185
186
0
    let margin = create_margin(cfg);
187
188
0
    let rowcfg = RowConfig::new(cfg, dims, colors, count_columns);
189
190
0
    let mut new_line = false;
191
192
0
    if margin.top.space.size > 0 {
193
0
        let width_total = wtotal + margin.left.space.size + margin.right.space.size;
194
0
        let indent = ColoredIndent::new(width_total, margin.top.space.fill, margin.top.color);
195
0
        print_indent_lines(f, indent)?;
196
0
        new_line = true;
197
0
    }
198
199
0
    if borders_chars.has_top() {
200
0
        if new_line {
201
0
            f.write_char('\n')?
202
0
        }
203
204
0
        let borders = create_horizontal_top(borders_chars);
205
0
        let borders_colors = create_horizontal_top_colors(borders_colors);
206
0
        print_horizontal_line(f, dims, &borders, &borders_colors, &margin, count_columns)?;
207
208
0
        new_line = true;
209
0
    }
210
211
0
    if borders_chars.has_horizontal() {
212
0
        if new_line {
213
0
            f.write_char('\n')?;
214
0
        }
215
216
0
        let cells = records_first.into_iter();
217
0
        let iter = RowIter::new(cells, 0);
218
0
        print_grid_row(f, iter, &rowcfg)?;
219
220
0
        for (row, cells) in records.enumerate() {
221
0
            f.write_char('\n')?;
222
223
0
            print_horizontal_line(
224
0
                f,
225
0
                dims,
226
0
                &horizontal_borders,
227
0
                &horizontal_colors,
228
0
                &margin,
229
0
                count_columns,
230
0
            )?;
231
232
0
            f.write_char('\n')?;
233
234
0
            let cells = cells.into_iter();
235
0
            let iter = RowIter::new(cells, row + 1);
236
0
            print_grid_row(f, iter, &rowcfg)?;
237
        }
238
    } else {
239
0
        if new_line {
240
0
            f.write_char('\n')?;
241
0
        }
242
243
0
        let cells = records_first.into_iter();
244
0
        let iter = RowIter::new(cells, 0);
245
0
        print_grid_row(f, iter, &rowcfg)?;
246
247
0
        for (row, cells) in records.enumerate() {
248
0
            f.write_char('\n')?;
249
250
0
            let cells = cells.into_iter();
251
0
            let iter = RowIter::new(cells, row + 1);
252
0
            print_grid_row(f, iter, &rowcfg)?;
253
        }
254
    }
255
256
0
    if borders_chars.has_bottom() {
257
0
        f.write_char('\n')?;
258
259
0
        let borders = create_horizontal_bottom(borders_chars);
260
0
        let colors = create_horizontal_bottom_colors(borders_colors);
261
0
        print_horizontal_line(f, dims, &borders, &colors, &margin, count_columns)?;
262
0
    }
263
264
0
    if cfg.get_margin().bottom.size > 0 {
265
0
        f.write_char('\n')?;
266
267
0
        let width_total = wtotal + margin.left.space.size + margin.right.space.size;
268
0
        let indent = ColoredIndent::new(width_total, margin.bottom.space.fill, margin.bottom.color);
269
0
        print_indent_lines(f, indent)?;
270
0
    }
271
272
0
    Ok(())
273
0
}
274
275
0
fn create_margin(cfg: &CompactConfig) -> Sides<ColoredIndent> {
276
0
    let margin = cfg.get_margin();
277
0
    let margin_color = cfg.get_margin_color();
278
0
    Sides::new(
279
0
        ColoredIndent::from_indent(margin.left, margin_color.left),
280
0
        ColoredIndent::from_indent(margin.right, margin_color.right),
281
0
        ColoredIndent::from_indent(margin.top, margin_color.top),
282
0
        ColoredIndent::from_indent(margin.bottom, margin_color.bottom),
283
    )
284
0
}
285
286
0
fn create_vertical_borders(
287
0
    borders: &Borders<char>,
288
0
    colors: &Borders<ANSIString>,
289
0
) -> HorizontalLine<ColoredIndent> {
290
0
    let intersect = borders
291
0
        .vertical
292
0
        .map(|c| ColoredIndent::new(0, c, colors.vertical));
293
0
    let left = borders.left.map(|c| ColoredIndent::new(0, c, colors.left));
294
0
    let right = borders
295
0
        .right
296
0
        .map(|c| ColoredIndent::new(0, c, colors.right));
297
298
0
    HorizontalLine::new(None, intersect, left, right)
299
0
}
300
301
0
fn print_horizontal_line<F, D>(
302
0
    f: &mut F,
303
0
    dims: &D,
304
0
    borders: &HorizontalLine<char>,
305
0
    borders_colors: &HorizontalLine<ANSIString>,
306
0
    margin: &Sides<ColoredIndent>,
307
0
    count_columns: usize,
308
0
) -> fmt::Result
309
0
where
310
0
    F: fmt::Write,
311
0
    D: Dimension,
312
{
313
0
    let is_not_colored = borders_colors.is_empty();
314
315
0
    print_indent(f, margin.left)?;
316
317
0
    if is_not_colored {
318
0
        print_split_line(f, dims, borders, count_columns)?;
319
    } else {
320
0
        print_split_line_colored(f, dims, borders, borders_colors, count_columns)?;
321
    }
322
323
0
    print_indent(f, margin.right)?;
324
325
0
    Ok(())
326
0
}
327
328
0
fn print_grid_row<F, I, D, C>(f: &mut F, iter: RowIter<I>, rowcfg: &RowConfig<D, C>) -> fmt::Result
329
0
where
330
0
    F: Write,
331
0
    I: Iterator,
332
0
    I::Item: AsRef<str>,
333
0
    D: Dimension,
334
0
    C: Colors,
335
{
336
0
    for _ in 0..rowcfg.pad.top.space.size {
337
0
        print_indent(f, rowcfg.margin.left)?;
338
0
        print_row_columns_empty(f, rowcfg, rowcfg.pad.top.color)?;
339
0
        print_indent(f, rowcfg.margin.right)?;
340
341
0
        f.write_char('\n')?;
342
    }
343
344
0
    print_indent(f, rowcfg.margin.left)?;
345
0
    print_row_columns(f, iter, rowcfg)?;
346
0
    print_indent(f, rowcfg.margin.right)?;
347
348
0
    for _ in 0..rowcfg.pad.bottom.space.size {
349
0
        f.write_char('\n')?;
350
351
0
        print_indent(f, rowcfg.margin.left)?;
352
0
        print_row_columns_empty(f, rowcfg, rowcfg.pad.bottom.color)?;
353
0
        print_indent(f, rowcfg.margin.right)?;
354
    }
355
356
0
    Ok(())
357
0
}
358
359
0
fn create_padding(cfg: &CompactConfig) -> Sides<ColoredIndent> {
360
0
    let pad = cfg.get_padding();
361
0
    let colors = cfg.get_padding_color();
362
0
    Sides::new(
363
0
        ColoredIndent::new(pad.left.size, pad.left.fill, create_color(colors.left)),
364
0
        ColoredIndent::new(pad.right.size, pad.right.fill, create_color(colors.right)),
365
0
        ColoredIndent::new(pad.top.size, pad.top.fill, create_color(colors.top)),
366
0
        ColoredIndent::new(
367
0
            pad.bottom.size,
368
0
            pad.bottom.fill,
369
0
            create_color(colors.bottom),
370
        ),
371
    )
372
0
}
373
374
0
fn create_horizontal(b: &Borders<char>) -> HorizontalLine<char> {
375
0
    HorizontalLine::new(b.horizontal, b.intersection, b.left, b.right)
376
0
}
377
378
0
fn create_horizontal_top(b: &Borders<char>) -> HorizontalLine<char> {
379
0
    HorizontalLine::new(b.top, b.top_intersection, b.top_left, b.top_right)
380
0
}
381
382
0
fn create_horizontal_bottom(b: &Borders<char>) -> HorizontalLine<char> {
383
0
    HorizontalLine::new(
384
0
        b.bottom,
385
0
        b.bottom_intersection,
386
0
        b.bottom_left,
387
0
        b.bottom_right,
388
    )
389
0
}
390
391
0
fn create_horizontal_colors(b: &Borders<ANSIString>) -> HorizontalLine<ANSIString> {
392
0
    HorizontalLine::new(b.horizontal, b.intersection, b.left, b.right)
393
0
}
394
395
0
fn create_horizontal_top_colors(b: &Borders<ANSIString>) -> HorizontalLine<ANSIString> {
396
0
    HorizontalLine::new(b.top, b.top_intersection, b.top_left, b.top_right)
397
0
}
398
399
0
fn create_horizontal_bottom_colors(b: &Borders<ANSIString>) -> HorizontalLine<ANSIString> {
400
0
    HorizontalLine::new(
401
0
        b.bottom,
402
0
        b.bottom_intersection,
403
0
        b.bottom_left,
404
0
        b.bottom_right,
405
    )
406
0
}
407
408
0
fn total_width<D>(cfg: &CompactConfig, dims: &D, count_columns: usize) -> usize
409
0
where
410
0
    D: Dimension,
411
{
412
0
    let content_width = total_columns_width(dims, count_columns);
413
0
    let count_verticals = count_verticals(cfg, count_columns);
414
415
0
    content_width + count_verticals
416
0
}
417
418
0
fn total_columns_width<D>(dims: &D, count_columns: usize) -> usize
419
0
where
420
0
    D: Dimension,
421
{
422
0
    (0..count_columns).map(|i| dims.get_width(i)).sum::<usize>()
423
0
}
424
425
0
fn count_verticals(cfg: &CompactConfig, count_columns: usize) -> usize {
426
0
    assert!(count_columns > 0);
427
428
0
    let count_verticals = count_columns - 1;
429
0
    let borders = cfg.get_borders();
430
0
    borders.has_vertical() as usize * count_verticals
431
0
        + borders.has_left() as usize
432
0
        + borders.has_right() as usize
433
0
}
434
435
0
fn print_row_columns<F, I, D, C>(
436
0
    f: &mut F,
437
0
    mut iter: RowIter<I>,
438
0
    rowcfg: &RowConfig<D, C>,
439
0
) -> fmt::Result
440
0
where
441
0
    F: Write,
442
0
    I: Iterator,
443
0
    I::Item: AsRef<str>,
444
0
    D: Dimension,
445
0
    C: Colors,
446
{
447
0
    if let Some(indent) = rowcfg.verticals.left {
448
0
        print_char(f, indent.space.fill, indent.color)?;
449
0
    }
450
451
0
    let text = iter
452
0
        .iter
453
0
        .next()
454
0
        .expect("we check in the beginning that size must be at least 1 column");
455
0
    let width = rowcfg.dims.get_width(0);
456
0
    let color = rowcfg.colors.get_color((iter.row, 0).into());
457
458
0
    let text = text.as_ref();
459
0
    let text = text.lines().next().unwrap_or("");
460
0
    print_cell(f, text, color, &rowcfg.pad, rowcfg.alignment, width)?;
461
462
0
    match rowcfg.verticals.intersection {
463
0
        Some(indent) => {
464
0
            for (col, text) in iter.iter.enumerate() {
465
0
                let col = col + 1;
466
467
0
                let width = rowcfg.dims.get_width(col);
468
0
                let color = rowcfg.colors.get_color((iter.row, col).into());
469
0
                let text = text.as_ref();
470
0
                let text = text.lines().next().unwrap_or("");
471
472
0
                print_char(f, indent.space.fill, indent.color)?;
473
0
                print_cell(f, text, color, &rowcfg.pad, rowcfg.alignment, width)?;
474
            }
475
        }
476
        None => {
477
0
            for (col, text) in iter.iter.enumerate() {
478
0
                let col = col + 1;
479
480
0
                let width = rowcfg.dims.get_width(col);
481
0
                let color = rowcfg.colors.get_color((iter.row, col).into());
482
0
                let text = text.as_ref();
483
0
                let text = text.lines().next().unwrap_or("");
484
485
0
                print_cell(f, text, color, &rowcfg.pad, rowcfg.alignment, width)?;
486
            }
487
        }
488
    }
489
490
0
    if let Some(indent) = rowcfg.verticals.right {
491
0
        print_char(f, indent.space.fill, indent.color)?;
492
0
    }
493
494
0
    Ok(())
495
0
}
496
497
0
fn print_row_columns_empty<F, D, C>(
498
0
    f: &mut F,
499
0
    rowcfg: &RowConfig<D, C>,
500
0
    color: Option<ANSIString>,
501
0
) -> fmt::Result
502
0
where
503
0
    F: Write,
504
0
    D: Dimension,
505
{
506
0
    if let Some(indent) = rowcfg.verticals.left {
507
0
        print_char(f, indent.space.fill, indent.color)?;
508
0
    }
509
510
0
    let width = rowcfg.dims.get_width(0);
511
0
    print_indent(f, ColoredIndent::new(width, ' ', color))?;
512
513
0
    match rowcfg.verticals.intersection {
514
0
        Some(indent) => {
515
0
            for column in 1..rowcfg.count_columns {
516
0
                let width = rowcfg.dims.get_width(column);
517
518
0
                print_char(f, indent.space.fill, indent.color)?;
519
0
                print_indent(f, ColoredIndent::new(width, ' ', color))?;
520
            }
521
        }
522
        None => {
523
0
            for column in 1..rowcfg.count_columns {
524
0
                let width = rowcfg.dims.get_width(column);
525
0
                print_indent(f, ColoredIndent::new(width, ' ', color))?;
526
            }
527
        }
528
    }
529
530
0
    if let Some(indent) = rowcfg.verticals.right {
531
0
        print_char(f, indent.space.fill, indent.color)?;
532
0
    }
533
534
0
    Ok(())
535
0
}
536
537
0
fn print_cell<F, C>(
538
0
    f: &mut F,
539
0
    text: &str,
540
0
    color: Option<C>,
541
0
    padding: &Sides<ColoredIndent>,
542
0
    alignment: AlignmentHorizontal,
543
0
    width: usize,
544
0
) -> fmt::Result
545
0
where
546
0
    F: Write,
547
0
    C: ANSIFmt,
548
{
549
0
    let available = width - (padding.left.space.size + padding.right.space.size);
550
551
0
    let text_width = get_line_width(text);
552
0
    let (left, right) = if available > text_width {
553
0
        calculate_indent(alignment, text_width, available)
554
    } else {
555
0
        (0, 0)
556
    };
557
558
0
    print_indent(f, padding.left)?;
559
560
0
    repeat_char(f, ' ', left)?;
561
0
    print_text(f, text, color)?;
562
0
    repeat_char(f, ' ', right)?;
563
564
0
    print_indent(f, padding.right)?;
565
566
0
    Ok(())
567
0
}
568
569
0
fn print_split_line_colored<F, D>(
570
0
    f: &mut F,
571
0
    dimension: &D,
572
0
    borders: &HorizontalLine<char>,
573
0
    borders_colors: &HorizontalLine<ANSIString>,
574
0
    count_columns: usize,
575
0
) -> fmt::Result
576
0
where
577
0
    F: Write,
578
0
    D: Dimension,
579
{
580
0
    let mut used_color = ANSIStr::default();
581
0
    let chars_main = borders.main.unwrap_or(' ');
582
583
0
    if let Some(c) = borders.left {
584
0
        if let Some(color) = &borders_colors.right {
585
0
            prepare_coloring(f, color, &mut used_color)?;
586
0
        }
587
588
0
        f.write_char(c)?;
589
0
    }
590
591
0
    let width = dimension.get_width(0);
592
0
    if width > 0 {
593
0
        if let Some(color) = borders_colors.main {
594
0
            prepare_coloring(f, &color, &mut used_color)?;
595
0
        }
596
597
0
        repeat_char(f, chars_main, width)?;
598
0
    }
599
600
0
    for col in 1..count_columns {
601
0
        if let Some(c) = borders.intersection {
602
0
            if let Some(color) = borders_colors.intersection {
603
0
                prepare_coloring(f, &color, &mut used_color)?;
604
0
            }
605
606
0
            f.write_char(c)?;
607
0
        }
608
609
0
        let width = dimension.get_width(col);
610
0
        if width > 0 {
611
0
            if let Some(color) = borders_colors.main {
612
0
                prepare_coloring(f, &color, &mut used_color)?;
613
0
            }
614
615
0
            repeat_char(f, chars_main, width)?;
616
0
        }
617
    }
618
619
0
    if let Some(c) = borders.right {
620
0
        if let Some(color) = &borders_colors.right {
621
0
            prepare_coloring(f, color, &mut used_color)?;
622
0
        }
623
624
0
        f.write_char(c)?;
625
0
    }
626
627
0
    used_color.fmt_ansi_suffix(f)?;
628
629
0
    Ok(())
630
0
}
631
632
0
fn print_split_line<F, D>(
633
0
    f: &mut F,
634
0
    dims: &D,
635
0
    chars: &HorizontalLine<char>,
636
0
    count_columns: usize,
637
0
) -> fmt::Result
638
0
where
639
0
    F: Write,
640
0
    D: Dimension,
641
{
642
0
    let chars_main = chars.main.unwrap_or(' ');
643
644
0
    if let Some(c) = chars.left {
645
0
        f.write_char(c)?;
646
0
    }
647
648
0
    let width = dims.get_width(0);
649
0
    if width > 0 {
650
0
        repeat_char(f, chars_main, width)?;
651
0
    }
652
653
0
    for col in 1..count_columns {
654
0
        if let Some(c) = chars.intersection {
655
0
            f.write_char(c)?;
656
0
        }
657
658
0
        let width = dims.get_width(col);
659
0
        if width > 0 {
660
0
            repeat_char(f, chars_main, width)?;
661
0
        }
662
    }
663
664
0
    if let Some(c) = chars.right {
665
0
        f.write_char(c)?;
666
0
    }
667
668
0
    Ok(())
669
0
}
670
671
0
fn print_text<F, C>(f: &mut F, text: &str, color: Option<C>) -> fmt::Result
672
0
where
673
0
    F: Write,
674
0
    C: ANSIFmt,
675
{
676
0
    match color {
677
0
        Some(color) => {
678
0
            color.fmt_ansi_prefix(f)?;
679
0
            f.write_str(text)?;
680
0
            color.fmt_ansi_suffix(f)?;
681
        }
682
        None => {
683
0
            f.write_str(text)?;
684
        }
685
    };
686
687
0
    Ok(())
688
0
}
689
690
0
fn prepare_coloring<F>(f: &mut F, clr: &ANSIString, used: &mut ANSIString) -> fmt::Result
691
0
where
692
0
    F: Write,
693
{
694
0
    if *used != *clr {
695
0
        used.fmt_ansi_suffix(f)?;
696
0
        clr.fmt_ansi_prefix(f)?;
697
0
        *used = *clr;
698
0
    }
699
700
0
    Ok(())
701
0
}
702
703
0
fn calculate_indent(
704
0
    alignment: AlignmentHorizontal,
705
0
    text_width: usize,
706
0
    available: usize,
707
0
) -> (usize, usize) {
708
0
    let diff = available - text_width;
709
0
    match alignment {
710
0
        AlignmentHorizontal::Left => (0, diff),
711
0
        AlignmentHorizontal::Right => (diff, 0),
712
        AlignmentHorizontal::Center => {
713
0
            let left = diff / 2;
714
0
            let rest = diff - left;
715
0
            (left, rest)
716
        }
717
    }
718
0
}
719
720
0
fn repeat_char<F>(f: &mut F, c: char, n: usize) -> fmt::Result
721
0
where
722
0
    F: Write,
723
{
724
0
    for _ in 0..n {
725
0
        f.write_char(c)?;
726
    }
727
728
0
    Ok(())
729
0
}
730
731
// todo: replace Option<StaticColor> to StaticColor and check performance
732
0
fn print_char<F>(f: &mut F, c: char, color: Option<ANSIString>) -> fmt::Result
733
0
where
734
0
    F: Write,
735
{
736
0
    match color {
737
0
        Some(color) => {
738
0
            color.fmt_ansi_prefix(f)?;
739
0
            f.write_char(c)?;
740
0
            color.fmt_ansi_suffix(f)
741
        }
742
0
        None => f.write_char(c),
743
    }
744
0
}
745
746
0
fn print_indent_lines<F>(f: &mut F, indent: ColoredIndent) -> fmt::Result
747
0
where
748
0
    F: Write,
749
{
750
0
    print_indent(f, indent)?;
751
0
    f.write_char('\n')?;
752
753
0
    for _ in 1..indent.space.size {
754
0
        f.write_char('\n')?;
755
0
        print_indent(f, indent)?;
756
    }
757
758
0
    Ok(())
759
0
}
760
761
0
fn print_indent<F>(f: &mut F, indent: ColoredIndent) -> fmt::Result
762
0
where
763
0
    F: Write,
764
{
765
0
    match indent.color {
766
0
        Some(color) => {
767
0
            color.fmt_ansi_prefix(f)?;
768
0
            repeat_char(f, indent.space.fill, indent.space.size)?;
769
0
            color.fmt_ansi_suffix(f)?;
770
        }
771
        None => {
772
0
            repeat_char(f, indent.space.fill, indent.space.size)?;
773
        }
774
    }
775
776
0
    Ok(())
777
0
}
778
779
#[derive(Debug, Clone, Copy)]
780
struct ColoredIndent {
781
    space: Indent,
782
    color: Option<ANSIString>,
783
}
784
785
impl ColoredIndent {
786
0
    fn new(width: usize, c: char, color: Option<ANSIString>) -> Self {
787
0
        Self {
788
0
            space: Indent::new(width, c),
789
0
            color,
790
0
        }
791
0
    }
792
793
0
    fn from_indent(indent: Indent, color: ANSIString) -> Self {
794
0
        Self {
795
0
            space: indent,
796
0
            color: create_color(color),
797
0
        }
798
0
    }
799
}
800
801
0
fn create_color(color: ANSIString) -> Option<ANSIString> {
802
0
    if color.is_empty() {
803
0
        None
804
    } else {
805
0
        Some(color)
806
    }
807
0
}