diff --git a/src/builtin/color.rs b/src/builtin/color.rs index d41e7d6..c3ac2f3 100644 --- a/src/builtin/color.rs +++ b/src/builtin/color.rs @@ -1,23 +1,20 @@ use std::collections::BTreeMap; +use std::convert::TryInto; use super::Builtin; use crate::color::Color; use crate::value::Value; pub(crate) fn register(f: &mut BTreeMap) { - f.insert( - "rgb".to_owned(), - Box::new(|args| { - let channels = args.get("channels").unwrap_or(&Value::Null); - if channels.is_null() { - let _red: &Value = arg!(args, 0, "red"); - let _green: &Value = arg!(args, 1, "green"); - let _blue: &Value = arg!(args, 2, "blue"); - // Value::Color(Color::RGB(red, blue, green)) - } else { - todo!("channels variable in `rgb`") - }; - todo!() - }), - ); + decl!(f "rgb", |args| { + let channels = args.get("channels").unwrap_or(&Value::Null); + if channels.is_null() { + let red: u16 = arg!(args, 0, "red").clone().try_into().unwrap(); + let green: u16 = arg!(args, 1, "green").clone().try_into().unwrap(); + let blue: u16 = arg!(args, 2, "blue").clone().try_into().unwrap(); + Some(Value::Color(Color::from_values(red, green, blue, 1))) + } else { + todo!("channels variable in `rgb`") + } + }); } diff --git a/src/color/mod.rs b/src/color/mod.rs index 3d10571..7107844 100644 --- a/src/color/mod.rs +++ b/src/color/mod.rs @@ -26,9 +26,9 @@ impl Color { pub fn from_values(red: u16, green: u16, blue: u16, alpha: u16) -> Self { let repr = if alpha >= 1 { - format!("#{:X}{:X}{:X}", red, green, blue) + format!("#{:0>2x}{:0>2x}{:0>2x}", red, green, blue) } else { - format!("#{:X}{:X}{:X}{:X}", red, green, blue, alpha) + format!("#{:0>2x}{:0>2x}{:0>2x}{:0>2x}", red, green, blue, alpha) }; Color { red, diff --git a/src/value.rs b/src/value.rs index cdea1a5..61f269b 100644 --- a/src/value.rs +++ b/src/value.rs @@ -1,8 +1,12 @@ #![allow(dead_code, unused_variables)] +use std::convert::TryInto; use std::fmt::{self, Display}; use std::iter::{Iterator, Peekable}; use std::ops::{Add, Sub}; +use num_rational::BigRational; +use num_bigint::BigInt; + use crate::args::eat_call_args; use crate::builtin::GLOBAL_FUNCTIONS; use crate::color::Color; @@ -70,7 +74,7 @@ pub(crate) enum Value { True, False, Null, - Dimension(Dimension, Unit), + Dimension(BigRational, Unit), List(Vec, ListSeparator), Color(Color), BinaryOp(Box, Op, Box), @@ -214,6 +218,28 @@ impl Display for Value { } } +impl TryInto for Value { + type Error = &'static str; + fn try_into(self) -> Result { + match self { + Self::Dimension(n, Unit::Percent) => { + todo!() + } + Self::Dimension(n, Unit::None) => { + if n >= BigRational::from_integer(BigInt::from(255)) { + Ok(255) + } else { + Ok(n.to_integer().to_str_radix(10).parse().unwrap()) + } + } + Self::Dimension(n, _) => { + Err("Expected `val` to have no units or \"%\".") + } + _ => Err("expected number") + } + } +} + impl Value { pub fn is_null(&self) -> bool { self == &Value::Null @@ -343,9 +369,7 @@ impl Value { Unit::None }; Some(Value::Dimension( - Dimension { - val: val.parse().unwrap(), - }, + val.parse().expect("error parsing integer"), unit, )) } diff --git a/tests/color.rs b/tests/color.rs index 5faae05..7de5282 100644 --- a/tests/color.rs +++ b/tests/color.rs @@ -27,7 +27,17 @@ test!(preserves_hex_3_val_000, "a {\n color: #000;\n}\n"); test!(preserves_hex_3_val_123, "a {\n color: #123;\n}\n"); test!(preserves_hex_3_val_ab2, "a {\n color: #ab2;\n}\n"); // test!( -// , -// "a {\n color: OrAnGe;\n}\n", -// "a {\n color: OrAnGe;\n}\n" +// converts_rgb_to_named_color, +// "a {\n color: rgb(0, 0, 0);\n}\n", +// "a {\n color: black;\n}\n" // ); +test!( + rgb_pads_0, + "a {\n color: rgb(1, 2, 3);\n}\n", + "a {\n color: #010203;\n}\n" +); +test!( + rgb_double_digits, + "a {\n color: rgb(255, 255, 255);\n}\n", + "a {\n color: #ffffff;\n}\n" +);