use std::fmt::Display;
use thiserror::Error;
#[derive(Error, Debug, PartialEq, Copy, Clone)]
pub enum ZplError {
#[error("Position {0} out of range (0-32000)")]
InvalidPosition(u16),
#[error("Font size {0} out of range (10-32000)")]
InvalidFontSize(u16),
#[error("Barcode width {0} out of range (1-10)")]
InvalidBarcodeWidth(u8),
#[error("Width ratio {0} out of range (2.0-3.0)")]
InvalidWidthRatio(f32),
#[error("Barcode height {0} out of range (1-32000)")]
InvalidBarcodeHeight(u16),
#[error("Thickness {0} out of range (1-32000)")]
InvalidThickness(u16),
#[error("Graphic dimension {0} out of range (3-4095)")]
InvalidGraphicDimension(u16),
#[error("Rounding {0} out of range (0-8)")]
InvalidRounding(u8),
#[error("Invalid font '{0}' (expected A-Z or 1-9)")]
InvalidFont(char),
#[error(
"Box dimension {value} is smaller than thickness {thickness} (must be {thickness}-32000)"
)]
InvalidBoxDimension {
value: u16,
thickness: u16,
},
}
macro_rules! bounded_u16 {
($name: ident, $min:expr, $max:expr, $err:ident) => {
#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
pub(crate) struct $name(pub(crate) u16);
impl TryFrom<u16> for $name {
type Error = ZplError;
fn try_from(v: u16) -> Result<Self, Self::Error> {
if ($min..=$max).contains(&v) {
Ok(Self(v))
} else {
Err(ZplError::$err(v))
}
}
}
impl Display for $name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
};
}
macro_rules! bounded_u8 {
($name: ident, $min:expr, $max:expr, $err:ident) => {
#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
pub(crate) struct $name(pub(crate) u8);
impl TryFrom<u8> for $name {
type Error = ZplError;
fn try_from(v: u8) -> Result<Self, Self::Error> {
if ($min..=$max).contains(&v) {
Ok(Self(v))
} else {
Err(ZplError::$err(v))
}
}
}
impl Display for $name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
};
}
bounded_u16!(AxisPosition, 0, 32000, InvalidPosition);
bounded_u16!(FontSize, 10, 32000, InvalidFontSize);
bounded_u16!(BarcodeHeight, 1, 32000, InvalidBarcodeHeight);
bounded_u16!(Thickness, 1, 32000, InvalidThickness);
bounded_u16!(GraphicDimension, 3, 4095, InvalidGraphicDimension);
bounded_u8!(BarcodeWidth, 1, 10, InvalidBarcodeWidth);
bounded_u8!(Rounding, 0, 8, InvalidRounding);
#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
pub(crate) struct WidthRatio(pub(crate) u8);
impl TryFrom<f32> for WidthRatio {
type Error = ZplError;
fn try_from(value: f32) -> Result<Self, Self::Error> {
let tenths = (value * 10.0).round() as i32;
if (20..=30).contains(&tenths) {
Ok(Self(tenths as u8))
} else {
Err(ZplError::InvalidWidthRatio(value))
}
}
}
impl Display for WidthRatio {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:.1}", self.0 as f32 / 10.0)
}
}
#[derive(Debug, PartialEq, Clone)]
pub(crate) struct Font(pub(crate) char);
impl TryFrom<char> for Font {
type Error = ZplError;
fn try_from(c: char) -> Result<Self, Self::Error> {
if c.is_ascii_uppercase() || ('1'..='9').contains(&c) {
Ok(Self(c))
} else {
Err(ZplError::InvalidFont(c))
}
}
}
impl Display for Font {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, PartialEq, Clone, Copy)]
pub(crate) struct BoxDimension(pub(crate) u16);
impl BoxDimension {
pub(crate) fn try_new(value: u16, thickness: Thickness) -> Result<Self, ZplError> {
if (thickness.0..=32000).contains(&value) {
Ok(Self(value))
} else {
Err(ZplError::InvalidBoxDimension {
value,
thickness: thickness.0,
})
}
}
}
impl Display for BoxDimension {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}