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 <tazjin@tvl.su>
This commit is contained in:
Griffin Smith 2022-10-10 00:32:57 -04:00 committed by grfn
parent 277c69cbe5
commit 5eb89be682
12 changed files with 123 additions and 13 deletions

View file

@ -390,6 +390,54 @@ impl From<PathBuf> for Value {
}
}
impl From<Vec<Value>> for Value {
fn from(val: Vec<Value>) -> Self {
Self::List(NixList::from(val))
}
}
impl TryFrom<serde_json::Value> for Value {
type Error = ErrorKind;
fn try_from(value: serde_json::Value) -> Result<Self, Self::Error> {
// 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::<Result<Vec<_>, _>>()?
.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::<Result<_, ErrorKind>>()?,
))),
}
}
}
}
}
fn type_error(expected: &'static str, actual: &Value) -> ErrorKind {
ErrorKind::TypeError {
expected,