restd/fmt/
pad.rs

1use super::{Format, Modifier, Result, Style, Write};
2use crate::io::Counter;
3
4/// The direction to place the text in when padding.
5#[allow(missing_docs)]
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
7pub enum Dir {
8    Left,
9    Center,
10    Right,
11}
12super::derive!(
13    enum Dir {
14        Left,
15        Center,
16        Right,
17    }
18);
19
20/// The style of padding.
21#[allow(missing_docs)]
22#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
23pub enum Kind {
24    /// Pad until the data is at least `count` characters wide.
25    #[default]
26    Full,
27
28    /// Pad until the data is a multiple of `count` characters wide.
29    Mod,
30}
31super::derive!(
32    enum Kind {
33        Full,
34        Mod,
35    }
36);
37
38// TODO: should this support arbitrary str padding?
39/// Pad any data with a given character until it reaches a certain width.
40#[derive(Clone, Copy, PartialEq, Eq)]
41pub struct Pad<S> {
42    /// The direction to place the text in when padding.
43    pub align: Dir,
44
45    /// The character to pad with.
46    pub with: char,
47
48    /// The width to target when padding.
49    pub count: usize,
50
51    /// The kind of padding.
52    pub kind: Kind,
53
54    /// The style being wrapped by this modifier.
55    pub style: S,
56}
57super::derive!(struct Pad<S!> { align, with, count, kind, style });
58
59impl<S: Style> Pad<S> {
60    /// Pad left.
61    ///
62    /// Shorthand for `Pad { dir: Dir::Left, with, count, kind: Full, style }`
63    pub const fn left(with: char, count: usize, style: S) -> Self {
64        Self {
65            align: Dir::Left,
66            with,
67            count,
68            kind: Kind::Full,
69            style,
70        }
71    }
72
73    /// Pad to the center.
74    ///
75    /// Shorthand for `Pad { dir: Dir::Center, with, count, kind: Full, style }`
76    pub const fn center(with: char, count: usize, style: S) -> Self {
77        Self {
78            align: Dir::Center,
79            with,
80            count,
81            kind: Kind::Full,
82            style,
83        }
84    }
85
86    /// Pad right.
87    ///
88    /// Shorthand for `Pad { dir: Dir::Right, with, count, kind: Full, style }`
89    pub const fn right(with: char, count: usize, style: S) -> Self {
90        Self {
91            align: Dir::Right,
92            with,
93            count,
94            kind: Kind::Full,
95            style,
96        }
97    }
98
99    /// Pad left using [modulo padding](Kind::Mod).
100    ///
101    /// Shorthand for `Pad { dir: Dir::Left, with, count, kind: Mod, style }`
102    pub const fn left_mod(with: char, count: usize, style: S) -> Self {
103        Self {
104            align: Dir::Left,
105            with,
106            count,
107            kind: Kind::Mod,
108            style,
109        }
110    }
111
112    /// Pad to the center using [modulo padding](Kind::Mod).
113    ///
114    /// Shorthand for `Pad { dir: Dir::Center, with, count, kind: Mod, style }`
115    pub const fn center_mod(with: char, count: usize, style: S) -> Self {
116        Self {
117            align: Dir::Center,
118            with,
119            count,
120            kind: Kind::Mod,
121            style,
122        }
123    }
124
125    /// Pad right using [modulo padding](Kind::Mod).
126    ///
127    /// Shorthand for `Pad { dir: Dir::Right, with, count, kind: Mod, style }`
128    pub const fn right_mod(with: char, count: usize, style: S) -> Self {
129        Self {
130            align: Dir::Right,
131            with,
132            count,
133            kind: Kind::Mod,
134            style,
135        }
136    }
137}
138
139impl<S: Style> Style for Pad<S> {}
140
141impl<S: Style> Modifier for Pad<S> {
142    type Inner = S;
143
144    fn apply<T>(&self, f: &mut dyn Write, data: &T) -> Result
145    where
146        T: Format<S> + ?Sized,
147    {
148        let mut counter = Counter::new();
149        data.fmt(&mut counter, &self.style)?;
150
151        let chs = match self.kind {
152            Kind::Full => self.count.saturating_sub(counter.0),
153            Kind::Mod => self.count - counter.0.checked_rem(self.count).unwrap_or(0),
154        };
155
156        match self.align {
157            Dir::Left => {
158                data.fmt(f, &self.style)?;
159                for _ in 0..chs {
160                    f.write_char(self.with)?;
161                }
162            }
163            Dir::Center => {
164                let before = chs / 2;
165                let after = chs.div_ceil(2);
166
167                for _ in 0..before {
168                    f.write_char(self.with)?;
169                }
170                data.fmt(f, &self.style)?;
171                for _ in 0..after {
172                    f.write_char(self.with)?;
173                }
174            }
175            Dir::Right => {
176                for _ in 0..chs {
177                    f.write_char(self.with)?;
178                }
179                data.fmt(f, &self.style)?;
180            }
181        }
182
183        Ok(())
184    }
185}