zero_copy_pads/
excess.rs

1use crate::{Unit, Width};
2use core::fmt::{Display, Error, Formatter};
3use derive_more::{AsMut, AsRef, Deref, DerefMut, From};
4
5/// Information about a situation where `total_width` is less than `value.width()`.
6///
7/// This information is to be passed to an excess handler.
8#[derive(Debug, Clone, Copy)]
9pub struct Excess<'a, Value, PadBlock = char>
10where
11    Value: Width,
12    PadBlock: Display,
13{
14    /// The value the caused the excess.
15    pub value: &'a Value,
16    /// The block that was used for the pad.
17    pub pad_block: &'a PadBlock,
18    /// The width of the value that caused the excess.
19    pub value_width: usize,
20    /// The total width that was exceeded by the value.
21    pub total_width: usize,
22}
23
24/// What to do when the width of the value exceeds total.
25///
26/// **Example:** Truncate to make it fit
27///
28/// ```
29/// # use pretty_assertions::assert_eq;
30/// use zero_copy_pads::{ExcessHandler, Excess, PaddedValue, AlignRight};
31/// use std::fmt::{Formatter, Result};
32/// struct TruncateExcessiveString;
33/// impl ExcessHandler<&str> for TruncateExcessiveString {
34///     fn handle_excess(&self, excess: Excess<&str>, formatter: &mut Formatter<'_>) -> Result {
35///         let mut value = excess.value.to_string();
36///         value.truncate(excess.total_width);
37///         write!(formatter, "{}", value)
38///     }
39/// }
40/// let padded_value = PaddedValue {
41///     handle_excess: TruncateExcessiveString,
42///     value: "abcdefghi",
43///     total_width: 4,
44///     pad_block: ' ',
45///     pad: AlignRight,
46/// };
47/// assert_eq!(padded_value.to_string(), "abcd");
48/// ```
49pub trait ExcessHandler<Value, PadBlock = char>
50where
51    Value: Width,
52    PadBlock: Display,
53{
54    /// Handle excessive width of a value.
55    fn handle_excess(
56        &self,
57        excess: Excess<Value, PadBlock>,
58        formatter: &mut Formatter<'_>,
59    ) -> Result<(), Error>;
60}
61
62type ExcessHandlingFunctionInner<Value, PadBlock> =
63    fn(Excess<Value, PadBlock>, &mut Formatter<'_>) -> Result<(), Error>;
64
65/// Turn a function (without closure) into a [`ExcessHandler`].
66///
67/// **Example:** Truncate to make it fit
68///
69/// ```
70/// # use pretty_assertions::assert_eq;
71/// use zero_copy_pads::{ExcessHandlingFunction, Excess, PaddedValue, AlignRight};
72/// use std::fmt::{Formatter, Result};
73/// let truncate = ExcessHandlingFunction::<&str>(|excess, formatter| {
74///     let mut value = excess.value.to_string();
75///     value.truncate(excess.total_width);
76///     write!(formatter, "{}", value)
77/// });
78/// let padded_value = PaddedValue {
79///     handle_excess: truncate,
80///     value: "abcdefghi",
81///     total_width: 4,
82///     pad_block: ' ',
83///     pad: AlignRight,
84/// };
85/// assert_eq!(padded_value.to_string(), "abcd");
86/// ```
87#[derive(Clone, Copy, AsMut, AsRef, Deref, DerefMut, From)]
88pub struct ExcessHandlingFunction<Value, PadBlock = char>(
89    pub ExcessHandlingFunctionInner<Value, PadBlock>,
90)
91where
92    Value: Width,
93    PadBlock: Display;
94
95impl<Value, PadBlock> ExcessHandler<Value, PadBlock> for ExcessHandlingFunction<Value, PadBlock>
96where
97    Value: Width,
98    PadBlock: Display,
99{
100    fn handle_excess(
101        &self,
102        excess: Excess<Value, PadBlock>,
103        formatter: &mut Formatter<'_>,
104    ) -> Result<(), Error> {
105        self.0(excess, formatter)
106    }
107}
108
109/// All pre-defined zero-sized [`ExcessHandler`] types in this [crate] implement this trait.
110pub trait UnitExcessHandler<Value, PadBlock = char>: Unit + ExcessHandler<Value, PadBlock>
111where
112    Value: Width,
113    PadBlock: Display,
114{
115}
116
117macro_rules! preset {
118    (
119        impl $implementation:expr;
120        $(#[$struct_attr:meta])*
121        struct $struct_name:ident;
122        $(#[$fn_attr:meta])*
123        fn $fn_name:ident;
124    ) => {
125        $(#[$struct_attr])*
126        #[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
127        pub struct $struct_name;
128
129        impl Unit for $struct_name {
130            const VALUE: Self = $struct_name;
131        }
132
133        impl<Value: Width, PadBlock: Display> UnitExcessHandler<Value, PadBlock> for $struct_name {}
134
135        impl<Value, PadBlock> ExcessHandler<Value, PadBlock> for $struct_name
136        where
137            Value: Width,
138            PadBlock: Display,
139        {
140            fn handle_excess(
141                &self,
142                excess: Excess<Value, PadBlock>,
143                formatter: &mut Formatter<'_>,
144            ) -> Result<(), Error> {
145                let handle_excess: ExcessHandlingFunctionInner<Value, PadBlock> = $implementation;
146                handle_excess(excess, formatter)
147            }
148        }
149
150        impl<Value, PadBlock> From<$struct_name> for ExcessHandlingFunction<Value, PadBlock>
151        where
152            Value: Width,
153            PadBlock: Display,
154        {
155            fn from(_: $struct_name) -> Self {
156                ExcessHandlingFunction(|excess, formatter| {
157                    $struct_name.handle_excess(excess, formatter)
158                })
159            }
160        }
161
162        $(#[$fn_attr])*
163        pub fn $fn_name<Value, PadBlock>() -> ExcessHandlingFunction<Value, PadBlock>
164        where
165            Value: Width,
166            PadBlock: Display,
167        {
168            ExcessHandlingFunction::from($struct_name)
169        }
170    };
171}
172
173preset! {
174    impl |excess, formatter| write!(formatter, "{}", excess.value);
175
176    #[doc = "Ignore excess, write `value` to `formatter` without padding."]
177    #[doc = ""]
178    #[doc = "**When `value.width()` is not greater than `total_width`,"]
179    #[doc = "add pads as usual:**"]
180    #[doc = "```"]
181    #[doc = "# use pretty_assertions::assert_eq;"]
182    #[doc = "use zero_copy_pads::{PaddedValue, AlignRight, IgnoreExcess};"]
183    #[doc = "let padded_value = PaddedValue {"]
184    #[doc = r#"    handle_excess: IgnoreExcess,"#]
185    #[doc = r#"    value: "abcdef","#]
186    #[doc = r#"    pad_block: '-',"#]
187    #[doc = r#"    total_width: 9,"#]
188    #[doc = r#"    pad: AlignRight,"#]
189    #[doc = "};"]
190    #[doc = r#"assert_eq!(padded_value.to_string(), "---abcdef");"#]
191    #[doc = "```"]
192    #[doc = ""]
193    #[doc = "**When `value.width()` is greater than `total_width`,"]
194    #[doc = "display `value` as is:**"]
195    #[doc = "```"]
196    #[doc = "# use pretty_assertions::assert_eq;"]
197    #[doc = "use zero_copy_pads::{PaddedValue, AlignRight, IgnoreExcess};"]
198    #[doc = "let padded_value = PaddedValue {"]
199    #[doc = r#"    handle_excess: IgnoreExcess,"#]
200    #[doc = r#"    value: "abcdefghijkl","#]
201    #[doc = r#"    pad_block: '-',"#]
202    #[doc = r#"    total_width: 9,"#]
203    #[doc = r#"    pad: AlignRight,"#]
204    #[doc = "};"]
205    #[doc = r#"assert_eq!(padded_value.to_string(), "abcdefghijkl");"#]
206    #[doc = "```"]
207    struct IgnoreExcess;
208
209    #[doc = "Create a [`ExcessHandlingFunction`] that ignores excesses."]
210    #[doc = ""]
211    #[doc = "see [`IgnoreExcess`]."]
212    fn ignore_excess;
213}
214
215preset! {
216    impl |_, _| Err(Error);
217
218    #[doc = "Forbid all excesses, raise `fmt::Error` once encounter one."]
219    #[doc = ""]
220    #[doc = "**When `value.width()` is not greater than `total_width`,"]
221    #[doc = "add pads as usual:**"]
222    #[doc = "```"]
223    #[doc = "# use pretty_assertions::assert_eq;"]
224    #[doc = "use zero_copy_pads::{PaddedValue, AlignRight, ErrorOnExcess};"]
225    #[doc = "let padded_value = PaddedValue {"]
226    #[doc = r#"    handle_excess: ErrorOnExcess,"#]
227    #[doc = r#"    value: "abcdef","#]
228    #[doc = r#"    pad_block: '-',"#]
229    #[doc = r#"    total_width: 9,"#]
230    #[doc = r#"    pad: AlignRight,"#]
231    #[doc = "};"]
232    #[doc = r#"assert_eq!(padded_value.to_string(), "---abcdef");"#]
233    #[doc = "```"]
234    #[doc = ""]
235    #[doc = "**When `value.width()` is greater than `total_width`,"]
236    #[doc = "return an [`Err`] of [`fmt::Error`](Error):**"]
237    #[doc = "```"]
238    #[doc = "# use pretty_assertions::assert_eq;"]
239    #[doc = "use zero_copy_pads::{PaddedValue, AlignRight, ErrorOnExcess};"]
240    #[doc = "let padded_value = PaddedValue {"]
241    #[doc = r#"    handle_excess: ErrorOnExcess,"#]
242    #[doc = r#"    value: "abcdefghijkl","#]
243    #[doc = r#"    pad_block: '-',"#]
244    #[doc = r#"    total_width: 9,"#]
245    #[doc = r#"    pad: AlignRight,"#]
246    #[doc = "};"]
247    #[doc = "let mut output = String::new();"]
248    #[doc = r#"std::fmt::write("#]
249    #[doc = r#"    &mut output,"#]
250    #[doc = r#"    format_args!("{}", padded_value),"#]
251    #[doc = r#").unwrap_err();"#]
252    #[doc = "```"]
253    struct ErrorOnExcess;
254
255    #[doc = "Create a [`ExcessHandlingFunction`] that forbids excesses."]
256    #[doc = ""]
257    #[doc = "see [`ErrorOnExcess`]."]
258    fn error_on_excess;
259}
260
261preset! {
262    impl |excess, _| panic!(
263        "value's width ({}) is greater than total_width ({})",
264        excess.value_width, excess.total_width,
265    );
266
267    #[doc = "Forbid all excesses, panic once encounter one."]
268    #[doc = ""]
269    #[doc = "**When `value.width()` is not greater than `total_width`,"]
270    #[doc = "add pads as usual:**"]
271    #[doc = "```"]
272    #[doc = "# use pretty_assertions::assert_eq;"]
273    #[doc = "use zero_copy_pads::{PaddedValue, AlignRight, PanicOnExcess};"]
274    #[doc = "let padded_value = PaddedValue {"]
275    #[doc = r#"    handle_excess: PanicOnExcess,"#]
276    #[doc = r#"    value: "abcdef","#]
277    #[doc = r#"    pad_block: '-',"#]
278    #[doc = r#"    total_width: 9,"#]
279    #[doc = r#"    pad: AlignRight,"#]
280    #[doc = "};"]
281    #[doc = r#"assert_eq!(padded_value.to_string(), "---abcdef");"#]
282    #[doc = "```"]
283    #[doc = ""]
284    #[doc = "**When `value.width()` is greater than `total_width`, panic:**"]
285    #[doc = "```should_panic"]
286    #[doc = "# use pretty_assertions::assert_eq;"]
287    #[doc = "use zero_copy_pads::{PaddedValue, AlignRight, PanicOnExcess};"]
288    #[doc = "let padded_value = PaddedValue {"]
289    #[doc = r#"    handle_excess: PanicOnExcess,"#]
290    #[doc = r#"    value: "abcdefghijkl","#]
291    #[doc = r#"    pad_block: '-',"#]
292    #[doc = r#"    total_width: 9,"#]
293    #[doc = r#"    pad: AlignRight,"#]
294    #[doc = "};"]
295    #[doc = r#"assert_eq!(padded_value.to_string(), "abcdefghijkl");"#]
296    #[doc = "```"]
297    struct PanicOnExcess;
298
299    #[doc = "Create a [`ExcessHandlingFunction`] that forbids excesses."]
300    #[doc = ""]
301    #[doc = "see [`PanicOnExcess`]."]
302    fn panic_on_excess;
303}