From d196a93f9126d3e1c5cc35d909f521756af180c4 Mon Sep 17 00:00:00 2001
From: ConnorSkees <39542938+ConnorSkees@users.noreply.github.com>
Date: Wed, 17 Jun 2020 05:24:42 -0400
Subject: [PATCH] arglists are lists too

---
 src/builtin/list.rs | 12 +++++-------
 src/parse/mod.rs    | 18 ++----------------
 src/value/mod.rs    |  1 +
 tests/arglist.rs    | 32 ++++++++++++++++++++++++++++++++
 4 files changed, 40 insertions(+), 23 deletions(-)
 create mode 100644 tests/arglist.rs

diff --git a/src/builtin/list.rs b/src/builtin/list.rs
index 91cdf12..b808da2 100644
--- a/src/builtin/list.rs
+++ b/src/builtin/list.rs
@@ -1,6 +1,6 @@
 use super::{Builtin, GlobalFunctionMap};
 
-use num_traits::{One, Signed, ToPrimitive, Zero};
+use num_traits::{Signed, ToPrimitive, Zero};
 
 use crate::{
     args::CallArgs,
@@ -13,12 +13,10 @@ use crate::{
 
 fn length(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
     args.max_args(1)?;
-    let len = match parser.arg(&mut args, 0, "list")? {
-        Value::List(v, ..) => Number::from(v.len()),
-        Value::Map(m) => Number::from(m.len()),
-        _ => Number::one(),
-    };
-    Ok(Value::Dimension(len, Unit::None))
+    Ok(Value::Dimension(
+        Number::from(parser.arg(&mut args, 0, "list")?.as_list().len()),
+        Unit::None,
+    ))
 }
 
 fn nth(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult<Value> {
diff --git a/src/parse/mod.rs b/src/parse/mod.rs
index 6dc41f9..4119782 100644
--- a/src/parse/mod.rs
+++ b/src/parse/mod.rs
@@ -856,14 +856,7 @@ impl<'a> Parser<'a> {
         self.whitespace();
         let iter_val_toks = read_until_open_curly_brace(self.toks)?;
         let iter_val = self.parse_value_from_vec(iter_val_toks)?;
-        let iter = match iter_val.node.eval(iter_val.span)?.node {
-            Value::List(v, ..) => v,
-            Value::Map(m) => m
-                .into_iter()
-                .map(|(k, v)| Value::List(vec![k, v], ListSeparator::Space, Brackets::None))
-                .collect(),
-            v => vec![v],
-        };
+        let iter = iter_val.node.eval(iter_val.span)?.node.as_list();
         self.toks.next();
         self.whitespace();
         let mut body = read_until_closing_curly_brace(self.toks)?;
@@ -873,14 +866,7 @@ impl<'a> Parser<'a> {
         let mut stmts = Vec::new();
 
         for row in iter {
-            let this_iterator = match row {
-                Value::List(v, ..) => v,
-                Value::Map(m) => m
-                    .into_iter()
-                    .map(|(k, v)| Value::List(vec![k, v], ListSeparator::Space, Brackets::None))
-                    .collect(),
-                v => vec![v],
-            };
+            let this_iterator = row.as_list();
 
             if vars.len() == 1 {
                 if this_iterator.len() == 1 {
diff --git a/src/value/mod.rs b/src/value/mod.rs
index a8245c9..f24ed4f 100644
--- a/src/value/mod.rs
+++ b/src/value/mod.rs
@@ -317,6 +317,7 @@ impl Value {
         match self {
             Value::List(v, ..) => v,
             Value::Map(m) => m.entries(),
+            Value::ArgList(v) => v.into_iter().map(|val| val.node).collect(),
             v => vec![v],
         }
     }
diff --git a/tests/arglist.rs b/tests/arglist.rs
new file mode 100644
index 0000000..4636417
--- /dev/null
+++ b/tests/arglist.rs
@@ -0,0 +1,32 @@
+#![cfg(test)]
+
+#[macro_use]
+mod macros;
+
+test!(
+    length_of_empty_arglist,
+    "@mixin foo($a...) {\n    color: length($list: $a);\n}\na {\n    @include foo;\n}\n",
+    "a {\n  color: 0;\n}\n"
+);
+test!(
+    length_of_arglist_in_mixin,
+    "@mixin foo($a...) {\n    color: length($list: $a);\n}\na {\n    @include foo(a, 2, c);\n}\n",
+    "a {\n  color: 3;\n}\n"
+);
+test!(
+    arglist_in_at_each,
+    "@function sum($numbers...) {
+        $sum: 0;
+    
+        @each $number in $numbers {
+            $sum: $sum + $number;
+        }
+    
+        @return $sum;
+    }
+    
+    a {
+        width: sum(50px, 30px, 100px);
+    }",
+    "a {\n  width: 180px;\n}\n"
+);