2020-03-03 19:51:02 -05:00
|
|
|
use std::collections::HashMap;
|
2020-02-14 12:43:12 -05:00
|
|
|
|
2020-03-22 22:28:54 -04:00
|
|
|
use num_traits::One;
|
|
|
|
|
2020-02-14 12:43:12 -05:00
|
|
|
use super::Builtin;
|
|
|
|
use crate::color::Color;
|
2020-02-16 16:11:24 -05:00
|
|
|
use crate::common::QuoteKind;
|
2020-03-19 16:24:31 -04:00
|
|
|
use crate::unit::Unit;
|
2020-02-14 12:43:12 -05:00
|
|
|
use crate::value::{Number, Value};
|
|
|
|
|
2020-03-03 19:51:02 -05:00
|
|
|
pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
2020-03-16 10:35:38 -04:00
|
|
|
f.insert(
|
|
|
|
"hsl".to_owned(),
|
|
|
|
Box::new(|args, _| {
|
2020-03-22 23:41:02 -04:00
|
|
|
if args.is_empty() {
|
|
|
|
return Err("Missing argument $channels.".into());
|
|
|
|
}
|
|
|
|
|
2020-03-19 23:44:53 -04:00
|
|
|
if args.len() == 1 {
|
|
|
|
let mut channels = match arg!(args, 0, "channels") {
|
|
|
|
Value::List(v, _) => v,
|
2020-03-23 12:42:58 -04:00
|
|
|
_ => return Err("Missing argument $channels.".into()),
|
2020-03-19 23:44:53 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
if channels.len() > 3 {
|
|
|
|
return Err(format!(
|
|
|
|
"Only 3 elements allowed, but {} were passed.",
|
|
|
|
channels.len()
|
|
|
|
)
|
|
|
|
.into());
|
2020-03-16 10:35:38 -04:00
|
|
|
}
|
2020-03-19 23:44:53 -04:00
|
|
|
|
|
|
|
let luminance = match channels.pop() {
|
|
|
|
Some(Value::Dimension(n, _)) => n / Number::from(100),
|
2020-03-23 12:42:58 -04:00
|
|
|
Some(v) => return Err(format!("$luminance: {} is not a number", v).into()),
|
2020-03-19 23:44:53 -04:00
|
|
|
None => return Err("Missing element $luminance.".into()),
|
|
|
|
};
|
|
|
|
|
|
|
|
let saturation = match channels.pop() {
|
|
|
|
Some(Value::Dimension(n, _)) => n / Number::from(100),
|
2020-03-23 12:42:58 -04:00
|
|
|
Some(v) => return Err(format!("$saturation: {} is not a number", v).into()),
|
2020-03-19 23:44:53 -04:00
|
|
|
None => return Err("Missing element $saturation.".into()),
|
|
|
|
};
|
|
|
|
|
|
|
|
let hue = match channels.pop() {
|
|
|
|
Some(Value::Dimension(n, _)) => n,
|
2020-03-23 12:42:58 -04:00
|
|
|
Some(v) => return Err(format!("$hue: {} is not a number", v).into()),
|
2020-03-19 23:44:53 -04:00
|
|
|
None => return Err("Missing element $hue.".into()),
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(Value::Color(Color::from_hsla(
|
|
|
|
hue,
|
|
|
|
saturation,
|
|
|
|
luminance,
|
2020-03-22 22:28:54 -04:00
|
|
|
Number::one(),
|
2020-03-19 23:44:53 -04:00
|
|
|
)))
|
|
|
|
} else {
|
|
|
|
let hue = match arg!(args, 0, "hue") {
|
|
|
|
Value::Dimension(n, _) => n,
|
|
|
|
v => return Err(format!("$hue: {} is not a number.", v).into()),
|
|
|
|
};
|
|
|
|
let saturation = match arg!(args, 1, "saturation") {
|
|
|
|
Value::Dimension(n, _) => n / Number::from(100),
|
|
|
|
v => return Err(format!("$saturation: {} is not a number.", v).into()),
|
|
|
|
};
|
|
|
|
let luminance = match arg!(args, 2, "luminance") {
|
|
|
|
Value::Dimension(n, _) => n / Number::from(100),
|
|
|
|
v => return Err(format!("$luminance: {} is not a number.", v).into()),
|
|
|
|
};
|
|
|
|
let alpha = match arg!(
|
|
|
|
args,
|
|
|
|
3,
|
2020-03-22 22:28:54 -04:00
|
|
|
"alpha" = Value::Dimension(Number::one(), Unit::None)
|
2020-03-19 23:44:53 -04:00
|
|
|
) {
|
|
|
|
Value::Dimension(n, Unit::None) => n,
|
|
|
|
Value::Dimension(n, Unit::Percent) => n / Number::from(100),
|
|
|
|
v @ Value::Dimension(..) => {
|
|
|
|
return Err(
|
|
|
|
format!("$alpha: Expected {} to have no units or \"%\".", v).into()
|
|
|
|
)
|
|
|
|
}
|
|
|
|
v => return Err(format!("$alpha: {} is not a number.", v).into()),
|
|
|
|
};
|
|
|
|
Ok(Value::Color(Color::from_hsla(
|
|
|
|
hue, saturation, luminance, alpha,
|
|
|
|
)))
|
|
|
|
}
|
2020-03-16 10:35:38 -04:00
|
|
|
}),
|
|
|
|
);
|
|
|
|
f.insert(
|
|
|
|
"hsla".to_owned(),
|
|
|
|
Box::new(|args, _| {
|
2020-03-22 23:41:02 -04:00
|
|
|
if args.is_empty() {
|
|
|
|
return Err("Missing argument $channels.".into());
|
|
|
|
}
|
|
|
|
|
2020-03-19 23:44:53 -04:00
|
|
|
if args.len() == 1 {
|
|
|
|
let mut channels = match arg!(args, 0, "channels") {
|
|
|
|
Value::List(v, _) => v,
|
2020-03-23 12:42:58 -04:00
|
|
|
_ => return Err("Missing argument $channels.".into()),
|
2020-03-19 23:44:53 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
if channels.len() > 3 {
|
|
|
|
return Err(format!(
|
|
|
|
"Only 3 elements allowed, but {} were passed.",
|
|
|
|
channels.len()
|
|
|
|
)
|
|
|
|
.into());
|
2020-03-16 10:35:38 -04:00
|
|
|
}
|
2020-03-19 23:44:53 -04:00
|
|
|
|
|
|
|
let luminance = match channels.pop() {
|
|
|
|
Some(Value::Dimension(n, _)) => n / Number::from(100),
|
2020-03-23 12:42:58 -04:00
|
|
|
Some(v) => return Err(format!("$luminance: {} is not a number", v).into()),
|
2020-03-19 23:44:53 -04:00
|
|
|
None => return Err("Missing element $luminance.".into()),
|
|
|
|
};
|
|
|
|
|
|
|
|
let saturation = match channels.pop() {
|
|
|
|
Some(Value::Dimension(n, _)) => n / Number::from(100),
|
2020-03-23 12:42:58 -04:00
|
|
|
Some(v) => return Err(format!("$saturation: {} is not a number", v).into()),
|
2020-03-19 23:44:53 -04:00
|
|
|
None => return Err("Missing element $saturation.".into()),
|
|
|
|
};
|
|
|
|
|
|
|
|
let hue = match channels.pop() {
|
|
|
|
Some(Value::Dimension(n, _)) => n,
|
2020-03-23 12:42:58 -04:00
|
|
|
Some(v) => return Err(format!("$hue: {} is not a number", v).into()),
|
2020-03-19 23:44:53 -04:00
|
|
|
None => return Err("Missing element $hue.".into()),
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(Value::Color(Color::from_hsla(
|
|
|
|
hue,
|
|
|
|
saturation,
|
|
|
|
luminance,
|
2020-03-22 22:28:54 -04:00
|
|
|
Number::one(),
|
2020-03-19 23:44:53 -04:00
|
|
|
)))
|
|
|
|
} else {
|
|
|
|
let hue = match arg!(args, 0, "hue") {
|
|
|
|
Value::Dimension(n, _) => n,
|
|
|
|
v => return Err(format!("$hue: {} is not a number.", v).into()),
|
|
|
|
};
|
|
|
|
let saturation = match arg!(args, 1, "saturation") {
|
|
|
|
Value::Dimension(n, _) => n / Number::from(100),
|
|
|
|
v => return Err(format!("$saturation: {} is not a number.", v).into()),
|
|
|
|
};
|
|
|
|
let luminance = match arg!(args, 2, "luminance") {
|
|
|
|
Value::Dimension(n, _) => n / Number::from(100),
|
|
|
|
v => return Err(format!("$luminance: {} is not a number.", v).into()),
|
|
|
|
};
|
|
|
|
let alpha = match arg!(
|
|
|
|
args,
|
|
|
|
3,
|
2020-03-22 22:28:54 -04:00
|
|
|
"alpha" = Value::Dimension(Number::one(), Unit::None)
|
2020-03-19 23:44:53 -04:00
|
|
|
) {
|
|
|
|
Value::Dimension(n, Unit::None) => n,
|
|
|
|
Value::Dimension(n, Unit::Percent) => n / Number::from(100),
|
|
|
|
v @ Value::Dimension(..) => {
|
|
|
|
return Err(
|
|
|
|
format!("$alpha: Expected {} to have no units or \"%\".", v).into()
|
|
|
|
)
|
|
|
|
}
|
|
|
|
v => return Err(format!("$alpha: {} is not a number.", v).into()),
|
|
|
|
};
|
|
|
|
Ok(Value::Color(Color::from_hsla(
|
|
|
|
hue, saturation, luminance, alpha,
|
|
|
|
)))
|
|
|
|
}
|
2020-03-16 10:35:38 -04:00
|
|
|
}),
|
|
|
|
);
|
|
|
|
f.insert(
|
|
|
|
"hue".to_owned(),
|
|
|
|
Box::new(|args, _| {
|
|
|
|
max_args!(args, 1);
|
|
|
|
match arg!(args, 0, "color") {
|
|
|
|
Value::Color(c) => Ok(Value::Dimension(c.hue(), Unit::Deg)),
|
|
|
|
v => Err(format!("$color: {} is not a color.", v).into()),
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
f.insert(
|
|
|
|
"saturation".to_owned(),
|
|
|
|
Box::new(|args, _| {
|
|
|
|
max_args!(args, 1);
|
|
|
|
match arg!(args, 0, "color") {
|
|
|
|
Value::Color(c) => Ok(Value::Dimension(c.saturation(), Unit::Percent)),
|
|
|
|
v => Err(format!("$color: {} is not a color.", v).into()),
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
f.insert(
|
|
|
|
"lightness".to_owned(),
|
|
|
|
Box::new(|args, _| {
|
|
|
|
max_args!(args, 1);
|
|
|
|
match arg!(args, 0, "color") {
|
|
|
|
Value::Color(c) => Ok(Value::Dimension(c.lightness(), Unit::Percent)),
|
|
|
|
v => Err(format!("$color: {} is not a color.", v).into()),
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
f.insert(
|
|
|
|
"adjust-hue".to_owned(),
|
|
|
|
Box::new(|args, _| {
|
|
|
|
max_args!(args, 2);
|
|
|
|
let color = match arg!(args, 0, "color") {
|
|
|
|
Value::Color(c) => c,
|
|
|
|
v => return Err(format!("$color: {} is not a color.", v).into()),
|
|
|
|
};
|
|
|
|
let degrees = match arg!(args, 1, "degrees") {
|
|
|
|
Value::Dimension(n, _) => n,
|
|
|
|
v => return Err(format!("$degrees: {} is not a number.", v).into()),
|
|
|
|
};
|
|
|
|
Ok(Value::Color(color.adjust_hue(degrees)))
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
f.insert(
|
|
|
|
"lighten".to_owned(),
|
|
|
|
Box::new(|args, _| {
|
|
|
|
max_args!(args, 2);
|
|
|
|
let color = match arg!(args, 0, "color") {
|
|
|
|
Value::Color(c) => c,
|
|
|
|
v => return Err(format!("$color: {} is not a color.", v).into()),
|
|
|
|
};
|
|
|
|
let amount = match arg!(args, 1, "amount") {
|
|
|
|
Value::Dimension(n, u) => bound!("amount", n, u, 0, 100) / Number::from(100),
|
|
|
|
v => return Err(format!("$amount: {} is not a number.", v).into()),
|
|
|
|
};
|
|
|
|
Ok(Value::Color(color.lighten(amount)))
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
f.insert(
|
|
|
|
"darken".to_owned(),
|
|
|
|
Box::new(|args, _| {
|
|
|
|
max_args!(args, 2);
|
|
|
|
let color = match arg!(args, 0, "color") {
|
|
|
|
Value::Color(c) => c,
|
|
|
|
v => return Err(format!("$color: {} is not a color.", v).into()),
|
|
|
|
};
|
|
|
|
let amount = match arg!(args, 1, "amount") {
|
|
|
|
Value::Dimension(n, u) => bound!("amount", n, u, 0, 100) / Number::from(100),
|
|
|
|
v => return Err(format!("$amount: {} is not a number.", v).into()),
|
|
|
|
};
|
|
|
|
Ok(Value::Color(color.darken(amount)))
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
f.insert(
|
|
|
|
"saturate".to_owned(),
|
|
|
|
Box::new(|args, _| {
|
|
|
|
max_args!(args, 2);
|
2020-03-22 23:28:19 -04:00
|
|
|
if args.len() == 1 {
|
2020-03-22 23:41:02 -04:00
|
|
|
return Ok(Value::Ident(
|
|
|
|
format!("saturate({})", arg!(args, 0, "amount")),
|
|
|
|
QuoteKind::None,
|
|
|
|
));
|
2020-03-22 23:28:19 -04:00
|
|
|
}
|
|
|
|
|
2020-03-16 10:35:38 -04:00
|
|
|
let amount = match arg!(args, 1, "amount") {
|
|
|
|
Value::Dimension(n, u) => bound!("amount", n, u, 0, 100) / Number::from(100),
|
|
|
|
v => return Err(format!("$amount: {} is not a number.", v).into()),
|
|
|
|
};
|
|
|
|
let color = match arg!(args, 0, "color") {
|
|
|
|
Value::Color(c) => c,
|
|
|
|
Value::Dimension(n, u) => {
|
|
|
|
return Ok(Value::Ident(
|
|
|
|
format!("saturate({}{})", n, u),
|
|
|
|
QuoteKind::None,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
v => return Err(format!("$color: {} is not a color.", v).into()),
|
|
|
|
};
|
|
|
|
Ok(Value::Color(color.saturate(amount)))
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
f.insert(
|
|
|
|
"desaturate".to_owned(),
|
|
|
|
Box::new(|args, _| {
|
|
|
|
max_args!(args, 2);
|
|
|
|
let color = match arg!(args, 0, "color") {
|
|
|
|
Value::Color(c) => c,
|
|
|
|
v => return Err(format!("$color: {} is not a color.", v).into()),
|
|
|
|
};
|
|
|
|
let amount = match arg!(args, 1, "amount") {
|
|
|
|
Value::Dimension(n, u) => bound!("amount", n, u, 0, 100) / Number::from(100),
|
|
|
|
v => return Err(format!("$amount: {} is not a number.", v).into()),
|
|
|
|
};
|
|
|
|
Ok(Value::Color(color.desaturate(amount)))
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
f.insert(
|
|
|
|
"grayscale".to_owned(),
|
|
|
|
Box::new(|args, _| {
|
|
|
|
max_args!(args, 1);
|
|
|
|
let color = match arg!(args, 0, "color") {
|
|
|
|
Value::Color(c) => c,
|
|
|
|
Value::Dimension(n, u) => {
|
|
|
|
return Ok(Value::Ident(
|
|
|
|
format!("grayscale({}{})", n, u),
|
|
|
|
QuoteKind::None,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
v => return Err(format!("$color: {} is not a color.", v).into()),
|
|
|
|
};
|
2020-03-22 22:28:54 -04:00
|
|
|
Ok(Value::Color(color.desaturate(Number::one())))
|
2020-03-16 10:35:38 -04:00
|
|
|
}),
|
|
|
|
);
|
|
|
|
f.insert(
|
|
|
|
"complement".to_owned(),
|
|
|
|
Box::new(|args, _| {
|
|
|
|
max_args!(args, 1);
|
|
|
|
let color = match arg!(args, 0, "color") {
|
|
|
|
Value::Color(c) => c,
|
|
|
|
v => return Err(format!("$color: {} is not a color.", v).into()),
|
|
|
|
};
|
|
|
|
Ok(Value::Color(color.complement()))
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
f.insert(
|
|
|
|
"invert".to_owned(),
|
|
|
|
Box::new(|args, _| {
|
|
|
|
max_args!(args, 2);
|
|
|
|
let weight = match arg!(
|
|
|
|
args,
|
|
|
|
1,
|
|
|
|
"weight" = Value::Dimension(Number::from(100), Unit::Percent)
|
|
|
|
) {
|
|
|
|
Value::Dimension(n, u) => bound!("weight", n, u, 0, 100) / Number::from(100),
|
|
|
|
v => return Err(format!("$weight: {} is not a number.", v).into()),
|
|
|
|
};
|
|
|
|
match arg!(args, 0, "color") {
|
|
|
|
Value::Color(c) => Ok(Value::Color(c.invert(weight))),
|
|
|
|
Value::Dimension(n, Unit::Percent) => {
|
|
|
|
Ok(Value::Ident(format!("invert({}%)", n), QuoteKind::None))
|
|
|
|
}
|
|
|
|
Value::Dimension(..) => Err(
|
|
|
|
"Only one argument may be passed to the plain-CSS invert() function.".into(),
|
|
|
|
),
|
|
|
|
v => Err(format!("$color: {} is not a color.", v).into()),
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
);
|
2020-02-14 14:23:54 -05:00
|
|
|
}
|