1use std::collections::HashMap;23use jrsonnet_gcmodule::Trace;4use jrsonnet_interner::IStr;5use jrsonnet_parser::{ArgsDesc, LocExpr, SourceFifo, SourcePath};67use crate::{evaluate, typed::Typed, Context, Result, Thunk, Val};8910pub trait OptionalContext {}1112pub trait ArgLike {13 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>>;14}1516impl ArgLike for &LocExpr {17 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {18 Ok(if tailstrict {19 Thunk::evaluated(evaluate(ctx, self)?)20 } else {21 let expr = (*self).clone();22 Thunk!(move || evaluate(ctx, &expr))23 })24 }25}2627impl<T> ArgLike for T28where29 T: Typed + Clone,30{31 fn evaluate_arg(&self, _ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {32 if T::provides_lazy() && !tailstrict {33 return Ok(T::into_lazy_untyped(self.clone()));34 }35 let val = T::into_untyped(self.clone())?;36 Ok(Thunk::evaluated(val))37 }38}39impl<T> OptionalContext for T where T: Typed + Clone {}4041#[derive(Clone, Trace)]42pub enum TlaArg {43 String(IStr),44 Val(Val),45 Lazy(Thunk<Val>),46 Import(String),47 ImportStr(String),48 InlineCode(String),49}50impl ArgLike for TlaArg {51 fn evaluate_arg(&self, ctx: Context, _tailstrict: bool) -> Result<Thunk<Val>> {52 match self {53 Self::String(s) => Ok(Thunk::evaluated(Val::string(s.clone()))),54 Self::Val(val) => Ok(Thunk::evaluated(val.clone())),55 Self::Lazy(lazy) => Ok(lazy.clone()),56 Self::Import(p) => {57 let resolved = ctx.state().resolve_from_default(&p.as_str())?;58 Ok(Thunk!(move || ctx.state().import_resolved(resolved)))59 }60 Self::ImportStr(p) => {61 let resolved = ctx.state().resolve_from_default(&p.as_str())?;62 Ok(Thunk!(move || ctx63 .state()64 .import_resolved_str(resolved)65 .map(Val::string)))66 }67 Self::InlineCode(p) => {68 let resolved =69 SourcePath::new(SourceFifo("<inline code>".to_owned(), p.as_bytes().into()));70 Ok(Thunk!(move || ctx.state().import_resolved(resolved)))71 }72 }73 }74}7576pub trait ArgsLike {77 fn unnamed_len(&self) -> usize;78 fn unnamed_iter(79 &self,80 ctx: Context,81 tailstrict: bool,82 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,83 ) -> Result<()>;84 fn named_iter(85 &self,86 ctx: Context,87 tailstrict: bool,88 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,89 ) -> Result<()>;90 fn named_names(&self, handler: &mut dyn FnMut(&IStr));91 fn is_empty(&self) -> bool;92}9394impl ArgsLike for Vec<Val> {95 fn unnamed_len(&self) -> usize {96 self.len()97 }98 fn unnamed_iter(99 &self,100 _ctx: Context,101 _tailstrict: bool,102 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,103 ) -> Result<()> {104 for (idx, el) in self.iter().enumerate() {105 handler(idx, Thunk::evaluated(el.clone()))?;106 }107 Ok(())108 }109 fn named_iter(110 &self,111 _ctx: Context,112 _tailstrict: bool,113 _handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,114 ) -> Result<()> {115 Ok(())116 }117 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}118 fn is_empty(&self) -> bool {119 self.is_empty()120 }121}122123impl ArgsLike for ArgsDesc {124 fn unnamed_len(&self) -> usize {125 self.unnamed.len()126 }127128 fn unnamed_iter(129 &self,130 ctx: Context,131 tailstrict: bool,132 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,133 ) -> Result<()> {134 for (id, arg) in self.unnamed.iter().enumerate() {135 handler(136 id,137 if tailstrict {138 Thunk::evaluated(evaluate(ctx.clone(), arg)?)139 } else {140 let ctx = ctx.clone();141 let arg = arg.clone();142143 Thunk!(move || evaluate(ctx, &arg))144 },145 )?;146 }147 Ok(())148 }149150 fn named_iter(151 &self,152 ctx: Context,153 tailstrict: bool,154 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,155 ) -> Result<()> {156 for (name, arg) in &self.named {157 handler(158 name,159 if tailstrict {160 Thunk::evaluated(evaluate(ctx.clone(), arg)?)161 } else {162 let ctx = ctx.clone();163 let arg = arg.clone();164165 Thunk!(move || evaluate(ctx, &arg))166 },167 )?;168 }169 Ok(())170 }171172 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {173 for (name, _) in &self.named {174 handler(name);175 }176 }177178 fn is_empty(&self) -> bool {179 self.unnamed.is_empty() && self.named.is_empty()180 }181}182183impl<V: ArgLike, S> ArgsLike for HashMap<IStr, V, S> {184 fn unnamed_len(&self) -> usize {185 0186 }187188 fn unnamed_iter(189 &self,190 _ctx: Context,191 _tailstrict: bool,192 _handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,193 ) -> Result<()> {194 Ok(())195 }196197 fn named_iter(198 &self,199 ctx: Context,200 tailstrict: bool,201 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,202 ) -> Result<()> {203 for (name, value) in self {204 handler(name, value.evaluate_arg(ctx.clone(), tailstrict)?)?;205 }206 Ok(())207 }208209 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {210 for (name, _) in self {211 handler(name);212 }213 }214215 fn is_empty(&self) -> bool {216 self.is_empty()217 }218}219impl<V, S> OptionalContext for HashMap<IStr, V, S> where V: ArgLike + OptionalContext {}220221macro_rules! impl_args_like {222 ($count:expr; $($gen:ident)*) => {223 impl<$($gen: ArgLike,)*> ArgsLike for ($($gen,)*) {224 fn unnamed_len(&self) -> usize {225 $count226 }227 #[allow(non_snake_case, unused_assignments)]228 fn unnamed_iter(229 &self,230 ctx: Context,231 tailstrict: bool,232 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,233 ) -> Result<()> {234 let mut i = 0usize;235 let ($($gen,)*) = self;236 $(237 handler(i, $gen.evaluate_arg(ctx.clone(), tailstrict)?)?;238 i+=1;239 )*240 Ok(())241 }242 fn named_iter(243 &self,244 _ctx: Context,245 _tailstrict: bool,246 _handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,247 ) -> Result<()> {248 Ok(())249 }250 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}251252 fn is_empty(&self) -> bool {253 254 false255 }256 }257 impl<$($gen: ArgLike,)*> OptionalContext for ($($gen,)*) where $($gen: OptionalContext),* {}258 };259 ($count:expr; $($cur:ident)* @ $c:ident $($rest:ident)*) => {260 impl_args_like!($count; $($cur)*);261 impl_args_like!($count + 1usize; $($cur)* $c @ $($rest)*);262 };263 ($count:expr; $($cur:ident)* @) => {264 impl_args_like!($count; $($cur)*);265 }266}267impl_args_like! {268 269 1usize; A @ B C D E F G H I J K L270}271272impl ArgsLike for () {273 fn unnamed_len(&self) -> usize {274 0275 }276277 fn unnamed_iter(278 &self,279 _ctx: Context,280 _tailstrict: bool,281 _handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,282 ) -> Result<()> {283 Ok(())284 }285286 fn named_iter(287 &self,288 _ctx: Context,289 _tailstrict: bool,290 _handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,291 ) -> Result<()> {292 Ok(())293 }294295 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}296 fn is_empty(&self) -> bool {297 true298 }299}300impl OptionalContext for () {}