use crate::{Unit, Width};
use core::fmt::{Display, Error, Formatter};
use derive_more::{AsMut, AsRef, Deref, DerefMut, From};
#[derive(Debug, Clone, Copy)]
pub struct Excess<'a, Value, PadBlock = char>
where
Value: Width,
PadBlock: Display,
{
pub value: &'a Value,
pub pad_block: &'a PadBlock,
pub value_width: usize,
pub total_width: usize,
}
pub trait ExcessHandler<Value, PadBlock = char>
where
Value: Width,
PadBlock: Display,
{
fn handle_excess(
&self,
excess: Excess<Value, PadBlock>,
formatter: &mut Formatter<'_>,
) -> Result<(), Error>;
}
type ExcessHandlingFunctionInner<Value, PadBlock> =
fn(Excess<Value, PadBlock>, &mut Formatter<'_>) -> Result<(), Error>;
#[derive(Clone, Copy, AsMut, AsRef, Deref, DerefMut, From)]
pub struct ExcessHandlingFunction<Value, PadBlock = char>(
pub ExcessHandlingFunctionInner<Value, PadBlock>,
)
where
Value: Width,
PadBlock: Display;
impl<Value, PadBlock> ExcessHandler<Value, PadBlock> for ExcessHandlingFunction<Value, PadBlock>
where
Value: Width,
PadBlock: Display,
{
fn handle_excess(
&self,
excess: Excess<Value, PadBlock>,
formatter: &mut Formatter<'_>,
) -> Result<(), Error> {
self.0(excess, formatter)
}
}
pub trait UnitExcessHandler<Value, PadBlock = char>: Unit + ExcessHandler<Value, PadBlock>
where
Value: Width,
PadBlock: Display,
{
}
macro_rules! preset {
(
impl $implementation:expr;
$(#[$struct_attr:meta])*
struct $struct_name:ident;
$(#[$fn_attr:meta])*
fn $fn_name:ident;
) => {
$(#[$struct_attr])*
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct $struct_name;
impl Unit for $struct_name {
const VALUE: Self = $struct_name;
}
impl<Value: Width, PadBlock: Display> UnitExcessHandler<Value, PadBlock> for $struct_name {}
impl<Value, PadBlock> ExcessHandler<Value, PadBlock> for $struct_name
where
Value: Width,
PadBlock: Display,
{
fn handle_excess(
&self,
excess: Excess<Value, PadBlock>,
formatter: &mut Formatter<'_>,
) -> Result<(), Error> {
let handle_excess: ExcessHandlingFunctionInner<Value, PadBlock> = $implementation;
handle_excess(excess, formatter)
}
}
impl<Value, PadBlock> From<$struct_name> for ExcessHandlingFunction<Value, PadBlock>
where
Value: Width,
PadBlock: Display,
{
fn from(_: $struct_name) -> Self {
ExcessHandlingFunction(|excess, formatter| {
$struct_name.handle_excess(excess, formatter)
})
}
}
$(#[$fn_attr])*
pub fn $fn_name<Value, PadBlock>() -> ExcessHandlingFunction<Value, PadBlock>
where
Value: Width,
PadBlock: Display,
{
ExcessHandlingFunction::from($struct_name)
}
};
}
preset! {
impl |excess, formatter| write!(formatter, "{}", excess.value);
#[doc = "Ignore excess, write `value` to `formatter` without padding."]
#[doc = ""]
#[doc = "**When `value.width()` is not greater than `total_width`,"]
#[doc = "add pads as usual:**"]
#[doc = "```"]
#[doc = "# use pretty_assertions::assert_eq;"]
#[doc = "use zero_copy_pads::{PaddedValue, AlignRight, IgnoreExcess};"]
#[doc = "let padded_value = PaddedValue {"]
#[doc = r#" handle_excess: IgnoreExcess,"#]
#[doc = r#" value: "abcdef","#]
#[doc = r#" pad_block: '-',"#]
#[doc = r#" total_width: 9,"#]
#[doc = r#" pad: AlignRight,"#]
#[doc = "};"]
#[doc = r#"assert_eq!(padded_value.to_string(), "---abcdef");"#]
#[doc = "```"]
#[doc = ""]
#[doc = "**When `value.width()` is greater than `total_width`,"]
#[doc = "display `value` as is:**"]
#[doc = "```"]
#[doc = "# use pretty_assertions::assert_eq;"]
#[doc = "use zero_copy_pads::{PaddedValue, AlignRight, IgnoreExcess};"]
#[doc = "let padded_value = PaddedValue {"]
#[doc = r#" handle_excess: IgnoreExcess,"#]
#[doc = r#" value: "abcdefghijkl","#]
#[doc = r#" pad_block: '-',"#]
#[doc = r#" total_width: 9,"#]
#[doc = r#" pad: AlignRight,"#]
#[doc = "};"]
#[doc = r#"assert_eq!(padded_value.to_string(), "abcdefghijkl");"#]
#[doc = "```"]
struct IgnoreExcess;
#[doc = "Create a [`ExcessHandlingFunction`] that ignores excesses."]
#[doc = ""]
#[doc = "see [`IgnoreExcess`]."]
fn ignore_excess;
}
preset! {
impl |_, _| Err(Error);
#[doc = "Forbid all excesses, raise `fmt::Error` once encounter one."]
#[doc = ""]
#[doc = "**When `value.width()` is not greater than `total_width`,"]
#[doc = "add pads as usual:**"]
#[doc = "```"]
#[doc = "# use pretty_assertions::assert_eq;"]
#[doc = "use zero_copy_pads::{PaddedValue, AlignRight, ErrorOnExcess};"]
#[doc = "let padded_value = PaddedValue {"]
#[doc = r#" handle_excess: ErrorOnExcess,"#]
#[doc = r#" value: "abcdef","#]
#[doc = r#" pad_block: '-',"#]
#[doc = r#" total_width: 9,"#]
#[doc = r#" pad: AlignRight,"#]
#[doc = "};"]
#[doc = r#"assert_eq!(padded_value.to_string(), "---abcdef");"#]
#[doc = "```"]
#[doc = ""]
#[doc = "**When `value.width()` is greater than `total_width`,"]
#[doc = "return an [`Err`] of [`fmt::Error`](Error):**"]
#[doc = "```"]
#[doc = "# use pretty_assertions::assert_eq;"]
#[doc = "use zero_copy_pads::{PaddedValue, AlignRight, ErrorOnExcess};"]
#[doc = "let padded_value = PaddedValue {"]
#[doc = r#" handle_excess: ErrorOnExcess,"#]
#[doc = r#" value: "abcdefghijkl","#]
#[doc = r#" pad_block: '-',"#]
#[doc = r#" total_width: 9,"#]
#[doc = r#" pad: AlignRight,"#]
#[doc = "};"]
#[doc = "let mut output = String::new();"]
#[doc = r#"std::fmt::write("#]
#[doc = r#" &mut output,"#]
#[doc = r#" format_args!("{}", padded_value),"#]
#[doc = r#").unwrap_err();"#]
#[doc = "```"]
struct ErrorOnExcess;
#[doc = "Create a [`ExcessHandlingFunction`] that forbids excesses."]
#[doc = ""]
#[doc = "see [`ErrorOnExcess`]."]
fn error_on_excess;
}
preset! {
impl |excess, _| panic!(
"value's width ({}) is greater than total_width ({})",
excess.value_width, excess.total_width,
);
#[doc = "Forbid all excesses, panic once encounter one."]
#[doc = ""]
#[doc = "**When `value.width()` is not greater than `total_width`,"]
#[doc = "add pads as usual:**"]
#[doc = "```"]
#[doc = "# use pretty_assertions::assert_eq;"]
#[doc = "use zero_copy_pads::{PaddedValue, AlignRight, PanicOnExcess};"]
#[doc = "let padded_value = PaddedValue {"]
#[doc = r#" handle_excess: PanicOnExcess,"#]
#[doc = r#" value: "abcdef","#]
#[doc = r#" pad_block: '-',"#]
#[doc = r#" total_width: 9,"#]
#[doc = r#" pad: AlignRight,"#]
#[doc = "};"]
#[doc = r#"assert_eq!(padded_value.to_string(), "---abcdef");"#]
#[doc = "```"]
#[doc = ""]
#[doc = "**When `value.width()` is greater than `total_width`, panic:**"]
#[doc = "```should_panic"]
#[doc = "# use pretty_assertions::assert_eq;"]
#[doc = "use zero_copy_pads::{PaddedValue, AlignRight, PanicOnExcess};"]
#[doc = "let padded_value = PaddedValue {"]
#[doc = r#" handle_excess: PanicOnExcess,"#]
#[doc = r#" value: "abcdefghijkl","#]
#[doc = r#" pad_block: '-',"#]
#[doc = r#" total_width: 9,"#]
#[doc = r#" pad: AlignRight,"#]
#[doc = "};"]
#[doc = r#"assert_eq!(padded_value.to_string(), "abcdefghijkl");"#]
#[doc = "```"]
struct PanicOnExcess;
#[doc = "Create a [`ExcessHandlingFunction`] that forbids excesses."]
#[doc = ""]
#[doc = "see [`PanicOnExcess`]."]
fn panic_on_excess;
}