diff --git a/src/builtin/functions/color/hsl.rs b/src/builtin/functions/color/hsl.rs index fb742eb..359afd5 100644 --- a/src/builtin/functions/color/hsl.rs +++ b/src/builtin/functions/color/hsl.rs @@ -18,7 +18,9 @@ fn inner_hsl(name: &'static str, mut args: CallArgs, parser: &mut Parser) -> Sas return Err(("Missing argument $channels.", args.span()).into()); } - if args.len() == 1 { + let len = args.len(); + + if len == 1 { let mut channels = match args.get_err(0, "channels")? { Value::List(v, ..) => v, _ => return Err(("Missing argument $channels.", args.span()).into()), @@ -99,30 +101,42 @@ fn inner_hsl(name: &'static str, mut args: CallArgs, parser: &mut Parser) -> Sas Number::one(), )))) } else { - let hue = match args.get_err(0, "hue")? { + 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(Some(Number::one()), Unit::None, true), + )?; + + if [&hue, &saturation, &lightness, &alpha] + .iter() + .copied() + .any(Value::is_special_function) + { + return Ok(Value::String( + format!( + "{}({})", + name, + Value::List( + if len == 4 { + vec![hue, saturation, lightness, alpha] + } else { + vec![hue, saturation, lightness] + }, + ListSeparator::Comma, + Brackets::None + ) + .to_css_string(args.span(), false)? + ), + QuoteKind::None, + )); + } + + let hue = match hue { Value::Dimension(Some(n), ..) => n, Value::Dimension(None, ..) => todo!(), - v if v.is_special_function() => { - let saturation = args.get_err(1, "saturation")?; - let lightness = args.get_err(2, "lightness")?; - let mut string = format!( - "{}({}, {}, {}", - name, - v.to_css_string(args.span(), parser.options.is_compressed())?, - saturation.to_css_string(args.span(), parser.options.is_compressed())?, - lightness.to_css_string(args.span(), parser.options.is_compressed())? - ); - if !args.is_empty() { - string.push_str(", "); - string.push_str( - &args - .get_err(3, "alpha")? - .to_css_string(args.span(), parser.options.is_compressed())?, - ); - } - string.push(')'); - return Ok(Value::String(string, QuoteKind::None)); - } v => { return Err(( format!("$hue: {} is not a number.", v.inspect(args.span())?), @@ -131,29 +145,9 @@ fn inner_hsl(name: &'static str, mut args: CallArgs, parser: &mut Parser) -> Sas .into()) } }; - let saturation = match args.get_err(1, "saturation")? { + let saturation = match saturation { Value::Dimension(Some(n), ..) => n / Number::from(100), Value::Dimension(None, ..) => todo!(), - v if v.is_special_function() => { - let lightness = args.get_err(2, "lightness")?; - let mut string = format!( - "{}({}, {}, {}", - name, - hue.to_string(parser.options.is_compressed()), - v.to_css_string(args.span(), parser.options.is_compressed())?, - lightness.to_css_string(args.span(), parser.options.is_compressed())? - ); - if !args.is_empty() { - string.push_str(", "); - string.push_str( - &args - .get_err(3, "alpha")? - .to_css_string(args.span(), parser.options.is_compressed())?, - ); - } - string.push(')'); - return Ok(Value::String(string, QuoteKind::None)); - } v => { return Err(( format!( @@ -165,28 +159,9 @@ fn inner_hsl(name: &'static str, mut args: CallArgs, parser: &mut Parser) -> Sas .into()) } }; - let lightness = match args.get_err(2, "lightness")? { + let lightness = match lightness { Value::Dimension(Some(n), ..) => n / Number::from(100), Value::Dimension(None, ..) => todo!(), - v if v.is_special_function() => { - let mut string = format!( - "{}({}, {}, {}", - name, - hue.to_string(parser.options.is_compressed()), - saturation.to_string(parser.options.is_compressed()), - v.to_css_string(args.span(), parser.options.is_compressed())? - ); - if !args.is_empty() { - string.push_str(", "); - string.push_str( - &args - .get_err(3, "alpha")? - .to_css_string(args.span(), parser.options.is_compressed())?, - ); - } - string.push(')'); - return Ok(Value::String(string, QuoteKind::None)); - } v => { return Err(( format!( @@ -198,11 +173,7 @@ fn inner_hsl(name: &'static str, mut args: CallArgs, parser: &mut Parser) -> Sas .into()) } }; - let alpha = match args.default_arg( - 3, - "alpha", - Value::Dimension(Some(Number::one()), Unit::None, true), - )? { + let alpha = match alpha { Value::Dimension(Some(n), Unit::None, _) => n, Value::Dimension(Some(n), Unit::Percent, _) => n / Number::from(100), Value::Dimension(None, ..) => todo!(), @@ -216,19 +187,6 @@ fn inner_hsl(name: &'static str, mut args: CallArgs, parser: &mut Parser) -> Sas ) .into()) } - v if v.is_special_function() => { - return Ok(Value::String( - format!( - "{}({}, {}, {}, {})", - name, - hue.to_string(parser.options.is_compressed()), - saturation.to_string(parser.options.is_compressed()), - lightness.to_string(parser.options.is_compressed()), - v.to_css_string(args.span(), parser.options.is_compressed())? - ), - QuoteKind::None, - )); - } v => { return Err(( format!("$alpha: {} is not a number.", v.inspect(args.span())?), diff --git a/src/builtin/functions/color/rgb.rs b/src/builtin/functions/color/rgb.rs index 9917d8a..ac85b20 100644 --- a/src/builtin/functions/color/rgb.rs +++ b/src/builtin/functions/color/rgb.rs @@ -20,7 +20,9 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser) -> Sas return Err(("Missing argument $channels.", args.span()).into()); } - if args.len() == 1 { + let len = args.len(); + + if len == 1 { let mut channels = match args.get_err(0, "channels")? { Value::List(v, ..) => v, _ => return Err(("Missing argument $channels.", args.span()).into()), @@ -150,7 +152,7 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser) -> Sas let color = Color::from_rgba(red, green, blue, Number::one()); Ok(Value::Color(Box::new(color))) - } else if args.len() == 2 { + } else if len == 2 { let color = match args.get_err(0, "color")? { Value::Color(c) => c, v if v.is_special_function() => { @@ -210,7 +212,40 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser) -> Sas }; Ok(Value::Color(Box::new(color.with_alpha(alpha)))) } else { - let red = match args.get_err(0, "red")? { + let red = args.get_err(0, "red")?; + let green = args.get_err(1, "green")?; + let blue = args.get_err(2, "blue")?; + let alpha = args.default_arg( + 3, + "alpha", + Value::Dimension(Some(Number::one()), Unit::None, true), + )?; + + if [&red, &green, &blue, &alpha] + .iter() + .copied() + .any(Value::is_special_function) + { + return Ok(Value::String( + format!( + "{}({})", + name, + Value::List( + if len == 4 { + vec![red, green, blue, alpha] + } else { + vec![red, green, blue] + }, + ListSeparator::Comma, + Brackets::None + ) + .to_css_string(args.span(), false)? + ), + QuoteKind::None, + )); + } + + let red = match red { Value::Dimension(Some(n), Unit::None, _) => n, Value::Dimension(Some(n), Unit::Percent, _) => { (n / Number::from(100)) * Number::from(255) @@ -226,27 +261,6 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser) -> Sas ) .into()) } - v if v.is_special_function() => { - let green = args.get_err(1, "green")?; - let blue = args.get_err(2, "blue")?; - let mut string = format!( - "{}({}, {}, {}", - name, - v.to_css_string(args.span(), parser.options.is_compressed())?, - green.to_css_string(args.span(), parser.options.is_compressed())?, - blue.to_css_string(args.span(), parser.options.is_compressed())? - ); - if !args.is_empty() { - string.push_str(", "); - string.push_str( - &args - .get_err(3, "alpha")? - .to_css_string(args.span(), parser.options.is_compressed())?, - ); - } - string.push(')'); - return Ok(Value::String(string, QuoteKind::None)); - } v => { return Err(( format!("$red: {} is not a number.", v.inspect(args.span())?), @@ -255,7 +269,7 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser) -> Sas .into()) } }; - let green = match args.get_err(1, "green")? { + let green = match green { Value::Dimension(Some(n), Unit::None, _) => n, Value::Dimension(Some(n), Unit::Percent, _) => { (n / Number::from(100)) * Number::from(255) @@ -271,26 +285,6 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser) -> Sas ) .into()) } - v if v.is_special_function() => { - let blue = args.get_err(2, "blue")?; - let mut string = format!( - "{}({}, {}, {}", - name, - red.to_string(parser.options.is_compressed()), - v.to_css_string(args.span(), parser.options.is_compressed())?, - blue.to_css_string(args.span(), parser.options.is_compressed())? - ); - if !args.is_empty() { - string.push_str(", "); - string.push_str( - &args - .get_err(3, "alpha")? - .to_css_string(args.span(), parser.options.is_compressed())?, - ); - } - string.push(')'); - return Ok(Value::String(string, QuoteKind::None)); - } v => { return Err(( format!("$green: {} is not a number.", v.inspect(args.span())?), @@ -299,7 +293,7 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser) -> Sas .into()) } }; - let blue = match args.get_err(2, "blue")? { + let blue = match blue { Value::Dimension(Some(n), Unit::None, _) => n, Value::Dimension(Some(n), Unit::Percent, _) => { (n / Number::from(100)) * Number::from(255) @@ -315,25 +309,6 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser) -> Sas ) .into()) } - v if v.is_special_function() => { - let mut string = format!( - "{}({}, {}, {}", - name, - red.to_string(parser.options.is_compressed()), - green.to_string(parser.options.is_compressed()), - v.to_css_string(args.span(), parser.options.is_compressed())? - ); - if !args.is_empty() { - string.push_str(", "); - string.push_str( - &args - .get_err(3, "alpha")? - .to_css_string(args.span(), parser.options.is_compressed())?, - ); - } - string.push(')'); - return Ok(Value::String(string, QuoteKind::None)); - } v => { return Err(( format!("$blue: {} is not a number.", v.inspect(args.span())?), @@ -342,11 +317,7 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser) -> Sas .into()) } }; - let alpha = match args.default_arg( - 3, - "alpha", - Value::Dimension(Some(Number::one()), Unit::None, true), - )? { + let alpha = match alpha { Value::Dimension(Some(n), Unit::None, _) => n, Value::Dimension(Some(n), Unit::Percent, _) => n / Number::from(100), Value::Dimension(None, ..) => todo!(), @@ -360,17 +331,6 @@ fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser) -> Sas ) .into()) } - v if v.is_special_function() => { - let string = format!( - "{}({}, {}, {}, {})", - name, - red.to_string(parser.options.is_compressed()), - green.to_string(parser.options.is_compressed()), - blue.to_string(parser.options.is_compressed()), - v.to_css_string(args.span(), parser.options.is_compressed())? - ); - return Ok(Value::String(string, QuoteKind::None)); - } v => { return Err(( format!("$alpha: {} is not a number.", v.inspect(args.span())?), diff --git a/tests/color.rs b/tests/color.rs index 57c9654..0742280 100644 --- a/tests/color.rs +++ b/tests/color.rs @@ -737,6 +737,26 @@ test!( "a {\n color: rgba(1 2 max(3, 3));\n}\n", "a {\n color: rgba(1, 2, max(3, 3));\n}\n" ); +test!( + rgb_special_fn_4_arg_maintains_units, + "a {\n color: rgb(1, 0.02, 3%, max(0.4));\n}\n", + "a {\n color: rgb(1, 0.02, 3%, max(0.4));\n}\n" +); +test!( + rgb_special_fn_3_arg_maintains_units, + "a {\n color: rgb(1, 0.02, max(0.4));\n}\n", + "a {\n color: rgb(1, 0.02, max(0.4));\n}\n" +); +test!( + hsl_special_fn_4_arg_maintains_units, + "a {\n color: hsl(1, 0.02, 3%, max(0.4));\n}\n", + "a {\n color: hsl(1, 0.02, 3%, max(0.4));\n}\n" +); +test!( + hsl_special_fn_3_arg_maintains_units, + "a {\n color: hsl(1, 0.02, max(0.4));\n}\n", + "a {\n color: hsl(1, 0.02, max(0.4));\n}\n" +); test!( #[ignore = "we do not check if interpolation occurred"] interpolated_named_color_is_not_color,