// Copyright 2018-2026 the Deno authors. MIT license. use std::f64; use std::ops; use cssparser::ParseError; use cssparser::Parser; pub use cssparser::ParserInput; use cssparser::Token; use cssparser::match_ignore_ascii_case; use crate::f64::maximum; use crate::f64::minimum; pub type CSSValueError<'i> = ParseError<'i, CSSValueCustomError>; #[derive(Debug, thiserror::Error)] #[cfg_attr(test, derive(PartialEq))] pub enum CSSValueCustomError { #[error("unexpected numeric type")] UnexpectedNumericType, #[error( "contains relative values that cannot be resolved at parse time" )] ContainsRelativeLengthValues, #[error("contains {0} calculations that cannot be resolved at parse time")] ContainPercentAndDimensionCalculations(&'static str), #[error("cannot add or subtract different numeric types")] NumericTypeMismatch, #[error("the dimension of the calculation result is incorrect")] InvalidDimension, #[error("contains invalid function: {0}")] InvalidFunction(String), } #[derive(Clone, Debug)] #[cfg_attr(test, derive(PartialEq))] pub struct Length { value: f64, unit: LengthUnit, } // Currently, only Absolute Length Units are supported #[derive(Clone, Copy, Debug)] #[cfg_attr(test, derive(Eq, PartialEq))] enum LengthUnit { Cm, Mm, Q, In, Pc, Pt, Px, } impl Length { const INCH_TO_PX: f64 = 96.0; const INCH_TO_CM: f64 = 2.54; #[inline] fn from_pixels(value: f64) -> Self { Self { value, unit: LengthUnit::Px, } } #[inline] pub fn to_pixels(&self) -> f64 { let value = self.value; match self.unit { LengthUnit::Cm => value * (Self::INCH_TO_PX / Self::INCH_TO_CM), LengthUnit::Mm => value * (Self::INCH_TO_PX / Self::INCH_TO_CM / 10.0), LengthUnit::Q => value * (Self::INCH_TO_PX / Self::INCH_TO_CM / 40.0), LengthUnit::In => value * Self::INCH_TO_PX, LengthUnit::Pc => value * (Self::INCH_TO_PX / 6.0), LengthUnit::Pt => value * (Self::INCH_TO_PX / 72.0), LengthUnit::Px => value, } } } #[derive(Clone, Debug)] #[cfg_attr(test, derive(PartialEq))] pub struct Angle { value: f64, unit: AngleUnit, } #[derive(Clone, Copy, Debug)] #[cfg_attr(test, derive(Eq, PartialEq))] enum AngleUnit { Deg, Grad, Rad, Turn, } impl Angle { const TURN_TO_DEG: f64 = 360.0; const TURN_TO_GRAD: f64 = 400.0; #[inline] fn from_degrees(value: f64) -> Self { Self { value, unit: AngleUnit::Deg, } } #[inline] fn from_radians(value: f64) -> Self { Self { value, unit: AngleUnit::Rad, } } #[inline] pub fn to_degrees(&self) -> f64 { let value = self.value; match self.unit { AngleUnit::Deg => value, AngleUnit::Grad => value * (Self::TURN_TO_DEG / Self::TURN_TO_GRAD), AngleUnit::Rad => value.to_degrees(), AngleUnit::Turn => value * Self::TURN_TO_DEG, } } #[inline] pub fn to_radians(&self) -> f64 { let value = self.value; match self.unit { AngleUnit::Deg => value.to_radians(), AngleUnit::Grad => { (value * (Self::TURN_TO_DEG / Self::TURN_TO_GRAD)).to_radians() } AngleUnit::Rad => value, AngleUnit::Turn => value * f64::consts::TAU, } } } #[derive(Clone, Debug)] #[cfg_attr(test, derive(PartialEq))] pub struct Time { value: f64, unit: TimeUnit, } #[derive(Clone, Copy, Debug)] #[cfg_attr(test, derive(Eq, PartialEq))] enum TimeUnit { S, Ms, } impl Time { #[inline] fn from_seconds(value: f64) -> Self { Self { value, unit: TimeUnit::S, } } #[inline] pub fn to_seconds(&self) -> f64 { let value = self.value; match self.unit { TimeUnit::S => value, TimeUnit::Ms => value * 1000.0, } } } #[derive(Clone, Debug)] #[cfg_attr(test, derive(PartialEq))] pub struct Frequency { value: f64, unit: FrequencyUnit, } #[derive(Clone, Copy, Debug)] #[cfg_attr(test, derive(Eq, PartialEq))] enum FrequencyUnit { Hz, Khz, } impl Frequency { #[inline] fn from_hertz(value: f64) -> Self { Self { value, unit: FrequencyUnit::Hz, } } #[inline] pub fn to_hertz(&self) -> f64 { let value = self.value; match self.unit { FrequencyUnit::Hz => value, FrequencyUnit::Khz => value * 1000.0, } } } #[derive(Clone, Debug)] #[cfg_attr(test, derive(PartialEq))] pub struct Resolution { value: f64, unit: ResolutionUnit, } #[derive(Clone, Copy, Debug)] #[cfg_attr(test, derive(Eq, PartialEq))] enum ResolutionUnit { Dpi, Dpcm, Dppx, } impl Resolution { const INCH_TO_PX: f64 = 96.0; const INCH_TO_CM: f64 = 2.54; #[inline] fn from_dot_per_pixels(value: f64) -> Self { Self { value, unit: ResolutionUnit::Dppx, } } #[inline] fn to_dot_per_pixels(&self) -> f64 { let value = self.value; match self.unit { ResolutionUnit::Dpi => value / Self::INCH_TO_PX, ResolutionUnit::Dpcm => value / (Self::INCH_TO_PX / Self::INCH_TO_CM), ResolutionUnit::Dppx => value, } } } #[derive(Debug)] #[cfg_attr(test, derive(PartialEq))] pub enum NumericValue { Zero, Number(f64), Percent(f64), Length(Length), Angle(Angle), Time(Time), Frequency(Frequency), Resolution(Resolution), Flex(f64), } impl From for NumericValue { #[inline] fn from(value: Length) -> Self { NumericValue::Length(value) } } impl From for NumericValue { #[inline] fn from(value: Angle) -> Self { NumericValue::Angle(value) } } impl From