difftreelog
feat derive(Typed) for struct
in: master
9 files changed
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -70,8 +70,9 @@
[[package]]
name = "clap"
-version = "3.0.0-beta.2"
-source = "git+https://github.com/clap-rs/clap?rev=f0c5ea5e1503de5c8e74d8c047a799cf51498e83#f0c5ea5e1503de5c8e74d8c047a799cf51498e83"
+version = "3.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "71c47df61d9e16dc010b55dba1952a57d8c215dbb533fd13cdd13369aac73b1c"
dependencies = [
"atty",
"bitflags",
@@ -82,27 +83,28 @@
"strsim",
"termcolor",
"textwrap",
- "vec_map",
]
[[package]]
+name = "clap_complete"
+version = "3.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df6f3613c0a3cddfd78b41b10203eb322cb29b600cbdf808a7d3db95691b8e25"
+dependencies = [
+ "clap",
+]
+
+[[package]]
name = "clap_derive"
-version = "3.0.0-beta.2"
-source = "git+https://github.com/clap-rs/clap?rev=f0c5ea5e1503de5c8e74d8c047a799cf51498e83#f0c5ea5e1503de5c8e74d8c047a799cf51498e83"
+version = "3.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3aab4734e083b809aaf5794e14e756d1c798d2c69c7f7de7a09a2f5214993c1"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
-]
-
-[[package]]
-name = "clap_generate"
-version = "3.0.0-beta.2"
-source = "git+https://github.com/clap-rs/clap?rev=f0c5ea5e1503de5c8e74d8c047a799cf51498e83#f0c5ea5e1503de5c8e74d8c047a799cf51498e83"
-dependencies = [
- "clap",
]
[[package]]
@@ -148,12 +150,9 @@
[[package]]
name = "heck"
-version = "0.3.3"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
-dependencies = [
- "unicode-segmentation",
-]
+checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
[[package]]
name = "hermit-abi"
@@ -185,7 +184,7 @@
version = "0.4.2"
dependencies = [
"clap",
- "clap_generate",
+ "clap_complete",
"gcmodule",
"jrsonnet-cli",
"jrsonnet-evaluator",
@@ -214,6 +213,7 @@
"bincode",
"gcmodule",
"jrsonnet-interner",
+ "jrsonnet-macros",
"jrsonnet-parser",
"jrsonnet-stdlib",
"jrsonnet-types",
@@ -236,6 +236,15 @@
]
[[package]]
+name = "jrsonnet-macros"
+version = "0.4.2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "jrsonnet-parser"
version = "0.4.2"
dependencies = [
@@ -301,6 +310,12 @@
checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
[[package]]
+name = "memchr"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
+
+[[package]]
name = "mimalloc-sys"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -321,9 +336,12 @@
[[package]]
name = "os_str_bytes"
-version = "3.1.0"
+version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6acbef58a60fe69ab50510a55bc8cdd4d6cf2283d27ad338f54cb52747a9cf2d"
+checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
+dependencies = [
+ "memchr",
+]
[[package]]
name = "parking_lot"
@@ -357,9 +375,9 @@
[[package]]
name = "peg"
-version = "0.7.0"
+version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07c0b841ea54f523f7aa556956fbd293bcbe06f2e67d2eb732b7278aaf1d166a"
+checksum = "af728fe826811af3b38c37e93de6d104485953ea373d656eebae53d6987fcd2c"
dependencies = [
"peg-macros",
"peg-runtime",
@@ -367,9 +385,9 @@
[[package]]
name = "peg-macros"
-version = "0.7.0"
+version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5aa52829b8decbef693af90202711348ab001456803ba2a98eb4ec8fb70844c"
+checksum = "4536be147b770b824895cbad934fccce8e49f14b4c4946eaa46a6e4a12fcdc16"
dependencies = [
"peg-runtime",
"proc-macro2",
@@ -378,9 +396,9 @@
[[package]]
name = "peg-runtime"
-version = "0.7.0"
+version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c719dcf55f09a3a7e764c6649ab594c18a177e3599c467983cdf644bfc0a4088"
+checksum = "f9b0efd3ba03c3a409d44d60425f279ec442bcf0b9e63ff4e410da31c8b0f69f"
[[package]]
name = "proc-macro-error"
@@ -536,12 +554,9 @@
[[package]]
name = "textwrap"
-version = "0.14.2"
+version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
-dependencies = [
- "unicode-width",
-]
+checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
[[package]]
name = "thiserror"
@@ -562,12 +577,6 @@
"quote",
"syn",
]
-
-[[package]]
-name = "unicode-segmentation"
-version = "1.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
[[package]]
name = "unicode-width"
@@ -580,12 +589,6 @@
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
-
-[[package]]
-name = "vec_map"
-version = "0.8.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "version_check"
crates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/error.rs
+++ b/crates/jrsonnet-evaluator/src/error.rs
@@ -179,7 +179,7 @@
}
}
-pub type Result<V> = std::result::Result<V, LocError>;
+pub type Result<V, E = LocError> = std::result::Result<V, E>;
#[macro_export]
macro_rules! throw {
crates/jrsonnet-evaluator/src/function.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/function.rs
+++ b/crates/jrsonnet-evaluator/src/function.rs
@@ -8,6 +8,7 @@
};
use gcmodule::Trace;
use jrsonnet_interner::IStr;
+pub use jrsonnet_macros::builtin;
use jrsonnet_parser::{ArgsDesc, ExprLocation, LocExpr, ParamsDesc};
use std::{borrow::Cow, collections::HashMap, convert::TryFrom};
@@ -377,6 +378,7 @@
pub has_default: bool,
}
+/// Do not implement it directly, instead use #[builtin] macro
pub trait Builtin: Trace {
fn name(&self) -> &str;
fn params(&self) -> &[BuiltinParam];
crates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/lib.rs
+++ b/crates/jrsonnet-evaluator/src/lib.rs
@@ -174,7 +174,11 @@
pub(crate) static EVAL_STATE: RefCell<Option<EvaluationState>> = RefCell::new(None)
}
pub(crate) fn with_state<T>(f: impl FnOnce(&EvaluationState) -> T) -> T {
- EVAL_STATE.with(|s| f(s.borrow().as_ref().unwrap()))
+ EVAL_STATE.with(|s| {
+ f(s.borrow().as_ref().expect(
+ "missing evaluation state, some functions should be called inside of run_in_state call",
+ ))
+ })
}
pub fn push_frame<T>(
e: Option<&ExprLocation>,
@@ -728,12 +732,15 @@
}
macro_rules! eval {
- ($str: expr) => {
- EvaluationState::default()
- .with_stdlib()
- .evaluate_snippet_raw(PathBuf::from("raw.jsonnet").into(), $str.into())
- .unwrap()
- };
+ ($str: expr) => {{
+ let evaluator = EvaluationState::default();
+ evaluator.with_stdlib();
+ evaluator.run_in_state(|| {
+ evaluator
+ .evaluate_snippet_raw(PathBuf::from("raw.jsonnet").into(), $str.into())
+ .unwrap()
+ })
+ }};
}
macro_rules! eval_json {
($str: expr) => {{
@@ -1265,4 +1272,47 @@
assert_eval!(r#"std.assertEqual(std.count(["a", "b", "a"], "d"), 0)"#);
assert_eval!(r#"std.assertEqual(std.count(["a", "b", "a"], "a"), 2)"#);
}
+
+ mod derive_typed {
+ use crate::{typed::Typed, EvaluationState};
+ use std::path::PathBuf;
+
+ #[derive(Typed, PartialEq, Debug)]
+ struct MyTyped {
+ a: u32,
+ b: String,
+ }
+
+ #[test]
+ fn test() {
+ let es = EvaluationState::default();
+ let val = eval!("{a: 14, b: 'Hello, world!'}");
+ let typed = es.run_in_state(|| MyTyped::try_from(val).unwrap());
+
+ assert_eq!(
+ typed,
+ MyTyped {
+ a: 14,
+ b: "Hello, world!".to_string()
+ }
+ );
+ es.settings_mut().globals.insert(
+ "mytyped".into(),
+ es.run_in_state(|| typed.try_into()).unwrap(),
+ );
+
+ let v = es
+ .evaluate_snippet_raw(
+ PathBuf::from("raw.jsonnet").into(),
+ "
+ mytyped == {a: 14, b: 'Hello, world!'}
+ "
+ .into(),
+ )
+ .unwrap()
+ .as_bool()
+ .unwrap();
+ assert!(v)
+ }
+ }
}
crates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/typed/conversions.rs
+++ b/crates/jrsonnet-evaluator/src/typed/conversions.rs
@@ -1,6 +1,7 @@
use std::convert::{TryFrom, TryInto};
use jrsonnet_interner::IStr;
+pub use jrsonnet_macros::Typed;
use jrsonnet_types::{ComplexValType, ValType};
use crate::{
crates/jrsonnet-evaluator/src/typed/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/typed/mod.rs
+++ b/crates/jrsonnet-evaluator/src/typed/mod.rs
@@ -8,20 +8,8 @@
push_description_frame, Val,
};
use gcmodule::Trace;
-use jrsonnet_types::{ComplexValType, ValType};
+pub use jrsonnet_types::{ComplexValType, ValType};
use thiserror::Error;
-
-#[macro_export]
-macro_rules! unwrap_type {
- ($desc:expr, $value:expr, $typ:expr => $match:path) => {{
- use $crate::{push_frame, typed::CheckType};
- push_frame(None, $desc, || Ok($typ.check(&$value)?))?;
- match $value {
- $match(v) => v,
- _ => unreachable!(),
- }
- }};
-}
#[derive(Debug, Error, Clone, Trace)]
pub enum TypeError {
@@ -136,7 +124,7 @@
impl Display for ValuePathItem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
- Self::Field(name) => write!(f, ".{}", name)?,
+ Self::Field(name) => write!(f, ".{:?}", name)?,
Self::Index(idx) => write!(f, "[{}]", idx)?,
}
Ok(())
crates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth1use crate::{2 builtin::manifest::{3 manifest_json_ex, manifest_yaml_ex, ManifestJsonOptions, ManifestType, ManifestYamlOptions,4 },5 cc_ptr_eq,6 error::{Error::*, LocError},7 evaluate,8 function::{parse_function_call, ArgsLike, Builtin, StaticBuiltin},9 gc::TraceBox,10 throw, Context, ObjValue, Result,11};12use gcmodule::{Cc, Trace};13use jrsonnet_interner::IStr;14use jrsonnet_parser::{ExprLocation, LocExpr, ParamsDesc};15use jrsonnet_types::ValType;16use std::{cell::RefCell, fmt::Debug, rc::Rc};1718pub trait LazyValValue: Trace {19 fn get(self: Box<Self>) -> Result<Val>;20}2122#[derive(Trace)]23enum LazyValInternals {24 Computed(Val),25 Errored(LocError),26 Waiting(TraceBox<dyn LazyValValue>),27 Pending,28}2930#[derive(Clone, Trace)]31pub struct LazyVal(Cc<RefCell<LazyValInternals>>);32impl LazyVal {33 pub fn new(f: TraceBox<dyn LazyValValue>) -> Self {34 Self(Cc::new(RefCell::new(LazyValInternals::Waiting(f))))35 }36 pub fn new_resolved(val: Val) -> Self {37 Self(Cc::new(RefCell::new(LazyValInternals::Computed(val))))38 }39 pub fn force(&self) -> Result<()> {40 self.evaluate()?;41 Ok(())42 }43 pub fn evaluate(&self) -> Result<Val> {44 match &*self.0.borrow() {45 LazyValInternals::Computed(v) => return Ok(v.clone()),46 LazyValInternals::Errored(e) => return Err(e.clone()),47 LazyValInternals::Pending => return Err(RecursiveLazyValueEvaluation.into()),48 _ => (),49 };50 let value = if let LazyValInternals::Waiting(value) =51 std::mem::replace(&mut *self.0.borrow_mut(), LazyValInternals::Pending)52 {53 value54 } else {55 unreachable!()56 };57 let new_value = match value.0.get() {58 Ok(v) => v,59 Err(e) => {60 *self.0.borrow_mut() = LazyValInternals::Errored(e.clone());61 return Err(e);62 }63 };64 *self.0.borrow_mut() = LazyValInternals::Computed(new_value.clone());65 Ok(new_value)66 }67}6869impl Debug for LazyVal {70 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {71 write!(f, "Lazy")72 }73}74impl PartialEq for LazyVal {75 fn eq(&self, other: &Self) -> bool {76 cc_ptr_eq(&self.0, &other.0)77 }78}7980#[derive(Debug, PartialEq, Trace)]81pub struct FuncDesc {82 pub name: IStr,83 pub ctx: Context,84 pub params: ParamsDesc,85 pub body: LocExpr,86}8788#[derive(Trace, Clone)]89pub enum FuncVal {90 /// Plain function implemented in jsonnet91 Normal(Cc<FuncDesc>),92 /// Standard library function93 StaticBuiltin(#[skip_trace] &'static dyn StaticBuiltin),9495 Builtin(Cc<TraceBox<dyn Builtin>>),96}9798impl Debug for FuncVal {99 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {100 match self {101 Self::Normal(arg0) => f.debug_tuple("Normal").field(arg0).finish(),102 Self::StaticBuiltin(arg0) => f.debug_tuple("Intrinsic").field(&arg0.name()).finish(),103 Self::Builtin(arg0) => f.debug_tuple("Intrinsic").field(&arg0.name()).finish(),104 }105 }106}107108impl PartialEq for FuncVal {109 fn eq(&self, other: &Self) -> bool {110 match (self, other) {111 (Self::Normal(a), Self::Normal(b)) => a == b,112 (Self::StaticBuiltin(an), Self::StaticBuiltin(bn)) => std::ptr::eq(*an, *bn),113 (..) => false,114 }115 }116}117impl FuncVal {118 pub fn args_len(&self) -> usize {119 match self {120 Self::Normal(n) => n.params.iter().filter(|p| p.1.is_none()).count(),121 Self::StaticBuiltin(i) => i.params().iter().filter(|p| !p.has_default).count(),122 Self::Builtin(i) => i.params().iter().filter(|p| !p.has_default).count(),123 }124 }125 pub fn name(&self) -> IStr {126 match self {127 Self::Normal(normal) => normal.name.clone(),128 Self::StaticBuiltin(builtin) => builtin.name().into(),129 Self::Builtin(builtin) => builtin.name().into(),130 }131 }132 pub fn evaluate(133 &self,134 call_ctx: Context,135 loc: Option<&ExprLocation>,136 args: &dyn ArgsLike,137 tailstrict: bool,138 ) -> Result<Val> {139 match self {140 Self::Normal(func) => {141 let ctx = parse_function_call(142 call_ctx,143 func.ctx.clone(),144 &func.params,145 args,146 tailstrict,147 )?;148 evaluate(ctx, &func.body)149 }150 Self::StaticBuiltin(name) => name.call(call_ctx, loc, args),151 Self::Builtin(b) => b.call(call_ctx, loc, args),152 }153 }154 pub fn evaluate_simple(&self, args: &dyn ArgsLike) -> Result<Val> {155 self.evaluate(Context::default(), None, args, true)156 }157}158159#[derive(Clone)]160pub enum ManifestFormat {161 YamlStream(Box<ManifestFormat>),162 Yaml(usize),163 Json(usize),164 ToString,165 String,166}167168#[derive(Debug, Clone, Trace)]169#[force_tracking]170pub enum ArrValue {171 Lazy(Cc<Vec<LazyVal>>),172 Eager(Cc<Vec<Val>>),173 Extended(Box<(Self, Self)>),174}175impl ArrValue {176 pub fn new_eager() -> Self {177 Self::Eager(Cc::new(Vec::new()))178 }179180 pub fn len(&self) -> usize {181 match self {182 Self::Lazy(l) => l.len(),183 Self::Eager(e) => e.len(),184 Self::Extended(v) => v.0.len() + v.1.len(),185 }186 }187188 pub fn is_empty(&self) -> bool {189 self.len() == 0190 }191192 pub fn get(&self, index: usize) -> Result<Option<Val>> {193 match self {194 Self::Lazy(vec) => {195 if let Some(v) = vec.get(index) {196 Ok(Some(v.evaluate()?))197 } else {198 Ok(None)199 }200 }201 Self::Eager(vec) => Ok(vec.get(index).cloned()),202 Self::Extended(v) => {203 let a_len = v.0.len();204 if a_len > index {205 v.0.get(index)206 } else {207 v.1.get(index - a_len)208 }209 }210 }211 }212213 pub fn get_lazy(&self, index: usize) -> Option<LazyVal> {214 match self {215 Self::Lazy(vec) => vec.get(index).cloned(),216 Self::Eager(vec) => vec.get(index).cloned().map(LazyVal::new_resolved),217 Self::Extended(v) => {218 let a_len = v.0.len();219 if a_len > index {220 v.0.get_lazy(index)221 } else {222 v.1.get_lazy(index - a_len)223 }224 }225 }226 }227228 pub fn evaluated(&self) -> Result<Cc<Vec<Val>>> {229 Ok(match self {230 Self::Lazy(vec) => {231 let mut out = Vec::with_capacity(vec.len());232 for item in vec.iter() {233 out.push(item.evaluate()?);234 }235 Cc::new(out)236 }237 Self::Eager(vec) => vec.clone(),238 Self::Extended(_v) => {239 let mut out = Vec::with_capacity(self.len());240 for item in self.iter() {241 out.push(item?);242 }243 Cc::new(out)244 }245 })246 }247248 pub fn iter(&self) -> impl DoubleEndedIterator<Item = Result<Val>> + '_ {249 (0..self.len()).map(move |idx| match self {250 Self::Lazy(l) => l[idx].evaluate(),251 Self::Eager(e) => Ok(e[idx].clone()),252 Self::Extended(_) => self.get(idx).map(|e| e.unwrap()),253 })254 }255256 pub fn iter_lazy(&self) -> impl DoubleEndedIterator<Item = LazyVal> + '_ {257 (0..self.len()).map(move |idx| match self {258 Self::Lazy(l) => l[idx].clone(),259 Self::Eager(e) => LazyVal::new_resolved(e[idx].clone()),260 Self::Extended(_) => self.get_lazy(idx).unwrap(),261 })262 }263264 pub fn reversed(self) -> Self {265 match self {266 Self::Lazy(vec) => {267 let mut out = (&vec as &Vec<_>).clone();268 out.reverse();269 Self::Lazy(Cc::new(out))270 }271 Self::Eager(vec) => {272 let mut out = (&vec as &Vec<_>).clone();273 out.reverse();274 Self::Eager(Cc::new(out))275 }276 Self::Extended(b) => Self::Extended(Box::new((b.1.reversed(), b.0.reversed()))),277 }278 }279280 pub fn map(self, mapper: impl Fn(Val) -> Result<Val>) -> Result<Self> {281 let mut out = Vec::with_capacity(self.len());282283 for value in self.iter() {284 out.push(mapper(value?)?);285 }286287 Ok(Self::Eager(Cc::new(out)))288 }289290 pub fn filter(self, filter: impl Fn(&Val) -> Result<bool>) -> Result<Self> {291 let mut out = Vec::with_capacity(self.len());292293 for value in self.iter() {294 let value = value?;295 if filter(&value)? {296 out.push(value);297 }298 }299300 Ok(Self::Eager(Cc::new(out)))301 }302303 pub fn ptr_eq(a: &Self, b: &Self) -> bool {304 match (a, b) {305 (Self::Lazy(a), Self::Lazy(b)) => cc_ptr_eq(a, b),306 (Self::Eager(a), Self::Eager(b)) => cc_ptr_eq(a, b),307 _ => false,308 }309 }310}311312impl From<Vec<LazyVal>> for ArrValue {313 fn from(v: Vec<LazyVal>) -> Self {314 Self::Lazy(Cc::new(v))315 }316}317318impl From<Vec<Val>> for ArrValue {319 fn from(v: Vec<Val>) -> Self {320 Self::Eager(Cc::new(v))321 }322}323324pub enum IndexableVal {325 Str(IStr),326 Arr(ArrValue),327}328329#[derive(Debug, Clone, Trace)]330pub enum Val {331 Bool(bool),332 Null,333 Str(IStr),334 Num(f64),335 Arr(ArrValue),336 Obj(ObjValue),337 Func(FuncVal),338}339340impl Val {341 /// Creates `Val::Num` after checking for numeric overflow.342 /// As numbers are `f64`, we can just check for their finity.343 pub fn new_checked_num(num: f64) -> Result<Self> {344 if num.is_finite() {345 Ok(Self::Num(num))346 } else {347 throw!(RuntimeError("overflow".into()))348 }349 }350351 pub fn try_cast_nullable_num(self, context: &'static str) -> Result<Option<f64>> {352 Ok(match self {353 Val::Null => None,354 Val::Num(num) => Some(num),355 _ => throw!(TypeMismatch(356 context,357 vec![ValType::Null, ValType::Num],358 self.value_type()359 )),360 })361 }362 pub const fn value_type(&self) -> ValType {363 match self {364 Self::Str(..) => ValType::Str,365 Self::Num(..) => ValType::Num,366 Self::Arr(..) => ValType::Arr,367 Self::Obj(..) => ValType::Obj,368 Self::Bool(_) => ValType::Bool,369 Self::Null => ValType::Null,370 Self::Func(..) => ValType::Func,371 }372 }373374 pub fn to_string(&self) -> Result<IStr> {375 Ok(match self {376 Self::Bool(true) => "true".into(),377 Self::Bool(false) => "false".into(),378 Self::Null => "null".into(),379 Self::Str(s) => s.clone(),380 v => manifest_json_ex(381 v,382 &ManifestJsonOptions {383 padding: "",384 mtype: ManifestType::ToString,385 newline: "\n",386 key_val_sep: ": ",387 },388 )?389 .into(),390 })391 }392393 /// Expects value to be object, outputs (key, manifested value) pairs394 pub fn manifest_multi(&self, ty: &ManifestFormat) -> Result<Vec<(IStr, IStr)>> {395 let obj = match self {396 Self::Obj(obj) => obj,397 _ => throw!(MultiManifestOutputIsNotAObject),398 };399 let keys = obj.fields();400 let mut out = Vec::with_capacity(keys.len());401 for key in keys {402 let value = obj403 .get(key.clone())?404 .expect("item in object")405 .manifest(ty)?;406 out.push((key, value));407 }408 Ok(out)409 }410411 /// Expects value to be array, outputs manifested values412 pub fn manifest_stream(&self, ty: &ManifestFormat) -> Result<Vec<IStr>> {413 let arr = match self {414 Self::Arr(a) => a,415 _ => throw!(StreamManifestOutputIsNotAArray),416 };417 let mut out = Vec::with_capacity(arr.len());418 for i in arr.iter() {419 out.push(i?.manifest(ty)?);420 }421 Ok(out)422 }423424 pub fn manifest(&self, ty: &ManifestFormat) -> Result<IStr> {425 Ok(match ty {426 ManifestFormat::YamlStream(format) => {427 let arr = match self {428 Self::Arr(a) => a,429 _ => throw!(StreamManifestOutputIsNotAArray),430 };431 let mut out = String::new();432433 match format as &ManifestFormat {434 ManifestFormat::YamlStream(_) => throw!(StreamManifestOutputCannotBeRecursed),435 ManifestFormat::String => throw!(StreamManifestCannotNestString),436 _ => {}437 };438439 if !arr.is_empty() {440 for v in arr.iter() {441 out.push_str("---\n");442 out.push_str(&v?.manifest(format)?);443 out.push('\n');444 }445 out.push_str("...");446 }447448 out.into()449 }450 ManifestFormat::Yaml(padding) => self.to_yaml(*padding)?,451 ManifestFormat::Json(padding) => self.to_json(*padding)?,452 ManifestFormat::ToString => self.to_string()?,453 ManifestFormat::String => match self {454 Self::Str(s) => s.clone(),455 _ => throw!(StringManifestOutputIsNotAString),456 },457 })458 }459460 /// For manifestification461 pub fn to_json(&self, padding: usize) -> Result<IStr> {462 manifest_json_ex(463 self,464 &ManifestJsonOptions {465 padding: &" ".repeat(padding),466 mtype: if padding == 0 {467 ManifestType::Minify468 } else {469 ManifestType::Manifest470 },471 newline: "\n",472 key_val_sep: ": ",473 },474 )475 .map(|s| s.into())476 }477478 /// Calls `std.manifestJson`479 pub fn to_std_json(&self, padding: usize) -> Result<Rc<str>> {480 manifest_json_ex(481 self,482 &ManifestJsonOptions {483 padding: &" ".repeat(padding),484 mtype: ManifestType::Std,485 newline: "\n",486 key_val_sep: ": ",487 },488 )489 .map(|s| s.into())490 }491492 pub fn to_yaml(&self, padding: usize) -> Result<IStr> {493 let padding = &" ".repeat(padding);494 manifest_yaml_ex(495 self,496 &ManifestYamlOptions {497 padding,498 arr_element_padding: padding,499 quote_keys: false,500 },501 )502 .map(|s| s.into())503 }504 pub fn into_indexable(self) -> Result<IndexableVal> {505 Ok(match self {506 Val::Str(s) => IndexableVal::Str(s),507 Val::Arr(arr) => IndexableVal::Arr(arr),508 _ => throw!(ValueIsNotIndexable(self.value_type())),509 })510 }511}512513const fn is_function_like(val: &Val) -> bool {514 matches!(val, Val::Func(_))515}516517/// Native implementation of `std.primitiveEquals`518pub fn primitive_equals(val_a: &Val, val_b: &Val) -> Result<bool> {519 Ok(match (val_a, val_b) {520 (Val::Bool(a), Val::Bool(b)) => a == b,521 (Val::Null, Val::Null) => true,522 (Val::Str(a), Val::Str(b)) => a == b,523 (Val::Num(a), Val::Num(b)) => (a - b).abs() <= f64::EPSILON,524 (Val::Arr(_), Val::Arr(_)) => throw!(RuntimeError(525 "primitiveEquals operates on primitive types, got array".into(),526 )),527 (Val::Obj(_), Val::Obj(_)) => throw!(RuntimeError(528 "primitiveEquals operates on primitive types, got object".into(),529 )),530 (a, b) if is_function_like(a) && is_function_like(b) => {531 throw!(RuntimeError("cannot test equality of functions".into()))532 }533 (_, _) => false,534 })535}536537/// Native implementation of `std.equals`538pub fn equals(val_a: &Val, val_b: &Val) -> Result<bool> {539 if val_a.value_type() != val_b.value_type() {540 return Ok(false);541 }542 match (val_a, val_b) {543 (Val::Arr(a), Val::Arr(b)) => {544 if ArrValue::ptr_eq(a, b) {545 return Ok(true);546 }547 if a.len() != b.len() {548 return Ok(false);549 }550 for (a, b) in a.iter().zip(b.iter()) {551 if !equals(&a?, &b?)? {552 return Ok(false);553 }554 }555 Ok(true)556 }557 (Val::Obj(a), Val::Obj(b)) => {558 if ObjValue::ptr_eq(a, b) {559 return Ok(true);560 }561 let fields = a.fields();562 if fields != b.fields() {563 return Ok(false);564 }565 for field in fields {566 if !equals(&a.get(field.clone())?.unwrap(), &b.get(field)?.unwrap())? {567 return Ok(false);568 }569 }570 Ok(true)571 }572 (a, b) => Ok(primitive_equals(a, b)?),573 }574}1use crate::{2 builtin::manifest::{3 manifest_json_ex, manifest_yaml_ex, ManifestJsonOptions, ManifestType, ManifestYamlOptions,4 },5 cc_ptr_eq,6 error::{Error::*, LocError},7 evaluate,8 function::{parse_function_call, ArgsLike, Builtin, StaticBuiltin},9 gc::TraceBox,10 throw, Context, ObjValue, Result,11};12use gcmodule::{Cc, Trace};13use jrsonnet_interner::IStr;14use jrsonnet_parser::{ExprLocation, LocExpr, ParamsDesc};15use jrsonnet_types::ValType;16use std::{cell::RefCell, fmt::Debug, rc::Rc};1718pub trait LazyValValue: Trace {19 fn get(self: Box<Self>) -> Result<Val>;20}2122#[derive(Trace)]23enum LazyValInternals {24 Computed(Val),25 Errored(LocError),26 Waiting(TraceBox<dyn LazyValValue>),27 Pending,28}2930#[derive(Clone, Trace)]31pub struct LazyVal(Cc<RefCell<LazyValInternals>>);32impl LazyVal {33 pub fn new(f: TraceBox<dyn LazyValValue>) -> Self {34 Self(Cc::new(RefCell::new(LazyValInternals::Waiting(f))))35 }36 pub fn new_resolved(val: Val) -> Self {37 Self(Cc::new(RefCell::new(LazyValInternals::Computed(val))))38 }39 pub fn force(&self) -> Result<()> {40 self.evaluate()?;41 Ok(())42 }43 pub fn evaluate(&self) -> Result<Val> {44 match &*self.0.borrow() {45 LazyValInternals::Computed(v) => return Ok(v.clone()),46 LazyValInternals::Errored(e) => return Err(e.clone()),47 LazyValInternals::Pending => return Err(RecursiveLazyValueEvaluation.into()),48 _ => (),49 };50 let value = if let LazyValInternals::Waiting(value) =51 std::mem::replace(&mut *self.0.borrow_mut(), LazyValInternals::Pending)52 {53 value54 } else {55 unreachable!()56 };57 let new_value = match value.0.get() {58 Ok(v) => v,59 Err(e) => {60 *self.0.borrow_mut() = LazyValInternals::Errored(e.clone());61 return Err(e);62 }63 };64 *self.0.borrow_mut() = LazyValInternals::Computed(new_value.clone());65 Ok(new_value)66 }67}6869impl Debug for LazyVal {70 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {71 write!(f, "Lazy")72 }73}74impl PartialEq for LazyVal {75 fn eq(&self, other: &Self) -> bool {76 cc_ptr_eq(&self.0, &other.0)77 }78}7980#[derive(Debug, PartialEq, Trace)]81pub struct FuncDesc {82 pub name: IStr,83 pub ctx: Context,84 pub params: ParamsDesc,85 pub body: LocExpr,86}8788#[derive(Trace, Clone)]89pub enum FuncVal {90 /// Plain function implemented in jsonnet91 Normal(Cc<FuncDesc>),92 /// Standard library function93 StaticBuiltin(#[skip_trace] &'static dyn StaticBuiltin),94 /// User-provided function95 Builtin(Cc<TraceBox<dyn Builtin>>),96}9798impl Debug for FuncVal {99 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {100 match self {101 Self::Normal(arg0) => f.debug_tuple("Normal").field(arg0).finish(),102 Self::StaticBuiltin(arg0) => {103 f.debug_tuple("StaticBuiltin").field(&arg0.name()).finish()104 }105 Self::Builtin(arg0) => f.debug_tuple("Builtin").field(&arg0.name()).finish(),106 }107 }108}109110impl PartialEq for FuncVal {111 fn eq(&self, other: &Self) -> bool {112 match (self, other) {113 (Self::Normal(a), Self::Normal(b)) => a == b,114 (Self::StaticBuiltin(an), Self::StaticBuiltin(bn)) => std::ptr::eq(*an, *bn),115 (..) => false,116 }117 }118}119impl FuncVal {120 pub fn args_len(&self) -> usize {121 match self {122 Self::Normal(n) => n.params.iter().filter(|p| p.1.is_none()).count(),123 Self::StaticBuiltin(i) => i.params().iter().filter(|p| !p.has_default).count(),124 Self::Builtin(i) => i.params().iter().filter(|p| !p.has_default).count(),125 }126 }127 pub fn name(&self) -> IStr {128 match self {129 Self::Normal(normal) => normal.name.clone(),130 Self::StaticBuiltin(builtin) => builtin.name().into(),131 Self::Builtin(builtin) => builtin.name().into(),132 }133 }134 pub fn evaluate(135 &self,136 call_ctx: Context,137 loc: Option<&ExprLocation>,138 args: &dyn ArgsLike,139 tailstrict: bool,140 ) -> Result<Val> {141 match self {142 Self::Normal(func) => {143 let ctx = parse_function_call(144 call_ctx,145 func.ctx.clone(),146 &func.params,147 args,148 tailstrict,149 )?;150 evaluate(ctx, &func.body)151 }152 Self::StaticBuiltin(name) => name.call(call_ctx, loc, args),153 Self::Builtin(b) => b.call(call_ctx, loc, args),154 }155 }156 pub fn evaluate_simple(&self, args: &dyn ArgsLike) -> Result<Val> {157 self.evaluate(Context::default(), None, args, true)158 }159}160161#[derive(Clone)]162pub enum ManifestFormat {163 YamlStream(Box<ManifestFormat>),164 Yaml(usize),165 Json(usize),166 ToString,167 String,168}169170#[derive(Debug, Clone, Trace)]171#[force_tracking]172pub enum ArrValue {173 Lazy(Cc<Vec<LazyVal>>),174 Eager(Cc<Vec<Val>>),175 Extended(Box<(Self, Self)>),176}177impl ArrValue {178 pub fn new_eager() -> Self {179 Self::Eager(Cc::new(Vec::new()))180 }181182 pub fn len(&self) -> usize {183 match self {184 Self::Lazy(l) => l.len(),185 Self::Eager(e) => e.len(),186 Self::Extended(v) => v.0.len() + v.1.len(),187 }188 }189190 pub fn is_empty(&self) -> bool {191 self.len() == 0192 }193194 pub fn get(&self, index: usize) -> Result<Option<Val>> {195 match self {196 Self::Lazy(vec) => {197 if let Some(v) = vec.get(index) {198 Ok(Some(v.evaluate()?))199 } else {200 Ok(None)201 }202 }203 Self::Eager(vec) => Ok(vec.get(index).cloned()),204 Self::Extended(v) => {205 let a_len = v.0.len();206 if a_len > index {207 v.0.get(index)208 } else {209 v.1.get(index - a_len)210 }211 }212 }213 }214215 pub fn get_lazy(&self, index: usize) -> Option<LazyVal> {216 match self {217 Self::Lazy(vec) => vec.get(index).cloned(),218 Self::Eager(vec) => vec.get(index).cloned().map(LazyVal::new_resolved),219 Self::Extended(v) => {220 let a_len = v.0.len();221 if a_len > index {222 v.0.get_lazy(index)223 } else {224 v.1.get_lazy(index - a_len)225 }226 }227 }228 }229230 pub fn evaluated(&self) -> Result<Cc<Vec<Val>>> {231 Ok(match self {232 Self::Lazy(vec) => {233 let mut out = Vec::with_capacity(vec.len());234 for item in vec.iter() {235 out.push(item.evaluate()?);236 }237 Cc::new(out)238 }239 Self::Eager(vec) => vec.clone(),240 Self::Extended(_v) => {241 let mut out = Vec::with_capacity(self.len());242 for item in self.iter() {243 out.push(item?);244 }245 Cc::new(out)246 }247 })248 }249250 pub fn iter(&self) -> impl DoubleEndedIterator<Item = Result<Val>> + '_ {251 (0..self.len()).map(move |idx| match self {252 Self::Lazy(l) => l[idx].evaluate(),253 Self::Eager(e) => Ok(e[idx].clone()),254 Self::Extended(_) => self.get(idx).map(|e| e.unwrap()),255 })256 }257258 pub fn iter_lazy(&self) -> impl DoubleEndedIterator<Item = LazyVal> + '_ {259 (0..self.len()).map(move |idx| match self {260 Self::Lazy(l) => l[idx].clone(),261 Self::Eager(e) => LazyVal::new_resolved(e[idx].clone()),262 Self::Extended(_) => self.get_lazy(idx).unwrap(),263 })264 }265266 pub fn reversed(self) -> Self {267 match self {268 Self::Lazy(vec) => {269 let mut out = (&vec as &Vec<_>).clone();270 out.reverse();271 Self::Lazy(Cc::new(out))272 }273 Self::Eager(vec) => {274 let mut out = (&vec as &Vec<_>).clone();275 out.reverse();276 Self::Eager(Cc::new(out))277 }278 Self::Extended(b) => Self::Extended(Box::new((b.1.reversed(), b.0.reversed()))),279 }280 }281282 pub fn map(self, mapper: impl Fn(Val) -> Result<Val>) -> Result<Self> {283 let mut out = Vec::with_capacity(self.len());284285 for value in self.iter() {286 out.push(mapper(value?)?);287 }288289 Ok(Self::Eager(Cc::new(out)))290 }291292 pub fn filter(self, filter: impl Fn(&Val) -> Result<bool>) -> Result<Self> {293 let mut out = Vec::with_capacity(self.len());294295 for value in self.iter() {296 let value = value?;297 if filter(&value)? {298 out.push(value);299 }300 }301302 Ok(Self::Eager(Cc::new(out)))303 }304305 pub fn ptr_eq(a: &Self, b: &Self) -> bool {306 match (a, b) {307 (Self::Lazy(a), Self::Lazy(b)) => cc_ptr_eq(a, b),308 (Self::Eager(a), Self::Eager(b)) => cc_ptr_eq(a, b),309 _ => false,310 }311 }312}313314impl From<Vec<LazyVal>> for ArrValue {315 fn from(v: Vec<LazyVal>) -> Self {316 Self::Lazy(Cc::new(v))317 }318}319320impl From<Vec<Val>> for ArrValue {321 fn from(v: Vec<Val>) -> Self {322 Self::Eager(Cc::new(v))323 }324}325326pub enum IndexableVal {327 Str(IStr),328 Arr(ArrValue),329}330331#[derive(Debug, Clone, Trace)]332pub enum Val {333 Bool(bool),334 Null,335 Str(IStr),336 Num(f64),337 Arr(ArrValue),338 Obj(ObjValue),339 Func(FuncVal),340}341342impl Val {343 pub fn as_bool(&self) -> Option<bool> {344 match self {345 Val::Bool(v) => Some(*v),346 _ => None,347 }348 }349 pub fn as_null(&self) -> Option<()> {350 match self {351 Val::Null => Some(()),352 _ => None,353 }354 }355 pub fn as_str(&self) -> Option<IStr> {356 match self {357 Val::Str(s) => Some(s.clone()),358 _ => None,359 }360 }361 pub fn as_num(&self) -> Option<f64> {362 match self {363 Val::Num(n) => Some(*n),364 _ => None,365 }366 }367 pub fn as_arr(&self) -> Option<ArrValue> {368 match self {369 Val::Arr(a) => Some(a.clone()),370 _ => None,371 }372 }373 pub fn as_obj(&self) -> Option<ObjValue> {374 match self {375 Val::Obj(o) => Some(o.clone()),376 _ => None,377 }378 }379 pub fn as_func(&self) -> Option<FuncVal> {380 match self {381 Val::Func(f) => Some(f.clone()),382 _ => None,383 }384 }385386 /// Creates `Val::Num` after checking for numeric overflow.387 /// As numbers are `f64`, we can just check for their finity.388 pub fn new_checked_num(num: f64) -> Result<Self> {389 if num.is_finite() {390 Ok(Self::Num(num))391 } else {392 throw!(RuntimeError("overflow".into()))393 }394 }395396 pub fn try_cast_nullable_num(self, context: &'static str) -> Result<Option<f64>> {397 Ok(match self {398 Val::Null => None,399 Val::Num(num) => Some(num),400 _ => throw!(TypeMismatch(401 context,402 vec![ValType::Null, ValType::Num],403 self.value_type()404 )),405 })406 }407 pub const fn value_type(&self) -> ValType {408 match self {409 Self::Str(..) => ValType::Str,410 Self::Num(..) => ValType::Num,411 Self::Arr(..) => ValType::Arr,412 Self::Obj(..) => ValType::Obj,413 Self::Bool(_) => ValType::Bool,414 Self::Null => ValType::Null,415 Self::Func(..) => ValType::Func,416 }417 }418419 pub fn to_string(&self) -> Result<IStr> {420 Ok(match self {421 Self::Bool(true) => "true".into(),422 Self::Bool(false) => "false".into(),423 Self::Null => "null".into(),424 Self::Str(s) => s.clone(),425 v => manifest_json_ex(426 v,427 &ManifestJsonOptions {428 padding: "",429 mtype: ManifestType::ToString,430 newline: "\n",431 key_val_sep: ": ",432 },433 )?434 .into(),435 })436 }437438 /// Expects value to be object, outputs (key, manifested value) pairs439 pub fn manifest_multi(&self, ty: &ManifestFormat) -> Result<Vec<(IStr, IStr)>> {440 let obj = match self {441 Self::Obj(obj) => obj,442 _ => throw!(MultiManifestOutputIsNotAObject),443 };444 let keys = obj.fields();445 let mut out = Vec::with_capacity(keys.len());446 for key in keys {447 let value = obj448 .get(key.clone())?449 .expect("item in object")450 .manifest(ty)?;451 out.push((key, value));452 }453 Ok(out)454 }455456 /// Expects value to be array, outputs manifested values457 pub fn manifest_stream(&self, ty: &ManifestFormat) -> Result<Vec<IStr>> {458 let arr = match self {459 Self::Arr(a) => a,460 _ => throw!(StreamManifestOutputIsNotAArray),461 };462 let mut out = Vec::with_capacity(arr.len());463 for i in arr.iter() {464 out.push(i?.manifest(ty)?);465 }466 Ok(out)467 }468469 pub fn manifest(&self, ty: &ManifestFormat) -> Result<IStr> {470 Ok(match ty {471 ManifestFormat::YamlStream(format) => {472 let arr = match self {473 Self::Arr(a) => a,474 _ => throw!(StreamManifestOutputIsNotAArray),475 };476 let mut out = String::new();477478 match format as &ManifestFormat {479 ManifestFormat::YamlStream(_) => throw!(StreamManifestOutputCannotBeRecursed),480 ManifestFormat::String => throw!(StreamManifestCannotNestString),481 _ => {}482 };483484 if !arr.is_empty() {485 for v in arr.iter() {486 out.push_str("---\n");487 out.push_str(&v?.manifest(format)?);488 out.push('\n');489 }490 out.push_str("...");491 }492493 out.into()494 }495 ManifestFormat::Yaml(padding) => self.to_yaml(*padding)?,496 ManifestFormat::Json(padding) => self.to_json(*padding)?,497 ManifestFormat::ToString => self.to_string()?,498 ManifestFormat::String => match self {499 Self::Str(s) => s.clone(),500 _ => throw!(StringManifestOutputIsNotAString),501 },502 })503 }504505 /// For manifestification506 pub fn to_json(&self, padding: usize) -> Result<IStr> {507 manifest_json_ex(508 self,509 &ManifestJsonOptions {510 padding: &" ".repeat(padding),511 mtype: if padding == 0 {512 ManifestType::Minify513 } else {514 ManifestType::Manifest515 },516 newline: "\n",517 key_val_sep: ": ",518 },519 )520 .map(|s| s.into())521 }522523 /// Calls `std.manifestJson`524 pub fn to_std_json(&self, padding: usize) -> Result<Rc<str>> {525 manifest_json_ex(526 self,527 &ManifestJsonOptions {528 padding: &" ".repeat(padding),529 mtype: ManifestType::Std,530 newline: "\n",531 key_val_sep: ": ",532 },533 )534 .map(|s| s.into())535 }536537 pub fn to_yaml(&self, padding: usize) -> Result<IStr> {538 let padding = &" ".repeat(padding);539 manifest_yaml_ex(540 self,541 &ManifestYamlOptions {542 padding,543 arr_element_padding: padding,544 quote_keys: false,545 },546 )547 .map(|s| s.into())548 }549 pub fn into_indexable(self) -> Result<IndexableVal> {550 Ok(match self {551 Val::Str(s) => IndexableVal::Str(s),552 Val::Arr(arr) => IndexableVal::Arr(arr),553 _ => throw!(ValueIsNotIndexable(self.value_type())),554 })555 }556}557558const fn is_function_like(val: &Val) -> bool {559 matches!(val, Val::Func(_))560}561562/// Native implementation of `std.primitiveEquals`563pub fn primitive_equals(val_a: &Val, val_b: &Val) -> Result<bool> {564 Ok(match (val_a, val_b) {565 (Val::Bool(a), Val::Bool(b)) => a == b,566 (Val::Null, Val::Null) => true,567 (Val::Str(a), Val::Str(b)) => a == b,568 (Val::Num(a), Val::Num(b)) => (a - b).abs() <= f64::EPSILON,569 (Val::Arr(_), Val::Arr(_)) => throw!(RuntimeError(570 "primitiveEquals operates on primitive types, got array".into(),571 )),572 (Val::Obj(_), Val::Obj(_)) => throw!(RuntimeError(573 "primitiveEquals operates on primitive types, got object".into(),574 )),575 (a, b) if is_function_like(a) && is_function_like(b) => {576 throw!(RuntimeError("cannot test equality of functions".into()))577 }578 (_, _) => false,579 })580}581582/// Native implementation of `std.equals`583pub fn equals(val_a: &Val, val_b: &Val) -> Result<bool> {584 if val_a.value_type() != val_b.value_type() {585 return Ok(false);586 }587 match (val_a, val_b) {588 (Val::Arr(a), Val::Arr(b)) => {589 if ArrValue::ptr_eq(a, b) {590 return Ok(true);591 }592 if a.len() != b.len() {593 return Ok(false);594 }595 for (a, b) in a.iter().zip(b.iter()) {596 if !equals(&a?, &b?)? {597 return Ok(false);598 }599 }600 Ok(true)601 }602 (Val::Obj(a), Val::Obj(b)) => {603 if ObjValue::ptr_eq(a, b) {604 return Ok(true);605 }606 let fields = a.fields();607 if fields != b.fields() {608 return Ok(false);609 }610 for field in fields {611 if !equals(&a.get(field.clone())?.unwrap(), &b.get(field)?.unwrap())? {612 return Ok(false);613 }614 }615 Ok(true)616 }617 (a, b) => Ok(primitive_equals(a, b)?),618 }619}crates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-macros/src/lib.rs
+++ b/crates/jrsonnet-macros/src/lib.rs
@@ -1,8 +1,8 @@
use quote::{quote, quote_spanned};
use syn::{
parenthesized, parse::Parse, parse_macro_input, punctuated::Punctuated, spanned::Spanned,
- token::Comma, FnArg, GenericArgument, Ident, ItemFn, Pat, PatType, Path, PathArguments, Token,
- Type,
+ token::Comma, DeriveInput, FnArg, GenericArgument, Ident, ItemFn, Pat, PatType, Path,
+ PathArguments, Token, Type,
};
fn is_location_arg(t: &PatType) -> bool {
@@ -254,3 +254,86 @@
})
.into()
}
+
+#[proc_macro_derive(Typed)]
+pub fn derive_typed(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
+ let input = parse_macro_input!(item as DeriveInput);
+ let data = match &input.data {
+ syn::Data::Struct(s) => s,
+ _ => {
+ return syn::Error::new(input.span(), "only structs supported")
+ .to_compile_error()
+ .into()
+ }
+ };
+
+ let ident = &input.ident;
+
+ let fields_def = data.fields.iter().map(|f| {
+ let name = f
+ .ident
+ .as_ref()
+ .expect("only named fields supported")
+ .to_string();
+ let ty = &f.ty;
+ quote! {
+ (#name, #ty::TYPE),
+ }
+ });
+ let fields_parse = data.fields.iter().map(|f| {
+ let ident = f.ident.as_ref().unwrap();
+ let name = ident.to_string();
+ let ty = &f.ty;
+ quote! {
+ #ident: #ty::try_from(obj.get(#name.into())?.expect("shape is correct"))?,
+ }
+ });
+ let fields_serialize = data.fields.iter().map(|f| {
+ let ident = f.ident.as_ref().unwrap();
+ let name = ident.to_string();
+ quote! {
+ out.member(#name.into()).value(self.#ident.try_into()?);
+ }
+ });
+ let field_count = data.fields.len();
+
+ quote! {
+ const _: () = {
+ use ::jrsonnet_evaluator::{
+ typed::{ComplexValType, Typed, CheckType},
+ Val,
+ error::LocError,
+ obj::ObjValueBuilder,
+ };
+
+ const ITEMS: [(&'static str, &'static ComplexValType); #field_count] = [
+ #(#fields_def)*
+ ];
+ impl Typed for #ident {
+ const TYPE: &'static ComplexValType = &ComplexValType::ObjectRef(&ITEMS);
+ }
+
+ impl TryFrom<Val> for #ident {
+ type Error = LocError;
+ fn try_from(value: Val) -> Result<Self, Self::Error> {
+ <Self as Typed>::TYPE.check(&value)?;
+ let obj = value.as_obj().expect("shape is correct");
+
+ Ok(Self {
+ #(#fields_parse)*
+ })
+ }
+ }
+ impl TryInto<Val> for #ident {
+ type Error = LocError;
+ fn try_into(self) -> Result<Val, Self::Error> {
+ let mut out = ObjValueBuilder::new();
+ #(#fields_serialize)*
+ Ok(Val::Obj(out.build()))
+ }
+ }
+ ()
+ };
+ }
+ .into()
+}
crates/jrsonnet-types/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-types/src/lib.rs
+++ b/crates/jrsonnet-types/src/lib.rs
@@ -122,7 +122,7 @@
BoundedNumber(Option<f64>, Option<f64>),
Array(Box<ComplexValType>),
ArrayRef(&'static ComplexValType),
- ObjectRef(&'static [(&'static str, ComplexValType)]),
+ ObjectRef(&'static [(&'static str, &'static ComplexValType)]),
Union(Vec<ComplexValType>),
UnionRef(&'static [&'static ComplexValType]),
Sum(Vec<ComplexValType>),