partially implement inverse units
This commit is contained in:
parent
fb24d4db4f
commit
b28309147e
@ -424,19 +424,8 @@ impl<'a, 'b: 'a> ValueVisitor<'a, 'b> {
|
|||||||
Value::Dimension(num * num2, unit2)
|
Value::Dimension(num * num2, unit2)
|
||||||
} else if unit2 == Unit::None {
|
} else if unit2 == Unit::None {
|
||||||
Value::Dimension(num * num2, unit)
|
Value::Dimension(num * num2, unit)
|
||||||
} else if let Unit::Mul(u) = unit {
|
|
||||||
let mut unit1 = u.into_vec();
|
|
||||||
unit1.push(unit2);
|
|
||||||
Value::Dimension(num * num2, Unit::Mul(unit1.into_boxed_slice()))
|
|
||||||
} else if let Unit::Mul(u2) = unit2 {
|
|
||||||
let mut u = vec![unit];
|
|
||||||
u.append(&mut u2.into_vec());
|
|
||||||
Value::Dimension(num * num2, Unit::Mul(u.into_boxed_slice()))
|
|
||||||
} else {
|
} else {
|
||||||
Value::Dimension(
|
Value::Dimension(num * num2, unit * unit2)
|
||||||
num * num2,
|
|
||||||
Unit::Mul(vec![unit, unit2].into_boxed_slice()),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
@ -486,20 +475,20 @@ impl<'a, 'b: 'a> ValueVisitor<'a, 'b> {
|
|||||||
),
|
),
|
||||||
Value::Dimension(num, unit) => match right {
|
Value::Dimension(num, unit) => match right {
|
||||||
Value::Dimension(num2, unit2) => {
|
Value::Dimension(num2, unit2) => {
|
||||||
if !unit.comparable(&unit2) {
|
// `unit(1em / 1em)` => `""`
|
||||||
return Err((
|
|
||||||
format!("Incompatible units {} and {}.", unit2, unit),
|
|
||||||
self.span,
|
|
||||||
)
|
|
||||||
.into());
|
|
||||||
}
|
|
||||||
if unit == unit2 {
|
if unit == unit2 {
|
||||||
Value::Dimension(num / num2, Unit::None)
|
Value::Dimension(num / num2, Unit::None)
|
||||||
|
|
||||||
|
// `unit(1 / 1em)` => `"em^-1"`
|
||||||
} else if unit == Unit::None {
|
} else if unit == Unit::None {
|
||||||
todo!("inverse units")
|
Value::Dimension(num / num2, Unit::None / unit2)
|
||||||
|
|
||||||
|
// `unit(1em / 1)` => `"em"`
|
||||||
} else if unit2 == Unit::None {
|
} else if unit2 == Unit::None {
|
||||||
Value::Dimension(num / num2, unit)
|
Value::Dimension(num / num2, unit)
|
||||||
} else {
|
|
||||||
|
// `unit(1in / 1px)` => `""`
|
||||||
|
} else if unit.comparable(&unit2) {
|
||||||
Value::Dimension(
|
Value::Dimension(
|
||||||
num / (num2
|
num / (num2
|
||||||
* UNIT_CONVERSION_TABLE[unit.to_string().as_str()]
|
* UNIT_CONVERSION_TABLE[unit.to_string().as_str()]
|
||||||
@ -507,6 +496,12 @@ impl<'a, 'b: 'a> ValueVisitor<'a, 'b> {
|
|||||||
.clone()),
|
.clone()),
|
||||||
Unit::None,
|
Unit::None,
|
||||||
)
|
)
|
||||||
|
// `unit(1em / 1px)` => `"em/px"`
|
||||||
|
// todo: this should probably be its own variant
|
||||||
|
// within the `Value` enum
|
||||||
|
} else {
|
||||||
|
// todo: remember to account for `Mul` and `Div`
|
||||||
|
todo!("non-comparable inverse units")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Value::String(s, q) => {
|
Value::String(s, q) => {
|
||||||
|
120
src/unit/mod.rs
120
src/unit/mod.rs
@ -1,4 +1,7 @@
|
|||||||
use std::fmt;
|
use std::{
|
||||||
|
fmt,
|
||||||
|
ops::{Div, Mul},
|
||||||
|
};
|
||||||
|
|
||||||
pub(crate) use conversion::UNIT_CONVERSION_TABLE;
|
pub(crate) use conversion::UNIT_CONVERSION_TABLE;
|
||||||
|
|
||||||
@ -102,6 +105,9 @@ pub(crate) enum Unit {
|
|||||||
|
|
||||||
/// Units multiplied together
|
/// Units multiplied together
|
||||||
Mul(Box<[Unit]>),
|
Mul(Box<[Unit]>),
|
||||||
|
|
||||||
|
/// Units divided by each other
|
||||||
|
Div(Box<DivUnit>),
|
||||||
}
|
}
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
pub(crate) enum UnitKind {
|
pub(crate) enum UnitKind {
|
||||||
@ -116,6 +122,113 @@ pub(crate) enum UnitKind {
|
|||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub(crate) struct DivUnit {
|
||||||
|
numer: Unit,
|
||||||
|
denom: Unit,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DivUnit {
|
||||||
|
pub const fn new(numer: Unit, denom: Unit) -> Self {
|
||||||
|
Self { numer, denom }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for DivUnit {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
if self.numer == Unit::None {
|
||||||
|
write!(f, "{}^-1", self.denom)
|
||||||
|
} else {
|
||||||
|
write!(f, "{}/{}", self.numer, self.denom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::match_same_arms)]
|
||||||
|
impl Mul<Unit> for DivUnit {
|
||||||
|
type Output = Unit;
|
||||||
|
fn mul(self, rhs: Unit) -> Self::Output {
|
||||||
|
match rhs {
|
||||||
|
Unit::Mul(..) => todo!(),
|
||||||
|
Unit::Div(..) => todo!(),
|
||||||
|
Unit::None => todo!(),
|
||||||
|
_ => {
|
||||||
|
if self.denom == rhs {
|
||||||
|
self.numer
|
||||||
|
} else {
|
||||||
|
match self.denom {
|
||||||
|
Unit::Mul(..) => todo!(),
|
||||||
|
Unit::Div(..) => unreachable!(),
|
||||||
|
_ => match self.numer {
|
||||||
|
Unit::Mul(..) => todo!(),
|
||||||
|
Unit::Div(..) => unreachable!(),
|
||||||
|
Unit::None => {
|
||||||
|
let numer = Unit::Mul(vec![rhs].into_boxed_slice());
|
||||||
|
Unit::Div(Box::new(DivUnit::new(numer, self.denom)))
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let numer = Unit::Mul(vec![self.numer, rhs].into_boxed_slice());
|
||||||
|
Unit::Div(Box::new(DivUnit::new(numer, self.denom)))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// impl Div<Unit> for DivUnit {
|
||||||
|
// type Output = Unit;
|
||||||
|
// fn div(self, rhs: Unit) -> Self::Output {
|
||||||
|
// todo!()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
impl Mul<Unit> for Unit {
|
||||||
|
type Output = Unit;
|
||||||
|
fn mul(self, rhs: Unit) -> Self::Output {
|
||||||
|
match self {
|
||||||
|
Unit::Mul(u) => match rhs {
|
||||||
|
Unit::Mul(u2) => {
|
||||||
|
let mut unit1 = u.into_vec();
|
||||||
|
unit1.extend_from_slice(&*u2);
|
||||||
|
Unit::Mul(unit1.into_boxed_slice())
|
||||||
|
}
|
||||||
|
Unit::Div(..) => todo!(),
|
||||||
|
_ => {
|
||||||
|
let mut unit1 = u.into_vec();
|
||||||
|
unit1.push(rhs);
|
||||||
|
Unit::Mul(unit1.into_boxed_slice())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Unit::Div(div) => *div * rhs,
|
||||||
|
_ => match rhs {
|
||||||
|
Unit::Mul(u2) => {
|
||||||
|
let mut unit1 = vec![self];
|
||||||
|
unit1.extend_from_slice(&*u2);
|
||||||
|
Unit::Mul(unit1.into_boxed_slice())
|
||||||
|
}
|
||||||
|
Unit::Div(..) => todo!(),
|
||||||
|
_ => Unit::Mul(vec![self, rhs].into_boxed_slice()),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Div<Unit> for Unit {
|
||||||
|
type Output = Unit;
|
||||||
|
fn div(self, rhs: Unit) -> Self::Output {
|
||||||
|
if let Unit::Div(..) = self {
|
||||||
|
todo!()
|
||||||
|
} else if let Unit::Div(..) = rhs {
|
||||||
|
todo!()
|
||||||
|
} else {
|
||||||
|
Unit::Div(Box::new(DivUnit::new(self, rhs)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Unit {
|
impl Unit {
|
||||||
pub fn comparable(&self, other: &Unit) -> bool {
|
pub fn comparable(&self, other: &Unit) -> bool {
|
||||||
if other == &Unit::None {
|
if other == &Unit::None {
|
||||||
@ -150,7 +263,9 @@ impl Unit {
|
|||||||
Unit::Hz | Unit::Khz => UnitKind::Frequency,
|
Unit::Hz | Unit::Khz => UnitKind::Frequency,
|
||||||
Unit::Dpi | Unit::Dpcm | Unit::Dppx | Unit::X => UnitKind::Resolution,
|
Unit::Dpi | Unit::Dpcm | Unit::Dppx | Unit::X => UnitKind::Resolution,
|
||||||
Unit::None => UnitKind::None,
|
Unit::None => UnitKind::None,
|
||||||
Unit::Fr | Unit::Percent | Unit::Unknown(..) | Unit::Mul(..) => UnitKind::Other,
|
Unit::Fr | Unit::Percent | Unit::Unknown(..) | Unit::Mul(..) | Unit::Div(..) => {
|
||||||
|
UnitKind::Other
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -246,6 +361,7 @@ impl fmt::Display for Unit {
|
|||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
.join("*")
|
.join("*")
|
||||||
),
|
),
|
||||||
|
Unit::Div(u) => write!(f, "{}", u),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -125,7 +125,7 @@ impl Value {
|
|||||||
Ok(match self {
|
Ok(match self {
|
||||||
Value::Important => Cow::const_str("!important"),
|
Value::Important => Cow::const_str("!important"),
|
||||||
Value::Dimension(num, unit) => match unit {
|
Value::Dimension(num, unit) => match unit {
|
||||||
Unit::Mul(..) => {
|
Unit::Mul(..) | Unit::Div(..) => {
|
||||||
return Err((format!("{}{} isn't a valid CSS value.", num, unit), span).into());
|
return Err((format!("{}{} isn't a valid CSS value.", num, unit), span).into());
|
||||||
}
|
}
|
||||||
_ => Cow::owned(format!("{}{}", num, unit)),
|
_ => Cow::owned(format!("{}{}", num, unit)),
|
||||||
|
@ -89,6 +89,55 @@ test!(
|
|||||||
"a {\n color: 2߄;\n}\n",
|
"a {\n color: 2߄;\n}\n",
|
||||||
"@charset \"UTF-8\";\na {\n color: 2߄;\n}\n"
|
"@charset \"UTF-8\";\na {\n color: 2߄;\n}\n"
|
||||||
);
|
);
|
||||||
|
test!(
|
||||||
|
unit_div_same,
|
||||||
|
"a {\n color: unit(1em / 1em);\n}\n",
|
||||||
|
"a {\n color: \"\";\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
unit_div_first_none,
|
||||||
|
"a {\n color: unit(1 / 1em);\n}\n",
|
||||||
|
"a {\n color: \"em^-1\";\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
unit_div_second_none,
|
||||||
|
"a {\n color: unit(1em / 1);\n}\n",
|
||||||
|
"a {\n color: \"em\";\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
unit_div_comparable,
|
||||||
|
"a {\n color: unit(1in / 1px);\n color: (1in / 1px);\n}\n",
|
||||||
|
"a {\n color: \"\";\n color: 96;\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
unit_mul_times_mul,
|
||||||
|
"a {\n color: unit((1em * 1px) * (1em * 1px));\n}\n",
|
||||||
|
"a {\n color: \"em*px*em*px\";\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
unit_single_times_mul,
|
||||||
|
"a {\n color: unit(1in * (1em * 1px));\n}\n",
|
||||||
|
"a {\n color: \"in*em*px\";\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
unit_div_lhs_mul_uncomparable,
|
||||||
|
"a {\n color: unit((1 / 1in) * 1em);\n}\n",
|
||||||
|
"a {\n color: \"em/in\";\n}\n"
|
||||||
|
);
|
||||||
|
test!(
|
||||||
|
unit_div_lhs_mul_same,
|
||||||
|
"a {\n color: unit((1 / 1in) * 1in);\n}\n",
|
||||||
|
"a {\n color: \"\";\n}\n"
|
||||||
|
);
|
||||||
|
error!(
|
||||||
|
display_single_div_with_none_numerator,
|
||||||
|
"a {\n color: (1 / 1em);\n}\n", "Error: 1em^-1 isn't a valid CSS value."
|
||||||
|
);
|
||||||
|
error!(
|
||||||
|
#[ignore = "non-comparable inverse units"]
|
||||||
|
display_single_div_with_non_comparable_numerator,
|
||||||
|
"a {\n color: (1px / 1em);\n}\n", "Error: 1px/em isn't a valid CSS value."
|
||||||
|
);
|
||||||
error!(
|
error!(
|
||||||
display_single_mul,
|
display_single_mul,
|
||||||
"a {\n color: 1rem * 1px;\n}\n", "Error: 1rem*px isn't a valid CSS value."
|
"a {\n color: 1rem * 1px;\n}\n", "Error: 1rem*px isn't a valid CSS value."
|
||||||
@ -99,8 +148,7 @@ error!(
|
|||||||
"Error: 1rem*px*rad*foo isn't a valid CSS value."
|
"Error: 1rem*px*rad*foo isn't a valid CSS value."
|
||||||
);
|
);
|
||||||
error!(
|
error!(
|
||||||
#[ignore]
|
display_single_div_with_none_numerator_percent,
|
||||||
none_div_unit,
|
|
||||||
"a {\n color: (35 / 7%);\n}\n", "Error: 5%^-1 isn't a valid CSS value."
|
"a {\n color: (35 / 7%);\n}\n", "Error: 5%^-1 isn't a valid CSS value."
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user