difftreelog
feat impl Typed for BTreeMap
in: master
2 files changed
crates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/typed/conversions.rs
+++ b/crates/jrsonnet-evaluator/src/typed/conversions.rs
@@ -347,6 +347,51 @@
}
}
+impl<K: Typed + Ord, V: Typed> Typed for BTreeMap<K, V> {
+ const TYPE: &'static ComplexValType = &ComplexValType::AttrsOf(V::TYPE);
+
+ fn into_untyped(typed: Self) -> Result<Val> {
+ let mut out = ObjValueBuilder::with_capacity(typed.len());
+ for (k, v) in typed {
+ let Some(key) = K::into_untyped(k)?.as_str() else {
+ throw!("map key should serialize to string");
+ };
+ let value = V::into_untyped(v)?;
+ out.member(key).value_unchecked(value);
+ }
+ Ok(Val::Obj(out.build()))
+ }
+
+ fn from_untyped(value: Val) -> Result<Self> {
+ Self::TYPE.check(&value)?;
+ let obj = value.as_obj().expect("typecheck should fail");
+
+ let mut out = BTreeMap::new();
+ if V::wants_lazy() {
+ for key in obj.fields_ex(
+ false,
+ #[cfg(feature = "exp-preserve-order")]
+ false,
+ ) {
+ let value = obj.get_lazy(key.clone()).expect("field exists");
+ let value = V::from_lazy_untyped(value)?;
+ let key = K::from_untyped(Val::Str(key.into()))?;
+ let _ = out.insert(key, value);
+ }
+ } else {
+ for (key, value) in obj.iter(
+ #[cfg(feature = "exp-preserve-order")]
+ false,
+ ) {
+ let key = K::from_untyped(Val::Str(key.into()))?;
+ let value = V::from_untyped(value?)?;
+ let _ = out.insert(key, value);
+ }
+ }
+ Ok(out)
+ }
+}
+
impl Typed for Val {
const TYPE: &'static ComplexValType = &ComplexValType::Any;
crates/jrsonnet-evaluator/src/typed/mod.rsdiffbeforeafterboth1use std::{fmt::Display, rc::Rc};23mod conversions;4pub use conversions::*;5use jrsonnet_gcmodule::Trace;6pub use jrsonnet_types::{ComplexValType, ValType};7use thiserror::Error;89use crate::{10 error::{Error, ErrorKind, Result},11 State, Val,12};1314#[derive(Debug, Error, Clone, Trace)]15pub enum TypeError {16 #[error("expected {0}, got {1}")]17 ExpectedGot(ComplexValType, ValType),18 #[error("missing property {0} from {1}")]19 MissingProperty(#[trace(skip)] Rc<str>, ComplexValType),20 #[error("every failed from {0}:\n{1}")]21 UnionFailed(ComplexValType, TypeLocErrorList),22 #[error(23 "number out of bounds: {0} not in {}..{}",24 .1.map(|v|v.to_string()).unwrap_or_default(),25 .2.map(|v|v.to_string()).unwrap_or_default(),26 )]27 BoundsFailed(f64, Option<f64>, Option<f64>),28}29impl From<TypeError> for Error {30 fn from(e: TypeError) -> Self {31 ErrorKind::TypeError(e.into()).into()32 }33}3435#[derive(Debug, Clone, Trace)]36pub struct TypeLocError(Box<TypeError>, ValuePathStack);37impl From<TypeError> for TypeLocError {38 fn from(e: TypeError) -> Self {39 Self(Box::new(e), ValuePathStack(Vec::new()))40 }41}42impl From<TypeLocError> for Error {43 fn from(e: TypeLocError) -> Self {44 ErrorKind::TypeError(e).into()45 }46}47impl Display for TypeLocError {48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {49 write!(f, "{}", self.0)?;50 if !(self.1).0.is_empty() {51 write!(f, " at {}", self.1)?;52 }53 Ok(())54 }55}5657#[derive(Debug, Clone, Trace)]58pub struct TypeLocErrorList(Vec<TypeLocError>);59impl Display for TypeLocErrorList {60 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {61 use std::fmt::Write;62 let mut out = String::new();63 for (i, err) in self.0.iter().enumerate() {64 if i != 0 {65 writeln!(f)?;66 }67 out.clear();68 write!(out, "{err}")?;6970 for (i, line) in out.lines().enumerate() {71 if line.trim().is_empty() {72 continue;73 }74 if i == 0 {75 write!(f, " - ")?;76 } else {77 writeln!(f)?;78 write!(f, " ")?;79 }80 write!(f, "{line}")?;81 }82 }83 Ok(())84 }85}8687fn push_type_description(88 error_reason: impl Fn() -> String,89 path: impl Fn() -> ValuePathItem,90 item: impl Fn() -> Result<()>,91) -> Result<()> {92 State::push_description(error_reason, || match item() {93 Ok(_) => Ok(()),94 Err(mut e) => {95 if let ErrorKind::TypeError(e) = &mut e.error_mut() {96 (e.1).0.push(path());97 }98 Err(e)99 }100 })101}102103// TODO: check_fast for fast path of union type checking104pub trait CheckType {105 fn check(&self, value: &Val) -> Result<()>;106}107108impl CheckType for ValType {109 fn check(&self, value: &Val) -> Result<()> {110 let got = value.value_type();111 if got != *self {112 let loc_error: TypeLocError = TypeError::ExpectedGot((*self).into(), got).into();113 return Err(loc_error.into());114 }115 Ok(())116 }117}118119#[derive(Clone, Debug, Trace)]120enum ValuePathItem {121 Field(#[trace(skip)] Rc<str>),122 Index(u64),123}124impl Display for ValuePathItem {125 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {126 match self {127 Self::Field(name) => write!(f, ".{name:?}")?,128 Self::Index(idx) => write!(f, "[{idx}]")?,129 }130 Ok(())131 }132}133134#[derive(Clone, Debug, Trace)]135struct ValuePathStack(Vec<ValuePathItem>);136impl Display for ValuePathStack {137 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {138 write!(f, "self")?;139 for elem in self.0.iter().rev() {140 write!(f, "{elem}")?;141 }142 Ok(())143 }144}145146impl CheckType for ComplexValType {147 #[allow(clippy::too_many_lines)]148 fn check(&self, value: &Val) -> Result<()> {149 match self {150 Self::Any => Ok(()),151 Self::Simple(t) => t.check(value),152 Self::Char => match value {153 Val::Str(s) if s.len() == 1 || s.clone().into_flat().chars().count() == 1 => Ok(()),154 v => Err(TypeError::ExpectedGot(self.clone(), v.value_type()).into()),155 },156 Self::BoundedNumber(from, to) => {157 if let Val::Num(n) = value {158 if from.map(|from| from > *n).unwrap_or(false)159 || to.map(|to| to < *n).unwrap_or(false)160 {161 return Err(TypeError::BoundsFailed(*n, *from, *to).into());162 }163 Ok(())164 } else {165 Err(TypeError::ExpectedGot(self.clone(), value.value_type()).into())166 }167 }168 Self::Array(elem_type) => match value {169 Val::Arr(a) => {170 for (i, item) in a.iter().enumerate() {171 push_type_description(172 || format!("array index {i}"),173 || ValuePathItem::Index(i as u64),174 || elem_type.check(&item.clone()?),175 )?;176 }177 Ok(())178 }179 v => Err(TypeError::ExpectedGot(self.clone(), v.value_type()).into()),180 },181 Self::ArrayRef(elem_type) => match value {182 Val::Arr(a) => {183 for (i, item) in a.iter().enumerate() {184 push_type_description(185 || format!("array index {i}"),186 || ValuePathItem::Index(i as u64),187 || elem_type.check(&item.clone()?),188 )?;189 }190 Ok(())191 }192 v => Err(TypeError::ExpectedGot(self.clone(), v.value_type()).into()),193 },194 Self::ObjectRef(elems) => match value {195 Val::Obj(obj) => {196 for (k, v) in elems.iter() {197 if let Some(got_v) = obj.get((*k).into())? {198 push_type_description(199 || format!("property {k}"),200 || ValuePathItem::Field((*k).into()),201 || v.check(&got_v),202 )?;203 } else {204 return Err(205 TypeError::MissingProperty((*k).into(), self.clone()).into()206 );207 }208 }209 Ok(())210 }211 v => Err(TypeError::ExpectedGot(self.clone(), v.value_type()).into()),212 },213 Self::Union(types) => {214 let mut errors = Vec::new();215 for ty in types.iter() {216 match ty.check(value) {217 Ok(()) => {218 return Ok(());219 }220 Err(e) => match e.error() {221 ErrorKind::TypeError(e) => errors.push(e.clone()),222 _ => return Err(e),223 },224 }225 }226 Err(TypeError::UnionFailed(self.clone(), TypeLocErrorList(errors)).into())227 }228 Self::UnionRef(types) => {229 let mut errors = Vec::new();230 for ty in types.iter() {231 match ty.check(value) {232 Ok(()) => {233 return Ok(());234 }235 Err(e) => match e.error() {236 ErrorKind::TypeError(e) => errors.push(e.clone()),237 _ => return Err(e),238 },239 }240 }241 Err(TypeError::UnionFailed(self.clone(), TypeLocErrorList(errors)).into())242 }243 Self::Sum(types) => {244 for ty in types.iter() {245 ty.check(value)?;246 }247 Ok(())248 }249 Self::SumRef(types) => {250 for ty in types.iter() {251 ty.check(value)?;252 }253 Ok(())254 }255 Self::Lazy(_lazy) => Ok(()),256 }257 }258}