1use std::fmt::Debug;23use jrsonnet_gcmodule::{Cc, Trace};4use jrsonnet_interner::IStr;5use rustc_hash::FxHashMap;67use crate::{8 error::ErrorKind::*, gc::WithCapacityExt as _, map::LayeredHashMap, ObjValue, Pending, Result,9 Thunk, Val,10};1112#[derive(Trace)]13struct ContextInternals {14 dollar: Option<ObjValue>,15 sup: Option<ObjValue>,16 this: Option<ObjValue>,17 bindings: LayeredHashMap,18}19impl Debug for ContextInternals {20 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {21 f.debug_struct("Context").finish()22 }23}2425262728#[derive(Debug, Clone, Trace)]29pub struct Context(Cc<ContextInternals>);30impl Context {31 pub fn new_future() -> Pending<Self> {32 Pending::new()33 }3435 pub fn dollar(&self) -> Option<&ObjValue> {36 self.0.dollar.as_ref()37 }3839 pub fn this(&self) -> Option<&ObjValue> {40 self.0.this.as_ref()41 }4243 pub fn super_obj(&self) -> Option<&ObjValue> {44 self.0.sup.as_ref()45 }4647 pub fn binding(&self, name: IStr) -> Result<Thunk<Val>> {48 use std::cmp::Ordering;4950 use crate::bail;5152 if let Some(val) = self.0.bindings.get(&name).cloned() {53 return Ok(val);54 }5556 let mut heap = Vec::new();57 self.0.bindings.clone().iter_keys(|k| {58 let conf = strsim::jaro_winkler(&k as &str, &name as &str);59 if conf < 0.8 {60 return;61 }62 heap.push((conf, k));63 });64 heap.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(Ordering::Equal));6566 bail!(VariableIsNotDefined(67 name,68 heap.into_iter().map(|(_, k)| k).collect()69 ))70 }71 pub fn contains_binding(&self, name: IStr) -> bool {72 self.0.bindings.contains_key(&name)73 }74 #[must_use]75 pub fn into_future(self, ctx: Pending<Self>) -> Self {76 {77 ctx.clone().fill(self);78 }79 ctx.unwrap()80 }8182 #[must_use]83 pub fn with_var(self, name: impl Into<IStr>, value: Val) -> Self {84 let mut new_bindings = FxHashMap::with_capacity(1);85 new_bindings.insert(name.into(), Thunk::evaluated(value));86 self.extend(new_bindings, None, None, None)87 }8889 #[must_use]90 pub fn extend(91 self,92 new_bindings: FxHashMap<IStr, Thunk<Val>>,93 new_dollar: Option<ObjValue>,94 new_sup: Option<ObjValue>,95 new_this: Option<ObjValue>,96 ) -> Self {97 let ctx = &self.0;98 let dollar = new_dollar.or_else(|| ctx.dollar.clone());99 let this = new_this.or_else(|| ctx.this.clone());100 let sup = new_sup.or_else(|| ctx.sup.clone());101 let bindings = if new_bindings.is_empty() {102 ctx.bindings.clone()103 } else {104 ctx.bindings.clone().extend(new_bindings)105 };106 Self(Cc::new(ContextInternals {107 dollar,108 sup,109 this,110 bindings,111 }))112 }113}114115impl PartialEq for Context {116 fn eq(&self, other: &Self) -> bool {117 Cc::ptr_eq(&self.0, &other.0)118 }119}120121pub struct ContextBuilder {122 bindings: FxHashMap<IStr, Thunk<Val>>,123 extend: Option<Context>,124}125126impl ContextBuilder {127 pub fn new() -> Self {128 Self::with_capacity(0)129 }130 pub fn with_capacity(capacity: usize) -> Self {131 Self {132 bindings: FxHashMap::with_capacity(capacity),133 extend: None,134 }135 }136 pub fn extend(parent: Context) -> Self {137 Self {138 bindings: FxHashMap::new(),139 extend: Some(parent),140 }141 }142 143 144 pub fn bind(&mut self, name: impl Into<IStr>, value: Thunk<Val>) -> &mut Self {145 let old = self.bindings.insert(name.into(), value);146 assert!(old.is_none(), "variable bound twice in single context call");147 self148 }149 pub fn build(self) -> Context {150 if let Some(parent) = self.extend {151 152 parent.extend(self.bindings, None, None, None)153 } else {154 Context(Cc::new(ContextInternals {155 bindings: LayeredHashMap::new(self.bindings),156 dollar: None,157 sup: None,158 this: None,159 }))160 }161 }162}