1use std::collections::HashMap;23use jrsonnet_gcmodule::Trace;4use jrsonnet_interner::IStr;5use jrsonnet_parser::{ArgsDesc, LocExpr};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 Code(LocExpr),45 Val(Val),46 Lazy(Thunk<Val>),47}48impl ArgLike for TlaArg {49 fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {50 match self {51 Self::String(s) => Ok(Thunk::evaluated(Val::string(s.clone()))),52 Self::Code(code) => Ok(if tailstrict {53 Thunk::evaluated(evaluate(ctx, code)?)54 } else {55 let code = code.clone();56 Thunk!(move || evaluate(ctx, &code))57 }),58 Self::Val(val) => Ok(Thunk::evaluated(val.clone())),59 Self::Lazy(lazy) => Ok(lazy.clone()),60 }61 }62}6364pub trait ArgsLike {65 fn unnamed_len(&self) -> usize;66 fn unnamed_iter(67 &self,68 ctx: Context,69 tailstrict: bool,70 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,71 ) -> Result<()>;72 fn named_iter(73 &self,74 ctx: Context,75 tailstrict: bool,76 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,77 ) -> Result<()>;78 fn named_names(&self, handler: &mut dyn FnMut(&IStr));79 fn is_empty(&self) -> bool;80}8182impl ArgsLike for Vec<Val> {83 fn unnamed_len(&self) -> usize {84 self.len()85 }86 fn unnamed_iter(87 &self,88 _ctx: Context,89 _tailstrict: bool,90 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,91 ) -> Result<()> {92 for (idx, el) in self.iter().enumerate() {93 handler(idx, Thunk::evaluated(el.clone()))?;94 }95 Ok(())96 }97 fn named_iter(98 &self,99 _ctx: Context,100 _tailstrict: bool,101 _handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,102 ) -> Result<()> {103 Ok(())104 }105 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}106 fn is_empty(&self) -> bool {107 self.is_empty()108 }109}110111impl ArgsLike for ArgsDesc {112 fn unnamed_len(&self) -> usize {113 self.unnamed.len()114 }115116 fn unnamed_iter(117 &self,118 ctx: Context,119 tailstrict: bool,120 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,121 ) -> Result<()> {122 for (id, arg) in self.unnamed.iter().enumerate() {123 handler(124 id,125 if tailstrict {126 Thunk::evaluated(evaluate(ctx.clone(), arg)?)127 } else {128 let ctx = ctx.clone();129 let arg = arg.clone();130131 Thunk!(move || evaluate(ctx, &arg))132 },133 )?;134 }135 Ok(())136 }137138 fn named_iter(139 &self,140 ctx: Context,141 tailstrict: bool,142 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,143 ) -> Result<()> {144 for (name, arg) in &self.named {145 handler(146 name,147 if tailstrict {148 Thunk::evaluated(evaluate(ctx.clone(), arg)?)149 } else {150 let ctx = ctx.clone();151 let arg = arg.clone();152153 Thunk!(move || evaluate(ctx, &arg))154 },155 )?;156 }157 Ok(())158 }159160 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {161 for (name, _) in &self.named {162 handler(name);163 }164 }165166 fn is_empty(&self) -> bool {167 self.unnamed.is_empty() && self.named.is_empty()168 }169}170171impl<V: ArgLike, S> ArgsLike for HashMap<IStr, V, S> {172 fn unnamed_len(&self) -> usize {173 0174 }175176 fn unnamed_iter(177 &self,178 _ctx: Context,179 _tailstrict: bool,180 _handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,181 ) -> Result<()> {182 Ok(())183 }184185 fn named_iter(186 &self,187 ctx: Context,188 tailstrict: bool,189 handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,190 ) -> Result<()> {191 for (name, value) in self {192 handler(name, value.evaluate_arg(ctx.clone(), tailstrict)?)?;193 }194 Ok(())195 }196197 fn named_names(&self, handler: &mut dyn FnMut(&IStr)) {198 for (name, _) in self {199 handler(name);200 }201 }202203 fn is_empty(&self) -> bool {204 self.is_empty()205 }206}207impl<V, S> OptionalContext for HashMap<IStr, V, S> where V: ArgLike + OptionalContext {}208209macro_rules! impl_args_like {210 ($count:expr; $($gen:ident)*) => {211 impl<$($gen: ArgLike,)*> ArgsLike for ($($gen,)*) {212 fn unnamed_len(&self) -> usize {213 $count214 }215 #[allow(non_snake_case, unused_assignments)]216 fn unnamed_iter(217 &self,218 ctx: Context,219 tailstrict: bool,220 handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,221 ) -> Result<()> {222 let mut i = 0usize;223 let ($($gen,)*) = self;224 $(225 handler(i, $gen.evaluate_arg(ctx.clone(), tailstrict)?)?;226 i+=1;227 )*228 Ok(())229 }230 fn named_iter(231 &self,232 _ctx: Context,233 _tailstrict: bool,234 _handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,235 ) -> Result<()> {236 Ok(())237 }238 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}239240 fn is_empty(&self) -> bool {241 242 false243 }244 }245 impl<$($gen: ArgLike,)*> OptionalContext for ($($gen,)*) where $($gen: OptionalContext),* {}246 };247 ($count:expr; $($cur:ident)* @ $c:ident $($rest:ident)*) => {248 impl_args_like!($count; $($cur)*);249 impl_args_like!($count + 1usize; $($cur)* $c @ $($rest)*);250 };251 ($count:expr; $($cur:ident)* @) => {252 impl_args_like!($count; $($cur)*);253 }254}255impl_args_like! {256 257 1usize; A @ B C D E F G H I J K L258}259260impl ArgsLike for () {261 fn unnamed_len(&self) -> usize {262 0263 }264265 fn unnamed_iter(266 &self,267 _ctx: Context,268 _tailstrict: bool,269 _handler: &mut dyn FnMut(usize, Thunk<Val>) -> Result<()>,270 ) -> Result<()> {271 Ok(())272 }273274 fn named_iter(275 &self,276 _ctx: Context,277 _tailstrict: bool,278 _handler: &mut dyn FnMut(&IStr, Thunk<Val>) -> Result<()>,279 ) -> Result<()> {280 Ok(())281 }282283 fn named_names(&self, _handler: &mut dyn FnMut(&IStr)) {}284 fn is_empty(&self) -> bool {285 true286 }287}288impl OptionalContext for () {}