simplify builtin fn argument handling
This commit is contained in:
parent
95efc582b5
commit
f5a654fe5b
@ -2,7 +2,10 @@ use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
use crate::{builtin::builtin_imports::*, serializer::serialize_number, value::SassNumber};
|
||||
|
||||
use super::rgb::{function_string, parse_channels, percentage_or_unitless, ParsedChannels};
|
||||
use super::{
|
||||
rgb::{function_string, parse_channels, percentage_or_unitless},
|
||||
ParsedChannels,
|
||||
};
|
||||
|
||||
fn hsl_3_args(
|
||||
name: &'static str,
|
||||
@ -14,15 +17,7 @@ fn hsl_3_args(
|
||||
let hue = args.get_err(0, "hue")?;
|
||||
let saturation = args.get_err(1, "saturation")?;
|
||||
let lightness = args.get_err(2, "lightness")?;
|
||||
let alpha = args.default_arg(
|
||||
3,
|
||||
"alpha",
|
||||
Value::Dimension(SassNumber {
|
||||
num: (Number::one()),
|
||||
unit: Unit::None,
|
||||
as_slash: None,
|
||||
}),
|
||||
);
|
||||
let alpha = args.default_arg(3, "alpha", Value::Dimension(SassNumber::new_unitless(1.0)));
|
||||
|
||||
if [&hue, &saturation, &lightness, &alpha]
|
||||
.iter()
|
||||
@ -195,25 +190,15 @@ fn darken(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value>
|
||||
.get_err(0, "color")?
|
||||
.assert_color_with_name("color", args.span())?;
|
||||
|
||||
let amount = match args.get_err(1, "amount")? {
|
||||
Value::Dimension(SassNumber { num: n, .. }) if n.is_nan() => todo!(),
|
||||
Value::Dimension(SassNumber {
|
||||
num: n,
|
||||
unit: u,
|
||||
as_slash: _,
|
||||
}) => bound!(args, "amount", n, u, 0, 100) / Number(100.0),
|
||||
v => {
|
||||
return Err((
|
||||
format!(
|
||||
"$amount: {} is not a number.",
|
||||
v.to_css_string(args.span(), false)?
|
||||
),
|
||||
args.span(),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
};
|
||||
Ok(Value::Color(Arc::new(color.darken(amount))))
|
||||
let mut amount = args
|
||||
.get_err(1, "amount")?
|
||||
.assert_number_with_name("amount", args.span())?;
|
||||
|
||||
amount.assert_bounds("amount", 0.0, 100.0, args.span())?;
|
||||
|
||||
amount.num /= Number(100.0);
|
||||
|
||||
Ok(Value::Color(Arc::new(color.darken(amount.num))))
|
||||
}
|
||||
|
||||
fn saturate(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||
@ -232,24 +217,14 @@ fn saturate(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value
|
||||
));
|
||||
}
|
||||
|
||||
let amount = match args.get_err(1, "amount")? {
|
||||
Value::Dimension(SassNumber { num: n, .. }) if n.is_nan() => todo!(),
|
||||
Value::Dimension(SassNumber {
|
||||
num: n,
|
||||
unit: u,
|
||||
as_slash: _,
|
||||
}) => bound!(args, "amount", n, u, 0, 100) / Number(100.0),
|
||||
v => {
|
||||
return Err((
|
||||
format!(
|
||||
"$amount: {} is not a number.",
|
||||
v.to_css_string(args.span(), false)?
|
||||
),
|
||||
args.span(),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
};
|
||||
let mut amount = args
|
||||
.get_err(1, "amount")?
|
||||
.assert_number_with_name("amount", args.span())?;
|
||||
|
||||
amount.assert_bounds("amount", 0.0, 100.0, args.span())?;
|
||||
|
||||
amount.num /= Number(100.0);
|
||||
|
||||
let color = match args.get_err(0, "color")? {
|
||||
Value::Color(c) => c,
|
||||
Value::Dimension(SassNumber {
|
||||
@ -271,7 +246,7 @@ fn saturate(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value
|
||||
.into())
|
||||
}
|
||||
};
|
||||
Ok(Value::Color(Arc::new(color.saturate(amount))))
|
||||
Ok(Value::Color(Arc::new(color.saturate(amount.num))))
|
||||
}
|
||||
|
||||
fn desaturate(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||
@ -279,25 +254,16 @@ fn desaturate(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Val
|
||||
let color = args
|
||||
.get_err(0, "color")?
|
||||
.assert_color_with_name("color", args.span())?;
|
||||
let amount = match args.get_err(1, "amount")? {
|
||||
Value::Dimension(SassNumber { num: n, .. }) if n.is_nan() => todo!(),
|
||||
Value::Dimension(SassNumber {
|
||||
num: n,
|
||||
unit: u,
|
||||
as_slash: _,
|
||||
}) => bound!(args, "amount", n, u, 0, 100) / Number(100.0),
|
||||
v => {
|
||||
return Err((
|
||||
format!(
|
||||
"$amount: {} is not a number.",
|
||||
v.to_css_string(args.span(), visitor.options.is_compressed())?
|
||||
),
|
||||
args.span(),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
};
|
||||
Ok(Value::Color(Arc::new(color.desaturate(amount))))
|
||||
|
||||
let mut amount = args
|
||||
.get_err(1, "amount")?
|
||||
.assert_number_with_name("amount", args.span())?;
|
||||
|
||||
amount.assert_bounds("amount", 0.0, 100.0, args.span())?;
|
||||
|
||||
amount.num /= Number(100.0);
|
||||
|
||||
Ok(Value::Color(Arc::new(color.desaturate(amount.num))))
|
||||
}
|
||||
|
||||
pub(crate) fn grayscale(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||
@ -335,32 +301,20 @@ pub(crate) fn complement(mut args: ArgumentResult, visitor: &mut Visitor) -> Sas
|
||||
|
||||
pub(crate) fn invert(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||
args.max_args(2)?;
|
||||
let weight = match args.get(1, "weight") {
|
||||
Some(Spanned {
|
||||
node: Value::Dimension(SassNumber { num: n, .. }),
|
||||
..
|
||||
}) if n.is_nan() => todo!(),
|
||||
Some(Spanned {
|
||||
node:
|
||||
Value::Dimension(SassNumber {
|
||||
num: n,
|
||||
unit: u,
|
||||
as_slash: _,
|
||||
}),
|
||||
..
|
||||
}) => Some(bound!(args, "weight", n, u, 0, 100) / Number(100.0)),
|
||||
None => None,
|
||||
Some(v) => {
|
||||
return Err((
|
||||
format!(
|
||||
"$weight: {} is not a number.",
|
||||
v.to_css_string(args.span(), visitor.options.is_compressed())?
|
||||
),
|
||||
args.span(),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
};
|
||||
let span = args.span();
|
||||
let weight = args
|
||||
.get(1, "weight")
|
||||
.map::<SassResult<_>, _>(|weight| {
|
||||
let mut weight = weight.node.assert_number_with_name("weight", span)?;
|
||||
|
||||
weight.assert_bounds("weight", 0.0, 100.0, span)?;
|
||||
|
||||
weight.num /= Number(100.0);
|
||||
|
||||
Ok(weight.num)
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
match args.get_err(0, "color")? {
|
||||
Value::Color(c) => Ok(Value::Color(Arc::new(
|
||||
c.invert(weight.unwrap_or_else(Number::one)),
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::builtin::builtin_imports::*;
|
||||
|
||||
use super::rgb::{parse_channels, ParsedChannels};
|
||||
use super::{rgb::parse_channels, ParsedChannels};
|
||||
|
||||
pub(crate) fn blackness(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||
args.max_args(1)?;
|
||||
|
@ -1,3 +1,5 @@
|
||||
use crate::value::Value;
|
||||
|
||||
use super::GlobalFunctionMap;
|
||||
|
||||
pub mod hsl;
|
||||
@ -6,6 +8,12 @@ pub mod opacity;
|
||||
pub mod other;
|
||||
pub mod rgb;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) enum ParsedChannels {
|
||||
String(String),
|
||||
List(Vec<Value>),
|
||||
}
|
||||
|
||||
pub(crate) fn declare(f: &mut GlobalFunctionMap) {
|
||||
hsl::declare(f);
|
||||
opacity::declare(f);
|
||||
|
@ -32,21 +32,17 @@ mod test {
|
||||
|
||||
pub(crate) fn alpha(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||
if args.len() <= 1 {
|
||||
match args.get_err(0, "color")? {
|
||||
Value::Color(c) => Ok(Value::Dimension(SassNumber {
|
||||
num: c.alpha(),
|
||||
unit: Unit::None,
|
||||
as_slash: None,
|
||||
})),
|
||||
Value::String(s, QuoteKind::None) if is_ms_filter(&s) => {
|
||||
Ok(Value::String(format!("alpha({})", s), QuoteKind::None))
|
||||
let color = args.get_err(0, "color")?;
|
||||
|
||||
if let Value::String(s, QuoteKind::None) = &color {
|
||||
if is_ms_filter(s) {
|
||||
return Ok(Value::String(format!("alpha({})", s), QuoteKind::None));
|
||||
}
|
||||
v => Err((
|
||||
format!("$color: {} is not a color.", v.inspect(args.span())?),
|
||||
args.span(),
|
||||
)
|
||||
.into()),
|
||||
}
|
||||
|
||||
let color = color.assert_color_with_name("color", args.span())?;
|
||||
|
||||
Ok(Value::Dimension(SassNumber::new_unitless(color.alpha())))
|
||||
} else {
|
||||
let err = args.max_args(1);
|
||||
let args = args
|
||||
@ -72,11 +68,7 @@ pub(crate) fn opacity(mut args: ArgumentResult, visitor: &mut Visitor) -> SassRe
|
||||
args.max_args(1)?;
|
||||
match args.get_err(0, "color")? {
|
||||
Value::Dimension(SassNumber { num: n, .. }) if n.is_nan() => todo!(),
|
||||
Value::Color(c) => Ok(Value::Dimension(SassNumber {
|
||||
num: c.alpha(),
|
||||
unit: Unit::None,
|
||||
as_slash: None,
|
||||
})),
|
||||
Value::Color(c) => Ok(Value::Dimension(SassNumber::new_unitless(c.alpha()))),
|
||||
Value::Dimension(SassNumber {
|
||||
num,
|
||||
unit,
|
||||
@ -102,9 +94,9 @@ fn opacify(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value>
|
||||
.get_err(1, "amount")?
|
||||
.assert_number_with_name("amount", args.span())?;
|
||||
|
||||
let amount = bound!(args, "amount", amount.num, amount.unit(), 0, 1);
|
||||
amount.assert_bounds("amount", 0.0, 1.0, args.span())?;
|
||||
|
||||
Ok(Value::Color(Arc::new(color.fade_in(amount))))
|
||||
Ok(Value::Color(Arc::new(color.fade_in(amount.num))))
|
||||
}
|
||||
|
||||
fn transparentize(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||
@ -112,22 +104,14 @@ fn transparentize(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult
|
||||
let color = args
|
||||
.get_err(0, "color")?
|
||||
.assert_color_with_name("color", args.span())?;
|
||||
let amount = match args.get_err(1, "amount")? {
|
||||
Value::Dimension(SassNumber { num: n, .. }) if n.is_nan() => todo!(),
|
||||
Value::Dimension(SassNumber {
|
||||
num: n,
|
||||
unit: u,
|
||||
as_slash: _,
|
||||
}) => bound!(args, "amount", n, u, 0, 1),
|
||||
v => {
|
||||
return Err((
|
||||
format!("$amount: {} is not a number.", v.inspect(args.span())?),
|
||||
args.span(),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
};
|
||||
Ok(Value::Color(Arc::new(color.fade_out(amount))))
|
||||
|
||||
let amount = args
|
||||
.get_err(1, "amount")?
|
||||
.assert_number_with_name("amount", args.span())?;
|
||||
|
||||
amount.assert_bounds("amount", 0.0, 1.0, args.span())?;
|
||||
|
||||
Ok(Value::Color(Arc::new(color.fade_out(amount.num))))
|
||||
}
|
||||
|
||||
pub(crate) fn declare(f: &mut GlobalFunctionMap) {
|
||||
|
@ -1,5 +1,7 @@
|
||||
use crate::{builtin::builtin_imports::*, serializer::inspect_number, value::fuzzy_round};
|
||||
|
||||
use super::ParsedChannels;
|
||||
|
||||
pub(crate) fn function_string(
|
||||
name: &'static str,
|
||||
args: &[Value],
|
||||
@ -173,12 +175,6 @@ pub(crate) fn percentage_or_unitless(
|
||||
Ok(value.clamp(0.0, max).0)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) enum ParsedChannels {
|
||||
String(String),
|
||||
List(Vec<Value>),
|
||||
}
|
||||
|
||||
fn is_var_slash(value: &Value) -> bool {
|
||||
match value {
|
||||
Value::String(text, QuoteKind::Quoted) => {
|
||||
@ -313,7 +309,6 @@ pub(crate) fn parse_channels(
|
||||
}
|
||||
}
|
||||
|
||||
/// name: Either `rgb` or `rgba` depending on the caller
|
||||
fn inner_rgb(
|
||||
name: &'static str,
|
||||
mut args: ArgumentResult,
|
||||
@ -363,11 +358,7 @@ pub(crate) fn red(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult
|
||||
.get_err(0, "color")?
|
||||
.assert_color_with_name("color", args.span())?;
|
||||
|
||||
Ok(Value::Dimension(SassNumber {
|
||||
num: color.red(),
|
||||
unit: Unit::None,
|
||||
as_slash: None,
|
||||
}))
|
||||
Ok(Value::Dimension(SassNumber::new_unitless(color.red())))
|
||||
}
|
||||
|
||||
pub(crate) fn green(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||
@ -376,11 +367,7 @@ pub(crate) fn green(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResu
|
||||
.get_err(0, "color")?
|
||||
.assert_color_with_name("color", args.span())?;
|
||||
|
||||
Ok(Value::Dimension(SassNumber {
|
||||
num: color.green(),
|
||||
unit: Unit::None,
|
||||
as_slash: None,
|
||||
}))
|
||||
Ok(Value::Dimension(SassNumber::new_unitless(color.green())))
|
||||
}
|
||||
|
||||
pub(crate) fn blue(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||
@ -389,11 +376,7 @@ pub(crate) fn blue(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResul
|
||||
.get_err(0, "color")?
|
||||
.assert_color_with_name("color", args.span())?;
|
||||
|
||||
Ok(Value::Dimension(SassNumber {
|
||||
num: color.blue(),
|
||||
unit: Unit::None,
|
||||
as_slash: None,
|
||||
}))
|
||||
Ok(Value::Dimension(SassNumber::new_unitless(color.blue())))
|
||||
}
|
||||
|
||||
pub(crate) fn mix(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||
@ -409,11 +392,7 @@ pub(crate) fn mix(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult
|
||||
let weight = match args.default_arg(
|
||||
2,
|
||||
"weight",
|
||||
Value::Dimension(SassNumber {
|
||||
num: (Number(50.0)),
|
||||
unit: Unit::None,
|
||||
as_slash: None,
|
||||
}),
|
||||
Value::Dimension(SassNumber::new_unitless(50.0)),
|
||||
) {
|
||||
Value::Dimension(SassNumber { num: n, .. }) if n.is_nan() => todo!(),
|
||||
Value::Dimension(SassNumber {
|
||||
|
@ -2,11 +2,10 @@ use crate::builtin::builtin_imports::*;
|
||||
|
||||
pub(crate) fn length(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||
args.max_args(1)?;
|
||||
Ok(Value::Dimension(SassNumber {
|
||||
num: (Number::from(args.get_err(0, "list")?.as_list().len())),
|
||||
unit: Unit::None,
|
||||
as_slash: None,
|
||||
}))
|
||||
|
||||
let len = args.get_err(0, "list")?.as_list().len();
|
||||
|
||||
Ok(Value::Dimension(SassNumber::new_unitless(len)))
|
||||
}
|
||||
|
||||
pub(crate) fn nth(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||
@ -260,14 +259,10 @@ pub(crate) fn index(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResu
|
||||
let list = args.get_err(0, "list")?.as_list();
|
||||
let value = args.get_err(1, "value")?;
|
||||
let index = match list.into_iter().position(|v| v == value) {
|
||||
Some(v) => Number::from(v + 1),
|
||||
Some(v) => v + 1,
|
||||
None => return Ok(Value::Null),
|
||||
};
|
||||
Ok(Value::Dimension(SassNumber {
|
||||
num: (index),
|
||||
unit: Unit::None,
|
||||
as_slash: None,
|
||||
}))
|
||||
Ok(Value::Dimension(SassNumber::new_unitless(index)))
|
||||
}
|
||||
|
||||
pub(crate) fn zip(args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||
|
@ -16,74 +16,47 @@ pub(crate) fn percentage(mut args: ArgumentResult, visitor: &mut Visitor) -> Sas
|
||||
|
||||
pub(crate) fn round(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||
args.max_args(1)?;
|
||||
match args.get_err(0, "number")? {
|
||||
// todo: better error message, consider finities
|
||||
Value::Dimension(SassNumber { num: n, .. }) if n.is_nan() => {
|
||||
Err(("Infinity or NaN toInt", args.span()).into())
|
||||
}
|
||||
Value::Dimension(SassNumber {
|
||||
num: n,
|
||||
unit: u,
|
||||
as_slash: _,
|
||||
}) => Ok(Value::Dimension(SassNumber {
|
||||
num: (n.round()),
|
||||
unit: u,
|
||||
as_slash: None,
|
||||
})),
|
||||
v => Err((
|
||||
format!("$number: {} is not a number.", v.inspect(args.span())?),
|
||||
args.span(),
|
||||
)
|
||||
.into()),
|
||||
let mut number = args
|
||||
.get_err(0, "number")?
|
||||
.assert_number_with_name("number", args.span())?;
|
||||
|
||||
if !number.num.is_finite() {
|
||||
return Err(("Infinity or NaN toInt", args.span()).into());
|
||||
}
|
||||
|
||||
number.num = number.num.round();
|
||||
|
||||
Ok(Value::Dimension(number))
|
||||
}
|
||||
|
||||
pub(crate) fn ceil(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||
args.max_args(1)?;
|
||||
match args.get_err(0, "number")? {
|
||||
// todo: better error message, consider finities
|
||||
Value::Dimension(SassNumber { num: n, .. }) if n.is_nan() => {
|
||||
Err(("Infinity or NaN toInt", args.span()).into())
|
||||
}
|
||||
Value::Dimension(SassNumber {
|
||||
num: n,
|
||||
unit: u,
|
||||
as_slash: _,
|
||||
}) => Ok(Value::Dimension(SassNumber {
|
||||
num: (n.ceil()),
|
||||
unit: u,
|
||||
as_slash: None,
|
||||
})),
|
||||
v => Err((
|
||||
format!("$number: {} is not a number.", v.inspect(args.span())?),
|
||||
args.span(),
|
||||
)
|
||||
.into()),
|
||||
let mut number = args
|
||||
.get_err(0, "number")?
|
||||
.assert_number_with_name("number", args.span())?;
|
||||
|
||||
if !number.num.is_finite() {
|
||||
return Err(("Infinity or NaN toInt", args.span()).into());
|
||||
}
|
||||
|
||||
number.num = number.num.ceil();
|
||||
|
||||
Ok(Value::Dimension(number))
|
||||
}
|
||||
|
||||
pub(crate) fn floor(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||
args.max_args(1)?;
|
||||
match args.get_err(0, "number")? {
|
||||
// todo: better error message, consider finities
|
||||
Value::Dimension(SassNumber { num: n, .. }) if n.is_nan() => {
|
||||
Err(("Infinity or NaN toInt", args.span()).into())
|
||||
}
|
||||
Value::Dimension(SassNumber {
|
||||
num: n,
|
||||
unit: u,
|
||||
as_slash: _,
|
||||
}) => Ok(Value::Dimension(SassNumber {
|
||||
num: (n.floor()),
|
||||
unit: u,
|
||||
as_slash: None,
|
||||
})),
|
||||
v => Err((
|
||||
format!("$number: {} is not a number.", v.inspect(args.span())?),
|
||||
args.span(),
|
||||
)
|
||||
.into()),
|
||||
let mut number = args
|
||||
.get_err(0, "number")?
|
||||
.assert_number_with_name("number", args.span())?;
|
||||
|
||||
if !number.num.is_finite() {
|
||||
return Err(("Infinity or NaN toInt", args.span()).into());
|
||||
}
|
||||
|
||||
number.num = number.num.floor();
|
||||
|
||||
Ok(Value::Dimension(number))
|
||||
}
|
||||
|
||||
pub(crate) fn abs(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||
@ -92,6 +65,7 @@ pub(crate) fn abs(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult
|
||||
.get_err(0, "number")?
|
||||
.assert_number_with_name("number", args.span())?;
|
||||
|
||||
// todo: test for nan+infinity
|
||||
num.num = num.num.abs();
|
||||
|
||||
Ok(Value::Dimension(num))
|
||||
@ -120,22 +94,16 @@ pub(crate) fn random(mut args: ArgumentResult, visitor: &mut Visitor) -> SassRes
|
||||
|
||||
if matches!(limit, Value::Null) {
|
||||
let mut rng = rand::thread_rng();
|
||||
return Ok(Value::Dimension(SassNumber {
|
||||
num: (Number::from(rng.gen_range(0.0..1.0))),
|
||||
unit: Unit::None,
|
||||
as_slash: None,
|
||||
}));
|
||||
return Ok(Value::Dimension(SassNumber::new_unitless(
|
||||
rng.gen_range(0.0..1.0),
|
||||
)));
|
||||
}
|
||||
|
||||
let limit = limit.assert_number_with_name("limit", args.span())?.num;
|
||||
let limit_int = limit.assert_int_with_name("limit", args.span())?;
|
||||
|
||||
if limit.is_one() {
|
||||
return Ok(Value::Dimension(SassNumber {
|
||||
num: (Number::one()),
|
||||
unit: Unit::None,
|
||||
as_slash: None,
|
||||
}));
|
||||
return Ok(Value::Dimension(SassNumber::new_unitless(1.0)));
|
||||
}
|
||||
|
||||
if limit.is_zero() || limit.is_negative() {
|
||||
@ -147,11 +115,9 @@ pub(crate) fn random(mut args: ArgumentResult, visitor: &mut Visitor) -> SassRes
|
||||
}
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
Ok(Value::Dimension(SassNumber {
|
||||
num: (Number::from(rng.gen_range(0..limit_int) + 1)),
|
||||
unit: Unit::None,
|
||||
as_slash: None,
|
||||
}))
|
||||
Ok(Value::Dimension(SassNumber::new_unitless(
|
||||
rng.gen_range(0..limit_int) + 1,
|
||||
)))
|
||||
}
|
||||
|
||||
pub(crate) fn min(args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||
|
@ -33,51 +33,40 @@ fn if_(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||
|
||||
pub(crate) fn feature_exists(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||
args.max_args(1)?;
|
||||
match args.get_err(0, "feature")? {
|
||||
#[allow(clippy::match_same_arms)]
|
||||
Value::String(s, _) => Ok(match s.as_str() {
|
||||
// A local variable will shadow a global variable unless
|
||||
// `!global` is used.
|
||||
"global-variable-shadowing" => Value::True,
|
||||
// the @extend rule will affect selectors nested in pseudo-classes
|
||||
// like :not()
|
||||
"extend-selector-pseudoclass" => Value::True,
|
||||
// Full support for unit arithmetic using units defined in the
|
||||
// [Values and Units Level 3][] spec.
|
||||
"units-level-3" => Value::True,
|
||||
// The Sass `@error` directive is supported.
|
||||
"at-error" => Value::True,
|
||||
// The "Custom Properties Level 1" spec is supported. This means
|
||||
// that custom properties are parsed statically, with only
|
||||
// interpolation treated as SassScript.
|
||||
"custom-property" => Value::True,
|
||||
_ => Value::False,
|
||||
}),
|
||||
v => Err((
|
||||
format!("$feature: {} is not a string.", v.inspect(args.span())?),
|
||||
args.span(),
|
||||
)
|
||||
.into()),
|
||||
}
|
||||
let feature = args
|
||||
.get_err(0, "feature")?
|
||||
.assert_string_with_name("feature", args.span())?
|
||||
.0;
|
||||
|
||||
#[allow(clippy::match_same_arms)]
|
||||
Ok(match feature.as_str() {
|
||||
// A local variable will shadow a global variable unless
|
||||
// `!global` is used.
|
||||
"global-variable-shadowing" => Value::True,
|
||||
// the @extend rule will affect selectors nested in pseudo-classes
|
||||
// like :not()
|
||||
"extend-selector-pseudoclass" => Value::True,
|
||||
// Full support for unit arithmetic using units defined in the
|
||||
// [Values and Units Level 3][] spec.
|
||||
"units-level-3" => Value::True,
|
||||
// The Sass `@error` directive is supported.
|
||||
"at-error" => Value::True,
|
||||
// The "Custom Properties Level 1" spec is supported. This means
|
||||
// that custom properties are parsed statically, with only
|
||||
// interpolation treated as SassScript.
|
||||
"custom-property" => Value::True,
|
||||
_ => Value::False,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn unit(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||
args.max_args(1)?;
|
||||
let unit = match args.get_err(0, "number")? {
|
||||
Value::Dimension(SassNumber {
|
||||
num: _,
|
||||
unit: u,
|
||||
as_slash: _,
|
||||
}) => u.to_string(),
|
||||
v => {
|
||||
return Err((
|
||||
format!("$number: {} is not a number.", v.inspect(args.span())?),
|
||||
args.span(),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
};
|
||||
Ok(Value::String(unit, QuoteKind::Quoted))
|
||||
|
||||
let number = args
|
||||
.get_err(0, "number")?
|
||||
.assert_number_with_name("number", args.span())?;
|
||||
|
||||
Ok(Value::String(number.unit.to_string(), QuoteKind::Quoted))
|
||||
}
|
||||
|
||||
pub(crate) fn type_of(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||
@ -88,19 +77,11 @@ pub(crate) fn type_of(mut args: ArgumentResult, visitor: &mut Visitor) -> SassRe
|
||||
|
||||
pub(crate) fn unitless(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||
args.max_args(1)?;
|
||||
Ok(match args.get_err(0, "number")? {
|
||||
Value::Dimension(SassNumber {
|
||||
unit: Unit::None, ..
|
||||
}) => Value::True,
|
||||
Value::Dimension(SassNumber { .. }) => Value::False,
|
||||
v => {
|
||||
return Err((
|
||||
format!("$number: {} is not a number.", v.inspect(args.span())?),
|
||||
args.span(),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
})
|
||||
let number = args
|
||||
.get_err(0, "number")?
|
||||
.assert_number_with_name("number", args.span())?;
|
||||
|
||||
Ok(Value::bool(number.unit == Unit::None))
|
||||
}
|
||||
|
||||
pub(crate) fn inspect(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||
@ -116,14 +97,14 @@ pub(crate) fn variable_exists(
|
||||
visitor: &mut Visitor,
|
||||
) -> SassResult<Value> {
|
||||
args.max_args(1)?;
|
||||
match args.get_err(0, "name")? {
|
||||
Value::String(s, _) => Ok(Value::bool(visitor.env.var_exists(s.into(), None)?)),
|
||||
v => Err((
|
||||
format!("$name: {} is not a string.", v.inspect(args.span())?),
|
||||
args.span(),
|
||||
)
|
||||
.into()),
|
||||
}
|
||||
|
||||
let name = Identifier::from(
|
||||
args.get_err(0, "name")?
|
||||
.assert_string_with_name("name", args.span())?
|
||||
.0,
|
||||
);
|
||||
|
||||
Ok(Value::bool(visitor.env.var_exists(name, None)?))
|
||||
}
|
||||
|
||||
pub(crate) fn global_variable_exists(
|
||||
@ -163,16 +144,11 @@ pub(crate) fn global_variable_exists(
|
||||
|
||||
pub(crate) fn mixin_exists(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||
args.max_args(2)?;
|
||||
let name: Identifier = match args.get_err(0, "name")? {
|
||||
Value::String(s, _) => s.into(),
|
||||
v => {
|
||||
return Err((
|
||||
format!("$name: {} is not a string.", v.inspect(args.span())?),
|
||||
args.span(),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
};
|
||||
let name = Identifier::from(
|
||||
args.get_err(0, "name")?
|
||||
.assert_string_with_name("name", args.span())?
|
||||
.0,
|
||||
);
|
||||
|
||||
let module = match args.default_arg(1, "module", Value::Null) {
|
||||
Value::String(s, _) => Some(s),
|
||||
@ -203,16 +179,11 @@ pub(crate) fn function_exists(
|
||||
) -> SassResult<Value> {
|
||||
args.max_args(2)?;
|
||||
|
||||
let name: Identifier = match args.get_err(0, "name")? {
|
||||
Value::String(s, _) => s.into(),
|
||||
v => {
|
||||
return Err((
|
||||
format!("$name: {} is not a string.", v.inspect(args.span())?),
|
||||
args.span(),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
};
|
||||
let name = Identifier::from(
|
||||
args.get_err(0, "name")?
|
||||
.assert_string_with_name("name", args.span())?
|
||||
.0,
|
||||
);
|
||||
|
||||
let module = match args.default_arg(1, "module", Value::Null) {
|
||||
Value::String(s, _) => Some(s),
|
||||
|
@ -2,72 +2,59 @@ use crate::builtin::builtin_imports::*;
|
||||
|
||||
pub(crate) fn to_upper_case(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||
args.max_args(1)?;
|
||||
match args.get_err(0, "string")? {
|
||||
Value::String(mut i, q) => {
|
||||
i.make_ascii_uppercase();
|
||||
Ok(Value::String(i, q))
|
||||
}
|
||||
v => Err((
|
||||
format!("$string: {} is not a string.", v.inspect(args.span())?),
|
||||
args.span(),
|
||||
)
|
||||
.into()),
|
||||
}
|
||||
let (mut s, q) = args
|
||||
.get_err(0, "string")?
|
||||
.assert_string_with_name("string", args.span())?;
|
||||
|
||||
s.make_ascii_uppercase();
|
||||
|
||||
Ok(Value::String(s, q))
|
||||
}
|
||||
|
||||
pub(crate) fn to_lower_case(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||
args.max_args(1)?;
|
||||
match args.get_err(0, "string")? {
|
||||
Value::String(mut i, q) => {
|
||||
i.make_ascii_lowercase();
|
||||
Ok(Value::String(i, q))
|
||||
}
|
||||
v => Err((
|
||||
format!("$string: {} is not a string.", v.inspect(args.span())?),
|
||||
args.span(),
|
||||
)
|
||||
.into()),
|
||||
}
|
||||
|
||||
let (mut s, q) = args
|
||||
.get_err(0, "string")?
|
||||
.assert_string_with_name("string", args.span())?;
|
||||
|
||||
s.make_ascii_lowercase();
|
||||
|
||||
Ok(Value::String(s, q))
|
||||
}
|
||||
|
||||
pub(crate) fn str_length(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||
args.max_args(1)?;
|
||||
match args.get_err(0, "string")? {
|
||||
Value::String(i, _) => Ok(Value::Dimension(SassNumber {
|
||||
num: (Number::from(i.chars().count())),
|
||||
unit: Unit::None,
|
||||
as_slash: None,
|
||||
})),
|
||||
v => Err((
|
||||
format!("$string: {} is not a string.", v.inspect(args.span())?),
|
||||
args.span(),
|
||||
)
|
||||
.into()),
|
||||
}
|
||||
let s = args
|
||||
.get_err(0, "string")?
|
||||
.assert_string_with_name("string", args.span())?
|
||||
.0;
|
||||
|
||||
Ok(Value::Dimension(SassNumber::new_unitless(
|
||||
s.chars().count(),
|
||||
)))
|
||||
}
|
||||
|
||||
pub(crate) fn quote(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||
args.max_args(1)?;
|
||||
match args.get_err(0, "string")? {
|
||||
Value::String(i, _) => Ok(Value::String(i, QuoteKind::Quoted)),
|
||||
v => Err((
|
||||
format!("$string: {} is not a string.", v.inspect(args.span())?),
|
||||
args.span(),
|
||||
)
|
||||
.into()),
|
||||
}
|
||||
|
||||
let s = args
|
||||
.get_err(0, "string")?
|
||||
.assert_string_with_name("string", args.span())?
|
||||
.0;
|
||||
|
||||
Ok(Value::String(s, QuoteKind::Quoted))
|
||||
}
|
||||
|
||||
pub(crate) fn unquote(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||
args.max_args(1)?;
|
||||
match args.get_err(0, "string")? {
|
||||
i @ Value::String(..) => Ok(i.unquote()),
|
||||
v => Err((
|
||||
format!("$string: {} is not a string.", v.inspect(args.span())?),
|
||||
args.span(),
|
||||
)
|
||||
.into()),
|
||||
}
|
||||
|
||||
let s = args
|
||||
.get_err(0, "string")?
|
||||
.assert_string_with_name("string", args.span())?
|
||||
.0;
|
||||
|
||||
Ok(Value::String(s, QuoteKind::None))
|
||||
}
|
||||
|
||||
pub(crate) fn str_slice(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||
@ -100,11 +87,7 @@ pub(crate) fn str_slice(mut args: ArgumentResult, visitor: &mut Visitor) -> Sass
|
||||
.default_arg(
|
||||
2,
|
||||
"end-at",
|
||||
Value::Dimension(SassNumber {
|
||||
num: Number(-1.0),
|
||||
unit: Unit::None,
|
||||
as_slash: None,
|
||||
}),
|
||||
Value::Dimension(SassNumber::new_unitless(-1.0)),
|
||||
)
|
||||
.assert_number_with_name("end-at", span)?;
|
||||
|
||||
@ -149,11 +132,7 @@ pub(crate) fn str_index(mut args: ArgumentResult, visitor: &mut Visitor) -> Sass
|
||||
None => return Ok(Value::Null),
|
||||
};
|
||||
|
||||
Ok(Value::Dimension(SassNumber {
|
||||
num: Number::from(char_position),
|
||||
unit: Unit::None,
|
||||
as_slash: None,
|
||||
}))
|
||||
Ok(Value::Dimension(SassNumber::new_unitless(char_position)))
|
||||
}
|
||||
|
||||
pub(crate) fn str_insert(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||
|
@ -206,8 +206,8 @@ fn log(mut args: ArgumentResult, _: &mut Visitor) -> SassResult<Value> {
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Value::Dimension(SassNumber {
|
||||
num: if let Some(base) = base {
|
||||
Ok(Value::Dimension(SassNumber::new_unitless(
|
||||
if let Some(base) = base {
|
||||
if base.is_zero() {
|
||||
Number::zero()
|
||||
} else {
|
||||
@ -221,9 +221,7 @@ fn log(mut args: ArgumentResult, _: &mut Visitor) -> SassResult<Value> {
|
||||
} else {
|
||||
number.ln()
|
||||
},
|
||||
unit: Unit::None,
|
||||
as_slash: None,
|
||||
}))
|
||||
)))
|
||||
}
|
||||
|
||||
fn pow(mut args: ArgumentResult, _: &mut Visitor) -> SassResult<Value> {
|
||||
@ -241,11 +239,9 @@ fn pow(mut args: ArgumentResult, _: &mut Visitor) -> SassResult<Value> {
|
||||
.assert_number_with_name("exponent", span)?;
|
||||
exponent.assert_no_units("exponent", span)?;
|
||||
|
||||
Ok(Value::Dimension(SassNumber {
|
||||
num: base.num.pow(exponent.num),
|
||||
unit: Unit::None,
|
||||
as_slash: None,
|
||||
}))
|
||||
Ok(Value::Dimension(SassNumber::new_unitless(
|
||||
base.num.pow(exponent.num),
|
||||
)))
|
||||
}
|
||||
|
||||
fn sqrt(mut args: ArgumentResult, _: &mut Visitor) -> SassResult<Value> {
|
||||
@ -255,11 +251,9 @@ fn sqrt(mut args: ArgumentResult, _: &mut Visitor) -> SassResult<Value> {
|
||||
.assert_number_with_name("number", args.span())?;
|
||||
number.assert_no_units("number", args.span())?;
|
||||
|
||||
Ok(Value::Dimension(SassNumber {
|
||||
num: number.num.sqrt(),
|
||||
unit: Unit::None,
|
||||
as_slash: None,
|
||||
}))
|
||||
Ok(Value::Dimension(SassNumber::new_unitless(
|
||||
number.num.sqrt(),
|
||||
)))
|
||||
}
|
||||
|
||||
macro_rules! trig_fn {
|
||||
@ -274,11 +268,9 @@ macro_rules! trig_fn {
|
||||
num,
|
||||
unit: unit @ (Unit::None | Unit::Rad | Unit::Deg | Unit::Grad | Unit::Turn),
|
||||
..
|
||||
}) => Value::Dimension(SassNumber {
|
||||
num: Number(coerce_to_rad(num.0, unit).$name()),
|
||||
unit: Unit::None,
|
||||
as_slash: None,
|
||||
}),
|
||||
}) => {
|
||||
Value::Dimension(SassNumber::new_unitless(coerce_to_rad(num.0, unit).$name()))
|
||||
}
|
||||
v @ Value::Dimension(..) => {
|
||||
return Err((
|
||||
format!(
|
||||
@ -482,58 +474,30 @@ pub(crate) fn declare(f: &mut Module) {
|
||||
|
||||
f.insert_builtin_var(
|
||||
"e",
|
||||
Value::Dimension(SassNumber {
|
||||
num: Number(std::f64::consts::E),
|
||||
unit: Unit::None,
|
||||
as_slash: None,
|
||||
}),
|
||||
Value::Dimension(SassNumber::new_unitless(std::f64::consts::E)),
|
||||
);
|
||||
f.insert_builtin_var(
|
||||
"pi",
|
||||
Value::Dimension(SassNumber {
|
||||
num: Number(std::f64::consts::PI),
|
||||
unit: Unit::None,
|
||||
as_slash: None,
|
||||
}),
|
||||
Value::Dimension(SassNumber::new_unitless(std::f64::consts::PI)),
|
||||
);
|
||||
f.insert_builtin_var(
|
||||
"epsilon",
|
||||
Value::Dimension(SassNumber {
|
||||
num: Number(std::f64::EPSILON),
|
||||
unit: Unit::None,
|
||||
as_slash: None,
|
||||
}),
|
||||
Value::Dimension(SassNumber::new_unitless(std::f64::EPSILON)),
|
||||
);
|
||||
f.insert_builtin_var(
|
||||
"max-safe-integer",
|
||||
Value::Dimension(SassNumber {
|
||||
num: Number(9007199254740991.0),
|
||||
unit: Unit::None,
|
||||
as_slash: None,
|
||||
}),
|
||||
Value::Dimension(SassNumber::new_unitless(9007199254740991.0)),
|
||||
);
|
||||
f.insert_builtin_var(
|
||||
"min-safe-integer",
|
||||
Value::Dimension(SassNumber {
|
||||
num: Number(-9007199254740991.0),
|
||||
unit: Unit::None,
|
||||
as_slash: None,
|
||||
}),
|
||||
Value::Dimension(SassNumber::new_unitless(-9007199254740991.0)),
|
||||
);
|
||||
f.insert_builtin_var(
|
||||
"max-number",
|
||||
Value::Dimension(SassNumber {
|
||||
num: Number(f64::MAX),
|
||||
unit: Unit::None,
|
||||
as_slash: None,
|
||||
}),
|
||||
Value::Dimension(SassNumber::new_unitless(f64::MAX)),
|
||||
);
|
||||
f.insert_builtin_var(
|
||||
"min-number",
|
||||
Value::Dimension(SassNumber {
|
||||
num: Number(f64::MIN_POSITIVE),
|
||||
unit: Unit::None,
|
||||
as_slash: None,
|
||||
}),
|
||||
Value::Dimension(SassNumber::new_unitless(f64::MIN_POSITIVE)),
|
||||
);
|
||||
}
|
||||
|
@ -19,16 +19,10 @@ fn load_css(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<()> {
|
||||
|
||||
let span = args.span();
|
||||
|
||||
let url = match args.get_err(0, "module")? {
|
||||
Value::String(s, ..) => s,
|
||||
v => {
|
||||
return Err((
|
||||
format!("$module: {} is not a string.", v.inspect(span)?),
|
||||
span,
|
||||
)
|
||||
.into())
|
||||
}
|
||||
};
|
||||
let url = args
|
||||
.get_err(0, "module")?
|
||||
.assert_string_with_name("module", args.span())?
|
||||
.0;
|
||||
|
||||
let with = match args.default_arg(1, "with", Value::Null) {
|
||||
Value::Map(map) => Some(map),
|
||||
@ -45,16 +39,8 @@ fn load_css(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<()> {
|
||||
|
||||
let mut values = BTreeMap::new();
|
||||
for (key, value) in with {
|
||||
let name = match key.node {
|
||||
Value::String(s, ..) => Identifier::from(s),
|
||||
v => {
|
||||
return Err((
|
||||
format!("$with key: {} is not a string.", v.inspect(span)?),
|
||||
span,
|
||||
)
|
||||
.into())
|
||||
}
|
||||
};
|
||||
let name =
|
||||
Identifier::from(key.node.assert_string_with_name("with key", args.span())?.0);
|
||||
|
||||
if values.contains_key(&name) {
|
||||
// todo: write test for this
|
||||
@ -97,16 +83,11 @@ fn load_css(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<()> {
|
||||
fn module_functions(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||
args.max_args(1)?;
|
||||
|
||||
let module = match args.get_err(0, "module")? {
|
||||
Value::String(s, ..) => s,
|
||||
v => {
|
||||
return Err((
|
||||
format!("$module: {} is not a string.", v.inspect(args.span())?),
|
||||
args.span(),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
};
|
||||
let module = Identifier::from(
|
||||
args.get_err(0, "module")?
|
||||
.assert_string_with_name("module", args.span())?
|
||||
.0,
|
||||
);
|
||||
|
||||
Ok(Value::Map(
|
||||
(*(*visitor.env.modules)
|
||||
@ -120,16 +101,11 @@ fn module_functions(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResu
|
||||
fn module_variables(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||
args.max_args(1)?;
|
||||
|
||||
let module = match args.get_err(0, "module")? {
|
||||
Value::String(s, ..) => s,
|
||||
v => {
|
||||
return Err((
|
||||
format!("$module: {} is not a string.", v.inspect(args.span())?),
|
||||
args.span(),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
};
|
||||
let module = Identifier::from(
|
||||
args.get_err(0, "module")?
|
||||
.assert_string_with_name("module", args.span())?
|
||||
.0,
|
||||
);
|
||||
|
||||
Ok(Value::Map(
|
||||
(*(*visitor.env.modules)
|
||||
|
@ -30,6 +30,14 @@ pub(crate) fn conversion_factor(from: &Unit, to: &Unit) -> Option<f64> {
|
||||
}
|
||||
|
||||
impl SassNumber {
|
||||
pub fn new_unitless<N: Into<Number>>(n: N) -> Self {
|
||||
Self {
|
||||
num: n.into(),
|
||||
unit: Unit::None,
|
||||
as_slash: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_comparable_units(&self, other_unit: &Unit) -> bool {
|
||||
self.unit.comparable(other_unit)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user