From 5eb89be68246f1e5a8cd28e48d5cec75921ca97a Mon Sep 17 00:00:00 2001 From: Griffin Smith Date: Mon, 10 Oct 2022 00:32:57 -0400 Subject: feat(tvix/eval): Implement builtins.fromJSON Using `serde_json` for parsing JSON here, plus an `impl FromJSON for Value`. The latter is primarily to stay "dependency light" for now - likely going with an actual serde `Deserialize` impl in the future is going to be way better as it allows saving significantly on intermediary allocations. Change-Id: I152a0448ff7c87cf7ebaac927c38912b99de1c18 Reviewed-on: https://cl.tvl.fyi/c/depot/+/6920 Tested-by: BuildkiteCI Reviewed-by: tazjin --- tvix/eval/src/value/attrs.rs | 14 +++++++++---- tvix/eval/src/value/mod.rs | 48 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 4 deletions(-) (limited to 'tvix/eval/src/value') diff --git a/tvix/eval/src/value/attrs.rs b/tvix/eval/src/value/attrs.rs index 318a8cfa8209..e9d5a239a3cf 100644 --- a/tvix/eval/src/value/attrs.rs +++ b/tvix/eval/src/value/attrs.rs @@ -274,6 +274,12 @@ impl NixAttrs { NixAttrs(AttrsRep::Map(map)) } + /// Construct an optimized "KV"-style attribute set given the value for the + /// `"name"` key, and the value for the `"value"` key + pub(crate) fn from_kv(name: Value, value: Value) -> Self { + NixAttrs(AttrsRep::KV { name, value }) + } + /// Compare `self` against `other` for equality using Nix equality semantics pub fn nix_eq(&self, other: &Self, vm: &mut VM) -> Result { match (&self.0, &other.0) { @@ -376,10 +382,10 @@ fn attempt_optimise_kv(slice: &mut [Value]) -> Option { } }; - Some(NixAttrs(AttrsRep::KV { - name: slice[name_idx].clone(), - value: slice[value_idx].clone(), - })) + Some(NixAttrs::from_kv( + slice[name_idx].clone(), + slice[value_idx].clone(), + )) } /// Set an attribute on an in-construction attribute set, while diff --git a/tvix/eval/src/value/mod.rs b/tvix/eval/src/value/mod.rs index 8672ffc1bb89..175b33bfa2e8 100644 --- a/tvix/eval/src/value/mod.rs +++ b/tvix/eval/src/value/mod.rs @@ -390,6 +390,54 @@ impl From for Value { } } +impl From> for Value { + fn from(val: Vec) -> Self { + Self::List(NixList::from(val)) + } +} + +impl TryFrom for Value { + type Error = ErrorKind; + + fn try_from(value: serde_json::Value) -> Result { + // TODO(grfn): Replace with a real serde::Deserialize impl (for perf) + match value { + serde_json::Value::Null => Ok(Self::Null), + serde_json::Value::Bool(b) => Ok(Self::Bool(b)), + serde_json::Value::Number(n) => { + if let Some(i) = n.as_i64() { + Ok(Self::Integer(i)) + } else if let Some(f) = n.as_f64() { + Ok(Self::Float(f)) + } else { + Err(ErrorKind::FromJsonError(format!( + "JSON number not representable as Nix value: {n}" + ))) + } + } + serde_json::Value::String(s) => Ok(s.into()), + serde_json::Value::Array(a) => Ok(a + .into_iter() + .map(Value::try_from) + .collect::, _>>()? + .into()), + serde_json::Value::Object(obj) => { + match (obj.len(), obj.get("name"), obj.get("value")) { + (2, Some(name), Some(value)) => Ok(Self::attrs(NixAttrs::from_kv( + name.clone().try_into()?, + value.clone().try_into()?, + ))), + _ => Ok(Self::attrs(NixAttrs::from_map( + obj.into_iter() + .map(|(k, v)| Ok((k.into(), v.try_into()?))) + .collect::>()?, + ))), + } + } + } + } +} + fn type_error(expected: &'static str, actual: &Value) -> ErrorKind { ErrorKind::TypeError { expected, -- cgit 1.4.1