git.delta.rocks / jrsonnet / refs/commits / de9f0cb0f75a

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::{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		// TODO: ArrValue::Picked(inner, indexes) for large arrays69		'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	/// Array length.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	/// Is array contains no elements?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	/// Get array element by index, evaluating it, if it is lazy.175	///176	/// Returns `None` on out-of-bounds condition.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	/// Get array element by index, without evaluation.190	///191	/// Returns `None` on out-of-bounds condition.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	/// Iterate over elements, returning lazy values.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	/// Return a reversed view on current array.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}240241/// Checks that the usize does not exceed 4g with debug assertions enabled242/// Should only be used on values that can't reasonably exceed this value243#[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}