1use std::fmt::Debug;23use educe::Educe;4use jrsonnet_gcmodule::{Cc, Trace};5use jrsonnet_interner::IStr;6use rustc_hash::FxHashMap;78use crate::{9 error::ErrorKind::*, gc::WithCapacityExt as _, map::LayeredHashMap, ObjValue, Pending, Result,10 SupThis, Thunk, Val,11};12131415#[derive(Debug, Trace, Clone, Educe)]16#[educe(PartialEq)]17pub struct Context(#[educe(PartialEq(method = Cc::ptr_eq))] Cc<ContextInternal>);1819#[derive(Debug, Trace)]20struct ContextInternal {21 dollar: Option<ObjValue>,22 sup_this: Option<SupThis>,23 bindings: LayeredHashMap,24}25impl Context {26 pub fn new_future() -> Pending<Self> {27 Pending::new()28 }2930 pub fn dollar(&self) -> Option<&ObjValue> {31 self.0.dollar.as_ref()32 }3334 pub fn try_dollar(&self) -> Result<ObjValue> {35 self.036 .dollar37 .clone()38 .ok_or_else(|| CantUseSelfSupOutsideOfObject.into())39 }4041 pub fn this(&self) -> Option<&ObjValue> {42 self.0.sup_this.as_ref().map(SupThis::this)43 }4445 pub fn try_this(&self) -> Result<ObjValue> {46 self.047 .sup_this48 .as_ref()49 .ok_or_else(|| CantUseSelfSupOutsideOfObject.into())50 .map(SupThis::this)51 .cloned()52 }5354 pub fn sup_this(&self) -> Option<&SupThis> {55 self.0.sup_this.as_ref()56 }5758 pub fn try_sup_this(&self) -> Result<SupThis> {59 self.060 .sup_this61 .clone()62 .ok_or_else(|| CantUseSelfSupOutsideOfObject.into())63 }6465 pub fn binding(&self, name: IStr) -> Result<Thunk<Val>> {66 use std::cmp::Ordering;6768 use crate::bail;6970 if let Some(val) = self.0.bindings.get(&name).cloned() {71 return Ok(val);72 }7374 let mut heap = Vec::new();75 self.0.bindings.clone().iter_keys(|k| {76 let conf = strsim::jaro_winkler(&k as &str, &name as &str);77 if conf < 0.8 {78 return;79 }80 heap.push((conf, k));81 });82 heap.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(Ordering::Equal));8384 bail!(VariableIsNotDefined(85 name,86 heap.into_iter().map(|(_, k)| k).collect()87 ))88 }89 pub fn contains_binding(&self, name: IStr) -> bool {90 self.0.bindings.contains_key(&name)91 }92 #[must_use]93 pub fn into_future(self, ctx: Pending<Self>) -> Self {94 {95 ctx.clone().fill(self);96 }97 ctx.unwrap()98 }99100 #[must_use]101 pub fn with_var(self, name: impl Into<IStr>, value: Val) -> Self {102 let mut new_bindings = FxHashMap::with_capacity(1);103 new_bindings.insert(name.into(), Thunk::evaluated(value));104 self.extend_bindings(new_bindings)105 }106107 #[must_use]108 pub fn extend_bindings_sup_this(109 self,110 new_bindings: FxHashMap<IStr, Thunk<Val>>,111 sup_this: SupThis,112 ) -> Self {113 let ctx = &self;114 let dollar = ctx115 .0116 .dollar117 .clone()118 .or_else(|| Some(sup_this.this().clone()));119 let bindings = if new_bindings.is_empty() {120 ctx.0.bindings.clone()121 } else {122 ctx.0.bindings.clone().extend(new_bindings)123 };124 Self(Cc::new(ContextInternal {125 dollar,126 sup_this: Some(sup_this),127 bindings,128 }))129 }130 #[must_use]131 pub fn extend_bindings(self, new_bindings: FxHashMap<IStr, Thunk<Val>>) -> Self {132 if new_bindings.is_empty() {133 return self;134 }135 let ctx = &self;136 let bindings = if new_bindings.is_empty() {137 ctx.0.bindings.clone()138 } else {139 ctx.0.bindings.clone().extend(new_bindings)140 };141 Self(Cc::new(ContextInternal {142 dollar: ctx.0.dollar.clone(),143 sup_this: ctx.0.sup_this.clone(),144 bindings,145 }))146 }147}148149#[derive(Default)]150pub struct ContextBuilder {151 bindings: FxHashMap<IStr, Thunk<Val>>,152 extend: Option<Context>,153}154155impl ContextBuilder {156 pub fn new() -> Self {157 Self::with_capacity(0)158 }159160 pub fn with_capacity(capacity: usize) -> Self {161 Self {162 bindings: FxHashMap::with_capacity(capacity),163 extend: None,164 }165 }166167 pub fn extend(parent: Context) -> Self {168 Self {169 bindings: FxHashMap::new(),170 extend: Some(parent),171 }172 }173174 175 176 177 178 pub fn bind(&mut self, name: impl Into<IStr>, value: Thunk<Val>) -> &mut Self {179 let old = self.bindings.insert(name.into(), value);180 assert!(old.is_none(), "variable bound twice in single context call");181 self182 }183 pub fn binds(&mut self, bindings: FxHashMap<IStr, Thunk<Val>>) -> &mut Self {184 for (k, v) in bindings {185 self.bind(k, v);186 }187 self188 }189 pub fn build(self) -> Context {190 if let Some(parent) = self.extend {191 parent.extend_bindings(self.bindings)192 } else {193 Context(Cc::new(ContextInternal {194 bindings: LayeredHashMap::new(self.bindings),195 dollar: None,196 sup_this: None,197 }))198 }199 }200}