diff --git a/src/builtin/color_hsl.rs b/src/builtin/color_hsl.rs index a6f579c..c26bee4 100644 --- a/src/builtin/color_hsl.rs +++ b/src/builtin/color_hsl.rs @@ -153,4 +153,4 @@ pub(crate) fn register(f: &mut BTreeMap) { _ => todo!("non-color given to builtin function `alpha()`") } }); -} \ No newline at end of file +} diff --git a/src/builtin/string.rs b/src/builtin/string.rs index c987b54..d9f6c17 100644 --- a/src/builtin/string.rs +++ b/src/builtin/string.rs @@ -1,5 +1,9 @@ use std::collections::BTreeMap; +use num_bigint::BigInt; +use num_traits::cast::ToPrimitive; +use num_traits::sign::Signed; + use super::Builtin; use crate::common::QuoteKind; use crate::units::Unit; @@ -37,4 +41,42 @@ pub(crate) fn register(f: &mut BTreeMap) { decl!(f "unquote", |args, _| { Some(arg!(args, 0, "string").eval().unquote()) }); + decl!(f "str-slice", |args, _| { + let (string, quotes) = match arg!(args, 0, "string").eval() { + Value::Ident(s, q) => (s, q), + _ => todo!("____ is not a string") + }; + let str_len = string.len(); + let start = match arg!(args, 1, "start-at").eval() { + Value::Dimension(n, Unit::None) if n.to_integer().is_positive() => n.to_integer().to_usize().unwrap(), + Value::Dimension(n, Unit::None) => (BigInt::from(str_len + 1) + n.to_integer()).to_usize().unwrap(), + Value::Dimension(..) => todo!("$start: Expected ___ to have no units."), + _ => todo!("$start-at: ____ is not a number") + }; + let mut end = match arg!(args, 2, "end-at"=Value::Null).eval() { + Value::Dimension(n, Unit::None) if n.to_integer().is_positive() => n.to_integer().to_usize().unwrap(), + Value::Dimension(n, Unit::None) => (BigInt::from(str_len + 1) + n.to_integer()).to_usize().unwrap(), + Value::Dimension(..) => todo!("$end: Expected ___ to have no units."), + Value::Null => str_len, + _ => todo!("$end-at: ____ is not a number") + }; + + if end > str_len { + end = str_len; + } + + if start > end || start > str_len { + match quotes { + QuoteKind::Double | QuoteKind::Single => Some(Value::Ident(String::new(), QuoteKind::Double)), + QuoteKind::None => Some(Value::Null), + } + } else { + let s = string[start-1..end].to_string(); + match quotes { + QuoteKind::Double | QuoteKind::Single => Some(Value::Ident(s, QuoteKind::Double)), + QuoteKind::None => Some(Value::Ident(s, QuoteKind::None)), + } + } + + }); } diff --git a/tests/strings.rs b/tests/strings.rs index 1306c90..00f6872 100644 --- a/tests/strings.rs +++ b/tests/strings.rs @@ -33,3 +33,33 @@ test!( "a {\n color: str-length($string: aBc123);\n}\n", "a {\n color: 6;\n}\n" ); +test!( + str_slice_dbl_quote, + "a {\n color: str-slice(\"abcd\", 2, 3);\n}\n", + "a {\n color: \"bc\";\n}\n" +); +test!( + str_slice_sgl_quote, + "a {\n color: str-slice('abcd', 2, 3);\n}\n", + "a {\n color: \"bc\";\n}\n" +); +test!( + str_slice_no_quote, + "a {\n color: str-slice(abcd, 2, 3);\n}\n", + "a {\n color: bc;\n}\n" +); +test!( + str_slice_no_end, + "a {\n color: str-slice(abcd, 2);\n}\n", + "a {\n color: bcd;\n}\n" +); +test!( + str_slice_negative_start_negative_end, + "a {\n color: str-slice(abcd, -3, -2);\n}\n", + "a {\n color: bc;\n}\n" +); +test!( + str_slice_negative_end, + "a {\n color: str-slice(abcd, 2, -2);\n}\n", + "a {\n color: bc;\n}\n" +);