mod ast;
mod evaluate;
mod parser;
#[cfg(test)]
mod tests;
use ast::MathExpression;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct LengthPercent {
length: f32,
percent: f32,
}
impl LengthPercent {
pub const ZERO: Self = Self::length(0.0);
pub const fn length(length: f32) -> Self {
Self {
length,
percent: 0.0,
}
}
pub const fn percent(percent: f32) -> Self {
Self {
length: 0.0,
percent,
}
}
pub const fn from_terms(length: f32, percent: f32) -> Self {
Self { length, percent }
}
pub fn resolve(self, basis: f32) -> f32 {
self.length + basis * self.percent / 100.0
}
pub const fn is_absolute(self) -> bool {
self.percent == 0.0
}
pub const fn absolute_length(self) -> Option<f32> {
if self.is_absolute() {
Some(self.length)
} else {
None
}
}
pub(crate) const fn terms(self) -> (f32, f32) {
(self.length, self.percent)
}
}
impl std::ops::Add for LengthPercent {
type Output = Self;
fn add(self, rhs: Self) -> Self {
Self::from_terms(self.length + rhs.length, self.percent + rhs.percent)
}
}
impl std::ops::Sub for LengthPercent {
type Output = Self;
fn sub(self, rhs: Self) -> Self {
Self::from_terms(self.length - rhs.length, self.percent - rhs.percent)
}
}
impl From<(f32, bool)> for LengthPercent {
fn from((value, is_percent): (f32, bool)) -> Self {
if is_percent {
Self::percent(value)
} else {
Self::length(value)
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct FontRelativeLengths {
pub em: f32,
pub rem: f32,
pub ex: f32,
pub rex: f32,
pub ch: f32,
pub rch: f32,
pub cap: f32,
pub rcap: f32,
pub ic: f32,
pub ric: f32,
pub lh: f32,
pub rlh: f32,
}
impl FontRelativeLengths {
pub const fn from_font_sizes(em: f32, rem: f32) -> Self {
Self {
em,
rem,
ex: em * 0.5,
rex: rem * 0.5,
ch: em * 0.5,
rch: rem * 0.5,
cap: em,
rcap: rem,
ic: em,
ric: rem,
lh: em,
rlh: rem,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct ViewportLengths {
pub width: f32,
pub height: f32,
pub inline: f32,
pub block: f32,
}
impl ViewportLengths {
pub const fn horizontal_writing_mode(width: f32, height: f32) -> Self {
Self {
width,
height,
inline: width,
block: height,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct MathUnitContext {
pub font: FontRelativeLengths,
pub viewport: ViewportLengths,
}
impl MathUnitContext {
pub const fn from_font_and_viewport(
font_size: f32,
root_font_size: f32,
viewport_width: f32,
viewport_height: f32,
) -> Self {
Self {
font: FontRelativeLengths::from_font_sizes(font_size, root_font_size),
viewport: ViewportLengths::horizontal_writing_mode(viewport_width, viewport_height),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CssMathExpression {
source: String,
expression: MathExpression,
}
impl CssMathExpression {
pub fn parse(input: &str) -> Option<Self> {
let source = input.trim();
let expression = parser::parse(source)?;
expression.math_type()?.is_length().then(|| Self {
source: source.to_string(),
expression,
})
}
pub fn resolve(&self, units: MathUnitContext, percentage_basis: f32) -> Option<f32> {
evaluate::resolve(&self.expression, units, percentage_basis)
}
pub fn affine(&self, units: MathUnitContext) -> Option<LengthPercent> {
evaluate::affine(&self.expression, units)
}
pub fn contains_percentage(&self) -> bool {
self.expression.contains_percentage()
}
pub fn to_css_string(&self) -> Option<String> {
Some(self.source.clone())
}
}