git.delta.rocks / jrsonnet / refs/heads / master

difftreelog

source

crates/jrsonnet-evaluator/src/arr/mod.rs5.7 KiBsourcehistory
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		// TODO: ArrValue::Picked(inner, indexes) for large arrays64		'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	/// Array length.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	/// Is array contains no elements?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	/// Get array element by index, evaluating it, if it is lazy.170	///171	/// Returns `None` on out-of-bounds condition.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	/// Get array element by index, without evaluation.185	///186	/// Returns `None` on out-of-bounds condition.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	/// Iterate over elements, returning lazy values.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	/// Return a reversed view on current array.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}235236/// Checks that the usize does not exceed 4g with debug assertions enabled237/// Should only be used on values that can't reasonably exceed this value238#[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}