use std::collections::HashMap;
use indexmap::IndexMap;
use crate::value::Value;
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct RuntimeContext {
global: Context,
pub scopes: Vec<HashMap<String, Value>>,
}
impl<'a> RuntimeContext {
pub fn new(global: Context) -> Self {
Self {
global,
scopes: Vec::new(),
}
}
pub fn resolve_path<I>(&self, path: I) -> Option<Value>
where
I: IntoIterator<Item = &'a str>,
{
let mut iter = path.into_iter();
let first = iter.next()?;
let mut current = self.get_from_scopes(first)?;
for key in iter {
current = match current {
Value::Map(map) => map.get(key)?.clone(),
_ => {
return None;
}
};
}
Some(current)
}
fn get_from_scopes(&self, key: &str) -> Option<Value> {
for scope in self.scopes.iter().rev() {
if let Some(val) = scope.get(key) {
return Some(val.clone());
}
}
None
}
pub fn push_scope(&mut self) {
self.scopes.push(HashMap::new());
}
pub fn pop_scope(&mut self) {
self.scopes.pop();
}
pub fn insert(&mut self, key: impl Into<String>, value: Value) {
if let Some(scope) = self.scopes.last_mut() {
scope.insert(key.into(), value);
} else {
self.scopes.push(HashMap::from([(key.into(), value)]));
}
}
}
#[derive(Debug, Clone)]
pub struct Context {
pub data: IndexMap<String, Value>,
}
impl Default for Context {
fn default() -> Self {
Self::new()
}
}
impl Context {
pub fn new() -> Self {
Self {
data: IndexMap::new(),
}
}
pub fn merge(&mut self, incoming: &IndexMap<String, Value>) {
merge_nested(&mut self.data, incoming);
}
pub fn merge_value(&mut self, incoming: Value) {
match incoming {
Value::Map(map) => {
merge_nested(&mut self.data, &map);
}
_ => panic!(""),
}
}
fn json_to_value_map(&self, json: serde_json::Value) -> Option<IndexMap<String, Value>> {
match Value::from(json) {
Value::Map(map) => Some(map),
_ => None,
}
}
pub fn merge_json(&mut self, json: serde_json::Value) {
if let Some(map) = self.json_to_value_map(json) {
self.merge(&map);
} else {
panic!("Expected a JSON object to merge into context.");
}
}
pub fn delete_key(&mut self, key: &str) {
self.data.swap_remove(key);
}
pub fn remove_path<'a, I>(&mut self, path: I) -> bool
where
I: IntoIterator<Item = &'a str>,
{
let mut iter = path.into_iter();
let Some(first) = iter.next() else {
return false;
};
let mut current = &mut self.data;
let mut last_key = first;
for key in iter {
match current.get_mut(last_key) {
Some(Value::Map(map)) => {
current = map;
last_key = key;
}
_ => return false,
}
}
current.swap_remove(last_key).is_some()
}
pub fn data(&self) -> &IndexMap<String, Value> {
&self.data
}
}
fn merge_nested(target: &mut IndexMap<String, Value>, source: &IndexMap<String, Value>) {
for (key, value) in source {
match (target.get_mut(key), value) {
(Some(Value::Map(target_obj)), Value::Map(source_obj)) => {
merge_nested(target_obj, source_obj);
}
_ => {
target.insert(key.clone(), value.clone());
}
}
}
}