difftreelog
refactor split TypedObj derives
in: master
5 files changed
crates/jrsonnet-evaluator/src/arr/spec.rsdiffbeforeafterboth1use std::rc::Rc;2use std::{any::Any, cell::RefCell, fmt::Debug, mem::replace};34use jrsonnet_gcmodule::{Cc, Trace};5use jrsonnet_interner::{IBytes, IStr};6use jrsonnet_parser::{Expr, Spanned};78use super::ArrValue;9use crate::function::NativeFn;10use crate::{11 error::ErrorKind::InfiniteRecursionDetected,12 evaluate,13 typed::{IntoUntyped, Typed},14 val::ThunkValue,15 Context, Error, ObjValue, Result, Thunk, Val,16};1718pub trait ArrayLike: Any + Trace + Debug {19 fn len(&self) -> usize;20 fn is_empty(&self) -> bool {21 self.len() == 022 }23 fn get(&self, index: usize) -> Result<Option<Val>>;24 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>>;25 fn get_cheap(&self, index: usize) -> Option<Val>;2627 fn is_cheap(&self) -> bool;28}2930#[derive(Debug, Trace)]31pub struct SliceArray {32 pub(crate) inner: ArrValue,33 pub(crate) from: u32,34 pub(crate) to: u32,35 pub(crate) step: u32,36}3738impl SliceArray {39 fn map_idx(&self, index: usize) -> usize {40 self.from as usize + self.step as usize * index41 }42}43impl ArrayLike for SliceArray {44 fn len(&self) -> usize {45 (self.to - self.from).div_ceil(self.step) as usize46 }4748 fn get(&self, index: usize) -> Result<Option<Val>> {49 self.inner.get(self.map_idx(index))50 }5152 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {53 self.inner.get_lazy(self.map_idx(index))54 }5556 fn get_cheap(&self, index: usize) -> Option<Val> {57 self.inner.get_cheap(self.map_idx(index))58 }59 fn is_cheap(&self) -> bool {60 self.inner.is_cheap()61 }62}6364#[derive(Trace, Debug)]65pub struct CharArray(pub Vec<char>);66impl ArrayLike for CharArray {67 fn len(&self) -> usize {68 self.0.len()69 }7071 fn get(&self, index: usize) -> Result<Option<Val>> {72 Ok(self.get_cheap(index))73 }7475 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {76 self.get_cheap(index).map(Thunk::evaluated)77 }7879 fn get_cheap(&self, index: usize) -> Option<Val> {80 self.0.get(index).map(|v| Val::string(*v))81 }82 fn is_cheap(&self) -> bool {83 true84 }85}8687#[derive(Trace, Debug)]88pub struct BytesArray(pub IBytes);89impl ArrayLike for BytesArray {90 fn len(&self) -> usize {91 self.0.len()92 }9394 fn get(&self, index: usize) -> Result<Option<Val>> {95 Ok(self.get_cheap(index))96 }9798 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {99 self.get_cheap(index).map(Thunk::evaluated)100 }101102 fn get_cheap(&self, index: usize) -> Option<Val> {103 self.0.get(index).map(|v| Val::Num((*v).into()))104 }105 fn is_cheap(&self) -> bool {106 true107 }108}109110#[derive(Debug, Trace, Clone)]111enum ArrayThunk {112 Computed(Val),113 Errored(Error),114 Waiting,115 Pending,116}117118#[derive(Debug, Trace, Clone)]119pub struct ExprArray {120 ctx: Context,121 src: Rc<Vec<Spanned<Expr>>>,122 cached: Cc<RefCell<Vec<ArrayThunk>>>,123}124impl ExprArray {125 pub fn new(ctx: Context, src: Rc<Vec<Spanned<Expr>>>) -> Self {126 Self {127 ctx,128 cached: Cc::new(RefCell::new(vec![ArrayThunk::Waiting; src.len()])),129 src,130 }131 }132}133impl ArrayLike for ExprArray {134 fn len(&self) -> usize {135 self.cached.borrow().len()136 }137 fn get(&self, index: usize) -> Result<Option<Val>> {138 if index >= self.len() {139 return Ok(None);140 }141 match &self.cached.borrow()[index] {142 ArrayThunk::Computed(c) => return Ok(Some(c.clone())),143 ArrayThunk::Errored(e) => return Err(e.clone()),144 ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),145 ArrayThunk::Waiting => {}146 }147148 let ArrayThunk::Waiting =149 replace(&mut self.cached.borrow_mut()[index], ArrayThunk::Pending)150 else {151 unreachable!()152 };153154 let new_value = match evaluate(self.ctx.clone(), &self.src[index]) {155 Ok(v) => v,156 Err(e) => {157 self.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());158 return Err(e);159 }160 };161 self.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());162 Ok(Some(new_value))163 }164 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {165 #[derive(Trace)]166 struct ExprArrThunk {167 expr: ExprArray,168 index: usize,169 }170 impl ThunkValue for ExprArrThunk {171 type Output = Val;172173 fn get(&self) -> Result<Self::Output> {174 self.expr175 .get(self.index)176 .transpose()177 .expect("index checked")178 }179 }180181 if index >= self.len() {182 return None;183 }184 match &self.cached.borrow()[index] {185 ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),186 ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),187 ArrayThunk::Waiting | ArrayThunk::Pending => {}188 }189190 Some(Thunk::new(ExprArrThunk {191 expr: self.clone(),192 index,193 }))194 }195 fn get_cheap(&self, _index: usize) -> Option<Val> {196 None197 }198 fn is_cheap(&self) -> bool {199 false200 }201}202203#[derive(Trace, Debug)]204pub struct ExtendedArray {205 pub a: ArrValue,206 pub b: ArrValue,207 split: usize,208 len: usize,209}210impl ExtendedArray {211 pub fn new(a: ArrValue, b: ArrValue) -> Self {212 let a_len = a.len();213 let b_len = b.len();214 Self {215 a,216 b,217 split: a_len,218 len: a_len.checked_add(b_len).expect("too large array value"),219 }220 }221}222223struct WithExactSize<I>(I, usize);224impl<I, T> Iterator for WithExactSize<I>225where226 I: Iterator<Item = T>,227{228 type Item = T;229230 fn next(&mut self) -> Option<Self::Item> {231 self.0.next()232 }233 fn nth(&mut self, n: usize) -> Option<Self::Item> {234 self.0.nth(n)235 }236 fn size_hint(&self) -> (usize, Option<usize>) {237 (self.1, Some(self.1))238 }239}240impl<I> DoubleEndedIterator for WithExactSize<I>241where242 I: DoubleEndedIterator,243{244 fn next_back(&mut self) -> Option<Self::Item> {245 self.0.next_back()246 }247 fn nth_back(&mut self, n: usize) -> Option<Self::Item> {248 self.0.nth_back(n)249 }250}251impl<I> ExactSizeIterator for WithExactSize<I>252where253 I: Iterator,254{255 fn len(&self) -> usize {256 self.1257 }258}259impl ArrayLike for ExtendedArray {260 fn get(&self, index: usize) -> Result<Option<Val>> {261 if self.split > index {262 self.a.get(index)263 } else {264 self.b.get(index - self.split)265 }266 }267 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {268 if self.split > index {269 self.a.get_lazy(index)270 } else {271 self.b.get_lazy(index - self.split)272 }273 }274275 fn len(&self) -> usize {276 self.len277 }278279 fn get_cheap(&self, index: usize) -> Option<Val> {280 if self.split > index {281 self.a.get_cheap(index)282 } else {283 self.b.get_cheap(index - self.split)284 }285 }286 fn is_cheap(&self) -> bool {287 self.a.is_cheap() && self.b.is_cheap()288 }289}290291#[derive(Trace, Debug)]292pub struct LazyArray(pub Vec<Thunk<Val>>);293impl ArrayLike for LazyArray {294 fn len(&self) -> usize {295 self.0.len()296 }297 fn get(&self, index: usize) -> Result<Option<Val>> {298 let Some(v) = self.0.get(index) else {299 return Ok(None);300 };301 v.evaluate().map(Some)302 }303 fn get_cheap(&self, _index: usize) -> Option<Val> {304 None305 }306 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {307 self.0.get(index).cloned()308 }309 fn is_cheap(&self) -> bool {310 false311 }312}313314#[derive(Trace, Debug)]315pub struct EagerArray(pub Vec<Val>);316impl ArrayLike for EagerArray {317 fn len(&self) -> usize {318 self.0.len()319 }320321 fn get(&self, index: usize) -> Result<Option<Val>> {322 Ok(self.0.get(index).cloned())323 }324325 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {326 self.0.get(index).cloned().map(Thunk::evaluated)327 }328329 fn get_cheap(&self, index: usize) -> Option<Val> {330 self.0.get(index).cloned()331 }332 fn is_cheap(&self) -> bool {333 true334 }335}336337/// Inclusive range type338#[derive(Debug, Trace, PartialEq, Eq)]339pub struct RangeArray {340 start: i32,341 end: i32,342}343impl RangeArray {344 pub fn empty() -> Self {345 Self::new_exclusive(0, 0)346 }347 pub fn new_exclusive(start: i32, end: i32) -> Self {348 end.checked_sub(1)349 .map_or_else(Self::empty, |end| Self { start, end })350 }351 pub fn new_inclusive(start: i32, end: i32) -> Self {352 Self { start, end }353 }354 fn range(&self) -> impl ExactSizeIterator<Item = i32> + DoubleEndedIterator {355 WithExactSize(356 self.start..=self.end,357 (self.end as usize)358 .wrapping_sub(self.start as usize)359 .wrapping_add(1),360 )361 }362}363364impl ArrayLike for RangeArray {365 fn len(&self) -> usize {366 self.range().len()367 }368 fn is_empty(&self) -> bool {369 self.range().len() == 0370 }371372 fn get(&self, index: usize) -> Result<Option<Val>> {373 Ok(self.get_cheap(index))374 }375376 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {377 self.get_cheap(index).map(Thunk::evaluated)378 }379380 fn get_cheap(&self, index: usize) -> Option<Val> {381 self.range().nth(index).map(|i| Val::Num(i.into()))382 }383 fn is_cheap(&self) -> bool {384 true385 }386}387388#[derive(Debug, Trace)]389pub struct ReverseArray(pub ArrValue);390impl ArrayLike for ReverseArray {391 fn len(&self) -> usize {392 self.0.len()393 }394395 fn get(&self, index: usize) -> Result<Option<Val>> {396 self.0.get(self.0.len() - index - 1)397 }398399 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {400 self.0.get_lazy(self.0.len() - index - 1)401 }402403 fn get_cheap(&self, index: usize) -> Option<Val> {404 self.0.get_cheap(self.0.len() - index - 1)405 }406 fn is_cheap(&self) -> bool {407 self.0.is_cheap()408 }409}410411#[derive(Trace, Clone, Debug)]412pub enum ArrayMapper {413 Plain(NativeFn!((Val) -> Val)),414 WithIndex(NativeFn!((u32, Val) -> Val)),415}416417#[derive(Trace, Debug, Clone)]418pub struct MappedArray {419 inner: ArrValue,420 cached: Cc<RefCell<Vec<ArrayThunk>>>,421 mapper: ArrayMapper,422}423impl MappedArray {424 pub fn new(inner: ArrValue, mapper: ArrayMapper) -> Self {425 let len = inner.len();426 Self {427 inner,428 cached: Cc::new(RefCell::new(vec![ArrayThunk::Waiting; len])),429 mapper,430 }431 }432 fn evaluate(&self, index: usize, value: Val) -> Result<Val> {433 match &self.mapper {434 ArrayMapper::Plain(f) => f.call(value),435 ArrayMapper::WithIndex(f) => f.call(index as u32, value),436 }437 }438}439impl ArrayLike for MappedArray {440 fn len(&self) -> usize {441 self.cached.borrow().len()442 }443444 fn get(&self, index: usize) -> Result<Option<Val>> {445 if index >= self.len() {446 return Ok(None);447 }448 match &self.cached.borrow()[index] {449 ArrayThunk::Computed(c) => return Ok(Some(c.clone())),450 ArrayThunk::Errored(e) => return Err(e.clone()),451 ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),452 ArrayThunk::Waiting => {}453 }454455 let ArrayThunk::Waiting =456 replace(&mut self.cached.borrow_mut()[index], ArrayThunk::Pending)457 else {458 unreachable!()459 };460461 let val = self462 .inner463 .get(index)464 .transpose()465 .expect("index checked")466 .and_then(|r| self.evaluate(index, r));467468 let new_value = match val {469 Ok(v) => v,470 Err(e) => {471 self.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());472 return Err(e);473 }474 };475 self.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());476 Ok(Some(new_value))477 }478 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {479 #[derive(Trace)]480 struct MappedArrayThunk {481 arr: MappedArray,482 index: usize,483 }484 impl ThunkValue for MappedArrayThunk {485 type Output = Val;486487 fn get(&self) -> Result<Self::Output> {488 self.arr.get(self.index).transpose().expect("index checked")489 }490 }491492 if index >= self.len() {493 return None;494 }495 match &self.cached.borrow()[index] {496 ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),497 ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),498 ArrayThunk::Waiting | ArrayThunk::Pending => {}499 }500501 Some(Thunk::new(MappedArrayThunk {502 arr: self.clone(),503 index,504 }))505 }506507 fn get_cheap(&self, _index: usize) -> Option<Val> {508 None509 }510 fn is_cheap(&self) -> bool {511 false512 }513}514515#[derive(Trace, Debug)]516pub struct RepeatedArray {517 data: ArrValue,518 repeats: usize,519 total_len: usize,520}521impl RepeatedArray {522 pub fn new(data: ArrValue, repeats: usize) -> Option<Self> {523 let total_len = data.len().checked_mul(repeats)?;524 Some(Self {525 data,526 repeats,527 total_len,528 })529 }530}531532impl ArrayLike for RepeatedArray {533 fn len(&self) -> usize {534 self.total_len535 }536537 fn get(&self, index: usize) -> Result<Option<Val>> {538 if index > self.total_len {539 return Ok(None);540 }541 self.data.get(index % self.data.len())542 }543544 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {545 if index > self.total_len {546 return None;547 }548 self.data.get_lazy(index % self.data.len())549 }550551 fn get_cheap(&self, index: usize) -> Option<Val> {552 if index > self.total_len {553 return None;554 }555 self.data.get_cheap(index % self.data.len())556 }557 fn is_cheap(&self) -> bool {558 self.data.is_cheap()559 }560}561562#[derive(Trace, Debug)]563pub struct PickObjectValues {564 obj: ObjValue,565 keys: Vec<IStr>,566}567568impl PickObjectValues {569 pub fn new(obj: ObjValue, keys: Vec<IStr>) -> Self {570 Self { obj, keys }571 }572}573574impl ArrayLike for PickObjectValues {575 fn len(&self) -> usize {576 self.keys.len()577 }578579 fn get(&self, index: usize) -> Result<Option<Val>> {580 let Some(key) = self.keys.get(index) else {581 return Ok(None);582 };583 Ok(Some(self.obj.get_or_bail(key.clone())?))584 }585586 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {587 let key = self.keys.get(index)?;588 Some(self.obj.get_lazy_or_bail(key.clone()))589 }590591 fn get_cheap(&self, _index: usize) -> Option<Val> {592 None593 }594595 fn is_cheap(&self) -> bool {596 false597 }598}599600#[derive(Trace, Debug)]601pub struct PickObjectKeyValues {602 obj: ObjValue,603 keys: Vec<IStr>,604}605606impl PickObjectKeyValues {607 pub fn new(obj: ObjValue, keys: Vec<IStr>) -> Self {608 Self { obj, keys }609 }610}611612#[derive(Typed)]613pub struct KeyValue {614 key: IStr,615 value: Thunk<Val>,616}617618impl ArrayLike for PickObjectKeyValues {619 fn len(&self) -> usize {620 self.keys.len()621 }622623 fn get(&self, index: usize) -> Result<Option<Val>> {624 let Some(key) = self.keys.get(index) else {625 return Ok(None);626 };627 Ok(Some(628 KeyValue::into_untyped(KeyValue {629 key: key.clone(),630 value: Thunk::evaluated(self.obj.get_or_bail(key.clone())?),631 })632 .expect("convertible"),633 ))634 }635636 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {637 let key = self.keys.get(index)?;638 // Nothing can fail in the key part, yet value is still639 // lazy-evaluated640 Some(Thunk::evaluated(641 KeyValue::into_untyped(KeyValue {642 key: key.clone(),643 value: self.obj.get_lazy_or_bail(key.clone()),644 })645 .expect("convertible"),646 ))647 }648649 fn get_cheap(&self, _index: usize) -> Option<Val> {650 None651 }652653 fn is_cheap(&self) -> bool {654 false655 }656}1use std::rc::Rc;2use std::{any::Any, cell::RefCell, fmt::Debug, mem::replace};34use jrsonnet_gcmodule::{Cc, Trace};5use jrsonnet_interner::{IBytes, IStr};6use jrsonnet_parser::{Expr, Spanned};78use super::ArrValue;9use crate::function::NativeFn;10use crate::{11 error::ErrorKind::InfiniteRecursionDetected,12 evaluate,13 typed::{IntoUntyped, Typed},14 val::ThunkValue,15 Context, Error, ObjValue, Result, Thunk, Val,16};1718pub trait ArrayLike: Any + Trace + Debug {19 fn len(&self) -> usize;20 fn is_empty(&self) -> bool {21 self.len() == 022 }23 fn get(&self, index: usize) -> Result<Option<Val>>;24 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>>;25 fn get_cheap(&self, index: usize) -> Option<Val>;2627 fn is_cheap(&self) -> bool;28}2930#[derive(Debug, Trace)]31pub struct SliceArray {32 pub(crate) inner: ArrValue,33 pub(crate) from: u32,34 pub(crate) to: u32,35 pub(crate) step: u32,36}3738impl SliceArray {39 fn map_idx(&self, index: usize) -> usize {40 self.from as usize + self.step as usize * index41 }42}43impl ArrayLike for SliceArray {44 fn len(&self) -> usize {45 (self.to - self.from).div_ceil(self.step) as usize46 }4748 fn get(&self, index: usize) -> Result<Option<Val>> {49 self.inner.get(self.map_idx(index))50 }5152 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {53 self.inner.get_lazy(self.map_idx(index))54 }5556 fn get_cheap(&self, index: usize) -> Option<Val> {57 self.inner.get_cheap(self.map_idx(index))58 }59 fn is_cheap(&self) -> bool {60 self.inner.is_cheap()61 }62}6364#[derive(Trace, Debug)]65pub struct CharArray(pub Vec<char>);66impl ArrayLike for CharArray {67 fn len(&self) -> usize {68 self.0.len()69 }7071 fn get(&self, index: usize) -> Result<Option<Val>> {72 Ok(self.get_cheap(index))73 }7475 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {76 self.get_cheap(index).map(Thunk::evaluated)77 }7879 fn get_cheap(&self, index: usize) -> Option<Val> {80 self.0.get(index).map(|v| Val::string(*v))81 }82 fn is_cheap(&self) -> bool {83 true84 }85}8687#[derive(Trace, Debug)]88pub struct BytesArray(pub IBytes);89impl ArrayLike for BytesArray {90 fn len(&self) -> usize {91 self.0.len()92 }9394 fn get(&self, index: usize) -> Result<Option<Val>> {95 Ok(self.get_cheap(index))96 }9798 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {99 self.get_cheap(index).map(Thunk::evaluated)100 }101102 fn get_cheap(&self, index: usize) -> Option<Val> {103 self.0.get(index).map(|v| Val::Num((*v).into()))104 }105 fn is_cheap(&self) -> bool {106 true107 }108}109110#[derive(Debug, Trace, Clone)]111enum ArrayThunk {112 Computed(Val),113 Errored(Error),114 Waiting,115 Pending,116}117118#[derive(Debug, Trace, Clone)]119pub struct ExprArray {120 ctx: Context,121 src: Rc<Vec<Spanned<Expr>>>,122 cached: Cc<RefCell<Vec<ArrayThunk>>>,123}124impl ExprArray {125 pub fn new(ctx: Context, src: Rc<Vec<Spanned<Expr>>>) -> Self {126 Self {127 ctx,128 cached: Cc::new(RefCell::new(vec![ArrayThunk::Waiting; src.len()])),129 src,130 }131 }132}133impl ArrayLike for ExprArray {134 fn len(&self) -> usize {135 self.cached.borrow().len()136 }137 fn get(&self, index: usize) -> Result<Option<Val>> {138 if index >= self.len() {139 return Ok(None);140 }141 match &self.cached.borrow()[index] {142 ArrayThunk::Computed(c) => return Ok(Some(c.clone())),143 ArrayThunk::Errored(e) => return Err(e.clone()),144 ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),145 ArrayThunk::Waiting => {}146 }147148 let ArrayThunk::Waiting =149 replace(&mut self.cached.borrow_mut()[index], ArrayThunk::Pending)150 else {151 unreachable!()152 };153154 let new_value = match evaluate(self.ctx.clone(), &self.src[index]) {155 Ok(v) => v,156 Err(e) => {157 self.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());158 return Err(e);159 }160 };161 self.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());162 Ok(Some(new_value))163 }164 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {165 #[derive(Trace)]166 struct ExprArrThunk {167 expr: ExprArray,168 index: usize,169 }170 impl ThunkValue for ExprArrThunk {171 type Output = Val;172173 fn get(&self) -> Result<Self::Output> {174 self.expr175 .get(self.index)176 .transpose()177 .expect("index checked")178 }179 }180181 if index >= self.len() {182 return None;183 }184 match &self.cached.borrow()[index] {185 ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),186 ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),187 ArrayThunk::Waiting | ArrayThunk::Pending => {}188 }189190 Some(Thunk::new(ExprArrThunk {191 expr: self.clone(),192 index,193 }))194 }195 fn get_cheap(&self, _index: usize) -> Option<Val> {196 None197 }198 fn is_cheap(&self) -> bool {199 false200 }201}202203#[derive(Trace, Debug)]204pub struct ExtendedArray {205 pub a: ArrValue,206 pub b: ArrValue,207 split: usize,208 len: usize,209}210impl ExtendedArray {211 pub fn new(a: ArrValue, b: ArrValue) -> Self {212 let a_len = a.len();213 let b_len = b.len();214 Self {215 a,216 b,217 split: a_len,218 len: a_len.checked_add(b_len).expect("too large array value"),219 }220 }221}222223struct WithExactSize<I>(I, usize);224impl<I, T> Iterator for WithExactSize<I>225where226 I: Iterator<Item = T>,227{228 type Item = T;229230 fn next(&mut self) -> Option<Self::Item> {231 self.0.next()232 }233 fn nth(&mut self, n: usize) -> Option<Self::Item> {234 self.0.nth(n)235 }236 fn size_hint(&self) -> (usize, Option<usize>) {237 (self.1, Some(self.1))238 }239}240impl<I> DoubleEndedIterator for WithExactSize<I>241where242 I: DoubleEndedIterator,243{244 fn next_back(&mut self) -> Option<Self::Item> {245 self.0.next_back()246 }247 fn nth_back(&mut self, n: usize) -> Option<Self::Item> {248 self.0.nth_back(n)249 }250}251impl<I> ExactSizeIterator for WithExactSize<I>252where253 I: Iterator,254{255 fn len(&self) -> usize {256 self.1257 }258}259impl ArrayLike for ExtendedArray {260 fn get(&self, index: usize) -> Result<Option<Val>> {261 if self.split > index {262 self.a.get(index)263 } else {264 self.b.get(index - self.split)265 }266 }267 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {268 if self.split > index {269 self.a.get_lazy(index)270 } else {271 self.b.get_lazy(index - self.split)272 }273 }274275 fn len(&self) -> usize {276 self.len277 }278279 fn get_cheap(&self, index: usize) -> Option<Val> {280 if self.split > index {281 self.a.get_cheap(index)282 } else {283 self.b.get_cheap(index - self.split)284 }285 }286 fn is_cheap(&self) -> bool {287 self.a.is_cheap() && self.b.is_cheap()288 }289}290291#[derive(Trace, Debug)]292pub struct LazyArray(pub Vec<Thunk<Val>>);293impl ArrayLike for LazyArray {294 fn len(&self) -> usize {295 self.0.len()296 }297 fn get(&self, index: usize) -> Result<Option<Val>> {298 let Some(v) = self.0.get(index) else {299 return Ok(None);300 };301 v.evaluate().map(Some)302 }303 fn get_cheap(&self, _index: usize) -> Option<Val> {304 None305 }306 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {307 self.0.get(index).cloned()308 }309 fn is_cheap(&self) -> bool {310 false311 }312}313314#[derive(Trace, Debug)]315pub struct EagerArray(pub Vec<Val>);316impl ArrayLike for EagerArray {317 fn len(&self) -> usize {318 self.0.len()319 }320321 fn get(&self, index: usize) -> Result<Option<Val>> {322 Ok(self.0.get(index).cloned())323 }324325 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {326 self.0.get(index).cloned().map(Thunk::evaluated)327 }328329 fn get_cheap(&self, index: usize) -> Option<Val> {330 self.0.get(index).cloned()331 }332 fn is_cheap(&self) -> bool {333 true334 }335}336337/// Inclusive range type338#[derive(Debug, Trace, PartialEq, Eq)]339pub struct RangeArray {340 start: i32,341 end: i32,342}343impl RangeArray {344 pub fn empty() -> Self {345 Self::new_exclusive(0, 0)346 }347 pub fn new_exclusive(start: i32, end: i32) -> Self {348 end.checked_sub(1)349 .map_or_else(Self::empty, |end| Self { start, end })350 }351 pub fn new_inclusive(start: i32, end: i32) -> Self {352 Self { start, end }353 }354 fn range(&self) -> impl ExactSizeIterator<Item = i32> + DoubleEndedIterator {355 WithExactSize(356 self.start..=self.end,357 (self.end as usize)358 .wrapping_sub(self.start as usize)359 .wrapping_add(1),360 )361 }362}363364impl ArrayLike for RangeArray {365 fn len(&self) -> usize {366 self.range().len()367 }368 fn is_empty(&self) -> bool {369 self.range().len() == 0370 }371372 fn get(&self, index: usize) -> Result<Option<Val>> {373 Ok(self.get_cheap(index))374 }375376 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {377 self.get_cheap(index).map(Thunk::evaluated)378 }379380 fn get_cheap(&self, index: usize) -> Option<Val> {381 self.range().nth(index).map(|i| Val::Num(i.into()))382 }383 fn is_cheap(&self) -> bool {384 true385 }386}387388#[derive(Debug, Trace)]389pub struct ReverseArray(pub ArrValue);390impl ArrayLike for ReverseArray {391 fn len(&self) -> usize {392 self.0.len()393 }394395 fn get(&self, index: usize) -> Result<Option<Val>> {396 self.0.get(self.0.len() - index - 1)397 }398399 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {400 self.0.get_lazy(self.0.len() - index - 1)401 }402403 fn get_cheap(&self, index: usize) -> Option<Val> {404 self.0.get_cheap(self.0.len() - index - 1)405 }406 fn is_cheap(&self) -> bool {407 self.0.is_cheap()408 }409}410411#[derive(Trace, Clone, Debug)]412pub enum ArrayMapper {413 Plain(NativeFn!((Val) -> Val)),414 WithIndex(NativeFn!((u32, Val) -> Val)),415}416417#[derive(Trace, Debug, Clone)]418pub struct MappedArray {419 inner: ArrValue,420 cached: Cc<RefCell<Vec<ArrayThunk>>>,421 mapper: ArrayMapper,422}423impl MappedArray {424 pub fn new(inner: ArrValue, mapper: ArrayMapper) -> Self {425 let len = inner.len();426 Self {427 inner,428 cached: Cc::new(RefCell::new(vec![ArrayThunk::Waiting; len])),429 mapper,430 }431 }432 fn evaluate(&self, index: usize, value: Val) -> Result<Val> {433 match &self.mapper {434 ArrayMapper::Plain(f) => f.call(value),435 ArrayMapper::WithIndex(f) => f.call(index as u32, value),436 }437 }438}439impl ArrayLike for MappedArray {440 fn len(&self) -> usize {441 self.cached.borrow().len()442 }443444 fn get(&self, index: usize) -> Result<Option<Val>> {445 if index >= self.len() {446 return Ok(None);447 }448 match &self.cached.borrow()[index] {449 ArrayThunk::Computed(c) => return Ok(Some(c.clone())),450 ArrayThunk::Errored(e) => return Err(e.clone()),451 ArrayThunk::Pending => return Err(InfiniteRecursionDetected.into()),452 ArrayThunk::Waiting => {}453 }454455 let ArrayThunk::Waiting =456 replace(&mut self.cached.borrow_mut()[index], ArrayThunk::Pending)457 else {458 unreachable!()459 };460461 let val = self462 .inner463 .get(index)464 .transpose()465 .expect("index checked")466 .and_then(|r| self.evaluate(index, r));467468 let new_value = match val {469 Ok(v) => v,470 Err(e) => {471 self.cached.borrow_mut()[index] = ArrayThunk::Errored(e.clone());472 return Err(e);473 }474 };475 self.cached.borrow_mut()[index] = ArrayThunk::Computed(new_value.clone());476 Ok(Some(new_value))477 }478 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {479 #[derive(Trace)]480 struct MappedArrayThunk {481 arr: MappedArray,482 index: usize,483 }484 impl ThunkValue for MappedArrayThunk {485 type Output = Val;486487 fn get(&self) -> Result<Self::Output> {488 self.arr.get(self.index).transpose().expect("index checked")489 }490 }491492 if index >= self.len() {493 return None;494 }495 match &self.cached.borrow()[index] {496 ArrayThunk::Computed(c) => return Some(Thunk::evaluated(c.clone())),497 ArrayThunk::Errored(e) => return Some(Thunk::errored(e.clone())),498 ArrayThunk::Waiting | ArrayThunk::Pending => {}499 }500501 Some(Thunk::new(MappedArrayThunk {502 arr: self.clone(),503 index,504 }))505 }506507 fn get_cheap(&self, _index: usize) -> Option<Val> {508 None509 }510 fn is_cheap(&self) -> bool {511 false512 }513}514515#[derive(Trace, Debug)]516pub struct RepeatedArray {517 data: ArrValue,518 repeats: usize,519 total_len: usize,520}521impl RepeatedArray {522 pub fn new(data: ArrValue, repeats: usize) -> Option<Self> {523 let total_len = data.len().checked_mul(repeats)?;524 Some(Self {525 data,526 repeats,527 total_len,528 })529 }530}531532impl ArrayLike for RepeatedArray {533 fn len(&self) -> usize {534 self.total_len535 }536537 fn get(&self, index: usize) -> Result<Option<Val>> {538 if index > self.total_len {539 return Ok(None);540 }541 self.data.get(index % self.data.len())542 }543544 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {545 if index > self.total_len {546 return None;547 }548 self.data.get_lazy(index % self.data.len())549 }550551 fn get_cheap(&self, index: usize) -> Option<Val> {552 if index > self.total_len {553 return None;554 }555 self.data.get_cheap(index % self.data.len())556 }557 fn is_cheap(&self) -> bool {558 self.data.is_cheap()559 }560}561562#[derive(Trace, Debug)]563pub struct PickObjectValues {564 obj: ObjValue,565 keys: Vec<IStr>,566}567568impl PickObjectValues {569 pub fn new(obj: ObjValue, keys: Vec<IStr>) -> Self {570 Self { obj, keys }571 }572}573574impl ArrayLike for PickObjectValues {575 fn len(&self) -> usize {576 self.keys.len()577 }578579 fn get(&self, index: usize) -> Result<Option<Val>> {580 let Some(key) = self.keys.get(index) else {581 return Ok(None);582 };583 Ok(Some(self.obj.get_or_bail(key.clone())?))584 }585586 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {587 let key = self.keys.get(index)?;588 Some(self.obj.get_lazy_or_bail(key.clone()))589 }590591 fn get_cheap(&self, _index: usize) -> Option<Val> {592 None593 }594595 fn is_cheap(&self) -> bool {596 false597 }598}599600#[derive(Trace, Debug)]601pub struct PickObjectKeyValues {602 obj: ObjValue,603 keys: Vec<IStr>,604}605606impl PickObjectKeyValues {607 pub fn new(obj: ObjValue, keys: Vec<IStr>) -> Self {608 Self { obj, keys }609 }610}611612#[derive(Typed, IntoUntyped)]613pub struct KeyValue {614 key: IStr,615 value: Thunk<Val>,616}617618impl ArrayLike for PickObjectKeyValues {619 fn len(&self) -> usize {620 self.keys.len()621 }622623 fn get(&self, index: usize) -> Result<Option<Val>> {624 let Some(key) = self.keys.get(index) else {625 return Ok(None);626 };627 Ok(Some(628 KeyValue::into_untyped(KeyValue {629 key: key.clone(),630 value: Thunk::evaluated(self.obj.get_or_bail(key.clone())?),631 })632 .expect("convertible"),633 ))634 }635636 fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {637 let key = self.keys.get(index)?;638 // Nothing can fail in the key part, yet value is still639 // lazy-evaluated640 Some(Thunk::evaluated(641 KeyValue::into_untyped(KeyValue {642 key: key.clone(),643 value: self.obj.get_lazy_or_bail(key.clone()),644 })645 .expect("convertible"),646 ))647 }648649 fn get_cheap(&self, _index: usize) -> Option<Val> {650 None651 }652653 fn is_cheap(&self) -> bool {654 false655 }656}crates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/typed/conversions.rs
+++ b/crates/jrsonnet-evaluator/src/typed/conversions.rs
@@ -2,7 +2,6 @@
use jrsonnet_gcmodule::Trace;
use jrsonnet_interner::{IBytes, IStr};
-pub use jrsonnet_macros::Typed;
use jrsonnet_types::{ComplexValType, ValType};
use crate::{
@@ -14,6 +13,19 @@
ObjValue, ObjValueBuilder, Result, ResultExt, Thunk, Val,
};
+#[doc(hidden)]
+pub mod __typed_macro_prelude {
+ pub use ::jrsonnet_evaluator::{
+ error::{ErrorKind, Result as JrResult},
+ typed::{
+ CheckType, ComplexValType, FromUntyped, IntoUntyped, ParseTypedObj, SerializeTypedObj,
+ Typed,
+ },
+ IStr, ObjValue, ObjValueBuilder, State, Val,
+ };
+}
+pub use jrsonnet_macros::{FromUntyped, IntoUntyped, Typed};
+
#[derive(Trace)]
struct ThunkFromUntyped<K: Trace>(PhantomData<fn() -> K>);
impl<K> ThunkMapper<Val> for ThunkFromUntyped<K>
@@ -49,9 +61,11 @@
}
}
-pub trait TypedObj: Typed {
- fn serialize(self, out: &mut ObjValueBuilder) -> Result<()>;
+pub trait ParseTypedObj: Typed {
fn parse(obj: &ObjValue) -> Result<Self>;
+}
+pub trait SerializeTypedObj: Typed {
+ fn serialize(self, out: &mut ObjValueBuilder) -> Result<()>;
fn into_object(self) -> Result<ObjValue> {
let mut builder = ObjValueBuilder::new();
self.serialize(&mut builder)?;
crates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-macros/src/lib.rs
+++ b/crates/jrsonnet-macros/src/lib.rs
@@ -13,7 +13,7 @@
LitStr, Meta, Pat, Path, PathArguments, Result, ReturnType, Token, Type,
};
-use self::typed::derive_typed_inner;
+use self::typed::{derive_from_untyped_inner, derive_into_untyped_inner, derive_typed_inner};
mod names;
mod typed;
@@ -451,6 +451,24 @@
Err(e) => e.to_compile_error().into(),
}
}
+#[proc_macro_derive(IntoUntyped, attributes(typed))]
+pub fn derive_into_untyped(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
+ let input = parse_macro_input!(item as DeriveInput);
+
+ match derive_into_untyped_inner(input) {
+ Ok(v) => v.into(),
+ Err(e) => e.to_compile_error().into(),
+ }
+}
+#[proc_macro_derive(FromUntyped, attributes(typed))]
+pub fn derive_from_untyped(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
+ let input = parse_macro_input!(item as DeriveInput);
+
+ match derive_from_untyped_inner(input) {
+ Ok(v) => v.into(),
+ Err(e) => e.to_compile_error().into(),
+ }
+}
struct FormatInput {
formatting: LitStr,
crates/jrsonnet-macros/src/typed.rsdiffbeforeafterboth--- a/crates/jrsonnet-macros/src/typed.rs
+++ b/crates/jrsonnet-macros/src/typed.rs
@@ -301,26 +301,49 @@
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
- let capacity = fields.len();
+ let fields = fields
+ .iter()
+ .filter_map(TypedField::expand_field)
+ .collect::<Vec<_>>();
+ Ok(quote! {
+ const _: () = {
+ use ::jrsonnet_evaluator::typed::__typed_macro_prelude::*;
- let typed = {
- let fields = fields
- .iter()
- .filter_map(TypedField::expand_field)
- .collect::<Vec<_>>();
- quote! {
impl #impl_generics Typed for #ident #ty_generics #where_clause {
const TYPE: &'static ComplexValType = &ComplexValType::ObjectRef(&[
#(#fields,)*
]);
}
+ };
+ })
+}
+pub fn derive_into_untyped_inner(input: DeriveInput) -> Result<TokenStream> {
+ let syn::Data::Struct(data) = &input.data else {
+ return Err(Error::new(input.span(), "only structs supported"));
+ };
+
+ let ident = &input.ident;
+ let fields = data
+ .fields
+ .iter()
+ .map(TypedField::parse)
+ .collect::<Result<Vec<_>>>()?;
+
+ let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
+
+ let capacity = fields.len();
+
+ let mut names = Names::default();
+
+ let fields_serialize = fields
+ .iter()
+ .map(|f| f.expand_serialize(&mut names))
+ .collect::<Vec<_>>();
- impl #impl_generics FromUntyped for #ident #ty_generics #where_clause {
- fn from_untyped(value: Val) -> JrResult<Self> {
- let obj = value.as_obj().expect("shape is correct");
- Self::parse(&obj)
- }
- }
+ let names_expanded = names.expand();
+ Ok(quote! {
+ const _: () = {
+ use ::jrsonnet_evaluator::typed::__typed_macro_prelude::*;
impl #impl_generics IntoUntyped for #ident #ty_generics #where_clause {
fn into_untyped(value: Self) -> JrResult<Val> {
@@ -329,42 +352,57 @@
Ok(Val::Obj(out.build()))
}
}
- }
+
+ #names_expanded
+
+ impl #impl_generics SerializeTypedObj for #ident #ty_generics #where_clause {
+ fn serialize(self, out: &mut ObjValueBuilder) -> JrResult<()> {
+ NAMES.with(|__names| {
+ #(#fields_serialize)*
+
+ Ok(())
+ })
+ }
+ }
+ };
+ })
+}
+pub fn derive_from_untyped_inner(input: DeriveInput) -> Result<TokenStream> {
+ let syn::Data::Struct(data) = &input.data else {
+ return Err(Error::new(input.span(), "only structs supported"));
};
+ let ident = &input.ident;
+ let fields = data
+ .fields
+ .iter()
+ .map(TypedField::parse)
+ .collect::<Result<Vec<_>>>()?;
+
+ let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
+
let mut names = Names::default();
let fields_parse = fields
.iter()
.map(|f| f.expand_parse(&mut names))
- .collect::<Vec<_>>();
- let fields_serialize = fields
- .iter()
- .map(|f| f.expand_serialize(&mut names))
.collect::<Vec<_>>();
let names_expanded = names.expand();
Ok(quote! {
const _: () = {
- use ::jrsonnet_evaluator::{
- typed::{ComplexValType, Typed, IntoUntyped, FromUntyped, TypedObj, CheckType},
- Val, State,
- error::{ErrorKind, Result as JrResult},
- ObjValueBuilder, ObjValue, IStr,
- };
+ use ::jrsonnet_evaluator::typed::__typed_macro_prelude::*;
- #typed
+ impl #impl_generics FromUntyped for #ident #ty_generics #where_clause {
+ fn from_untyped(value: Val) -> JrResult<Self> {
+ let obj = value.as_obj().expect("shape is correct");
+ Self::parse(&obj)
+ }
+ }
#names_expanded
- impl #impl_generics TypedObj for #ident #ty_generics #where_clause {
- fn serialize(self, out: &mut ObjValueBuilder) -> JrResult<()> {
- NAMES.with(|__names| {
- #(#fields_serialize)*
-
- Ok(())
- })
- }
+ impl #impl_generics ParseTypedObj for #ident #ty_generics #where_clause {
fn parse(obj: &ObjValue) -> JrResult<Self> {
NAMES.with(|__names| Ok(Self {
#(#fields_parse)*
crates/jrsonnet-stdlib/src/manifest/ini.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/manifest/ini.rs
+++ b/crates/jrsonnet-stdlib/src/manifest/ini.rs
@@ -82,7 +82,7 @@
Ok(())
}
-#[derive(Typed)]
+#[derive(Typed, FromUntyped)]
struct IniObj {
main: Option<ObjValue>,
// TODO: Preserve section order?