Skip to content

Commit

Permalink
Some cleanup of function parameter checking.
Browse files Browse the repository at this point in the history
  • Loading branch information
kaj committed Jun 19, 2021
1 parent 38a2441 commit c14e7f8
Show file tree
Hide file tree
Showing 9 changed files with 173 additions and 198 deletions.
51 changes: 22 additions & 29 deletions src/sass/functions/color/hsl.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
use super::channels::Channels;
use super::{
bad_arg, check_alpha, check_pct_rational_range, check_rational,
get_checked, get_color, get_opt_check, is_special, make_call, CheckedArg,
FunctionMap,
bad_arg, check_alpha, check_pct_range, check_rational, get_checked,
get_color, get_opt_check, is_special, make_call, CheckedArg, FunctionMap,
};
use crate::css::{CallArgs, Value};
use crate::output::Format;
use crate::sass::{ArgsError, FormalArgs, Name};
use crate::value::{Hsla, Numeric, Rational, Unit};
use crate::{Error, Scope, ScopeRef};
use num_traits::{one, zero};
use num_traits::zero;
use std::convert::TryFrom;

pub fn register(f: &mut Scope) {
Expand Down Expand Up @@ -70,15 +69,15 @@ pub fn expose(m: &Scope, global: &mut FunctionMap) {
def!(f, darken(color, amount), |s| {
let col = get_color(s, "color")?;
let hsla = col.to_hsla();
let lum = hsla.lum()
- get_checked(s, name!(amount), check_pct_rational_range)?;
let lum =
hsla.lum() - get_checked(s, name!(amount), check_pct_range)?;
Ok(Hsla::new(hsla.hue(), hsla.sat(), lum, hsla.alpha()).into())
});
def!(f, desaturate(color, amount), |s| {
let col = get_color(s, "color")?;
let hsla = col.to_hsla();
let sat = hsla.sat()
- get_checked(s, name!(amount), check_pct_rational_range)?;
let sat =
hsla.sat() - get_checked(s, name!(amount), check_pct_range)?;
Ok(Hsla::new(hsla.hue(), sat, hsla.lum(), hsla.alpha()).into())
});
def_va!(f, saturate(kwargs), |s| {
Expand All @@ -88,8 +87,7 @@ pub fn expose(m: &Scope, global: &mut FunctionMap) {
match a1.eval(s.clone(), args.clone()) {
Ok(s) => {
let col = get_color(&s, "color")?;
let sat =
get_checked(&s, name!(amount), check_pct_rational_range)?;
let sat = get_checked(&s, name!(amount), check_pct_range)?;
let hsla = col.to_hsla();
let sat = hsla.sat() + sat;
Ok(Hsla::new(hsla.hue(), sat, hsla.lum(), hsla.alpha())
Expand All @@ -100,7 +98,7 @@ pub fn expose(m: &Scope, global: &mut FunctionMap) {
.eval(s.clone(), args)
.map_err(|e| bad_arg(e, &name!(saturate), &a2))?;
let sat = s.get("amount")?;
check_pct_rational_range(sat.clone()).named(name!(amount))?;
check_pct_range(sat.clone()).named(name!(amount))?;
Ok(make_call("saturate", vec![sat]))
}
Err(ae) => Err(bad_arg(ae, &name!(saturate), &a1)),
Expand All @@ -109,8 +107,8 @@ pub fn expose(m: &Scope, global: &mut FunctionMap) {
def!(f, lighten(color, amount), |s| {
let col = get_color(s, "color")?;
let hsla = col.to_hsla();
let lum = hsla.lum()
+ get_checked(s, name!(amount), check_pct_rational_range)?;
let lum =
hsla.lum() + get_checked(s, name!(amount), check_pct_range)?;
Ok(Hsla::new(hsla.hue(), hsla.sat(), lum, hsla.alpha()).into())
});
for (gname, lname) in &[
Expand Down Expand Up @@ -181,8 +179,8 @@ fn hsla_from_values(
} else {
Ok(Hsla::new(
check_rational(h).named(name!(hue))?,
check_rational_percent(s).named(name!(saturation))?,
check_rational_percent(l).named(name!(lightness))?,
check_pct_opt(s).named(name!(saturation))?,
check_pct_opt(l).named(name!(lightness))?,
check_alpha(a).named(name!(alpha))?,
)
.into())
Expand All @@ -195,22 +193,17 @@ pub fn percentage(v: Rational) -> Value {

/// Gets a percentage as a fraction 0 .. 1.
/// If v is not a percentage, keep it as it is.
fn check_rational_percent(v: Value) -> Result<Rational, String> {
match v {
Value::Null => Ok(zero()),
Value::Numeric(v, ..) => {
let r = v.value.as_ratio().map_err(|e| e.to_string())?;
if v.unit.is_percent() || r > one() {
Ok(r / 100)
} else {
Ok(r)
}
}
v => Err(format!(
"{} is not a number",
fn check_pct_opt(v: Value) -> Result<Rational, String> {
let v = super::check::numeric(v)?;
if !v.unit.is_percent() {
// Note: The deprecation warning should include the parameter name
// and line of call, but we don't have that here.
dep_warn!(
"Passing a number without unit % ({}) is deprecated.",
v.format(Format::introspect())
)),
);
}
Ok(v.as_ratio()? / 100)
}

#[test]
Expand Down
14 changes: 7 additions & 7 deletions src/sass/functions/color/hwb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use super::{
use crate::css::{CallArgs, Value};
use crate::output::Format;
use crate::sass::FormalArgs;
use crate::value::{Hwba, ListSeparator, Number, Unit};
use crate::value::{Hwba, ListSeparator, Rational, Unit};
use crate::{Error, Scope, ScopeRef};

pub fn register(f: &mut Scope) {
Expand Down Expand Up @@ -43,11 +43,11 @@ fn hwb(s: &ScopeRef) -> Result<Value, Error> {
))
})?
};
let hue = as_hue(hue).named(name!(hue))?.as_ratio()?;
let w = check_expl_pct(w).named(name!(whiteness))?.as_ratio()?;
let b = check_expl_pct(b).named(name!(blackness))?.as_ratio()?;
let hue = check_hue(hue).named(name!(hue))?;
let w = check_expl_pct(w).named(name!(whiteness))?;
let b = check_expl_pct(b).named(name!(blackness))?;
let a = check_alpha(a).named(name!(alpha))?;
Ok(Hwba::new(hue, w / 100, b / 100, a).into())
Ok(Hwba::new(hue, w, b, a).into())
}

fn hwb_from_channels(
Expand Down Expand Up @@ -106,10 +106,10 @@ fn badchannels(v: &Value) -> Error {
))
}

fn as_hue(v: Value) -> Result<Number, String> {
fn check_hue(v: Value) -> Result<Rational, String> {
let vv = check::numeric(v)?;
if let Some(scaled) = vv.as_unit_def(Unit::Deg) {
Ok(scaled)
Ok(scaled.as_ratio()?)
} else {
Err(format!(
"{} is not an angle",
Expand Down
118 changes: 55 additions & 63 deletions src/sass/functions/color/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::css::{CallArgs, Value};
use crate::output::Format;
use crate::parser::SourcePos;
use crate::sass::{ArgsError, FormalArgs, Name};
use crate::value::{Color, Number, Quotes, Rational, Unit};
use crate::value::{Color, Number, Numeric, Quotes, Rational, Unit};
use crate::Scope;
use num_traits::{one, zero, Signed};
mod channels;
Expand Down Expand Up @@ -73,19 +73,35 @@ fn check_color(v: Value) -> Result<Color, String> {
/// For the alpha parameter of rgba, hsla, hwba functions
/// Special perk: Defaults to 1.0 if the value is null.
fn check_alpha(v: Value) -> Result<Rational, String> {
match v {
Value::Null => Ok(one()),
Ok(match v {
Value::Null => one(),
v => {
let num = check::numeric(v)?;
let r = num.value.as_ratio().map_err(|e| e.to_string())?;
if num.unit.is_percent() {
Ok(r / 100)
} else if num.unit.is_none() {
Ok(r)
} else {
Err(expected_to(&num, "have no units or \"%\""))
}
num.as_unit(Unit::None)
.ok_or_else(|| expected_to(&num, "have no units or \"%\""))?
.as_ratio()?
}
})
}
/// Get a rational number in the 0..1 range.
fn check_alpha_range(v: Value) -> Result<Rational, String> {
let val = check::numeric(v)?;
let a = val
.as_unit_def(Unit::None)
.ok_or_else(|| expected_to(&val, "have no units or \"%\""))?
.as_ratio()?;
if a < zero() || a > one() {
Err(expected_to(&val, "be within 0 and 1"))
} else {
Ok(a)
}
}
fn check_alpha_pm(v: Value) -> Result<Rational, String> {
let v = check_rational(v)?;
if v.abs() > one() {
Err(expected_to(&Number::from(v), "be within -1 and 1"))
} else {
Ok(v)
}
}

Expand All @@ -95,81 +111,57 @@ fn check_pct(v: Value) -> Result<Number, String> {
.ok_or_else(|| expected_to(&val, "have unit \"%\""))
}

fn check_pct_expl_rational_pm1(v: Value) -> Result<Rational, String> {
let val = check::numeric(v)?;
if !val.unit.is_percent() {
return Err(expected_to(&val, "have unit \"%\""));
}
if val.value.clone().abs() > 100.into() {
Err(expected_to(&val, "be within -100% and 100%"))
} else {
let r = val.value.as_ratio().map_err(|e| e.to_string())?;
Ok(r / 100)
}
}

fn check_expl_pct(v: Value) -> Result<Number, String> {
fn check_expl_pct(v: Value) -> Result<Rational, String> {
let val = check::numeric(v)?;
if !val.unit.is_percent() {
return Err(expected_to(&val, "have unit \"%\""));
}
if val.value < 0.into() || val.value > 100.into() {
if val.value < zero() || val.value > 100.into() {
Err(expected_to(&val, "be within 0% and 100%"))
} else {
Ok(val.value)
Ok(val.as_ratio()? / 100)
}
}
fn check_expl_pct_r(v: Value) -> Result<Rational, String> {
check_expl_pct(v)?
.as_ratio()
.map(|r| r / 100)
.map_err(|e| e.to_string())
}

fn check_pct_rational_range(v: Value) -> Result<Rational, String> {
fn check_pct_range(v: Value) -> Result<Rational, String> {
let val = check_pct(v)?;
if val < 0.into() || val > 100.into() {
if val < zero() || val > 100.into() {
Err(expected_to(&val, "be within 0 and 100"))
} else {
val.as_ratio().map_err(|e| e.to_string()).map(|v| v / 100)
Ok(val.as_ratio()? / 100)
}
}
fn check_rational(v: Value) -> Result<Rational, String> {
check::numeric(v)?.as_ratio().map_err(|e| e.to_string())
Ok(check::numeric(v)?.as_ratio()?)
}
fn check_rational_pm1(v: Value) -> Result<Rational, String> {
let v = check_rational(v)?;
if v.abs() > one() {
Err(expected_to(&Number::from(v), "be within -1 and 1"))
} else {
Ok(v)
}
}
fn check_rational_byte(v: Value) -> Result<Rational, String> {
let v = check_rational(v)?;
if v > Rational::from_integer(255) || v < zero() {
Err(expected_to(&Number::from(v), "be within 0 and 255"))

fn check_channel(v: Value) -> Result<Rational, String> {
num2chan(&check::numeric(v)?)
}
fn check_channel_range(v: Value) -> Result<Rational, String> {
let v = check::numeric(v)?;
let r = num2chan(&v)?;
if r > Rational::from_integer(255) || r < zero() {
Err(expected_to(&v, "be within 0 and 255"))
} else {
Ok(v)
Ok(r)
}
}
fn check_rational_pmbyte(v: Value) -> Result<Rational, String> {
let v = check_rational(v)?;
if v > Rational::from_integer(255) || v < Rational::from_integer(-255) {
Err(expected_to(&Number::from(v), "be within -255 and 255"))
fn check_channel_pm(v: Value) -> Result<Rational, String> {
let v = check::numeric(v)?;
let r = num2chan(&v)?;
if r.abs() > Rational::from_integer(255) {
Err(expected_to(&v, "be within -255 and 255"))
} else {
Ok(v)
Ok(r)
}
}
/// Get a rational number in the 0..1 range.
fn check_rational_fract(v: Value) -> Result<Rational, String> {
let val = check::numeric(v)?
.as_unit_def(Unit::None)
.ok_or_else(|| "xyzzy".to_string())?;
if val < 0.into() || val > 1.into() {
Err(expected_to(&val, "be within 0 and 1"))
fn num2chan(v: &Numeric) -> Result<Rational, String> {
let r = v.as_ratio()?;
if v.unit.is_percent() {
Ok(r * 255 / 100)
} else {
val.as_ratio().map_err(|e| e.to_string())
Ok(r)
}
}

Expand Down
Loading

0 comments on commit c14e7f8

Please sign in to comment.