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

difftreelog

feat impl Typed for BTreeMap

Yaroslav Bolyukin2023-07-27parent: #9567d36.patch.diff
in: master

2 files changed

modifiedcrates/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;
 
modifiedcrates/jrsonnet-evaluator/src/typed/mod.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/typed/mod.rs
1use 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}
after · crates/jrsonnet-evaluator/src/typed/mod.rs
1use 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::AttrsOf(a) => match value {195				Val::Obj(o) => {196					for (_key, value) in o.iter(197						#[cfg(feature = "exp-preserve-order")]198						false,199					) {200						let value = value?;201						a.check(&value)?;202					}203					Ok(())204				}205				v => Err(TypeError::ExpectedGot(self.clone(), v.value_type()).into()),206			},207			Self::ObjectRef(elems) => match value {208				Val::Obj(obj) => {209					for (k, v) in elems.iter() {210						if let Some(got_v) = obj.get((*k).into())? {211							push_type_description(212								|| format!("property {k}"),213								|| ValuePathItem::Field((*k).into()),214								|| v.check(&got_v),215							)?;216						} else {217							return Err(218								TypeError::MissingProperty((*k).into(), self.clone()).into()219							);220						}221					}222					Ok(())223				}224				v => Err(TypeError::ExpectedGot(self.clone(), v.value_type()).into()),225			},226			Self::Union(types) => {227				let mut errors = Vec::new();228				for ty in types.iter() {229					match ty.check(value) {230						Ok(()) => {231							return Ok(());232						}233						Err(e) => match e.error() {234							ErrorKind::TypeError(e) => errors.push(e.clone()),235							_ => return Err(e),236						},237					}238				}239				Err(TypeError::UnionFailed(self.clone(), TypeLocErrorList(errors)).into())240			}241			Self::UnionRef(types) => {242				let mut errors = Vec::new();243				for ty in types.iter() {244					match ty.check(value) {245						Ok(()) => {246							return Ok(());247						}248						Err(e) => match e.error() {249							ErrorKind::TypeError(e) => errors.push(e.clone()),250							_ => return Err(e),251						},252					}253				}254				Err(TypeError::UnionFailed(self.clone(), TypeLocErrorList(errors)).into())255			}256			Self::Sum(types) => {257				for ty in types.iter() {258					ty.check(value)?;259				}260				Ok(())261			}262			Self::SumRef(types) => {263				for ty in types.iter() {264					ty.check(value)?;265				}266				Ok(())267			}268			Self::Lazy(_lazy) => Ok(()),269		}270	}271}