From fbcee00bddc5ba79e4e694c7b8fe31917c235f67 Mon Sep 17 00:00:00 2001 From: Connor Skees Date: Sun, 26 Jul 2020 23:45:00 -0400 Subject: [PATCH] allow NaN to take units and refactor `math.acos` --- src/builtin/modules/math.rs | 39 ++++++++++++++++++++++++++++++++++++- src/value/mod.rs | 21 +++++++++++++++----- src/value/number/mod.rs | 12 +++++++++++- 3 files changed, 65 insertions(+), 7 deletions(-) diff --git a/src/builtin/modules/math.rs b/src/builtin/modules/math.rs index 1e6ebe1..814f83b 100644 --- a/src/builtin/modules/math.rs +++ b/src/builtin/modules/math.rs @@ -1,5 +1,7 @@ use std::cmp::Ordering; +use num_traits::{One, Zero}; + use crate::{ args::CallArgs, builtin::{ @@ -180,10 +182,45 @@ macro_rules! trig_fn { trig_fn!(cos, cos_deg); trig_fn!(sin, sin_deg); trig_fn!(tan, tan_deg); -trig_fn!(acos, acos_deg); + trig_fn!(asin, asin_deg); trig_fn!(atan, atan_deg); +fn acos(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { + args.max_args(1)?; + let number = args.get_err(0, "number")?; + + Ok(match number { + Value::Dimension(Some(n), Unit::None, ..) => { + if n > Number::from(1) || n < Number::from(-1) { + return Ok(Value::Dimension(None, Unit::Deg, true)); + } else if n.is_one() { + return Ok(Value::Dimension(Some(Number::zero()), Unit::Deg, true)); + } + + Value::Dimension(n.acos(), Unit::Deg, true) + } + v @ Value::Dimension(Some(..), ..) => { + return Err(( + format!( + "$number: Expected {} to be unitless.", + v.inspect(args.span())? + ), + args.span(), + ) + .into()) + } + Value::Dimension(None, ..) => Value::Dimension(None, Unit::Deg, true), + v => { + return Err(( + format!("$number: {} is not a number.", v.inspect(args.span())?), + args.span(), + ) + .into()) + } + }) +} + fn atan2(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(2)?; todo!() diff --git a/src/value/mod.rs b/src/value/mod.rs index 4e50ac0..f88c6c5 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -202,13 +202,24 @@ impl Value { pub fn to_css_string(&self, span: Span) -> SassResult> { Ok(match self { Value::Important => Cow::const_str("!important"), - Value::Dimension(Some(num), unit, _) => match unit { + Value::Dimension(num, unit, _) => match unit { Unit::Mul(..) | Unit::Div(..) => { - return Err((format!("{}{} isn't a valid CSS value.", num, unit), span).into()); + if let Some(num) = num { + return Err( + (format!("{}{} isn't a valid CSS value.", num, unit), span).into() + ); + } else { + return Err((format!("NaN{} isn't a valid CSS value.", unit), span).into()); + } + } + _ => { + if let Some(num) = num { + Cow::owned(format!("{}{}", num, unit)) + } else { + Cow::owned(format!("NaN{}", unit)) + } } - _ => Cow::owned(format!("{}{}", num, unit)), }, - Value::Dimension(None, ..) => Cow::const_str("NaN"), Value::Map(..) | Value::FunctionRef(..) => { return Err(( format!("{} isn't a valid CSS value.", self.inspect(span)?), @@ -470,7 +481,7 @@ impl Value { .join(", ") )), Value::Dimension(Some(num), unit, _) => Cow::owned(format!("{}{}", num, unit)), - Value::Dimension(None, ..) => Cow::const_str("NaN"), + Value::Dimension(None, unit, ..) => Cow::owned(format!("NaN{}", unit)), Value::ArgList(args) if args.is_empty() => Cow::const_str("()"), Value::ArgList(args) if args.len() == 1 => Cow::owned(format!( "({},)", diff --git a/src/value/number/mod.rs b/src/value/number/mod.rs index 7c3158b..18547cf 100644 --- a/src/value/number/mod.rs +++ b/src/value/number/mod.rs @@ -140,12 +140,22 @@ macro_rules! trig_fn( } ); +macro_rules! inverse_trig_fn( + ($name:ident) => { + pub fn $name(self) -> Option { + Some(Number::Big(Box::new(BigRational::from_float( + self.as_float()?.$name().to_degrees(), + )?))) + } + } +); + /// Trigonometry methods impl Number { trig_fn!(cos, cos_deg); trig_fn!(sin, sin_deg); trig_fn!(tan, tan_deg); - trig_fn!(acos, acos_deg); + inverse_trig_fn!(acos); trig_fn!(asin, asin_deg); trig_fn!(atan, atan_deg); }