use std::cell::RefCell; use std::collections::HashMap; use codemap::Spanned; use crate::atrule::{Function, Mixin}; use crate::common::Identifier; use crate::error::SassResult; use crate::value::Value; thread_local!(pub(crate) static GLOBAL_SCOPE: RefCell = RefCell::new(Scope::new())); pub(crate) fn get_global_var>(s: Spanned) -> SassResult> { GLOBAL_SCOPE.with(|scope| match scope.borrow().vars().get(&s.node.into()) { Some(v) => Ok(v.clone()), None => Err(("Undefined variable.", s.span).into()), }) } /// Returns true if a variable exists in the *global* scope pub(crate) fn global_var_exists>(v: T) -> bool { GLOBAL_SCOPE.with(|scope| scope.borrow().vars().contains_key(&v.into())) } pub(crate) fn insert_global_var>( s: T, v: Spanned, ) -> SassResult>> { GLOBAL_SCOPE.with(|scope| scope.borrow_mut().insert_var(s.into(), v)) } pub(crate) fn get_global_fn>(s: Spanned) -> SassResult { GLOBAL_SCOPE.with( |scope| match scope.borrow().functions().get(&s.node.into()) { Some(v) => Ok(v.clone()), None => Err(("Undefined function.", s.span).into()), }, ) } /// Returns true if a function exists in the *global* scope pub(crate) fn global_fn_exists>(v: T) -> bool { GLOBAL_SCOPE.with(|scope| scope.borrow().functions().contains_key(&v.into())) } pub(crate) fn insert_global_fn>(s: T, v: Function) -> Option { GLOBAL_SCOPE.with(|scope| scope.borrow_mut().insert_fn(s.into(), v)) } pub(crate) fn get_global_mixin>(s: Spanned) -> SassResult { GLOBAL_SCOPE.with(|scope| match scope.borrow().mixins().get(&s.node.into()) { Some(v) => Ok(v.clone()), None => Err(("Undefined mixin.", s.span).into()), }) } /// Returns true if a mixin exists in the *global* scope pub(crate) fn global_mixin_exists>(v: T) -> bool { GLOBAL_SCOPE.with(|scope| scope.borrow().mixins().contains_key(&v.into())) } pub(crate) fn insert_global_mixin>(s: T, v: Mixin) -> Option { GLOBAL_SCOPE.with(|scope| scope.borrow_mut().insert_mixin(s.into(), v)) } #[derive(Debug, Clone)] pub(crate) struct Scope { vars: HashMap>, mixins: HashMap, functions: HashMap, } impl Scope { #[must_use] pub fn new() -> Self { Self { vars: HashMap::new(), mixins: HashMap::new(), functions: HashMap::new(), } } pub const fn vars(&self) -> &HashMap> { &self.vars } pub const fn functions(&self) -> &HashMap { &self.functions } pub const fn mixins(&self) -> &HashMap { &self.mixins } pub fn get_var>(&self, name: Spanned) -> SassResult> { let name = name.map_node(|n| n.into()); match self.vars.get(&name.node) { Some(v) => Ok(v.clone()), None => get_global_var(name), } } pub fn insert_var>( &mut self, s: T, v: Spanned, ) -> SassResult>> { let Spanned { node, span } = v; Ok(self.vars.insert(s.into(), node.eval(span)?)) } pub fn var_exists>(&self, v: T) -> bool { let name = v.into(); self.vars.contains_key(&name) || global_var_exists(name) } pub fn get_mixin>(&self, name: Spanned) -> SassResult { let name = name.map_node(|n| n.into()); match self.mixins.get(&name.node) { Some(v) => Ok(v.clone()), None => get_global_mixin(name), } } pub fn insert_mixin>(&mut self, s: T, v: Mixin) -> Option { self.mixins.insert(s.into(), v) } pub fn mixin_exists>(&self, v: T) -> bool { let name = v.into(); self.mixins.contains_key(&name) || global_mixin_exists(name) } pub fn get_fn>(&self, name: Spanned) -> SassResult { let name = name.map_node(|n| n.into()); match self.functions.get(&name.node) { Some(v) => Ok(v.clone()), None => get_global_fn(name), } } pub fn insert_fn>(&mut self, s: T, v: Function) -> Option { self.functions.insert(s.into(), v) } pub fn fn_exists>(&self, v: T) -> bool { let name = v.into(); self.functions.contains_key(&name) || global_fn_exists(name) } pub fn extend(&mut self, other: Scope) { self.vars.extend(other.vars); self.mixins.extend(other.mixins); self.functions.extend(other.functions); } }