use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Length(i64);
impl Length {
pub const EMUS_PER_INCH: i64 = 914_400;
pub const EMUS_PER_CM: i64 = 360_000;
pub const EMUS_PER_MM: i64 = 36_000;
pub const EMUS_PER_PT: i64 = 12_700;
pub const EMUS_PER_TWIP: i64 = 635;
#[inline]
pub const fn new(emu: i64) -> Self {
Self(emu)
}
#[inline]
pub fn from_inches(inches: f64) -> Self {
Self((inches * Self::EMUS_PER_INCH as f64) as i64)
}
#[inline]
pub fn from_cm(cm: f64) -> Self {
Self((cm * Self::EMUS_PER_CM as f64) as i64)
}
#[inline]
pub fn from_mm(mm: f64) -> Self {
Self((mm * Self::EMUS_PER_MM as f64) as i64)
}
#[inline]
pub fn from_pt(pt: f64) -> Self {
Self((pt * Self::EMUS_PER_PT as f64) as i64)
}
#[inline]
pub fn from_twips(twips: f64) -> Self {
Self((twips * Self::EMUS_PER_TWIP as f64) as i64)
}
#[inline]
pub const fn emu(self) -> i64 {
self.0
}
#[inline]
pub fn inches(self) -> f64 {
self.0 as f64 / Self::EMUS_PER_INCH as f64
}
#[inline]
pub fn cm(self) -> f64 {
self.0 as f64 / Self::EMUS_PER_CM as f64
}
#[inline]
pub fn mm(self) -> f64 {
self.0 as f64 / Self::EMUS_PER_MM as f64
}
#[inline]
pub fn pt(self) -> f64 {
self.0 as f64 / Self::EMUS_PER_PT as f64
}
#[inline]
pub fn twips(self) -> i64 {
(self.0 as f64 / Self::EMUS_PER_TWIP as f64).round() as i64
}
}
impl fmt::Display for Length {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}emu", self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct RGBColor {
pub r: u8,
pub g: u8,
pub b: u8,
}
impl RGBColor {
#[inline]
pub const fn new(r: u8, g: u8, b: u8) -> Self {
Self { r, g, b }
}
pub fn from_hex(hex: &str) -> Result<Self, &'static str> {
if hex.len() != 6 {
return Err("Hex color must be exactly 6 characters");
}
let r =
u8::from_str_radix(&hex[0..2], 16).map_err(|_| "Invalid hex digit in red component")?;
let g = u8::from_str_radix(&hex[2..4], 16)
.map_err(|_| "Invalid hex digit in green component")?;
let b = u8::from_str_radix(&hex[4..6], 16)
.map_err(|_| "Invalid hex digit in blue component")?;
Ok(Self { r, g, b })
}
pub fn to_hex(&self) -> String {
format!("{:02X}{:02X}{:02X}", self.r, self.g, self.b)
}
}
impl fmt::Display for RGBColor {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"RGBColor({:02X}, {:02X}, {:02X})",
self.r, self.g, self.b
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_length_conversions() {
let len = Length::from_inches(1.0);
assert_eq!(len.emu(), 914400);
assert!((len.inches() - 1.0).abs() < 1e-6);
let len = Length::from_cm(1.0);
assert_eq!(len.emu(), 360000);
assert!((len.cm() - 1.0).abs() < 1e-6);
let len = Length::from_pt(72.0);
assert_eq!(len.emu(), 72 * 12700);
assert!((len.pt() - 72.0).abs() < 1e-6);
}
#[test]
fn test_rgb_color() {
let color = RGBColor::new(60, 47, 128);
assert_eq!(color.to_hex(), "3C2F80");
let from_hex = RGBColor::from_hex("3C2F80").unwrap();
assert_eq!(color, from_hex);
assert!(RGBColor::from_hex("GGGGGG").is_err());
assert!(RGBColor::from_hex("FF00").is_err());
}
}