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::{12 Context, Result, Thunk, Val,13 analyze::{ClosureShape, LExpr},14 function::NativeFn,15 typed::IntoUntyped,16};1718mod spec;19pub use spec::{ArrayLike, *};2021cc_dyn!(22 #[doc = "Represents a Jsonnet array value."]23 #[derive(Clone)]24 ArrValue,25 ArrayLike,26 pub fn new() {...}27);28impl fmt::Debug for ArrValue {29 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {30 self.0.fmt(f)31 }32}3334pub trait ArrayLikeIter<T>: Iterator<Item = T> + DoubleEndedIterator + ExactSizeIterator {}35impl<I, T> ArrayLikeIter<T> for I where36 I: Iterator<Item = T> + DoubleEndedIterator + ExactSizeIterator37{38}3940impl ArrValue {41 pub fn empty() -> Self {42 Self::new(())43 }4445 pub fn expr(ctx: Context, shape: &ClosureShape, exprs: Rc<Vec<LExpr>>) -> Self {46 Self::new(ExprArray::new(ctx, shape, exprs))47 }4849 pub fn repeated(data: Self, repeats: u32) -> Option<Self> {50 Some(Self::new(RepeatedArray::new(data, repeats)?))51 }5253 pub fn make(len: u32, cb: NativeFn!((u32,)->Val)) -> Self {54 Self::new(MakeArray::new(len, cb))55 }5657 #[must_use]58 pub fn map(self, mapper: NativeFn!((Val) -> Val)) -> Self {59 Self::new(<MappedArray>::new(self, ArrayMapper::Plain(mapper)))60 }6162 #[must_use]63 pub fn map_with_index(self, mapper: NativeFn!((u32, Val) -> Val)) -> Self {64 Self::new(<MappedArray>::new(self, ArrayMapper::WithIndex(mapper)))65 }6667 pub fn filter(self, filter: NativeFn!((Thunk<Val>) -> bool)) -> Result<Self> {68 69 'eager: {70 let mut out = Vec::new();71 for i in self.iter() {72 let Ok(i) = i else {73 break 'eager;74 };75 if filter.call(IntoUntyped::into_lazy_untyped(i.clone()))? {76 out.push(i);77 }78 }79 return Ok(Self::new(out));80 };8182 let mut out = Vec::new();83 for i in self.iter_lazy() {84 if filter.call(i.clone())? {85 out.push(i);86 }87 }88 Ok(Self::new(out))89 }9091 pub fn extended(a: Self, b: Self) -> Option<Self> {92 Some(if a.is_empty() {93 b94 } else if b.is_empty() {95 a96 } else {97 Self::new(ExtendedArray::new(a, b)?)98 })99 }100101 pub fn range_exclusive(a: i32, b: i32) -> Self {102 Self::new(RangeArray::new_exclusive(a, b))103 }104 pub fn range_inclusive(a: i32, b: i32) -> Self {105 Self::new(RangeArray::new_inclusive(a, b))106 }107108 #[inline]109 #[must_use]110 pub fn slice(self, range: impl RangeBounds<usize>) -> Self {111 fn map_bound(start: bool, bound: Bound<&usize>) -> Option<i32> {112 match bound {113 Bound::Included(&v) => Some(i32::try_from(v).unwrap_or(i32::MAX)),114 Bound::Excluded(&v) => Some(115 i32::try_from(v)116 .unwrap_or(i32::MAX)117 .saturating_add(if start { 1 } else { -1 }),118 ),119 Bound::Unbounded => None,120 }121 }122 self.slice32(123 map_bound(true, range.start_bound()),124 map_bound(false, range.end_bound()),125 None,126 )127 }128129 #[must_use]130 pub fn slice32(self, index: Option<i32>, end: Option<i32>, step: Option<NonZeroU32>) -> Self {131 let get_idx = |pos: Option<i32>, len: u32, default| match pos {132 Some(v) if v < 0 => len.saturating_add_signed(v),133 #[expect(clippy::cast_sign_loss, reason = "abs value is used")]134 Some(v) => (v as u32).min(len),135 None => default,136 };137 let index = get_idx(index, self.len32(), 0);138 let end = get_idx(end, self.len32(), self.len32());139 let step = step.unwrap_or_else(|| NonZeroU32::new(1).expect("1 != 0"));140141 if index >= end {142 return Self::empty();143 }144145 Self::new(SliceArray {146 inner: self,147 from: index,148 to: end,149 step: step.get(),150 })151 }152153 154 #[inline]155 pub fn len32(&self) -> u32 {156 self.0.len32()157 }158159 pub fn len(&self) -> usize {160 self.len32() as usize161 }162163 164 #[inline]165 pub fn is_empty(&self) -> bool {166 self.0.is_empty()167 }168169 #[inline]170 pub fn is_cheap(&self) -> bool {171 self.0.is_cheap()172 }173174 175 176 177 #[inline]178 pub fn get32(&self, index: u32) -> Result<Option<Val>> {179 self.0.get32(index)180 }181182 pub fn get(&self, index: usize) -> Result<Option<Val>> {183 let Ok(i) = u32::try_from(index) else {184 return Ok(None);185 };186 self.get32(i)187 }188189 190 191 192 #[inline]193 pub fn get_lazy32(&self, index: u32) -> Option<Thunk<Val>> {194 self.0.get_lazy32(index)195 }196197 pub fn get_lazy(&self, index: usize) -> Option<Thunk<Val>> {198 u32::try_from(index).ok().and_then(|i| self.get_lazy32(i))199 }200201 pub fn iter(&self) -> impl ArrayLikeIter<Result<Val>> + '_ {202 (0..self.len32()).map(|i| self.get32(i).transpose().expect("length checked"))203 }204205 206 pub fn iter_lazy(&self) -> impl ArrayLikeIter<Thunk<Val>> + '_ {207 (0..self.len32()).map(|i| self.get_lazy32(i).expect("length checked"))208 }209210 211 #[must_use]212 pub fn reversed(self) -> Self {213 Self::new(ReverseArray(self))214 }215216 pub fn ptr_eq(a: &Self, b: &Self) -> bool {217 Cc::ptr_eq(&a.0, &b.0)218 }219220 pub fn as_any(&self) -> &dyn Any {221 &self.0222 }223}224impl<T> From<T> for ArrValue225where226 T: ArrayLike,227{228 fn from(value: T) -> Self {229 Self::new(value)230 }231}232impl<I> FromIterator<I> for ArrValue233where234 Vec<I>: ArrayLike,235{236 fn from_iter<T: IntoIterator<Item = I>>(iter: T) -> Self {237 Self::new(iter.into_iter().collect::<Vec<_>>())238 }239}240241242243#[inline]244pub(crate) fn arridx(i: usize) -> u32 {245 #[allow(246 clippy::cast_possible_truncation,247 reason = "array indexes never exceed 4g"248 )]249 if cfg!(debug_assertions) {250 u32::try_from(i).expect("4g hard limit")251 } else {252 i as u32253 }254}