fix(tvix/eval): implement cppnix JSON-serialisation semantics

This drops the usage of serde::Serialize, as the trait can not be used
to implement the correct semantics (function colouring!).

Instead, a manual JSON serialisation function is written which
correctly handles toString, outPath and other similar weirdnesses.

Unexpectedly, the eval-okay-tojson test from the C++ Nix test suite
now passes, too.

This fixes an issue where serialising data structures containing
derivations to JSON would fail.

Change-Id: I5c39e3d8356ee93a07eda481410f88610f6dd9f8
Reviewed-on: https://cl.tvl.fyi/c/depot/+/8209
Reviewed-by: raitobezarius <tvl@lahfa.xyz>
Tested-by: BuildkiteCI
This commit is contained in:
Vincent Ambo 2023-03-04 02:12:06 +03:00 committed by tazjin
parent 1e37f8b52e
commit 939cebd0f1
12 changed files with 138 additions and 70 deletions

View file

@ -359,36 +359,8 @@ mod pure_builtins {
#[builtin("toJSON")]
async fn builtin_to_json(co: GenCo, val: Value) -> Result<Value, ErrorKind> {
let mut val = val; // shadow mutably, not supported by macro
loop {
if let Value::Attrs(attrs) = &val {
// Attribute sets with a callable `__toString` attribute
// serialise to the string-coerced version of the result of
// calling that.
if let Some(s) = attrs.try_to_string(&co, CoercionKind::Weak).await {
return Ok(Value::String(serde_json::to_string(&s)?.into()));
}
// Attribute sets with an `outPath` attribute
// serialise to a JSON serialisation of that inner
// value (regardless of what it is!).
if let Some(out_path) = attrs.select("outPath") {
val = out_path.clone();
continue;
}
// Attribute set should be serialised normally (by
// traversing it and serialising keys/values).
break;
}
break;
}
// All thunks need to be evaluated before serialising, as the
// data structure is fully traversed by the Serializer.
let val = generators::request_deep_force(&co, val, SharedThunkSet::default()).await;
let json_str = serde_json::to_string(&val)?;
let json_value = val.to_json(&co).await?;
let json_str = serde_json::to_string(&json_value)?;
Ok(json_str.into())
}