1use super::{Call, CallError, Name, Value};
2use crate::ordermap::OrderMap;
3use crate::value::ListSeparator;
4use crate::{css, Error, Invalid, ScopeRef};
5use std::default::Default;
6
7#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd)]
12pub struct CallArgs {
13 positional: Vec<Value>,
14 named: OrderMap<Name, Value>,
16 trailing_comma: bool,
17}
18
19impl CallArgs {
20 pub fn new(
24 v: Vec<(Option<Name>, Value)>,
25 trailing_comma: bool,
26 ) -> Result<Self, Invalid> {
27 let mut positional = Vec::new();
28 let mut named = OrderMap::new();
29 for (name, value) in v {
30 if let Some(name) = name {
31 if let Some(_old) = named.insert(name, value) {
32 return Err(Invalid::DuplicateArgument);
33 }
34 } else if named.is_empty() || is_splat(&value).is_some() {
35 positional.push(value);
36 } else {
37 return Err(Invalid::PositionalArgAfterNamed);
38 }
39 }
40 Ok(Self {
41 positional,
42 named,
43 trailing_comma,
44 })
45 }
46
47 pub fn new_single(value: Value) -> Self {
49 Self {
50 positional: vec![value],
51 named: Default::default(),
52 trailing_comma: false,
53 }
54 }
55
56 pub fn evaluate(&self, scope: ScopeRef) -> Result<Call, CallError> {
58 let named = self.named.iter().try_fold(
59 OrderMap::new(),
60 |mut acc, (name, arg)| {
61 let arg = arg.do_evaluate(scope.clone(), true)?;
62 acc.insert(name.clone(), arg);
63 Ok::<_, Error>(acc)
64 },
65 )?;
66 let mut result = css::CallArgs {
67 positional: Vec::new(),
68 named,
69 trailing_comma: self.trailing_comma,
70 };
71 for arg in &self.positional {
72 match is_splat(arg) {
73 Some([one]) => match one.do_evaluate(scope.clone(), true)? {
74 css::Value::ArgList(args) => {
75 result.positional.extend(args.positional);
76 for (name, value) in args.named {
77 if let Some(_existing) =
78 result.named.insert(name, value)
79 {
80 return Err(Invalid::DuplicateArgument.into());
81 }
82 }
83 }
84 css::Value::Map(map) => {
85 result
86 .add_from_value_map(map)
87 .map_err(CallError::msg)?;
88 }
89 css::Value::List(items, ..) => {
90 for item in items {
91 result.positional.push(item);
92 }
93 }
94 css::Value::Null => (),
95 item => {
96 result.positional.push(item);
97 result.trailing_comma = false;
98 }
99 },
100 Some(splat) => {
101 for arg in splat {
102 result
103 .positional
104 .push(arg.do_evaluate(scope.clone(), true)?);
105 }
106 }
107 None => {
108 result
109 .positional
110 .push(arg.do_evaluate(scope.clone(), true)?);
111 }
112 }
113 }
114 Ok(Call {
115 args: result,
116 scope,
117 })
118 }
119
120 pub fn evaluate_single(
125 &self,
126 scope: ScopeRef,
127 name: Name,
128 num: usize,
129 ) -> Result<css::Value, Error> {
130 if let Some(v) = self.named.get(&name) {
132 return v.do_evaluate(scope, true);
133 }
134 let mut i = 0;
135 for a in &self.positional {
136 match is_splat(a) {
137 Some([one]) => match one.do_evaluate(scope.clone(), true)? {
138 css::Value::ArgList(args) => {
139 if let Some(v) = args
140 .named
141 .get(&name)
142 .or_else(|| args.positional.get(num - i))
143 {
144 return Ok(v.clone());
145 }
146 i += if args.named.is_empty() {
147 args.len()
148 } else {
149 num + 1
150 };
151 }
152 css::Value::Map(map) => {
153 if let Some(v) = map.get(&name.to_string().into()) {
154 return Ok(v.clone());
155 }
156 i += num + 1;
157 }
158 css::Value::List(items, ..) => {
159 if let Some(v) = items.get(num - i) {
160 return Ok(v.clone());
161 }
162 i += items.len();
163 }
164 css::Value::Null => (),
165 v => {
166 if i == num {
167 return Ok(v);
168 }
169 i += 1;
170 }
171 },
172 Some(splat) => {
173 if let Some(v) = splat.get(num - i) {
174 return v.do_evaluate(scope, true);
175 }
176 i += splat.len();
177 }
178 None => {
179 if i == num {
180 return a.do_evaluate(scope, true);
181 }
182 i += 1;
183 }
184 }
185 }
186 Ok(css::Value::Null)
187 }
188}
189
190fn is_splat(arg: &Value) -> Option<&[Value]> {
191 if let Value::List(list, sep, false) = arg {
192 if let Some((Value::Literal(v, ..), splat)) = list.split_last() {
193 if v.is_unquoted()
194 && v.single_raw() == Some("...")
195 && sep.unwrap_or_default() == ListSeparator::Space
196 {
197 return Some(splat);
198 }
199 }
200 }
201 None
202}