2020-07-25 20:05:46 -04:00
|
|
|
use std::collections::BTreeMap;
|
|
|
|
|
2020-07-30 17:46:56 -04:00
|
|
|
use codemap::{Span, Spanned};
|
2020-07-26 00:05:13 -04:00
|
|
|
|
2020-07-25 20:05:46 -04:00
|
|
|
use crate::{
|
2020-07-26 00:05:13 -04:00
|
|
|
args::CallArgs,
|
2020-08-06 03:46:58 -04:00
|
|
|
atrule::mixin::{BuiltinMixin, Mixin},
|
2020-07-26 00:05:13 -04:00
|
|
|
builtin::Builtin,
|
2020-07-27 17:58:29 -04:00
|
|
|
common::{Identifier, QuoteKind},
|
2020-07-26 00:05:13 -04:00
|
|
|
error::SassResult,
|
|
|
|
parse::Parser,
|
2020-07-30 17:21:32 -04:00
|
|
|
scope::Scope,
|
2020-07-27 17:58:29 -04:00
|
|
|
value::{SassFunction, SassMap, Value},
|
2020-07-25 20:05:46 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
mod color;
|
|
|
|
mod list;
|
|
|
|
mod map;
|
|
|
|
mod math;
|
|
|
|
mod meta;
|
|
|
|
mod selector;
|
|
|
|
mod string;
|
|
|
|
|
|
|
|
#[derive(Debug, Default)]
|
2020-08-07 02:01:04 -04:00
|
|
|
pub(crate) struct Module {
|
|
|
|
pub scope: Scope,
|
|
|
|
|
|
|
|
/// Whether or not this module is builtin
|
|
|
|
/// e.g. `"sass:math"`
|
|
|
|
is_builtin: bool,
|
|
|
|
}
|
2020-07-25 20:05:46 -04:00
|
|
|
|
2020-07-30 17:46:56 -04:00
|
|
|
#[derive(Debug, Default)]
|
|
|
|
pub(crate) struct Modules(BTreeMap<Identifier, Module>);
|
|
|
|
|
2020-08-06 21:00:34 -04:00
|
|
|
#[derive(Debug, Default)]
|
|
|
|
pub(crate) struct ModuleConfig(BTreeMap<Identifier, Value>);
|
|
|
|
|
|
|
|
impl ModuleConfig {
|
|
|
|
/// Removes and returns element with name
|
|
|
|
pub fn get(&mut self, name: Identifier) -> Option<Value> {
|
|
|
|
self.0.remove(&name)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// If this structure is not empty at the end of
|
|
|
|
/// an `@use`, we must throw an error
|
|
|
|
pub fn is_empty(&self) -> bool {
|
|
|
|
self.0.is_empty()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn insert(&mut self, name: Spanned<Identifier>, value: Spanned<Value>) -> SassResult<()> {
|
|
|
|
if self.0.insert(name.node, value.node).is_some() {
|
|
|
|
Err((
|
|
|
|
"The same variable may only be configured once.",
|
|
|
|
name.span.merge(value.span),
|
|
|
|
)
|
|
|
|
.into())
|
|
|
|
} else {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-30 17:46:56 -04:00
|
|
|
impl Modules {
|
2020-08-06 04:00:45 -04:00
|
|
|
pub fn insert(&mut self, name: Identifier, module: Module, span: Span) -> SassResult<()> {
|
|
|
|
if self.0.contains_key(&name) {
|
|
|
|
return Err((
|
|
|
|
format!("There's already a module with namespace \"{}\".", name),
|
|
|
|
span,
|
|
|
|
)
|
|
|
|
.into());
|
|
|
|
}
|
|
|
|
|
2020-07-30 17:46:56 -04:00
|
|
|
self.0.insert(name, module);
|
2020-08-06 04:00:45 -04:00
|
|
|
|
|
|
|
Ok(())
|
2020-07-30 17:46:56 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get(&self, name: Identifier, span: Span) -> SassResult<&Module> {
|
|
|
|
match self.0.get(&name) {
|
|
|
|
Some(v) => Ok(v),
|
|
|
|
None => Err((
|
|
|
|
format!(
|
|
|
|
"There is no module with the namespace \"{}\".",
|
|
|
|
name.as_str()
|
|
|
|
),
|
|
|
|
span,
|
|
|
|
)
|
|
|
|
.into()),
|
|
|
|
}
|
|
|
|
}
|
2020-08-07 02:01:04 -04:00
|
|
|
|
|
|
|
pub fn get_mut(&mut self, name: Identifier, span: Span) -> SassResult<&mut Module> {
|
|
|
|
match self.0.get_mut(&name) {
|
|
|
|
Some(v) => Ok(v),
|
|
|
|
None => Err((
|
|
|
|
format!(
|
|
|
|
"There is no module with the namespace \"{}\".",
|
|
|
|
name.as_str()
|
|
|
|
),
|
|
|
|
span,
|
|
|
|
)
|
|
|
|
.into()),
|
|
|
|
}
|
|
|
|
}
|
2020-07-30 17:46:56 -04:00
|
|
|
}
|
|
|
|
|
2020-07-26 00:05:13 -04:00
|
|
|
impl Module {
|
2020-08-07 02:01:04 -04:00
|
|
|
pub fn new_builtin() -> Self {
|
2020-08-07 02:12:13 -04:00
|
|
|
Module {
|
|
|
|
scope: Scope::default(),
|
|
|
|
is_builtin: true,
|
|
|
|
}
|
2020-08-07 02:01:04 -04:00
|
|
|
}
|
|
|
|
|
2020-07-26 00:05:13 -04:00
|
|
|
pub fn get_var(&self, name: Spanned<Identifier>) -> SassResult<&Value> {
|
2020-08-02 04:20:08 -04:00
|
|
|
if name.node.as_str().starts_with('-') {
|
|
|
|
return Err((
|
|
|
|
"Private members can't be accessed from outside their modules.",
|
|
|
|
name.span,
|
|
|
|
)
|
|
|
|
.into());
|
|
|
|
}
|
|
|
|
|
2020-08-07 02:01:04 -04:00
|
|
|
match self.scope.vars.get(&name.node) {
|
2020-07-26 13:49:13 -04:00
|
|
|
Some(v) => Ok(v),
|
2020-07-26 00:05:13 -04:00
|
|
|
None => Err(("Undefined variable.", name.span).into()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-07 02:01:04 -04:00
|
|
|
pub fn update_var(&mut self, name: Spanned<Identifier>, value: Value) -> SassResult<()> {
|
|
|
|
if self.is_builtin {
|
2020-08-07 02:12:13 -04:00
|
|
|
return Err(("Cannot modify built-in variable.", name.span).into());
|
2020-08-07 02:01:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if name.node.as_str().starts_with('-') {
|
|
|
|
return Err((
|
|
|
|
"Private members can't be accessed from outside their modules.",
|
|
|
|
name.span,
|
|
|
|
)
|
|
|
|
.into());
|
|
|
|
}
|
|
|
|
|
|
|
|
if self.scope.insert_var(name.node, value).is_some() {
|
|
|
|
Ok(())
|
|
|
|
} else {
|
|
|
|
Err(("Undefined variable.", name.span).into())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-06 03:46:58 -04:00
|
|
|
pub fn get_mixin(&self, name: Spanned<Identifier>) -> SassResult<Mixin> {
|
|
|
|
if name.node.as_str().starts_with('-') {
|
|
|
|
return Err((
|
|
|
|
"Private members can't be accessed from outside their modules.",
|
|
|
|
name.span,
|
|
|
|
)
|
|
|
|
.into());
|
|
|
|
}
|
|
|
|
|
2020-08-07 02:01:04 -04:00
|
|
|
match self.scope.mixins.get(&name.node) {
|
2020-08-06 03:46:58 -04:00
|
|
|
Some(v) => Ok(v.clone()),
|
|
|
|
None => Err(("Undefined mixin.", name.span).into()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn insert_builtin_mixin(&mut self, name: &'static str, mixin: BuiltinMixin) {
|
2020-08-07 02:01:04 -04:00
|
|
|
self.scope.mixins.insert(name.into(), Mixin::Builtin(mixin));
|
2020-08-06 03:46:58 -04:00
|
|
|
}
|
|
|
|
|
2020-07-26 00:10:55 -04:00
|
|
|
pub fn insert_builtin_var(&mut self, name: &'static str, value: Value) {
|
2020-08-07 02:01:04 -04:00
|
|
|
self.scope.vars.insert(name.into(), value);
|
2020-07-26 00:10:55 -04:00
|
|
|
}
|
|
|
|
|
2020-08-02 04:20:08 -04:00
|
|
|
pub fn get_fn(&self, name: Spanned<Identifier>) -> SassResult<Option<SassFunction>> {
|
|
|
|
if name.node.as_str().starts_with('-') {
|
|
|
|
return Err((
|
|
|
|
"Private members can't be accessed from outside their modules.",
|
|
|
|
name.span,
|
|
|
|
)
|
|
|
|
.into());
|
|
|
|
}
|
|
|
|
|
2020-08-07 02:01:04 -04:00
|
|
|
Ok(self.scope.functions.get(&name.node).cloned())
|
2020-07-26 00:05:13 -04:00
|
|
|
}
|
|
|
|
|
2020-07-30 18:52:16 -04:00
|
|
|
pub fn var_exists(&self, name: Identifier) -> bool {
|
2020-08-07 02:01:04 -04:00
|
|
|
!name.as_str().starts_with('-') && self.scope.var_exists(name)
|
2020-07-30 18:52:16 -04:00
|
|
|
}
|
|
|
|
|
2020-07-30 18:35:34 -04:00
|
|
|
pub fn mixin_exists(&self, name: Identifier) -> bool {
|
2020-08-07 02:01:04 -04:00
|
|
|
!name.as_str().starts_with('-') && self.scope.mixin_exists(name)
|
2020-07-30 18:35:34 -04:00
|
|
|
}
|
|
|
|
|
2020-08-16 19:47:18 -04:00
|
|
|
pub fn fn_exists(&self, name: Identifier) -> bool {
|
|
|
|
!name.as_str().starts_with('-') && self.scope.fn_exists(name)
|
|
|
|
}
|
|
|
|
|
2020-07-26 00:05:13 -04:00
|
|
|
pub fn insert_builtin(
|
|
|
|
&mut self,
|
|
|
|
name: &'static str,
|
|
|
|
function: fn(CallArgs, &mut Parser<'_>) -> SassResult<Value>,
|
|
|
|
) {
|
|
|
|
let ident = name.into();
|
2020-08-07 02:01:04 -04:00
|
|
|
self.scope
|
2020-07-30 17:21:32 -04:00
|
|
|
.functions
|
2020-07-26 00:05:13 -04:00
|
|
|
.insert(ident, SassFunction::Builtin(Builtin::new(function), ident));
|
|
|
|
}
|
2020-07-27 17:58:29 -04:00
|
|
|
|
|
|
|
pub fn functions(&self) -> SassMap {
|
|
|
|
SassMap::new_with(
|
2020-08-07 02:01:04 -04:00
|
|
|
self.scope
|
2020-07-30 17:21:32 -04:00
|
|
|
.functions
|
2020-07-27 17:58:29 -04:00
|
|
|
.iter()
|
2020-08-02 04:20:08 -04:00
|
|
|
.filter(|(key, _)| !key.as_str().starts_with('-'))
|
2020-07-27 17:58:29 -04:00
|
|
|
.map(|(key, value)| {
|
|
|
|
(
|
|
|
|
Value::String(key.to_string(), QuoteKind::Quoted),
|
|
|
|
Value::FunctionRef(value.clone()),
|
|
|
|
)
|
|
|
|
})
|
2020-07-27 18:06:00 -04:00
|
|
|
.collect::<Vec<(Value, Value)>>(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn variables(&self) -> SassMap {
|
|
|
|
SassMap::new_with(
|
2020-08-07 02:01:04 -04:00
|
|
|
self.scope
|
2020-07-30 17:21:32 -04:00
|
|
|
.vars
|
2020-07-27 18:06:00 -04:00
|
|
|
.iter()
|
2020-08-02 04:20:08 -04:00
|
|
|
.filter(|(key, _)| !key.as_str().starts_with('-'))
|
2020-07-27 18:06:00 -04:00
|
|
|
.map(|(key, value)| {
|
|
|
|
(
|
|
|
|
Value::String(key.to_string(), QuoteKind::Quoted),
|
|
|
|
value.clone(),
|
|
|
|
)
|
|
|
|
})
|
2020-07-27 17:58:29 -04:00
|
|
|
.collect::<Vec<(Value, Value)>>(),
|
|
|
|
)
|
|
|
|
}
|
2020-07-30 17:21:32 -04:00
|
|
|
|
2020-08-07 02:01:04 -04:00
|
|
|
pub const fn new_from_scope(scope: Scope, is_builtin: bool) -> Self {
|
|
|
|
Module { scope, is_builtin }
|
2020-07-30 17:21:32 -04:00
|
|
|
}
|
2020-07-26 00:05:13 -04:00
|
|
|
}
|
|
|
|
|
2020-07-25 20:05:46 -04:00
|
|
|
pub(crate) fn declare_module_color() -> Module {
|
2020-08-07 02:01:04 -04:00
|
|
|
let mut module = Module::new_builtin();
|
2020-07-25 20:05:46 -04:00
|
|
|
color::declare(&mut module);
|
|
|
|
module
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn declare_module_list() -> Module {
|
2020-08-07 02:01:04 -04:00
|
|
|
let mut module = Module::new_builtin();
|
2020-07-25 20:05:46 -04:00
|
|
|
list::declare(&mut module);
|
|
|
|
module
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn declare_module_map() -> Module {
|
2020-08-07 02:01:04 -04:00
|
|
|
let mut module = Module::new_builtin();
|
2020-07-25 20:05:46 -04:00
|
|
|
map::declare(&mut module);
|
|
|
|
module
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn declare_module_math() -> Module {
|
2020-08-07 02:01:04 -04:00
|
|
|
let mut module = Module::new_builtin();
|
2020-07-25 20:05:46 -04:00
|
|
|
math::declare(&mut module);
|
|
|
|
module
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn declare_module_meta() -> Module {
|
2020-08-07 02:01:04 -04:00
|
|
|
let mut module = Module::new_builtin();
|
2020-07-25 20:05:46 -04:00
|
|
|
meta::declare(&mut module);
|
|
|
|
module
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn declare_module_selector() -> Module {
|
2020-08-07 02:01:04 -04:00
|
|
|
let mut module = Module::new_builtin();
|
2020-07-25 20:05:46 -04:00
|
|
|
selector::declare(&mut module);
|
|
|
|
module
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn declare_module_string() -> Module {
|
2020-08-07 02:01:04 -04:00
|
|
|
let mut module = Module::new_builtin();
|
2020-07-25 20:05:46 -04:00
|
|
|
string::declare(&mut module);
|
|
|
|
module
|
|
|
|
}
|