From dd1c1e5b06e333c5940faff289e32779f06cec8f Mon Sep 17 00:00:00 2001 From: ConnorSkees <39542938+ConnorSkees@users.noreply.github.com> Date: Fri, 20 Mar 2020 12:09:08 -0400 Subject: [PATCH] implement builtin function `nth` --- src/builtin/list.rs | 39 +++++++++++++++++++++++++++++++++++++++ src/value/number.rs | 8 ++++---- tests/list.rs | 15 +++++++++++++++ 3 files changed, 58 insertions(+), 4 deletions(-) diff --git a/src/builtin/list.rs b/src/builtin/list.rs index 477308b..ef977bb 100644 --- a/src/builtin/list.rs +++ b/src/builtin/list.rs @@ -1,5 +1,7 @@ use std::collections::HashMap; +use num_traits::cast::ToPrimitive; + use super::Builtin; use crate::unit::Unit; use crate::value::{Number, Value}; @@ -16,4 +18,41 @@ pub(crate) fn register(f: &mut HashMap) { Ok(Value::Dimension(len, Unit::None)) }), ); + f.insert( + "nth".to_owned(), + Box::new(|args, _| { + max_args!(args, 2); + let list = match arg!(args, 0, "list") { + Value::List(v, _) => v, + _ => return Err("Missing argument $list.".into()), + }; + let n = match arg!(args, 1, "n") { + Value::Dimension(num, _) => num, + v => return Err(format!("$n: {} is not a number.", v).into()), + }; + + if n == Number::from(0) { + return Err("$n: List index may not be 0.".into()); + } + + if n.abs() > Number::from(list.len()) { + return Err(format!( + "$n: Invalid index {} for a list with {} elements.", + n, + list.len() + ) + .into()); + } + + if n.is_decimal() { + return Err(format!("$n: {} is not an int.", n).into()); + } + + if n > Number::from(0) { + Ok(list[n.to_integer().to_usize().unwrap() - 1].clone()) + } else { + Ok(list[list.len() - n.abs().to_integer().to_usize().unwrap()].clone()) + } + }), + ); } diff --git a/src/value/number.rs b/src/value/number.rs index add476d..88a1091 100644 --- a/src/value/number.rs +++ b/src/value/number.rs @@ -28,25 +28,25 @@ impl Number { Number::new(BigRational::new(a.into(), b.into())) } - pub fn round(self) -> Self { + pub fn round(&self) -> Self { Number { val: self.val.round(), } } - pub fn ceil(self) -> Self { + pub fn ceil(&self) -> Self { Number { val: self.val.ceil(), } } - pub fn floor(self) -> Self { + pub fn floor(&self) -> Self { Number { val: self.val.floor(), } } - pub fn abs(self) -> Self { + pub fn abs(&self) -> Self { Number { val: self.val.abs(), } diff --git a/tests/list.rs b/tests/list.rs index 0dbe1c0..d6852ac 100644 --- a/tests/list.rs +++ b/tests/list.rs @@ -18,3 +18,18 @@ test!( "a {\n color: length((1, 2, 3, 4, 5));\n}\n", "a {\n color: 5;\n}\n" ); +test!( + nth_space_separated, + "a {\n color: nth(a b c, 1);\n}\n", + "a {\n color: a;\n}\n" +); +test!( + nth_negative_index, + "a {\n color: nth(a b c, -2);\n}\n", + "a {\n color: b;\n}\n" +); +test!( + nth_comma_separated, + "a {\n color: nth((a, b, c), 3);\n}\n", + "a {\n color: c;\n}\n" +);