//! See [`Display`].

use alloc::boxed::Box;
use core::fmt;

use crate::traits::Hex;

wrapper_lite::wrapper!(
    #[wrapper(Debug)]
    #[wrapper(AsRef)]
    #[wrapper([const] AsMut)]
    #[wrapper(BorrowMut)]
    #[wrapper(DerefMut)]
    #[wrapper(From)]
    #[bound(T: AsRef<[u8]>)]
    #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
    #[derive(Clone, Copy, PartialEq, Eq)]
    /// A wrapper for displaying a byte slice as a hexadecimal string.
    ///
    /// ```rust
    /// let bytes = &[0xde, 0xad, 0xbe, 0xef];
    ///
    /// let displayed = fashex::Display::from(bytes);
    ///
    /// assert_eq!(format!("{}", &displayed), "deadbeef");
    /// assert_eq!(format!("{:X}", &displayed), "DEADBEEF");
    /// assert_eq!(format!("{:#x}", &displayed), "0xdeadbeef");
    /// assert_eq!(format!("{:#X}", &displayed), "0xDEADBEEF");
    /// ```
    pub struct Display<T> {
        value: T,
    }
);

impl<T: AsRef<[u8]>> fmt::Display for Display<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::LowerHex::fmt(self, f)
    }
}

impl<T: AsRef<[u8]>> fmt::LowerHex for Display<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.write::<false>(f)
    }
}

impl<T: AsRef<[u8]>> fmt::UpperHex for Display<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.write::<true>(f)
    }
}

impl<T> Display<T>
where
    T: AsRef<[u8]>,
{
    fn write<const UPPER: bool>(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if f.alternate() {
            f.write_str("0x")?;
        }

        f.write_str(
            &self
                .encode_hex::<Box<str>, UPPER>()
                .map_err(|_| fmt::Error)?,
        )
    }
}
