difftreelog
feat impl Typed for BTreeMap
in: master
2 files changed
crates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth1use std::{collections::BTreeMap, marker::PhantomData, ops::Deref};23use jrsonnet_gcmodule::{Cc, Trace};4use jrsonnet_interner::{IBytes, IStr};5pub use jrsonnet_macros::Typed;6use jrsonnet_types::{ComplexValType, ValType};78use crate::{9 arr::ArrValue,10 error::Result,11 function::{native::NativeDesc, FuncDesc, FuncVal},12 throw,13 typed::CheckType,14 val::{IndexableVal, StrValue, ThunkMapper},15 ObjValue, ObjValueBuilder, Thunk, Val,16};1718#[derive(Trace)]19struct FromUntyped<K: Trace>(PhantomData<fn() -> K>);20impl<K> ThunkMapper<Val> for FromUntyped<K>21where22 K: Typed + Trace,23{24 type Output = K;2526 fn map(self, from: Val) -> Result<Self::Output> {27 K::from_untyped(from)28 }29}30impl<K: Trace> Default for FromUntyped<K> {31 fn default() -> Self {32 Self(PhantomData)33 }34}3536pub trait TypedObj: Typed {37 fn serialize(self, out: &mut ObjValueBuilder) -> Result<()>;38 fn parse(obj: &ObjValue) -> Result<Self>;39 fn into_object(self) -> Result<ObjValue> {40 let mut builder = ObjValueBuilder::new();41 self.serialize(&mut builder)?;42 Ok(builder.build())43 }44}4546pub trait Typed: Sized {47 const TYPE: &'static ComplexValType;48 fn into_untyped(typed: Self) -> Result<Val>;49 fn into_lazy_untyped(typed: Self) -> Thunk<Val> {50 Thunk::from(Self::into_untyped(typed))51 }52 fn from_untyped(untyped: Val) -> Result<Self>;53 fn from_lazy_untyped(lazy: Thunk<Val>) -> Result<Self> {54 Self::from_untyped(lazy.evaluate()?)55 }5657 // Whatever caller should use `into_lazy_untyped` instead of `into_untyped`58 fn provides_lazy() -> bool {59 false60 }6162 // Whatever caller should use `from_lazy_untyped` instead of `from_untyped` when possible63 fn wants_lazy() -> bool {64 false65 }6667 /// Hack to make builtins be able to return non-result values, and make macros able to convert those values to result68 /// This method returns identity in impl Typed for Result, and should not be overriden69 #[doc(hidden)]70 fn into_result(typed: Self) -> Result<Val> {71 let value = Self::into_untyped(typed)?;72 Ok(value)73 }74}7576impl<T> Typed for Thunk<T>77where78 T: Typed + Trace + Clone,79{80 const TYPE: &'static ComplexValType = &ComplexValType::Lazy(T::TYPE);8182 fn into_untyped(typed: Self) -> Result<Val> {83 T::into_untyped(typed.evaluate()?)84 }8586 fn from_untyped(untyped: Val) -> Result<Self> {87 Self::from_lazy_untyped(Thunk::evaluated(untyped))88 }8990 fn provides_lazy() -> bool {91 true92 }9394 fn into_lazy_untyped(inner: Self) -> Thunk<Val> {95 #[derive(Trace)]96 struct IntoUntyped<K: Trace>(PhantomData<fn() -> K>);97 impl<K> ThunkMapper<K> for IntoUntyped<K>98 where99 K: Typed + Trace,100 {101 type Output = Val;102103 fn map(self, from: K) -> Result<Self::Output> {104 K::into_untyped(from)105 }106 }107 impl<K: Trace> Default for IntoUntyped<K> {108 fn default() -> Self {109 Self(PhantomData)110 }111 }112 inner.map(<IntoUntyped<T>>::default())113 }114115 fn wants_lazy() -> bool {116 true117 }118119 fn from_lazy_untyped(inner: Thunk<Val>) -> Result<Self> {120 Ok(inner.map(<FromUntyped<T>>::default()))121 }122}123124const MAX_SAFE_INTEGER: f64 = ((1u64 << (f64::MANTISSA_DIGITS + 1)) - 1) as f64;125126macro_rules! impl_int {127 ($($ty:ty)*) => {$(128 impl Typed for $ty {129 const TYPE: &'static ComplexValType =130 &ComplexValType::BoundedNumber(Some(Self::MIN as f64), Some(Self::MAX as f64));131 fn from_untyped(value: Val) -> Result<Self> {132 <Self as Typed>::TYPE.check(&value)?;133 match value {134 Val::Num(n) => {135 #[allow(clippy::float_cmp)]136 if n.trunc() != n {137 throw!(138 "cannot convert number with fractional part to {}",139 stringify!($ty)140 )141 }142 Ok(n as Self)143 }144 _ => unreachable!(),145 }146 }147 fn into_untyped(value: Self) -> Result<Val> {148 Ok(Val::Num(value as f64))149 }150 }151 )*};152}153154impl_int!(i8 u8 i16 u16 i32 u32);155156macro_rules! impl_bounded_int {157 ($($name:ident = $ty:ty)*) => {$(158 #[derive(Clone, Copy)]159 pub struct $name<const MIN: $ty, const MAX: $ty>($ty);160 impl<const MIN: $ty, const MAX: $ty> $name<MIN, MAX> {161 pub const fn new(value: $ty) -> Option<$name<MIN, MAX>> {162 if value >= MIN && value <= MAX {163 Some(Self(value))164 } else {165 None166 }167 }168 pub const fn value(self) -> $ty {169 self.0170 }171 }172 impl<const MIN: $ty, const MAX: $ty> Deref for $name<MIN, MAX> {173 type Target = $ty;174 fn deref(&self) -> &Self::Target {175 &self.0176 }177 }178179 impl<const MIN: $ty, const MAX: $ty> Typed for $name<MIN, MAX> {180 const TYPE: &'static ComplexValType =181 &ComplexValType::BoundedNumber(182 Some(MIN as f64),183 Some(MAX as f64),184 );185186 fn from_untyped(value: Val) -> Result<Self> {187 <Self as Typed>::TYPE.check(&value)?;188 match value {189 Val::Num(n) => {190 #[allow(clippy::float_cmp)]191 if n.trunc() != n {192 throw!(193 "cannot convert number with fractional part to {}",194 stringify!($ty)195 )196 }197 Ok(Self(n as $ty))198 }199 _ => unreachable!(),200 }201 }202203 fn into_untyped(value: Self) -> Result<Val> {204 Ok(Val::Num(value.0 as f64))205 }206 }207 )*};208}209210impl_bounded_int!(211 BoundedI8 = i8212 BoundedI16 = i16213 BoundedI32 = i32214 BoundedI64 = i64215 BoundedUsize = usize216);217218impl Typed for f64 {219 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Num);220221 fn into_untyped(value: Self) -> Result<Val> {222 Ok(Val::Num(value))223 }224225 fn from_untyped(value: Val) -> Result<Self> {226 <Self as Typed>::TYPE.check(&value)?;227 match value {228 Val::Num(n) => Ok(n),229 _ => unreachable!(),230 }231 }232}233234pub struct PositiveF64(pub f64);235impl Typed for PositiveF64 {236 const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(0.0), None);237238 fn into_untyped(value: Self) -> Result<Val> {239 Ok(Val::Num(value.0))240 }241242 fn from_untyped(value: Val) -> Result<Self> {243 <Self as Typed>::TYPE.check(&value)?;244 match value {245 Val::Num(n) => Ok(Self(n)),246 _ => unreachable!(),247 }248 }249}250impl Typed for usize {251 const TYPE: &'static ComplexValType =252 &ComplexValType::BoundedNumber(Some(0.0), Some(MAX_SAFE_INTEGER));253254 fn into_untyped(value: Self) -> Result<Val> {255 if value > MAX_SAFE_INTEGER as Self {256 throw!("number is too large")257 }258 Ok(Val::Num(value as f64))259 }260261 fn from_untyped(value: Val) -> Result<Self> {262 <Self as Typed>::TYPE.check(&value)?;263 match value {264 Val::Num(n) => {265 #[allow(clippy::float_cmp)]266 if n.trunc() != n {267 throw!("cannot convert number with fractional part to usize")268 }269 Ok(n as Self)270 }271 _ => unreachable!(),272 }273 }274}275276impl Typed for IStr {277 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Str);278279 fn into_untyped(value: Self) -> Result<Val> {280 Ok(Val::Str(StrValue::Flat(value)))281 }282283 fn from_untyped(value: Val) -> Result<Self> {284 <Self as Typed>::TYPE.check(&value)?;285 match value {286 Val::Str(s) => Ok(s.into_flat()),287 _ => unreachable!(),288 }289 }290}291292impl Typed for String {293 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Str);294295 fn into_untyped(value: Self) -> Result<Val> {296 Ok(Val::Str(StrValue::Flat(value.into())))297 }298299 fn from_untyped(value: Val) -> Result<Self> {300 <Self as Typed>::TYPE.check(&value)?;301 match value {302 Val::Str(s) => Ok(s.to_string()),303 _ => unreachable!(),304 }305 }306}307308impl Typed for char {309 const TYPE: &'static ComplexValType = &ComplexValType::Char;310311 fn into_untyped(value: Self) -> Result<Val> {312 Ok(Val::Str(StrValue::Flat(value.to_string().into())))313 }314315 fn from_untyped(value: Val) -> Result<Self> {316 <Self as Typed>::TYPE.check(&value)?;317 match value {318 Val::Str(s) => Ok(s.into_flat().chars().next().unwrap()),319 _ => unreachable!(),320 }321 }322}323324impl<T> Typed for Vec<T>325where326 T: Typed,327{328 const TYPE: &'static ComplexValType = &ComplexValType::ArrayRef(T::TYPE);329330 fn into_untyped(value: Self) -> Result<Val> {331 Ok(Val::Arr(332 value333 .into_iter()334 .map(T::into_untyped)335 .collect::<Result<ArrValue>>()?,336 ))337 }338339 fn from_untyped(value: Val) -> Result<Self> {340 let Val::Arr(a) = value else {341 <Self as Typed>::TYPE.check(&value)?;342 unreachable!("typecheck should fail")343 };344 a.iter()345 .map(|r| r.and_then(T::from_untyped))346 .collect::<Result<Vec<T>>>()347 }348}349350impl Typed for Val {351 const TYPE: &'static ComplexValType = &ComplexValType::Any;352353 fn into_untyped(typed: Self) -> Result<Val> {354 Ok(typed)355 }356 fn from_untyped(untyped: Val) -> Result<Self> {357 Ok(untyped)358 }359}360361// Hack362#[doc(hidden)]363impl<T> Typed for Result<T>364where365 T: Typed,366{367 const TYPE: &'static ComplexValType = &ComplexValType::Any;368369 fn into_untyped(_typed: Self) -> Result<Val> {370 panic!("do not use this conversion")371 }372373 fn from_untyped(_untyped: Val) -> Result<Self> {374 panic!("do not use this conversion")375 }376377 fn into_result(typed: Self) -> Result<Val> {378 typed.map(T::into_untyped)?379 }380}381382/// Specialization383impl Typed for IBytes {384 const TYPE: &'static ComplexValType =385 &ComplexValType::ArrayRef(&ComplexValType::BoundedNumber(Some(0.0), Some(255.0)));386387 fn into_untyped(value: Self) -> Result<Val> {388 Ok(Val::Arr(ArrValue::bytes(value)))389 }390391 fn from_untyped(value: Val) -> Result<Self> {392 if let Val::Arr(ArrValue::Bytes(bytes)) = value {393 return Ok(bytes.0);394 }395 <Self as Typed>::TYPE.check(&value)?;396 match value {397 Val::Arr(a) => {398 let mut out = Vec::with_capacity(a.len());399 for e in a.iter() {400 let r = e?;401 out.push(u8::from_untyped(r)?);402 }403 Ok(out.as_slice().into())404 }405 _ => unreachable!(),406 }407 }408}409410pub struct M1;411impl Typed for M1 {412 const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(-1.0), Some(-1.0));413414 fn into_untyped(_: Self) -> Result<Val> {415 Ok(Val::Num(-1.0))416 }417418 fn from_untyped(value: Val) -> Result<Self> {419 <Self as Typed>::TYPE.check(&value)?;420 Ok(Self)421 }422}423424macro_rules! decl_either {425 ($($name: ident, $($id: ident)*);*) => {$(426 #[derive(Clone)]427 pub enum $name<$($id),*> {428 $($id($id)),*429 }430 impl<$($id),*> Typed for $name<$($id),*>431 where432 $($id: Typed,)*433 {434 const TYPE: &'static ComplexValType = &ComplexValType::UnionRef(&[$($id::TYPE),*]);435436 fn into_untyped(value: Self) -> Result<Val> {437 match value {$(438 $name::$id(v) => $id::into_untyped(v)439 ),*}440 }441442 fn from_untyped(value: Val) -> Result<Self> {443 $(444 if $id::TYPE.check(&value).is_ok() {445 $id::from_untyped(value).map(Self::$id)446 } else447 )* {448 <Self as Typed>::TYPE.check(&value)?;449 unreachable!()450 }451 }452 }453 )*}454}455decl_either!(456 Either1, A;457 Either2, A B;458 Either3, A B C;459 Either4, A B C D;460 Either5, A B C D E;461 Either6, A B C D E F;462 Either7, A B C D E F G463);464#[macro_export]465macro_rules! Either {466 ($a:ty) => {Either1<$a>};467 ($a:ty, $b:ty) => {Either2<$a, $b>};468 ($a:ty, $b:ty, $c:ty) => {Either3<$a, $b, $c>};469 ($a:ty, $b:ty, $c:ty, $d:ty) => {Either4<$a, $b, $c, $d>};470 ($a:ty, $b:ty, $c:ty, $d:ty, $e:ty) => {Either5<$a, $b, $c, $d, $e>};471 ($a:ty, $b:ty, $c:ty, $d:ty, $e:ty, $f:ty) => {Either6<$a, $b, $c, $d, $e, $f>};472 ($a:ty, $b:ty, $c:ty, $d:ty, $e:ty, $f:ty, $g:ty) => {Either7<$a, $b, $c, $d, $e, $f, $g>};473}474pub use Either;475476pub type MyType = Either![u32, f64, String];477478impl Typed for ArrValue {479 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Arr);480481 fn into_untyped(value: Self) -> Result<Val> {482 Ok(Val::Arr(value))483 }484485 fn from_untyped(value: Val) -> Result<Self> {486 <Self as Typed>::TYPE.check(&value)?;487 match value {488 Val::Arr(a) => Ok(a),489 _ => unreachable!(),490 }491 }492}493494impl Typed for FuncVal {495 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);496497 fn into_untyped(value: Self) -> Result<Val> {498 Ok(Val::Func(value))499 }500501 fn from_untyped(value: Val) -> Result<Self> {502 <Self as Typed>::TYPE.check(&value)?;503 match value {504 Val::Func(a) => Ok(a),505 _ => unreachable!(),506 }507 }508}509510impl Typed for Cc<FuncDesc> {511 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);512513 fn into_untyped(value: Self) -> Result<Val> {514 Ok(Val::Func(FuncVal::Normal(value)))515 }516517 fn from_untyped(value: Val) -> Result<Self> {518 <Self as Typed>::TYPE.check(&value)?;519 match value {520 Val::Func(FuncVal::Normal(desc)) => Ok(desc),521 Val::Func(_) => throw!("expected normal function, not builtin"),522 _ => unreachable!(),523 }524 }525}526527impl Typed for ObjValue {528 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Obj);529530 fn into_untyped(value: Self) -> Result<Val> {531 Ok(Val::Obj(value))532 }533534 fn from_untyped(value: Val) -> Result<Self> {535 <Self as Typed>::TYPE.check(&value)?;536 match value {537 Val::Obj(a) => Ok(a),538 _ => unreachable!(),539 }540 }541}542543impl Typed for bool {544 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Bool);545546 fn into_untyped(value: Self) -> Result<Val> {547 Ok(Val::Bool(value))548 }549550 fn from_untyped(value: Val) -> Result<Self> {551 <Self as Typed>::TYPE.check(&value)?;552 match value {553 Val::Bool(a) => Ok(a),554 _ => unreachable!(),555 }556 }557}558impl Typed for IndexableVal {559 const TYPE: &'static ComplexValType = &ComplexValType::UnionRef(&[560 &ComplexValType::Simple(ValType::Arr),561 &ComplexValType::Simple(ValType::Str),562 ]);563564 fn into_untyped(value: Self) -> Result<Val> {565 match value {566 IndexableVal::Str(s) => Ok(Val::Str(StrValue::Flat(s))),567 IndexableVal::Arr(a) => Ok(Val::Arr(a)),568 }569 }570571 fn from_untyped(value: Val) -> Result<Self> {572 <Self as Typed>::TYPE.check(&value)?;573 value.into_indexable()574 }575}576577pub struct Null;578impl Typed for Null {579 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Null);580581 fn into_untyped(_: Self) -> Result<Val> {582 Ok(Val::Null)583 }584585 fn from_untyped(value: Val) -> Result<Self> {586 <Self as Typed>::TYPE.check(&value)?;587 Ok(Self)588 }589}590591pub struct NativeFn<D: NativeDesc>(D::Value);592impl<D: NativeDesc> Deref for NativeFn<D> {593 type Target = D::Value;594595 fn deref(&self) -> &Self::Target {596 &self.0597 }598}599impl<D: NativeDesc> Typed for NativeFn<D> {600 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Func);601602 fn into_untyped(_typed: Self) -> Result<Val> {603 throw!("can only convert functions from jsonnet to native")604 }605606 fn from_untyped(untyped: Val) -> Result<Self> {607 Ok(Self(608 untyped609 .as_func()610 .expect("shape is checked")611 .into_native::<D>(),612 ))613 }614}crates/jrsonnet-evaluator/src/typed/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/typed/mod.rs
+++ b/crates/jrsonnet-evaluator/src/typed/mod.rs
@@ -191,6 +191,19 @@
}
v => Err(TypeError::ExpectedGot(self.clone(), v.value_type()).into()),
},
+ Self::AttrsOf(a) => match value {
+ Val::Obj(o) => {
+ for (_key, value) in o.iter(
+ #[cfg(feature = "exp-preserve-order")]
+ false,
+ ) {
+ let value = value?;
+ a.check(&value)?;
+ }
+ Ok(())
+ }
+ v => Err(TypeError::ExpectedGot(self.clone(), v.value_type()).into()),
+ },
Self::ObjectRef(elems) => match value {
Val::Obj(obj) => {
for (k, v) in elems.iter() {