use crate::error::{JsonnetError, Result};
use crate::value::JsonnetValue;
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct Scope {
variables: HashMap<String, JsonnetValue>,
}
impl Scope {
pub fn new() -> Self {
Scope {
variables: HashMap::new(),
}
}
pub fn get(&self, name: &str) -> Option<&JsonnetValue> {
self.variables.get(name)
}
pub fn set(&mut self, name: String, value: JsonnetValue) {
self.variables.insert(name, value);
}
pub fn contains(&self, name: &str) -> bool {
self.variables.contains_key(name)
}
pub fn variables(&self) -> &HashMap<String, JsonnetValue> {
&self.variables
}
}
#[derive(Debug)]
pub struct Context {
global_scope: Scope,
local_scopes: Vec<Scope>,
depth: usize,
max_depth: usize,
}
impl Context {
pub fn new() -> Self {
Context {
global_scope: Scope::new(),
local_scopes: Vec::new(),
depth: 0,
max_depth: 100, }
}
pub fn with_max_depth(max_depth: usize) -> Self {
Context {
global_scope: Scope::new(),
local_scopes: Vec::new(),
depth: 0,
max_depth,
}
}
pub fn depth(&self) -> usize {
self.depth
}
pub fn check_depth(&self) -> Result<()> {
if self.depth >= self.max_depth {
return Err(JsonnetError::runtime_error(
format!("Maximum evaluation depth ({}) exceeded", self.max_depth)
));
}
Ok(())
}
pub fn push_depth(&mut self) -> Result<()> {
self.depth += 1;
self.check_depth()
}
pub fn pop_depth(&mut self) {
if self.depth > 0 {
self.depth -= 1;
}
}
pub fn get_variable(&self, name: &str) -> Option<&JsonnetValue> {
for scope in self.local_scopes.iter().rev() {
if let Some(value) = scope.get(name) {
return Some(value);
}
}
self.global_scope.get(name)
}
pub fn set_variable(&mut self, name: String, value: JsonnetValue) {
if let Some(scope) = self.local_scopes.last_mut() {
scope.set(name, value);
} else {
self.global_scope.set(name, value);
}
}
pub fn set_global(&mut self, name: String, value: JsonnetValue) {
self.global_scope.set(name, value);
}
pub fn has_variable(&self, name: &str) -> bool {
for scope in self.local_scopes.iter().rev() {
if scope.contains(name) {
return true;
}
}
self.global_scope.contains(name)
}
pub fn push_scope(&mut self) {
self.local_scopes.push(Scope::new());
}
pub fn pop_scope(&mut self) -> Option<Scope> {
self.local_scopes.pop()
}
pub fn current_scope(&mut self) -> Option<&mut Scope> {
self.local_scopes.last_mut()
}
pub fn global_scope(&self) -> &Scope {
&self.global_scope
}
pub fn global_scope_mut(&mut self) -> &mut Scope {
&mut self.global_scope
}
pub fn all_variables(&self) -> HashMap<String, &JsonnetValue> {
let mut result = HashMap::new();
for (name, value) in self.global_scope.variables() {
result.insert(name.clone(), value);
}
for scope in &self.local_scopes {
for (name, value) in scope.variables() {
result.insert(name.clone(), value);
}
}
result
}
pub fn fork(&self) -> Self {
Context {
global_scope: self.global_scope.clone(),
local_scopes: Vec::new(),
depth: 0,
max_depth: self.max_depth,
}
}
pub fn merge_locals_to_global(&mut self) {
for scope in &self.local_scopes {
for (name, value) in scope.variables() {
self.global_scope.set(name.clone(), value.clone());
}
}
self.local_scopes.clear();
}
}
impl Default for Context {
fn default() -> Self {
Self::new()
}
}