Skip to content

Commit

Permalink
Use a NumOrSpecial type to improve css functions.
Browse files Browse the repository at this point in the history
  • Loading branch information
kaj committed May 9, 2024
1 parent c314ba7 commit 6baa6ee
Show file tree
Hide file tree
Showing 37 changed files with 822 additions and 821 deletions.
8 changes: 8 additions & 0 deletions rsass/src/css/call_args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ impl CallArgs {
}
}

pub(crate) fn from_iter<T, I>(positional: I) -> Self
where
T: Into<Value>,
I: IntoIterator<Item = T>,
{
Self::from_list(positional.into_iter().map(Into::into).collect())
}

pub(crate) fn add_from_value_map(
&mut self,
map: OrderMap<Value, Value>,
Expand Down
2 changes: 1 addition & 1 deletion rsass/src/css/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ pub use self::value::{InvalidCss, Value, ValueMap, ValueToMapError};
pub(crate) use self::selectors::{
CssSelectorSet, LogicalSelector, SelectorCtx,
};
pub(crate) use self::util::{is_calc_name, is_function_name, is_not};
pub(crate) use self::util::{is_calc_name, is_function_name, is_not, IsNot};
7 changes: 7 additions & 0 deletions rsass/src/css/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ impl Value {
Value::Numeric(Numeric::scalar(v), true)
}

pub(crate) fn call<T: Into<Value>, I>(name: &str, args: I) -> Value
where
I: IntoIterator<Item = T>,
{
Value::Call(name.into(), CallArgs::from_iter(args))
}

/// Check that the value is valid in css.
pub fn valid_css(self) -> Result<Self, InvalidCss> {
match self {
Expand Down
12 changes: 8 additions & 4 deletions rsass/src/sass/functions/call_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ impl CallError {
}

/// The values were expected to be compatible, but wasn't.
pub fn incompatible_values(a: &Value, b: &Value) -> Self {
pub fn incompatible_values<T: Into<Value>>(a: T, b: T) -> Self {
Self::msg(format!(
"{} and {} are incompatible.",
a.format(Format::introspect()),
b.format(Format::introspect()),
a.into().format(Format::introspect()),
b.into().format(Format::introspect()),
))
}

Expand All @@ -46,7 +46,11 @@ impl CallError {
Error::BadCall(format!("{err:?}"), call_pos.clone(), None)
}
CallError::BadArgument(name, problem) => Error::BadCall(
format!("${name}: {problem}"),
if name.as_ref().is_empty() {
problem
} else {
format!("${name}: {problem}")
},
call_pos.clone(),
None,
),
Expand Down
24 changes: 14 additions & 10 deletions rsass/src/sass/functions/color/hsl.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use super::channels::Channels;
use super::{
check_alpha, check_amount, check_hue, check_pct, eval_inner, is_not,
is_special, make_call, relative_color, CallError, CheckedArg,
FunctionMap, ResolvedArgs,
is_special, relative_color, CallError, CheckedArg, FunctionMap,
NumOrSpecial, ResolvedArgs,
};
use crate::css::{CallArgs, Value};
use crate::output::Format;
Expand Down Expand Up @@ -34,7 +34,7 @@ pub fn register(f: &mut Scope) {
Ok(Hsla::new(col.hue(), zero(), col.lum(), col.alpha(), false)
.into())
}
v @ Value::Numeric(..) => Ok(make_call("grayscale", vec![v])),
v @ Value::Numeric(..) => Ok(Value::call("grayscale", [v])),
v => Err(is_not(&v, "a color")).named(name!(color)),
});
}
Expand Down Expand Up @@ -77,9 +77,10 @@ pub fn expose(m: &Scope, global: &mut FunctionMap) {
Ok(Hsla::new(col.hue(), zero(), col.lum(), col.alpha(), false)
.into())
}
v @ Value::Numeric(..) => Ok(make_call("grayscale", vec![v])),
v if is_special(&v) => Ok(make_call("grayscale", vec![v])),
v => Err(is_not(&v, "a color")).named(name!(color)),
v => NumOrSpecial::try_from(v)
.map_err(|e| is_not(e.value(), "a color"))
.named(name!(color))
.map(|v| Value::call("grayscale", [v])),
});
def_va!(f, saturate(kwargs), |s| {
let a1 = FormalArgs::new(vec![one_arg!(color), one_arg!(amount)]);
Expand All @@ -103,7 +104,7 @@ pub fn expose(m: &Scope, global: &mut FunctionMap) {
check_pct(v.clone()).map(|_| v) // validate only
}
})?;
Ok(make_call("saturate", vec![sat]))
Ok(Value::call("saturate", [sat]))
}
Err(ae) => Err(ae),
}
Expand Down Expand Up @@ -152,7 +153,7 @@ fn do_hsla(fn_name: &Name, s: &ResolvedArgs) -> Result<Value, CallError> {
hsla_from_values(fn_name, h, s, l, a)
}
Channels::Special(channels) => {
Ok(make_call(fn_name.as_ref(), vec![channels]))
Ok(Value::call(fn_name.as_ref(), [channels]))
}
}),
Err(err @ CallError::Args(ArgsError::Missing(_), _)) => Err(err),
Expand All @@ -179,7 +180,10 @@ fn hsla_from_values(
a: Value,
) -> Result<Value, CallError> {
if is_special(&h) || is_special(&s) || is_special(&l) || is_special(&a) {
Ok(make_call(fn_name.as_ref(), vec![h, s, l, a]))
Ok(Value::call(
fn_name.as_ref(),
[h, s, l, a].into_iter().filter(|v| v != &Value::Null),
))
} else if l == Value::Null {
Err(CallError::msg("Missing argument $lightness."))
} else {
Expand All @@ -195,7 +199,7 @@ fn hsla_from_values(
}

pub fn percentage(v: Rational) -> Value {
Numeric::new(v * 100, Unit::Percent).into()
Numeric::percentage(v).into()
}

/// Gets a percentage as a fraction 0 .. 1.
Expand Down
34 changes: 16 additions & 18 deletions rsass/src/sass/functions/color/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::{
expected_to, is_not, is_special, CallError, CheckedArg, FunctionMap,
expected_to, is_not, CallError, CheckedArg, FunctionMap, NumOrSpecial,
ResolvedArgs,
};
use crate::css::{CallArgs, CssString, Value};
Expand Down Expand Up @@ -114,7 +114,7 @@ fn check_hue(v: Value) -> Result<Rational, String> {
fn check_pct(v: Value) -> Result<Number, String> {
let val = Numeric::try_from(v)?;
match val.as_unit_def(Unit::Percent) {
Some(v) => Ok(v),
Some(v) => Ok(v / 100),
None => {
dep_warn!(
"Passing a number without unit % ({}) is deprecated.\
Expand All @@ -123,7 +123,7 @@ fn check_pct(v: Value) -> Result<Number, String> {
val.format(Format::introspect()),
val.unit
);
Ok(val.value)
Ok(val.value / 100)
}
}
}
Expand All @@ -142,22 +142,22 @@ fn check_expl_pct(v: Value) -> Result<Rational, String> {

fn check_pct_range(v: Value) -> Result<Rational, String> {
let val = check_pct(v)?;
if val < zero() || val > 100.into() {
if val < zero() || val > one() {
Err(expected_to(
Numeric::new(val, Unit::Percent),
Numeric::percentage(val),
"be within 0% and 100%",
))
} else {
Ok(val.as_ratio()? / 100)
Ok(val.as_ratio()?)
}
}

fn check_amount(v: Value) -> Result<Rational, String> {
let val = check_pct(v)?;
if val < zero() || val > 100.into() {
Err(expected_to(Value::scalar(val), "be within 0 and 100"))
if val < zero() || val > one() {
Err(expected_to(Value::scalar(val * 100), "be within 0 and 100"))
} else {
Ok(val.as_ratio()? / 100)
Ok(val.as_ratio()?)
}
}

Expand Down Expand Up @@ -191,15 +191,6 @@ fn num2chan(v: &Numeric) -> Result<Rational, String> {
}
}

fn make_call(name: &str, args: Vec<Value>) -> Value {
Value::Call(
name.into(),
CallArgs::from_list(
args.into_iter().filter(|v| v != &Value::Null).collect(),
),
)
}

pub(crate) fn eval_inner(
name: &Name,
decl: &FormalArgs,
Expand Down Expand Up @@ -243,3 +234,10 @@ fn relative_color(args: &CallArgs) -> bool {
}
args.get_single().map(inner).unwrap_or(false)
}

fn is_special(v: &Value) -> bool {
matches!(
NumOrSpecial::try_from(v.clone()),
Ok(NumOrSpecial::Special(_))
)
}
6 changes: 3 additions & 3 deletions rsass/src/sass/functions/color/other.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::{
check_alpha_pm, check_alpha_range, check_channel_pm, check_channel_range,
check_expl_pct, check_hue, expected_to, make_call, CallError, CheckedArg,
check_expl_pct, check_hue, expected_to, CallError, CheckedArg,
FunctionMap, Name,
};
use crate::css::{CallArgs, Value};
Expand Down Expand Up @@ -136,12 +136,12 @@ pub fn register(f: &mut Scope) {

def!(f, opacity(color), |s| match s.get(name!(color))? {
Value::Color(ref col, _) => Ok(Value::scalar(col.get_alpha())),
v => Ok(make_call("opacity", vec![v])),
v => Ok(Value::call("opacity", [v])),
});
def!(f, alpha(color), |s| {
let v = s.get(name!(color))?;
if ok_as_filterarg(&v) {
Ok(make_call("alpha", vec![v]))
Ok(Value::call("alpha", [v]))
} else {
let color = Color::try_from(v).named(name!(color))?;
Ok(Value::scalar(color.get_alpha()))
Expand Down
32 changes: 15 additions & 17 deletions rsass/src/sass/functions/color/rgb.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use super::channels::Channels;
use super::{
check_alpha, check_channel, check_pct_range, eval_inner, is_not,
is_special, make_call, relative_color, CallError, CheckedArg,
FunctionMap, ResolvedArgs,
is_special, relative_color, CallError, CheckedArg, FunctionMap,
NumOrSpecial, ResolvedArgs,
};
use crate::css::{CallArgs, Value};
use crate::sass::{ArgsError, FormalArgs, Name};
Expand Down Expand Up @@ -66,7 +66,7 @@ pub fn register(f: &mut Scope) {
if w == one() {
match col {
v @ Value::Numeric(..) => {
Ok(make_call("invert", vec![v]))
Ok(Value::call("invert", [v]))
}
v => Err(is_not(&v, "a color")).named(name!(color)),
}
Expand Down Expand Up @@ -99,15 +99,10 @@ pub fn expose(m: &Scope, global: &mut FunctionMap) {
col => {
let w = s.get_map(name!(weight), check_pct_range)?;
if w == one() {
match col {
v @ Value::Numeric(..) => {
Ok(make_call("invert", vec![v]))
}
v if is_special(&v) => {
Ok(make_call("invert", vec![v]))
}
v => Err(is_not(&v, "a color")).named(name!(color)),
}
NumOrSpecial::try_from(col)
.map_err(|e| is_not(e.value(), "a color"))
.named(name!(color))
.map(|v| Value::call("invert", [v]))
} else {
Err(CallError::msg("Only one argument may be passed to the plain-CSS invert() function."))
}
Expand Down Expand Up @@ -161,7 +156,7 @@ fn do_rgba(fn_name: &Name, s: &ResolvedArgs) -> Result<Value, CallError> {
rgba_from_values(fn_name, h, s, l, a)
}
Channels::Special(channels) => {
Ok(make_call(fn_name.as_ref(), vec![channels]))
Ok(Value::call(fn_name.as_ref(), [channels]))
}
}),
Err(err @ CallError::Args(ArgsError::Missing(_), _)) => Err(err),
Expand All @@ -172,17 +167,17 @@ fn do_rgba(fn_name: &Name, s: &ResolvedArgs) -> Result<Value, CallError> {
if is_special(&c) || is_special(&a) {
if let Ok(c) = Color::try_from(c.clone()) {
let c = c.to_rgba();
Ok(make_call(
Ok(Value::call(
fn_name.as_ref(),
vec![
[
Value::scalar(c.red()),
Value::scalar(c.green()),
Value::scalar(c.blue()),
a,
],
))
} else {
Ok(make_call(fn_name.as_ref(), vec![c, a]))
Ok(Value::call(fn_name.as_ref(), [c, a]))
}
} else {
let mut c = Color::try_from(c).named(name!(color))?;
Expand Down Expand Up @@ -216,7 +211,10 @@ fn rgba_from_values(
a: Value,
) -> Result<Value, CallError> {
if is_special(&r) || is_special(&g) || is_special(&b) || is_special(&a) {
Ok(make_call(fn_name.as_ref(), vec![r, g, b, a]))
Ok(Value::call(
fn_name.as_ref(),
[r, g, b, a].into_iter().filter(|v| v != &Value::Null),
))
} else {
Ok(Rgba::new(
check_channel(r).named(name!(red))?,
Expand Down
4 changes: 2 additions & 2 deletions rsass/src/sass/functions/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ pub fn create_module() -> Scope {
Ok(sep.into())
});
def_va!(f, slash(elements), |s| {
let list = s.get_map(name!(elements), check::va_list)?;
let list = s.get_va(name!(elements))?;
if list.len() < 2 {
return Err(CallError::msg(
"At least two elements are required.",
Expand Down Expand Up @@ -140,7 +140,7 @@ pub fn create_module() -> Scope {
});
def_va!(f, zip(lists), |s| {
let lists = s
.get_map(name!(lists), check::va_list)?
.get_va(name!(lists))?
.into_iter()
.map(Value::iter_items)
.collect::<Vec<_>>();
Expand Down
Loading

0 comments on commit 6baa6ee

Please sign in to comment.