1use std::{2 any::Any,3 fmt::{self},4 num::NonZeroU32,5 ops::{Bound, RangeBounds},6 rc::Rc,7};89use jrsonnet_gcmodule::{Cc, cc_dyn};1011use crate::{Context, Result, Thunk, Val, analyze::LExpr, function::NativeFn, typed::IntoUntyped};1213mod spec;14pub use spec::{ArrayLike, *};1516cc_dyn!(17 #[doc = "Represents a Jsonnet array value."]18 #[derive(Clone)]19 ArrValue,20 ArrayLike,21 pub fn new() {...}22);23impl fmt::Debug for ArrValue {24 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {25 self.0.fmt(f)26 }27}2829pub trait ArrayLikeIter<T>: Iterator<Item = T> + DoubleEndedIterator + ExactSizeIterator {}30impl<I, T> ArrayLikeIter<T> for I where31 I: Iterator<Item = T> + DoubleEndedIterator + ExactSizeIterator32{33}3435impl ArrValue {36 pub fn empty() -> Self {37 Self::new(())38 }3940 pub fn expr(ctx: Context, exprs: Rc<Vec<LExpr>>) -> Self {41 Self::new(ExprArray::new(ctx, exprs))42 }4344 pub fn repeated(data: Self, repeats: u32) -> Option<Self> {45 Some(Self::new(RepeatedArray::new(data, repeats)?))46 }4748 pub fn make(len: u32, cb: NativeFn!((u32,)->Val)) -> Self {49 Self::new(MakeArray::new(len, cb))50 }5152 #[must_use]53 pub fn map(self, mapper: NativeFn!((Val) -> Val)) -> Self {54 Self::new(<MappedArray>::new(self, ArrayMapper::Plain(mapper)))55 }5657 #[must_use]58 pub fn map_with_index(self, mapper: NativeFn!((u32, Val) -> Val)) -> Self {59 Self::new(<MappedArray>::new(self, ArrayMapper::WithIndex(mapper)))60 }6162 pub fn filter(self, filter: NativeFn!((Thunk<Val>) -> bool)) -> Result<Self> {63 64 'eager: {65 let mut out = Vec::new();66 for i in self.iter() {67 let Ok(i) = i else {68 break 'eager;69 };70 if filter.call(IntoUntyped::into_lazy_untyped(i.clone()))? {71 out.push(i);72 }73 }74 return Ok(Self::new(out));75 };7677 let mut out = Vec::new();78 for i in self.iter_lazy() {79 if filter.call(i.clone())? {80 out.push(i);81 }82 }83 Ok(Self::new(out))84 }8586 pub fn extended(a: Self, b: Self) -> Option<Self> {87 Some(if a.is_empty() {88 b89 } else if b.is_empty() {90 a91 } else {92 Self::new(ExtendedArray::new(a, b)?)93 })94 }9596 pub fn range_exclusive(a: i32, b: i32) -> Self {97 Self::new(RangeArray::new_exclusive(a, b))98 }99 pub fn range_inclusive(a: i32, b: i32) -> Self {100 Self::new(RangeArray::new_inclusive(a, b))101 }102103 #[inline]104 #[must_use]105 pub fn slice(self, range: impl RangeBounds<usize>) -> Self {106 fn map_bound(start: bool, bound: Bound<&usize>) -> Option<i32> {107 match bound {108 Bound::Included(&v) => Some(i32::try_from(v).unwrap_or(i32::MAX)),109 Bound::Excluded(&v) => Some(110 i32::try_from(v)111 .unwrap_or(i32::MAX)112 .saturating_add(if start { 1 } else { -1 }),113 ),114 Bound::Unbounded => None,115 }116 }117 self.slice32(118 map_bound(true, range.start_bound()),119 map_bound(false, range.end_bound()),120 None,121 )122 }123124 #[must_use]125 pub fn slice32(self, index: Option<i32>, end: Option<i32>, step: Option<NonZeroU32>) -> Self {126 let get_idx = |pos: Option<i32>, len: u32, default| match pos {127 Some(v) if v < 0 => len.saturating_add_signed(v),128 #[expect(clippy::cast_sign_loss, reason = "abs value is used")]129 Some(v) => (v as u32).min(len),130 None => default,131 };132 let index = get_idx(index, self.len32(), 0);133 let end = get_idx(end, self.len32(), self.len32());134 let step = step.unwrap_or_else(|| NonZeroU32::new(1).expect("1 != 0"));135136 if index >= end {137 return Self::empty();138 }139140 Self::new(SliceArray {141 inner: self,142 from: index,143 to: end,144 step: step.get(),145 })146 }147148 149 #[inline]150 pub fn len32(&self) -> u32 {151 self.0.len32()152 }153154 pub fn len(&self) -> usize {155 self.len32() as usize156 }157158 159 #[inline]160 pub fn is_empty(&self) -> bool {161 self.0.is_empty()162 }163164 #[inline]165 pub fn is_cheap(&self) -> bool {166 self.0.is_cheap()167 }168169 170 171 172 #[inline]173 pub fn get32(&self, index: u32) -> Result<Option<Val>> {174 self.0.get32(index)175 }176177 pub fn get(&self, index: usize) -> Result<Option<Val>> {178 let Ok(i) = u32::try_from(index) else {179 return Ok(None);180 };181 self.get32(i)182 }183184 185 186 187 #[inline]188 pub fn get_lazy32(&self, index: u32) -> Option<Thunk<Val>> {189 self.0.get_lazy32(index)190 }191192 pub fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {193 u32::try_from(index).ok().and_then(|i| self.get_lazy32(i))194 }195196 pub fn iter(&self) -> impl ArrayLikeIter<Result<Val>> + '_ {197 (0..self.len32()).map(|i| self.get32(i).transpose().expect("length checked"))198 }199200 201 pub fn iter_lazy(&self) -> impl ArrayLikeIter<Thunk<Val>> + '_ {202 (0..self.len32()).map(|i| self.get_lazy32(i).expect("length checked"))203 }204205 206 #[must_use]207 pub fn reversed(self) -> Self {208 Self::new(ReverseArray(self))209 }210211 pub fn ptr_eq(a: &Self, b: &Self) -> bool {212 Cc::ptr_eq(&a.0, &b.0)213 }214215 pub fn as_any(&self) -> &dyn Any {216 &self.0217 }218}219impl<T> From<T> for ArrValue220where221 T: ArrayLike,222{223 fn from(value: T) -> Self {224 Self::new(value)225 }226}227impl<I> FromIterator<I> for ArrValue228where229 Vec<I>: ArrayLike,230{231 fn from_iter<T: IntoIterator<Item = I>>(iter: T) -> Self {232 Self::new(iter.into_iter().collect::<Vec<_>>())233 }234}235236237238#[inline]239pub(crate) fn arridx(i: usize) -> u32 {240 #[allow(241 clippy::cast_possible_truncation,242 reason = "array indexes never exceed 4g"243 )]244 if cfg!(debug_assertions) {245 u32::try_from(i).expect("4g hard limit")246 } else {247 i as u32248 }249}