termimad/
spacing.rs

1use {
2    crate::{
3        compound_style::CompoundStyle,
4        errors::Result,
5    },
6    minimad::Alignment,
7};
8
9#[derive(Debug, Clone, Copy)]
10pub struct Spacing {
11    pub width: usize,
12    pub align: Alignment,
13}
14
15fn truncate(s: &str, max_chars: usize) -> &str {
16    match s.char_indices().nth(max_chars) {
17        None => s,
18        Some((idx, _)) => &s[..idx],
19    }
20}
21
22impl Spacing {
23    /// compute the number of chars to add left and write of inner_width
24    /// to fill outer_width
25    #[inline(always)]
26    pub const fn completions(
27        align: Alignment,
28        inner_width: usize,
29        outer_width: usize,
30    ) -> (usize, usize) {
31        if inner_width >= outer_width {
32            return (0, 0);
33        }
34        match align {
35            Alignment::Left | Alignment::Unspecified => (0, outer_width - inner_width),
36            Alignment::Center => {
37                let lp = (outer_width - inner_width) / 2;
38                (lp, outer_width - inner_width - lp)
39            }
40            Alignment::Right => (outer_width - inner_width, 0),
41        }
42    }
43    #[inline(always)]
44    pub const fn optional_completions(
45        align: Alignment,
46        inner_width: usize,
47        outer_width: Option<usize>,
48    ) -> (usize, usize) {
49        match outer_width {
50            Some(outer_width) => Spacing::completions(align, inner_width, outer_width),
51            None => (0, 0),
52        }
53    }
54    #[inline(always)]
55    pub const fn completions_for(&self, inner_width: usize) -> (usize, usize) {
56        Spacing::completions(self.align, inner_width, self.width)
57    }
58    pub fn write_counted_str<W>(
59        &self,
60        w: &mut W,
61        s: &str,
62        str_width: usize,
63        style: &CompoundStyle,
64    ) -> Result<()>
65    where
66        W: std::io::Write,
67    {
68        if str_width >= self.width {
69            // we must truncate
70            let s = truncate(s, self.width);
71            style.queue_str(w, s)?;
72        } else {
73            // we must complete with spaces
74            // This part could be written in a more efficient way
75            let (lp, rp) = self.completions_for(str_width);
76            let mut con = String::new();
77            for _ in 0..lp {
78                con.push(' ');
79            }
80            con.push_str(s);
81            for _ in 0..rp {
82                con.push(' ');
83            }
84            style.queue(w, con)?;
85        }
86        Ok(())
87    }
88    // FIXME use the number of chars instead of their real width,
89    // the crop writer should be used when wide characters are expected
90    pub fn write_str<W>(&self, w: &mut W, s: &str, style: &CompoundStyle) -> Result<()>
91    where
92        W: std::io::Write,
93    {
94        self.write_counted_str(w, s, s.chars().count(), style)
95    }
96}