use std::{fmt::Display, str::FromStr};
use nom::{branch::alt, bytes::complete::tag, combinator::map, sequence::tuple, Err};
use crate::Error;
use super::Unit;
#[derive(Debug, PartialEq, PartialOrd, Clone, Copy)]
pub struct Length(pub f32, pub Unit);
impl Display for Length {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}{}", self.0, self.1.as_ref())
}
}
impl FromStr for Length {
type Err = crate::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let (_, (value, unit)) = tuple((
nom::number::complete::float,
alt((
map(tag("em"), |_| Unit::Em),
map(tag("ex"), |_| Unit::Ex),
map(tag("px"), |_| Unit::Px),
map(tag("in"), |_| Unit::In),
map(tag("cm"), |_| Unit::Cm),
map(tag("mm"), |_| Unit::Mm),
map(tag("pt"), |_| Unit::Pt),
map(tag("pc"), |_| Unit::Pc),
map(tag("%"), |_| Unit::Percentages),
)),
))(s)
.map_err(|_: Err<nom::error::Error<&str>>| Error::Angle(s.to_owned()))?;
Ok(Self(value, unit))
}
}
impl From<f32> for Length {
fn from(value: f32) -> Self {
Self::px(value)
}
}
impl Length {
pub fn em(value: f32) -> Self {
Self(value, Unit::Em)
}
pub fn ex(value: f32) -> Self {
Self(value, Unit::Ex)
}
pub fn px(value: f32) -> Self {
Self(value, Unit::Px)
}
pub fn inch(value: f32) -> Self {
Self(value, Unit::In)
}
pub fn cm(value: f32) -> Self {
Self(value, Unit::Cm)
}
pub fn mm(value: f32) -> Self {
Self(value, Unit::Mm)
}
pub fn pt(value: f32) -> Self {
Self(value, Unit::Pt)
}
pub fn pc(value: f32) -> Self {
Self(value, Unit::Pc)
}
pub fn percentage(value: f32) -> Self {
Self(value, Unit::Percentages)
}
}
#[cfg(test)]
mod tests {
use crate::primitives::Length;
#[test]
fn display() {
let str = Length::cm(100.111).to_string();
assert_eq!(str.parse::<Length>().unwrap(), Length::cm(100.111));
assert_eq!("99.1%".parse::<Length>().unwrap(), Length::percentage(99.1));
}
}