1use std::fmt::Debug;23use gcmodule::{Cc, Trace};4use jrsonnet_interner::IStr;56use crate::{7 cc_ptr_eq, error::Error::*, gc::GcHashMap, map::LayeredHashMap, ObjValue, Pending, Result,8 Thunk, Val,9};1011#[derive(Trace)]12struct ContextInternals {13 dollar: Option<ObjValue>,14 sup: Option<ObjValue>,15 this: Option<ObjValue>,16 bindings: LayeredHashMap,17}18impl Debug for ContextInternals {19 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {20 f.debug_struct("Context").finish()21 }22}2324#[derive(Debug, Clone, Trace)]25pub struct Context(Cc<ContextInternals>);26impl Context {27 pub fn new_future() -> Pending<Self> {28 Pending::new()29 }3031 pub fn dollar(&self) -> &Option<ObjValue> {32 &self.0.dollar33 }3435 pub fn this(&self) -> &Option<ObjValue> {36 &self.0.this37 }3839 pub fn super_obj(&self) -> &Option<ObjValue> {40 &self.0.sup41 }4243 pub fn new() -> Self {44 Self(Cc::new(ContextInternals {45 dollar: None,46 this: None,47 sup: None,48 bindings: LayeredHashMap::default(),49 }))50 }5152 #[cfg(not(feature = "friendly-errors"))]53 pub fn binding(&self, name: IStr) -> Result<Thunk<Val>> {54 Ok(self55 .056 .bindings57 .get(&name)58 .cloned()59 .ok_or(VariableIsNotDefined(name, vec![]))?)60 }6162 #[cfg(feature = "friendly-errors")]63 pub fn binding(&self, name: IStr) -> Result<Thunk<Val>> {64 use std::cmp::Ordering;6566 use crate::throw;6768 if let Some(val) = self.0.bindings.get(&name).cloned() {69 return Ok(val);70 }7172 let mut heap = Vec::new();73 self.0.bindings.clone().iter_keys(|k| {74 let conf = strsim::jaro_winkler(&k as &str, &name as &str);75 if conf < 0.8 {76 return;77 }78 heap.push((conf, k));79 });80 heap.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(Ordering::Equal));8182 throw!(VariableIsNotDefined(83 name,84 heap.into_iter().map(|(_, k)| k).collect()85 ))86 }87 pub fn contains_binding(&self, name: IStr) -> bool {88 self.0.bindings.contains_key(&name)89 }90 #[must_use]91 pub fn into_future(self, ctx: Pending<Self>) -> Self {92 {93 ctx.0.borrow_mut().replace(self);94 }95 ctx.unwrap()96 }9798 #[must_use]99 pub fn with_var(self, name: IStr, value: Val) -> Self {100 let mut new_bindings = GcHashMap::with_capacity(1);101 new_bindings.insert(name, Thunk::evaluated(value));102 self.extend(new_bindings, None, None, None)103 }104105 #[must_use]106 pub fn extend(107 self,108 new_bindings: GcHashMap<IStr, Thunk<Val>>,109 new_dollar: Option<ObjValue>,110 new_sup: Option<ObjValue>,111 new_this: Option<ObjValue>,112 ) -> Self {113 let ctx = &self.0;114 let dollar = new_dollar.or_else(|| ctx.dollar.clone());115 let this = new_this.or_else(|| ctx.this.clone());116 let sup = new_sup.or_else(|| ctx.sup.clone());117 let bindings = if new_bindings.is_empty() {118 ctx.bindings.clone()119 } else {120 ctx.bindings.clone().extend(new_bindings)121 };122 Self(Cc::new(ContextInternals {123 dollar,124 sup,125 this,126 bindings,127 }))128 }129}130131impl Default for Context {132 fn default() -> Self {133 Self::new()134 }135}136137impl PartialEq for Context {138 fn eq(&self, other: &Self) -> bool {139 cc_ptr_eq(&self.0, &other.0)140 }141}