2020-04-02 12:07:54 -04:00
|
|
|
use std::collections::HashMap;
|
2020-01-25 11:00:29 -05:00
|
|
|
|
2020-04-12 19:37:12 -04:00
|
|
|
use codemap::{Span, Spanned};
|
|
|
|
|
2020-06-16 20:00:11 -04:00
|
|
|
use crate::{
|
|
|
|
common::Identifier,
|
|
|
|
error::SassResult,
|
|
|
|
parse::Parser,
|
2020-07-02 16:32:43 -04:00
|
|
|
value::Value,
|
2020-06-16 20:00:11 -04:00
|
|
|
{Cow, Token},
|
|
|
|
};
|
2020-01-25 11:00:29 -05:00
|
|
|
|
2020-07-08 23:49:30 -04:00
|
|
|
#[derive(Debug, Clone)]
|
2020-01-25 11:00:29 -05:00
|
|
|
pub(crate) struct FuncArgs(pub Vec<FuncArg>);
|
|
|
|
|
2020-07-08 23:49:30 -04:00
|
|
|
#[derive(Debug, Clone)]
|
2020-01-25 11:00:29 -05:00
|
|
|
pub(crate) struct FuncArg {
|
2020-05-22 22:43:26 -04:00
|
|
|
pub name: Identifier,
|
2020-04-04 18:17:04 -04:00
|
|
|
pub default: Option<Vec<Token>>,
|
2020-04-02 12:07:54 -04:00
|
|
|
pub is_variadic: bool,
|
2020-01-25 11:00:29 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
impl FuncArgs {
|
|
|
|
pub const fn new() -> Self {
|
|
|
|
FuncArgs(Vec::new())
|
|
|
|
}
|
2020-07-02 10:31:32 -04:00
|
|
|
|
|
|
|
pub fn len(&self) -> usize {
|
|
|
|
self.0.len()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(dead_code)]
|
|
|
|
pub fn is_empty(&self) -> bool {
|
|
|
|
self.0.is_empty()
|
|
|
|
}
|
2020-01-25 11:00:29 -05:00
|
|
|
}
|
|
|
|
|
2020-07-04 11:27:57 -04:00
|
|
|
#[derive(Debug, Clone)]
|
2020-07-03 15:06:26 -04:00
|
|
|
pub(crate) struct CallArgs(pub HashMap<CallArg, SassResult<Spanned<Value>>>, pub Span);
|
2020-04-02 12:28:28 -04:00
|
|
|
|
|
|
|
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
2020-06-16 19:38:30 -04:00
|
|
|
pub(crate) enum CallArg {
|
2020-05-22 22:28:38 -04:00
|
|
|
Named(Identifier),
|
2020-04-02 12:28:28 -04:00
|
|
|
Positional(usize),
|
|
|
|
}
|
2020-01-25 11:00:29 -05:00
|
|
|
|
2020-04-02 13:33:26 -04:00
|
|
|
impl CallArg {
|
2020-04-12 19:37:12 -04:00
|
|
|
pub fn position(&self) -> Result<usize, String> {
|
2020-04-02 13:33:26 -04:00
|
|
|
match self {
|
2020-05-22 22:28:38 -04:00
|
|
|
Self::Named(ref name) => Err(name.to_string()),
|
2020-04-02 13:33:26 -04:00
|
|
|
Self::Positional(p) => Ok(*p),
|
|
|
|
}
|
|
|
|
}
|
2020-04-04 12:31:43 -04:00
|
|
|
|
|
|
|
pub fn decrement(self) -> CallArg {
|
|
|
|
match self {
|
|
|
|
Self::Named(..) => self,
|
|
|
|
Self::Positional(p) => Self::Positional(p - 1),
|
|
|
|
}
|
|
|
|
}
|
2020-04-02 13:33:26 -04:00
|
|
|
}
|
|
|
|
|
2020-01-25 11:00:29 -05:00
|
|
|
impl CallArgs {
|
2020-04-12 19:37:12 -04:00
|
|
|
pub fn new(span: Span) -> Self {
|
|
|
|
CallArgs(HashMap::new(), span)
|
2020-01-25 11:00:29 -05:00
|
|
|
}
|
|
|
|
|
2020-06-16 19:38:30 -04:00
|
|
|
pub fn to_css_string(self, parser: &mut Parser<'_>) -> SassResult<Spanned<String>> {
|
2020-04-06 15:35:46 -04:00
|
|
|
let mut string = String::with_capacity(2 + self.len() * 10);
|
|
|
|
string.push('(');
|
2020-04-12 19:37:12 -04:00
|
|
|
let mut span = self.1;
|
|
|
|
|
|
|
|
if self.is_empty() {
|
|
|
|
return Ok(Spanned {
|
|
|
|
node: "()".to_string(),
|
|
|
|
span,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-06-16 19:38:30 -04:00
|
|
|
let args = match parser.variadic_args(self) {
|
2020-04-06 15:35:46 -04:00
|
|
|
Ok(v) => v,
|
2020-04-12 19:37:12 -04:00
|
|
|
Err(..) => {
|
|
|
|
return Err(("Plain CSS functions don't support keyword arguments.", span).into())
|
|
|
|
}
|
2020-04-06 15:35:46 -04:00
|
|
|
};
|
2020-04-12 19:37:12 -04:00
|
|
|
|
2020-04-06 15:35:46 -04:00
|
|
|
string.push_str(
|
|
|
|
&args
|
|
|
|
.iter()
|
2020-04-12 19:37:12 -04:00
|
|
|
.map(|a| {
|
|
|
|
span = span.merge(a.span);
|
2020-07-08 23:49:30 -04:00
|
|
|
a.node.to_css_string(a.span)
|
2020-04-12 19:37:12 -04:00
|
|
|
})
|
2020-05-25 13:09:20 -04:00
|
|
|
.collect::<SassResult<Vec<Cow<'static, str>>>>()?
|
2020-04-06 15:35:46 -04:00
|
|
|
.join(", "),
|
|
|
|
);
|
|
|
|
string.push(')');
|
2020-04-12 19:37:12 -04:00
|
|
|
Ok(Spanned { node: string, span })
|
2020-04-06 15:35:46 -04:00
|
|
|
}
|
|
|
|
|
2020-04-04 18:17:04 -04:00
|
|
|
/// Get argument by name
|
|
|
|
///
|
|
|
|
/// Removes the argument
|
2020-07-03 15:06:26 -04:00
|
|
|
pub fn get_named<T: Into<Identifier>>(&mut self, val: T) -> Option<SassResult<Spanned<Value>>> {
|
2020-06-16 19:38:30 -04:00
|
|
|
self.0.remove(&CallArg::Named(val.into()))
|
2020-04-02 12:28:28 -04:00
|
|
|
}
|
|
|
|
|
2020-04-04 18:17:04 -04:00
|
|
|
/// Get a positional argument by 0-indexed position
|
|
|
|
///
|
|
|
|
/// Removes the argument
|
2020-07-03 15:06:26 -04:00
|
|
|
pub fn get_positional(&mut self, val: usize) -> Option<SassResult<Spanned<Value>>> {
|
2020-06-16 19:38:30 -04:00
|
|
|
self.0.remove(&CallArg::Positional(val))
|
2020-01-25 11:00:29 -05:00
|
|
|
}
|
2020-02-14 11:52:31 -05:00
|
|
|
|
2020-07-03 15:06:26 -04:00
|
|
|
pub fn get<T: Into<Identifier>>(
|
|
|
|
&mut self,
|
|
|
|
position: usize,
|
|
|
|
name: T,
|
|
|
|
) -> Option<SassResult<Spanned<Value>>> {
|
2020-06-16 19:38:30 -04:00
|
|
|
match self.get_named(name) {
|
2020-05-16 18:38:37 -04:00
|
|
|
Some(v) => Some(v),
|
2020-06-16 19:38:30 -04:00
|
|
|
None => self.get_positional(position),
|
2020-05-13 01:32:29 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-02 16:32:43 -04:00
|
|
|
pub fn get_err(&mut self, position: usize, name: &'static str) -> SassResult<Spanned<Value>> {
|
2020-06-16 19:38:30 -04:00
|
|
|
match self.get_named(name) {
|
2020-07-03 15:06:26 -04:00
|
|
|
Some(v) => v,
|
2020-06-16 19:38:30 -04:00
|
|
|
None => match self.get_positional(position) {
|
2020-07-03 15:06:26 -04:00
|
|
|
Some(v) => v,
|
2020-06-16 19:38:30 -04:00
|
|
|
None => Err((format!("Missing argument ${}.", name), self.span()).into()),
|
|
|
|
},
|
2020-04-02 13:33:26 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-06 12:19:03 -04:00
|
|
|
/// Decrement all positional arguments by 1
|
|
|
|
///
|
|
|
|
/// This is used by builtin function `call` to pass
|
|
|
|
/// positional arguments to the other function
|
2020-04-04 12:31:43 -04:00
|
|
|
pub fn decrement(self) -> Self {
|
|
|
|
CallArgs(
|
|
|
|
self.0
|
|
|
|
.into_iter()
|
|
|
|
.map(|(k, v)| (k.decrement(), v))
|
|
|
|
.collect(),
|
2020-04-12 19:37:12 -04:00
|
|
|
self.1,
|
2020-04-04 12:31:43 -04:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2020-04-21 18:22:26 -04:00
|
|
|
pub const fn span(&self) -> Span {
|
2020-04-12 19:37:12 -04:00
|
|
|
self.1
|
|
|
|
}
|
|
|
|
|
2020-02-14 11:52:31 -05:00
|
|
|
pub fn len(&self) -> usize {
|
|
|
|
self.0.len()
|
|
|
|
}
|
2020-02-16 22:04:54 -05:00
|
|
|
|
2020-03-22 23:28:19 -04:00
|
|
|
pub fn is_empty(&self) -> bool {
|
2020-04-12 19:37:12 -04:00
|
|
|
self.0.is_empty()
|
2020-03-22 23:28:19 -04:00
|
|
|
}
|
2020-04-22 06:17:52 -04:00
|
|
|
|
2020-06-22 10:11:30 -04:00
|
|
|
pub fn min_args(&self, min: usize) -> SassResult<()> {
|
|
|
|
let len = self.len();
|
|
|
|
if len < min {
|
|
|
|
if min == 1 {
|
|
|
|
return Err(("At least one argument must be passed.", self.span()).into());
|
|
|
|
}
|
|
|
|
todo!("min args greater than one")
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-04-22 06:17:52 -04:00
|
|
|
pub fn max_args(&self, max: usize) -> SassResult<()> {
|
|
|
|
let len = self.len();
|
|
|
|
if len > max {
|
|
|
|
let mut err = String::with_capacity(50);
|
|
|
|
err.push_str(&format!("Only {} argument", max));
|
|
|
|
if max != 1 {
|
|
|
|
err.push('s');
|
|
|
|
}
|
|
|
|
err.push_str(" allowed, but ");
|
|
|
|
err.push_str(&len.to_string());
|
|
|
|
err.push(' ');
|
|
|
|
if len == 1 {
|
|
|
|
err.push_str("was passed.")
|
|
|
|
} else {
|
|
|
|
err.push_str("were passed.")
|
|
|
|
}
|
|
|
|
return Err((err, self.span()).into());
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-01-25 11:00:29 -05:00
|
|
|
}
|