difftreelog
refactor! implement serde support directly
in: master
Instead of relying on `serde_json::Value`. BREAKING CHANGE: There is no longer `impl Typed for serde_json::Value`, either parse directly into jrsonnet-evaluator, or use `serde_transcode`
9 files changed
.envrcdiffbeforeafterboth--- /dev/null
+++ b/.envrc
@@ -0,0 +1 @@
+use flake
cmds/jrsonnet/Cargo.tomldiffbeforeafterboth--- a/cmds/jrsonnet/Cargo.toml
+++ b/cmds/jrsonnet/Cargo.toml
@@ -13,9 +13,7 @@
# Experimental feature, which allows to preserve order of object fields
exp-preserve-order = [
"jrsonnet-evaluator/exp-preserve-order",
- "jrsonnet-evaluator/exp-serde-preserve-order",
"jrsonnet-cli/exp-preserve-order",
- "jrsonnet-cli/exp-serde-preserve-order",
]
# Destructuring of locals
exp-destruct = ["jrsonnet-evaluator/exp-destruct"]
crates/jrsonnet-cli/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-cli/Cargo.toml
+++ b/crates/jrsonnet-cli/Cargo.toml
@@ -11,10 +11,6 @@
"jrsonnet-evaluator/exp-preserve-order",
"jrsonnet-stdlib/exp-preserve-order",
]
-exp-serde-preserve-order = [
- "jrsonnet-evaluator/exp-serde-preserve-order",
- "jrsonnet-stdlib/exp-serde-preserve-order",
-]
legacy-this-file = ["jrsonnet-stdlib/legacy-this-file"]
[dependencies]
crates/jrsonnet-evaluator/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-evaluator/Cargo.toml
+++ b/crates/jrsonnet-evaluator/Cargo.toml
@@ -18,7 +18,6 @@
# Allows to preserve field order in objects
exp-preserve-order = []
-exp-serde-preserve-order = ["serde_json/preserve_order"]
# Implements field destructuring
exp-destruct = ["jrsonnet-parser/exp-destruct"]
# Provide Typed for conversions to/from serde_json::Value type
@@ -40,8 +39,6 @@
thiserror = "1.0"
serde = "1.0"
-# Optional integration
-serde_json = { version = "1.0.82", optional = true }
anyhow = { version = "1.0", optional = true }
# Friendly errors
crates/jrsonnet-evaluator/src/integrations/serde.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/integrations/serde.rs
+++ b/crates/jrsonnet-evaluator/src/integrations/serde.rs
@@ -1,78 +1,164 @@
-use jrsonnet_types::ComplexValType;
-use serde_json::{Map, Number, Value};
+use std::borrow::Cow;
+
+use jrsonnet_gcmodule::Cc;
+use serde::{de::Visitor, ser::Error, Deserialize, Serialize};
+
+use crate::{error::Result, val::ArrValue, ObjValueBuilder, State, Val};
-use crate::{
- error::{Error::*, Result},
- throw,
- typed::Typed,
- ObjValueBuilder, State, Val,
-};
+impl<'de> Deserialize<'de> for Val {
+ fn deserialize<D>(deserializer: D) -> Result<Val, D::Error>
+ where
+ D: serde::Deserializer<'de>,
+ {
+ struct ValVisitor;
+
+ // macro_rules! visit_num {
+ // ($($method:ident => $ty:ty),* $(,)?) => {$(
+ // fn $method<E>(self, v: $ty) -> Result<Self::Value, E>
+ // where
+ // E: serde::de::Error,
+ // {
+ // Ok(Val::Num(f64::from(v)))
+ // }
+ // )*};
+ // }
-impl Typed for Value {
- const TYPE: &'static ComplexValType = &ComplexValType::Any;
+ impl<'de> Visitor<'de> for ValVisitor {
+ type Value = Val;
- fn into_untyped(value: Self, s: State) -> Result<Val> {
- Ok(match value {
- Self::Null => Val::Null,
- Self::Bool(v) => Val::Bool(v),
- Self::Number(n) => Val::Num(n.as_f64().ok_or_else(|| {
- RuntimeError(format!("json number can't be represented as jsonnet: {n}").into())
- })?),
- Self::String(s) => Val::Str((&s as &str).into()),
- Self::Array(a) => {
- let mut out: Vec<Val> = Vec::with_capacity(a.len());
- for v in a {
- out.push(Self::into_untyped(v, s.clone())?);
+ fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ Ok(Val::Bool(v))
+ }
+ fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ if !v.is_finite() {
+ return Err(E::custom("only finite numbers are supported"));
}
- Val::Arr(out.into())
+ Ok(Val::Num(v))
+ }
+ fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ Ok(Val::Str(v.into()))
+ }
+
+ // visit_num! {
+ // visit_i8 => i8,
+ // visit_i16 => i16,
+ // visit_i32 => i32,
+ // visit_u8 => u8,
+ // visit_u16 => u16,
+ // visit_u32 => u32,
+ // }
+ fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ Ok(Val::Num(v as f64))
+ }
+ fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ Ok(Val::Num(v as f64))
+ }
+
+ fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ Ok(Val::Arr(ArrValue::Bytes(v.into())))
+ }
+
+ fn visit_none<E>(self) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ Ok(Val::Null)
+ }
+ fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
+ where
+ D: serde::Deserializer<'de>,
+ {
+ deserializer.deserialize_any(self)
+ }
+
+ fn visit_unit<E>(self) -> Result<Self::Value, E>
+ where
+ E: serde::de::Error,
+ {
+ Ok(Val::Null)
}
- Self::Object(o) => {
- let mut builder = ObjValueBuilder::with_capacity(o.len());
- for (k, v) in o {
- builder
- .member((&k as &str).into())
- .value(s.clone(), Self::into_untyped(v, s.clone())?)?;
+
+ fn visit_newtype_struct<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
+ where
+ D: serde::Deserializer<'de>,
+ {
+ deserializer.deserialize_any(self)
+ }
+
+ fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
+ where
+ A: serde::de::SeqAccess<'de>,
+ {
+ let mut out = seq.size_hint().map_or_else(Vec::new, Vec::with_capacity);
+
+ while let Some(val) = seq.next_element::<Val>()? {
+ out.push(val);
}
- Val::Obj(builder.build())
+
+ Ok(Val::Arr(ArrValue::Eager(Cc::new(out))))
}
- })
- }
- fn from_untyped(value: Val, s: State) -> Result<Self> {
- Ok(match value {
- Val::Bool(b) => Self::Bool(b),
- Val::Null => Self::Null,
- Val::Str(s) => Self::String((&s as &str).into()),
- Val::Num(n) => Self::Number(if n.fract() <= f64::EPSILON {
- (n as i64).into()
- } else {
- Number::from_f64(n).expect("jsonnet numbers can't be infinite or NaN")
- }),
- Val::Arr(a) => {
- let mut out = Vec::with_capacity(a.len());
- for item in a.iter(s.clone()) {
- out.push(Self::from_untyped(item?, s.clone())?);
+ fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
+ where
+ A: serde::de::MapAccess<'de>,
+ {
+ let mut out = map
+ .size_hint()
+ .map_or_else(ObjValueBuilder::new, ObjValueBuilder::with_capacity);
+
+ while let Some((k, v)) = map.next_entry::<Cow<'de, str>, Val>()? {
+ // Jsonnet ignores duplicate keys
+ out.member(k.into()).value_unchecked(v);
}
- Self::Array(out)
+
+ Ok(Val::Obj(out.build()))
}
- Val::Obj(o) => {
- let mut out = Map::new();
- for key in o.fields(
- #[cfg(feature = "exp-preserve-order")]
- cfg!(feature = "exp-serde-preserve-order"),
- ) {
- out.insert(
- (&key as &str).into(),
- Self::from_untyped(
- o.get(s.clone(), key)?
- .expect("key is present in fields, so value should exist"),
- s.clone(),
- )?,
- );
+
+ fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(formatter, "any valid jsonnet value")
+ }
+ }
+ deserializer.deserialize_any(ValVisitor)
+ }
+}
+
+impl Serialize for Val {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: serde::Serializer,
+ {
+ match self {
+ Val::Bool(v) => serializer.serialize_bool(*v),
+ Val::Null => serializer.serialize_none(),
+ Val::Str(s) => serializer.serialize_str(s),
+ Val::Num(n) => serializer.serialize_f64(*n),
+ Val::Arr(arr) => {
+ let mut seq = serializer.serialize_seq(Some(arr.len()))?;
+ for element in arr.iter(State::default()) {
+ // seq.serialize_element()
}
- Self::Object(out)
+ todo!()
}
- Val::Func(_) => throw!("tried to manifest function"),
- })
+ Val::Obj(_) => todo!(),
+ Val::Func(_) => Err(S::Error::custom("tried to manifest function")),
+ }
}
}
crates/jrsonnet-stdlib/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-stdlib/Cargo.toml
+++ b/crates/jrsonnet-stdlib/Cargo.toml
@@ -15,18 +15,9 @@
legacy-this-file = []
# Add order preservation flag to some functions
exp-preserve-order = ["jrsonnet-evaluator/exp-preserve-order"]
-# Preserve order for files parsed via `std.parseJson`
-# Shame it isn't possible to enable per parse call, instead of globally
-exp-serde-preserve-order = [
- "serde_json/preserve_order",
- "jrsonnet-evaluator/exp-serde-preserve-order",
-]
[dependencies]
-jrsonnet-evaluator = { path = "../jrsonnet-evaluator", features = [
- # std.parseJson parses file via serde, then converts Value to evaluator Val
- "serde_json",
-], version = "0.4.2" }
+jrsonnet-evaluator = { path = "../jrsonnet-evaluator", version = "0.4.2" }
jrsonnet-macros = { path = "../jrsonnet-macros", version = "0.4.2" }
jrsonnet-parser = { path = "../jrsonnet-parser", version = "0.4.2" }
jrsonnet-gcmodule = "0.3.4"
crates/jrsonnet-stdlib/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-stdlib/src/lib.rs
+++ b/crates/jrsonnet-stdlib/src/lib.rs
@@ -6,16 +6,13 @@
use jrsonnet_evaluator::{
error::{Error::*, Result},
- function::{builtin::Builtin, ArgLike, CallLocation, FuncVal, TlaArg},
+ function::{builtin::Builtin, CallLocation, FuncVal, TlaArg},
gc::{GcHashMap, TraceBox},
- tb, throw,
+ tb,
trace::PathResolver,
- typed::{Any, Either, Either2, Either4, VecVal, M1},
- val::{equals, ArrValue},
Context, ContextBuilder, IStr, ObjValue, ObjValueBuilder, State, Thunk, Val,
};
use jrsonnet_gcmodule::Cc;
-use jrsonnet_macros::builtin;
use jrsonnet_parser::Source;
mod expr;
crates/jrsonnet-stdlib/src/parse.rsdiffbeforeafterboth1use jrsonnet_evaluator::{1use jrsonnet_evaluator::{2 error::{Error::RuntimeError, Result},2 error::{Error::RuntimeError, Result},3 function::builtin,3 function::builtin,4 typed::{Any, Typed},4 typed::Any,5 IStr, State, Val,5 IStr, Val,6};6};7use serde::Deserialize;7use serde::Deserialize;889#[builtin]9#[builtin]10pub fn builtin_parse_json(st: State, s: IStr) -> Result<Any> {10pub fn builtin_parse_json(s: IStr) -> Result<Any> {11 use serde_json::Value;12 let value: Value = serde_json::from_str(&s)11 let value: Val = serde_json::from_str(&s)13 .map_err(|e| RuntimeError(format!("failed to parse json: {}", e).into()))?;12 .map_err(|e| RuntimeError(format!("failed to parse json: {}", e).into()))?;14 Ok(Any(Value::into_untyped(value, st)?))13 Ok(Any(value))15}14}161517#[builtin]16#[builtin]18pub fn builtin_parse_yaml(st: State, s: IStr) -> Result<Any> {17pub fn builtin_parse_yaml(s: IStr) -> Result<Any> {19 use serde_json::Value;20 use serde_yaml_with_quirks::DeserializingQuirks;18 use serde_yaml_with_quirks::DeserializingQuirks;21 let value = serde_yaml_with_quirks::Deserializer::from_str_with_quirks(19 let value = serde_yaml_with_quirks::Deserializer::from_str_with_quirks(22 &s,20 &s,23 DeserializingQuirks { old_octals: true },21 DeserializingQuirks { old_octals: true },24 );22 );25 let mut out = vec![];23 let mut out = vec![];26 for item in value {24 for item in value {27 let value = Value::deserialize(item)25 let val = Val::deserialize(item)28 .map_err(|e| RuntimeError(format!("failed to parse yaml: {}", e).into()))?;26 .map_err(|e| RuntimeError(format!("failed to parse yaml: {}", e).into()))?;29 let val = Value::into_untyped(value, st.clone())?;30 out.push(val);27 out.push(val);31 }28 }32 Ok(Any(if out.is_empty() {29 Ok(Any(if out.is_empty() {flake.nixdiffbeforeafterboth--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,28 @@
+{
+ description = "Jrsonnet";
+ inputs = {
+ nixpkgs.url = "github:nixos/nixpkgs";
+ flake-utils.url = "github:numtide/flake-utils";
+ rust-overlay.url = "github:oxalica/rust-overlay";
+ };
+ outputs = { nixpkgs, flake-utils, rust-overlay, ... }:
+ flake-utils.lib.eachDefaultSystem (system:
+ let
+ pkgs = import nixpkgs {
+ inherit system;
+ overlays = [ rust-overlay.overlays.default ];
+ };
+ rust = ((pkgs.rustChannelOf { date = "2022-10-22"; channel = "nightly"; }).default.override {
+ extensions = [ "rust-src" ];
+ });
+ in
+ rec {
+ devShell = pkgs.mkShell {
+ nativeBuildInputs = with pkgs;[
+ rust
+ cargo-edit
+ ];
+ };
+ }
+ );
+}