grass_compiler/builtin/functions/
meta.rs1use crate::builtin::builtin_imports::*;
2
3pub(crate) fn if_arguments() -> ArgumentDeclaration {
6 ArgumentDeclaration {
7 args: vec![
8 Argument {
9 name: Identifier::from("condition"),
10 default: None,
11 },
12 Argument {
13 name: Identifier::from("if-true"),
14 default: None,
15 },
16 Argument {
17 name: Identifier::from("if-false"),
18 default: None,
19 },
20 ],
21 rest: None,
22 }
23}
24
25fn if_(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
26 args.max_args(3)?;
27 if args.get_err(0, "condition")?.is_truthy() {
28 Ok(args.get_err(1, "if-true")?)
29 } else {
30 Ok(args.get_err(2, "if-false")?)
31 }
32}
33
34pub(crate) fn feature_exists(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
35 args.max_args(1)?;
36 let feature = args
37 .get_err(0, "feature")?
38 .assert_string_with_name("feature", args.span())?
39 .0;
40
41 #[allow(clippy::match_same_arms)]
42 Ok(match feature.as_str() {
43 "global-variable-shadowing" => Value::True,
46 "extend-selector-pseudoclass" => Value::True,
49 "units-level-3" => Value::True,
52 "at-error" => Value::True,
54 "custom-property" => Value::True,
58 _ => Value::False,
59 })
60}
61
62pub(crate) fn unit(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
63 args.max_args(1)?;
64
65 let number = args
66 .get_err(0, "number")?
67 .assert_number_with_name("number", args.span())?;
68
69 Ok(Value::String(number.unit.to_string(), QuoteKind::Quoted))
70}
71
72pub(crate) fn type_of(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
73 args.max_args(1)?;
74 let value = args.get_err(0, "value")?;
75 Ok(Value::String(value.kind().to_owned(), QuoteKind::None))
76}
77
78pub(crate) fn unitless(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
79 args.max_args(1)?;
80 let number = args
81 .get_err(0, "number")?
82 .assert_number_with_name("number", args.span())?;
83
84 Ok(Value::bool(number.unit == Unit::None))
85}
86
87pub(crate) fn inspect(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
88 args.max_args(1)?;
89 Ok(Value::String(
90 args.get_err(0, "value")?.inspect(args.span())?,
91 QuoteKind::None,
92 ))
93}
94
95pub(crate) fn variable_exists(
96 mut args: ArgumentResult,
97 visitor: &mut Visitor,
98) -> SassResult<Value> {
99 args.max_args(1)?;
100
101 let name = Identifier::from(
102 args.get_err(0, "name")?
103 .assert_string_with_name("name", args.span())?
104 .0,
105 );
106
107 Ok(Value::bool(visitor.env.var_exists(name, None)?))
108}
109
110pub(crate) fn global_variable_exists(
111 mut args: ArgumentResult,
112 visitor: &mut Visitor,
113) -> SassResult<Value> {
114 args.max_args(2)?;
115
116 let name = Identifier::from(
117 args.get_err(0, "name")?
118 .assert_string_with_name("name", args.span())?
119 .0,
120 );
121
122 let module = match args.default_arg(1, "module", Value::Null) {
123 Value::String(s, _) => Some(s),
124 Value::Null => None,
125 v => {
126 return Err((
127 format!("$module: {} is not a string.", v.inspect(args.span())?),
128 args.span(),
129 )
130 .into())
131 }
132 };
133
134 Ok(Value::bool(if let Some(module_name) = module {
135 (*(*visitor.env.modules)
136 .borrow()
137 .get(module_name.into(), args.span())?)
138 .borrow()
139 .var_exists(name)
140 } else {
141 (*visitor.env.global_vars()).borrow().contains_key(&name)
142 }))
143}
144
145pub(crate) fn mixin_exists(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
146 args.max_args(2)?;
147 let name = Identifier::from(
148 args.get_err(0, "name")?
149 .assert_string_with_name("name", args.span())?
150 .0,
151 );
152
153 let module = match args.default_arg(1, "module", Value::Null) {
154 Value::String(s, _) => Some(s),
155 Value::Null => None,
156 v => {
157 return Err((
158 format!("$module: {} is not a string.", v.inspect(args.span())?),
159 args.span(),
160 )
161 .into())
162 }
163 };
164
165 Ok(Value::bool(if let Some(module_name) = module {
166 (*(*visitor.env.modules)
167 .borrow()
168 .get(module_name.into(), args.span())?)
169 .borrow()
170 .mixin_exists(name)
171 } else {
172 visitor.env.mixin_exists(name)
173 }))
174}
175
176pub(crate) fn function_exists(
177 mut args: ArgumentResult,
178 visitor: &mut Visitor,
179) -> SassResult<Value> {
180 args.max_args(2)?;
181
182 let name = Identifier::from(
183 args.get_err(0, "name")?
184 .assert_string_with_name("name", args.span())?
185 .0,
186 );
187
188 let module = match args.default_arg(1, "module", Value::Null) {
189 Value::String(s, _) => Some(s),
190 Value::Null => None,
191 v => {
192 return Err((
193 format!("$module: {} is not a string.", v.inspect(args.span())?),
194 args.span(),
195 )
196 .into())
197 }
198 };
199
200 Ok(Value::bool(if let Some(module_name) = module {
201 (*(*visitor.env.modules)
202 .borrow()
203 .get(module_name.into(), args.span())?)
204 .borrow()
205 .fn_exists(name)
206 } else {
207 visitor.env.fn_exists(name)
208 }))
209}
210
211pub(crate) fn get_function(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
212 args.max_args(3)?;
213 let name: Identifier = match args.get_err(0, "name")? {
214 Value::String(s, _) => s.into(),
215 v => {
216 return Err((
217 format!("$name: {} is not a string.", v.inspect(args.span())?),
218 args.span(),
219 )
220 .into())
221 }
222 };
223 let css = args.default_arg(1, "css", Value::False).is_truthy();
224 let module = match args.default_arg(2, "module", Value::Null) {
225 Value::String(s, ..) => Some(s),
226 Value::Null => None,
227 v => {
228 return Err((
229 format!("$module: {} is not a string.", v.inspect(args.span())?),
230 args.span(),
231 )
232 .into())
233 }
234 };
235
236 if css && module.is_some() {
237 return Err((
238 "$css and $module may not both be passed at once.",
239 args.span(),
240 )
241 .into());
242 }
243
244 let func = if css {
245 Some(SassFunction::Plain { name })
246 } else if let Some(module_name) = module {
247 visitor.env.get_fn(
248 name,
249 Some(Spanned {
250 node: module_name.into(),
251 span: args.span(),
252 }),
253 )?
254 } else {
255 match visitor.env.get_fn(name, None)? {
256 Some(f) => Some(f),
257 None => GLOBAL_FUNCTIONS
258 .get(name.as_str())
259 .map(|f| SassFunction::Builtin(f.clone(), name)),
260 }
261 };
262
263 match func {
264 Some(func) => Ok(Value::FunctionRef(Box::new(func))),
265 None => Err((format!("Function not found: {}", name), args.span()).into()),
266 }
267}
268
269pub(crate) fn call(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
270 let span = args.span();
271 let func = match args.get_err(0, "function")? {
272 Value::FunctionRef(f) => *f,
273 Value::String(name, ..) => {
274 let name = Identifier::from(name);
275
276 match visitor.env.get_fn(name, None)? {
277 Some(f) => f,
278 None => match GLOBAL_FUNCTIONS.get(name.as_str()) {
279 Some(f) => SassFunction::Builtin(f.clone(), name),
280 None => SassFunction::Plain { name },
281 },
282 }
283 }
284 v => {
285 return Err((
286 format!(
287 "$function: {} is not a function reference.",
288 v.inspect(span)?
289 ),
290 span,
291 )
292 .into())
293 }
294 };
295
296 args.remove_positional(0);
297
298 visitor.run_function_callable_with_maybe_evaled(func, MaybeEvaledArguments::Evaled(args), span)
299}
300
301#[allow(clippy::needless_pass_by_value)]
302pub(crate) fn content_exists(args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
303 args.max_args(0)?;
304 if !visitor.flags.in_mixin() {
305 return Err((
306 "content-exists() may only be called within a mixin.",
307 args.span(),
308 )
309 .into());
310 }
311 Ok(Value::bool(visitor.env.content.is_some()))
312}
313
314pub(crate) fn keywords(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
315 args.max_args(1)?;
316
317 let span = args.span();
318
319 let args = match args.get_err(0, "args")? {
320 Value::ArgList(args) => args,
321 v => {
322 return Err((
323 format!("$args: {} is not an argument list.", v.inspect(span)?),
324 span,
325 )
326 .into())
327 }
328 };
329
330 Ok(Value::Map(SassMap::new_with(
331 args.into_keywords()
332 .into_iter()
333 .map(|(name, val)| {
334 (
335 Value::String(name.to_string(), QuoteKind::None).span(span),
336 val,
337 )
338 })
339 .collect(),
340 )))
341}
342
343pub(crate) fn declare(f: &mut GlobalFunctionMap) {
344 f.insert("if", Builtin::new(if_));
345 f.insert("feature-exists", Builtin::new(feature_exists));
346 f.insert("unit", Builtin::new(unit));
347 f.insert("type-of", Builtin::new(type_of));
348 f.insert("unitless", Builtin::new(unitless));
349 f.insert("inspect", Builtin::new(inspect));
350 f.insert("variable-exists", Builtin::new(variable_exists));
351 f.insert(
352 "global-variable-exists",
353 Builtin::new(global_variable_exists),
354 );
355 f.insert("mixin-exists", Builtin::new(mixin_exists));
356 f.insert("function-exists", Builtin::new(function_exists));
357 f.insert("get-function", Builtin::new(get_function));
358 f.insert("call", Builtin::new(call));
359 f.insert("content-exists", Builtin::new(content_exists));
360 f.insert("keywords", Builtin::new(keywords));
361}