1use crate::{2 error::Error::*, map::LayeredHashMap, rc_fn_helper, resolved_lazy_val, FutureWrapper,3 LazyBinding, LazyVal, ObjValue, Result, Val,4};5use jrsonnet_interner::IStr;6use rustc_hash::FxHashMap;7use std::hash::BuildHasherDefault;8use std::{collections::HashMap, fmt::Debug, rc::Rc};910rc_fn_helper!(11 ContextCreator,12 context_creator,13 dyn Fn(Option<ObjValue>, Option<ObjValue>) -> Result<Context>14);1516struct ContextInternals {17 dollar: Option<ObjValue>,18 this: Option<ObjValue>,19 super_obj: Option<ObjValue>,20 bindings: LayeredHashMap<LazyVal>,21}22impl Debug for ContextInternals {23 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {24 f.debug_struct("Context")25 .field("this", &self.this.as_ref().map(|e| Rc::as_ptr(&e.0)))26 .field("bindings", &self.bindings)27 .finish()28 }29}3031#[derive(Debug, Clone)]32pub struct Context(Rc<ContextInternals>);33impl Context {34 pub fn new_future() -> FutureWrapper<Context> {35 FutureWrapper::new()36 }3738 pub fn dollar(&self) -> &Option<ObjValue> {39 &self.0.dollar40 }4142 pub fn this(&self) -> &Option<ObjValue> {43 &self.0.this44 }4546 pub fn super_obj(&self) -> &Option<ObjValue> {47 &self.0.super_obj48 }4950 pub fn new() -> Self {51 Self(Rc::new(ContextInternals {52 dollar: None,53 this: None,54 super_obj: None,55 bindings: LayeredHashMap::default(),56 }))57 }5859 pub fn binding(&self, name: IStr) -> Result<LazyVal> {60 Ok(self61 .062 .bindings63 .get(&name)64 .cloned()65 .ok_or(VariableIsNotDefined(name))?)66 }67 pub fn into_future(self, ctx: FutureWrapper<Context>) -> Self {68 {69 ctx.0.borrow_mut().replace(self);70 }71 ctx.unwrap()72 }7374 pub fn with_var(self, name: IStr, value: Val) -> Self {75 let mut new_bindings =76 FxHashMap::with_capacity_and_hasher(1, BuildHasherDefault::default());77 new_bindings.insert(name, resolved_lazy_val!(value));78 self.extend(new_bindings, None, None, None)79 }8081 pub fn extend(82 self,83 new_bindings: FxHashMap<IStr, LazyVal>,84 new_dollar: Option<ObjValue>,85 new_this: Option<ObjValue>,86 new_super_obj: Option<ObjValue>,87 ) -> Self {88 match Rc::try_unwrap(self.0) {89 Ok(mut ctx) => {90 91 if let Some(dollar) = new_dollar {92 ctx.dollar = Some(dollar);93 }94 if let Some(this) = new_this {95 ctx.this = Some(this);96 }97 if let Some(super_obj) = new_super_obj {98 ctx.super_obj = Some(super_obj);99 }100 if !new_bindings.is_empty() {101 ctx.bindings = ctx.bindings.extend(new_bindings);102 }103 Self(Rc::new(ctx))104 }105 Err(ctx) => {106 let dollar = new_dollar.or_else(|| ctx.dollar.clone());107 let this = new_this.or_else(|| ctx.this.clone());108 let super_obj = new_super_obj.or_else(|| ctx.super_obj.clone());109 let bindings = if new_bindings.is_empty() {110 ctx.bindings.clone()111 } else {112 ctx.bindings.clone().extend(new_bindings)113 };114 Self(Rc::new(ContextInternals {115 dollar,116 this,117 super_obj,118 bindings,119 }))120 }121 }122 }123 pub fn extend_unbound(124 self,125 new_bindings: HashMap<IStr, LazyBinding>,126 new_dollar: Option<ObjValue>,127 new_this: Option<ObjValue>,128 new_super_obj: Option<ObjValue>,129 ) -> Result<Self> {130 let this = new_this.or_else(|| self.0.this.clone());131 let super_obj = new_super_obj.or_else(|| self.0.super_obj.clone());132 let mut new =133 FxHashMap::with_capacity_and_hasher(new_bindings.len(), BuildHasherDefault::default());134 for (k, v) in new_bindings.into_iter() {135 new.insert(k, v.evaluate(this.clone(), super_obj.clone())?);136 }137 Ok(self.extend(new, new_dollar, this, super_obj))138 }139 #[cfg(feature = "unstable")]140 pub fn into_weak(self) -> WeakContext {141 WeakContext(Rc::downgrade(&self.0))142 }143}144145impl Default for Context {146 fn default() -> Self {147 Self::new()148 }149}150151impl PartialEq for Context {152 fn eq(&self, other: &Self) -> bool {153 Rc::ptr_eq(&self.0, &other.0)154 }155}156157#[cfg(feature = "unstable")]158#[derive(Debug, Clone)]159pub struct WeakContext(std::rc::Weak<ContextInternals>);160#[cfg(feature = "unstable")]161impl WeakContext {162 pub fn upgrade(&self) -> Context {163 Context(self.0.upgrade().expect("context is removed"))164 }165}166#[cfg(feature = "unstable")]167impl PartialEq for WeakContext {168 fn eq(&self, other: &Self) -> bool {169 self.0.ptr_eq(&other.0)170 }171}