`toJSON` transform a Nix structure into a JSON string. For each context in that Nix structure, the JSON string must possess it. Thus, it is necessary to take the union of all contexts and attach it to the final structure. Unfortunately, the return type of `into_json` is a serde's JSON object, not a string. Therefore, it is not possible to reuse `NixString` machinery. Context tests are reinforced as Nix does not test those behaviors. Fixes b/393. Change-Id: I5afdbc4e18dd70469192c1aa657d1049ba330149 Signed-off-by: Ryan Lahfa <tvl@lahfa.xyz> Reviewed-on: https://cl.tvl.fyi/c/depot/+/11266 Reviewed-by: flokli <flokli@flokli.de> Tested-by: BuildkiteCI
475 lines
13 KiB
Rust
475 lines
13 KiB
Rust
//! Deserialisation from Nix to Rust values.
|
|
|
|
use bstr::ByteSlice;
|
|
use serde::de::value::{MapDeserializer, SeqDeserializer};
|
|
use serde::de::{self, EnumAccess, VariantAccess};
|
|
pub use tvix_eval::Evaluation;
|
|
use tvix_eval::{EvalIO, Value};
|
|
|
|
use crate::error::Error;
|
|
|
|
struct NixDeserializer {
|
|
value: tvix_eval::Value,
|
|
}
|
|
|
|
impl NixDeserializer {
|
|
fn new(value: Value) -> Self {
|
|
if let Value::Thunk(thunk) = value {
|
|
Self::new(thunk.value().clone())
|
|
} else {
|
|
Self { value }
|
|
}
|
|
}
|
|
}
|
|
|
|
impl de::IntoDeserializer<'_, Error> for NixDeserializer {
|
|
type Deserializer = Self;
|
|
|
|
fn into_deserializer(self) -> Self::Deserializer {
|
|
self
|
|
}
|
|
}
|
|
|
|
/// Evaluate the Nix code in `src` and attempt to deserialise the
|
|
/// value it returns to `T`.
|
|
pub fn from_str<'code, T>(src: &'code str) -> Result<T, Error>
|
|
where
|
|
T: serde::Deserialize<'code>,
|
|
{
|
|
from_str_with_config(src, |_| /* no extra config */ ())
|
|
}
|
|
|
|
/// Evaluate the Nix code in `src`, with extra configuration for the
|
|
/// `tvix_eval::Evaluation` provided by the given closure.
|
|
pub fn from_str_with_config<'code, T, F>(src: &'code str, config: F) -> Result<T, Error>
|
|
where
|
|
T: serde::Deserialize<'code>,
|
|
F: FnOnce(&mut Evaluation<Box<dyn EvalIO>>),
|
|
{
|
|
// First step is to evaluate the Nix code ...
|
|
let mut eval = Evaluation::new_pure();
|
|
config(&mut eval);
|
|
|
|
eval.strict = true;
|
|
let result = eval.evaluate(src, None);
|
|
|
|
if !result.errors.is_empty() {
|
|
return Err(Error::NixErrors {
|
|
errors: result.errors,
|
|
});
|
|
}
|
|
|
|
let de = NixDeserializer::new(result.value.expect("value should be present on success"));
|
|
|
|
T::deserialize(de)
|
|
}
|
|
|
|
fn unexpected(expected: &'static str, got: &Value) -> Error {
|
|
Error::UnexpectedType {
|
|
expected,
|
|
got: got.type_of(),
|
|
}
|
|
}
|
|
|
|
fn visit_integer<I: TryFrom<i64>>(v: &Value) -> Result<I, Error> {
|
|
match v {
|
|
Value::Integer(i) => I::try_from(*i).map_err(|_| Error::IntegerConversion {
|
|
got: *i,
|
|
need: std::any::type_name::<I>(),
|
|
}),
|
|
|
|
_ => Err(unexpected("integer", v)),
|
|
}
|
|
}
|
|
|
|
impl<'de> de::Deserializer<'de> for NixDeserializer {
|
|
type Error = Error;
|
|
|
|
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
|
where
|
|
V: de::Visitor<'de>,
|
|
{
|
|
match self.value {
|
|
Value::Null => visitor.visit_unit(),
|
|
Value::Bool(b) => visitor.visit_bool(b),
|
|
Value::Integer(i) => visitor.visit_i64(i),
|
|
Value::Float(f) => visitor.visit_f64(f),
|
|
Value::String(s) => visitor.visit_string(s.to_string()),
|
|
Value::Path(p) => visitor.visit_string(p.to_string_lossy().into()), // TODO: hmm
|
|
Value::Attrs(_) => self.deserialize_map(visitor),
|
|
Value::List(_) => self.deserialize_seq(visitor),
|
|
|
|
// tvix-eval types that can not be deserialized through serde.
|
|
Value::Closure(_)
|
|
| Value::Builtin(_)
|
|
| Value::Thunk(_)
|
|
| Value::AttrNotFound
|
|
| Value::Blueprint(_)
|
|
| Value::DeferredUpvalue(_)
|
|
| Value::UnresolvedPath(_)
|
|
| Value::Json(..)
|
|
| Value::Catchable(_)
|
|
| Value::FinaliseRequest(_) => Err(Error::Unserializable {
|
|
value_type: self.value.type_of(),
|
|
}),
|
|
}
|
|
}
|
|
|
|
fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
|
where
|
|
V: de::Visitor<'de>,
|
|
{
|
|
match self.value {
|
|
Value::Bool(b) => visitor.visit_bool(b),
|
|
_ => Err(unexpected("bool", &self.value)),
|
|
}
|
|
}
|
|
|
|
fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
|
where
|
|
V: de::Visitor<'de>,
|
|
{
|
|
visitor.visit_i8(visit_integer(&self.value)?)
|
|
}
|
|
|
|
fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
|
where
|
|
V: de::Visitor<'de>,
|
|
{
|
|
visitor.visit_i16(visit_integer(&self.value)?)
|
|
}
|
|
|
|
fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
|
where
|
|
V: de::Visitor<'de>,
|
|
{
|
|
visitor.visit_i32(visit_integer(&self.value)?)
|
|
}
|
|
|
|
fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
|
where
|
|
V: de::Visitor<'de>,
|
|
{
|
|
visitor.visit_i64(visit_integer(&self.value)?)
|
|
}
|
|
|
|
fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
|
where
|
|
V: de::Visitor<'de>,
|
|
{
|
|
visitor.visit_u8(visit_integer(&self.value)?)
|
|
}
|
|
|
|
fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
|
where
|
|
V: de::Visitor<'de>,
|
|
{
|
|
visitor.visit_u16(visit_integer(&self.value)?)
|
|
}
|
|
|
|
fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
|
where
|
|
V: de::Visitor<'de>,
|
|
{
|
|
visitor.visit_u32(visit_integer(&self.value)?)
|
|
}
|
|
|
|
fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
|
where
|
|
V: de::Visitor<'de>,
|
|
{
|
|
visitor.visit_u64(visit_integer(&self.value)?)
|
|
}
|
|
|
|
fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
|
where
|
|
V: de::Visitor<'de>,
|
|
{
|
|
if let Value::Float(f) = self.value {
|
|
return visitor.visit_f32(f as f32);
|
|
}
|
|
|
|
Err(unexpected("float", &self.value))
|
|
}
|
|
|
|
fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
|
where
|
|
V: de::Visitor<'de>,
|
|
{
|
|
if let Value::Float(f) = self.value {
|
|
return visitor.visit_f64(f);
|
|
}
|
|
|
|
Err(unexpected("float", &self.value))
|
|
}
|
|
|
|
fn deserialize_char<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
|
where
|
|
V: de::Visitor<'de>,
|
|
{
|
|
if let Value::String(s) = &self.value {
|
|
let chars = s.chars().collect::<Vec<_>>();
|
|
if chars.len() == 1 {
|
|
return visitor.visit_char(chars[0]);
|
|
}
|
|
}
|
|
|
|
Err(unexpected("char", &self.value))
|
|
}
|
|
|
|
fn deserialize_str<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
|
where
|
|
V: de::Visitor<'de>,
|
|
{
|
|
if let Value::String(s) = &self.value {
|
|
if let Ok(s) = s.to_str() {
|
|
return visitor.visit_str(s);
|
|
}
|
|
}
|
|
|
|
Err(unexpected("string", &self.value))
|
|
}
|
|
|
|
fn deserialize_string<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
|
where
|
|
V: de::Visitor<'de>,
|
|
{
|
|
if let Value::String(s) = &self.value {
|
|
if let Ok(s) = s.to_str() {
|
|
return visitor.visit_str(s);
|
|
}
|
|
}
|
|
|
|
Err(unexpected("string", &self.value))
|
|
}
|
|
|
|
fn deserialize_bytes<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
|
|
where
|
|
V: de::Visitor<'de>,
|
|
{
|
|
unimplemented!()
|
|
}
|
|
|
|
fn deserialize_byte_buf<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
|
|
where
|
|
V: de::Visitor<'de>,
|
|
{
|
|
unimplemented!()
|
|
}
|
|
|
|
// Note that this can not distinguish between a serialisation of
|
|
// `Some(())` and `None`.
|
|
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
|
where
|
|
V: de::Visitor<'de>,
|
|
{
|
|
if let Value::Null = self.value {
|
|
visitor.visit_none()
|
|
} else {
|
|
visitor.visit_some(self)
|
|
}
|
|
}
|
|
|
|
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
|
where
|
|
V: de::Visitor<'de>,
|
|
{
|
|
if let Value::Null = self.value {
|
|
return visitor.visit_unit();
|
|
}
|
|
|
|
Err(unexpected("null", &self.value))
|
|
}
|
|
|
|
fn deserialize_unit_struct<V>(
|
|
self,
|
|
_name: &'static str,
|
|
visitor: V,
|
|
) -> Result<V::Value, Self::Error>
|
|
where
|
|
V: de::Visitor<'de>,
|
|
{
|
|
self.deserialize_unit(visitor)
|
|
}
|
|
|
|
fn deserialize_newtype_struct<V>(
|
|
self,
|
|
_name: &'static str,
|
|
visitor: V,
|
|
) -> Result<V::Value, Self::Error>
|
|
where
|
|
V: de::Visitor<'de>,
|
|
{
|
|
visitor.visit_newtype_struct(self)
|
|
}
|
|
|
|
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
|
where
|
|
V: de::Visitor<'de>,
|
|
{
|
|
if let Value::List(list) = self.value {
|
|
let mut seq = SeqDeserializer::new(list.into_iter().map(NixDeserializer::new));
|
|
let result = visitor.visit_seq(&mut seq)?;
|
|
seq.end()?;
|
|
return Ok(result);
|
|
}
|
|
|
|
Err(unexpected("list", &self.value))
|
|
}
|
|
|
|
fn deserialize_tuple<V>(self, _len: usize, visitor: V) -> Result<V::Value, Self::Error>
|
|
where
|
|
V: de::Visitor<'de>,
|
|
{
|
|
// just represent tuples as lists ...
|
|
self.deserialize_seq(visitor)
|
|
}
|
|
|
|
fn deserialize_tuple_struct<V>(
|
|
self,
|
|
_name: &'static str,
|
|
_len: usize,
|
|
visitor: V,
|
|
) -> Result<V::Value, Self::Error>
|
|
where
|
|
V: de::Visitor<'de>,
|
|
{
|
|
// same as above
|
|
self.deserialize_seq(visitor)
|
|
}
|
|
|
|
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
|
where
|
|
V: de::Visitor<'de>,
|
|
{
|
|
if let Value::Attrs(attrs) = self.value {
|
|
let mut map = MapDeserializer::new(attrs.into_iter().map(|(k, v)| {
|
|
(
|
|
NixDeserializer::new(Value::from(k)),
|
|
NixDeserializer::new(v),
|
|
)
|
|
}));
|
|
let result = visitor.visit_map(&mut map)?;
|
|
map.end()?;
|
|
return Ok(result);
|
|
}
|
|
|
|
Err(unexpected("map", &self.value))
|
|
}
|
|
|
|
fn deserialize_struct<V>(
|
|
self,
|
|
_name: &'static str,
|
|
_fields: &'static [&'static str],
|
|
visitor: V,
|
|
) -> Result<V::Value, Self::Error>
|
|
where
|
|
V: de::Visitor<'de>,
|
|
{
|
|
self.deserialize_map(visitor)
|
|
}
|
|
|
|
// This method is responsible for deserializing the externally
|
|
// tagged enum variant serialisation.
|
|
fn deserialize_enum<V>(
|
|
self,
|
|
name: &'static str,
|
|
_variants: &'static [&'static str],
|
|
visitor: V,
|
|
) -> Result<V::Value, Self::Error>
|
|
where
|
|
V: de::Visitor<'de>,
|
|
{
|
|
match self.value {
|
|
// a string represents a unit variant
|
|
Value::String(ref s) => {
|
|
if let Ok(s) = s.to_str() {
|
|
visitor.visit_enum(de::value::StrDeserializer::new(s))
|
|
} else {
|
|
Err(unexpected(name, &self.value))
|
|
}
|
|
}
|
|
|
|
// an attribute set however represents an externally
|
|
// tagged enum with content
|
|
Value::Attrs(attrs) => visitor.visit_enum(Enum(*attrs)),
|
|
|
|
_ => Err(unexpected(name, &self.value)),
|
|
}
|
|
}
|
|
|
|
fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
|
where
|
|
V: de::Visitor<'de>,
|
|
{
|
|
self.deserialize_str(visitor)
|
|
}
|
|
|
|
fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
|
where
|
|
V: de::Visitor<'de>,
|
|
{
|
|
visitor.visit_unit()
|
|
}
|
|
}
|
|
|
|
struct Enum(tvix_eval::NixAttrs);
|
|
|
|
impl<'de> EnumAccess<'de> for Enum {
|
|
type Error = Error;
|
|
type Variant = NixDeserializer;
|
|
|
|
// TODO: pass the known variants down here and check against them
|
|
fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error>
|
|
where
|
|
V: de::DeserializeSeed<'de>,
|
|
{
|
|
if self.0.len() != 1 {
|
|
return Err(Error::AmbiguousEnum);
|
|
}
|
|
|
|
let (key, value) = self.0.into_iter().next().expect("length asserted above");
|
|
if let Ok(k) = key.to_str() {
|
|
let val = seed.deserialize(de::value::StrDeserializer::<Error>::new(k))?;
|
|
Ok((val, NixDeserializer::new(value)))
|
|
} else {
|
|
Err(unexpected("string", &key.clone().into()))
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'de> VariantAccess<'de> for NixDeserializer {
|
|
type Error = Error;
|
|
|
|
fn unit_variant(self) -> Result<(), Self::Error> {
|
|
// If this case is hit, a user specified the name of a unit
|
|
// enum variant but gave it content. Unit enum deserialisation
|
|
// is handled in `deserialize_enum` above.
|
|
Err(Error::UnitEnumContent)
|
|
}
|
|
|
|
fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value, Self::Error>
|
|
where
|
|
T: de::DeserializeSeed<'de>,
|
|
{
|
|
seed.deserialize(self)
|
|
}
|
|
|
|
fn tuple_variant<V>(self, _len: usize, visitor: V) -> Result<V::Value, Self::Error>
|
|
where
|
|
V: de::Visitor<'de>,
|
|
{
|
|
de::Deserializer::deserialize_seq(self, visitor)
|
|
}
|
|
|
|
fn struct_variant<V>(
|
|
self,
|
|
_fields: &'static [&'static str],
|
|
visitor: V,
|
|
) -> Result<V::Value, Self::Error>
|
|
where
|
|
V: de::Visitor<'de>,
|
|
{
|
|
de::Deserializer::deserialize_map(self, visitor)
|
|
}
|
|
}
|