git.delta.rocks / jrsonnet / refs/commits / fad91f8a3923

difftreelog

feat nixlike serialization

Yaroslav Bolyukin2021-09-18parent: #c02ef18.patch.diff
in: trunk

6 files changed

modifiedCargo.tomldiffbeforeafterboth
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -21,3 +21,6 @@
 tempfile = "3.2"
 once_cell = "1.5"
 hostname = "0.3.1"
+
+[workspace]
+members = ["crates/nixlike"]
addedcrates/nixlike/Cargo.tomldiffbeforeafterboth
--- /dev/null
+++ b/crates/nixlike/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "nixlike"
+version = "0.1.0"
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+anyhow = "1.0.44"
+dprint-core = "0.46.2"
+peg = "0.7.0"
+serde = "1.0.130"
+serde_json = "1.0.68"
+thiserror = "1.0.29"
addedcrates/nixlike/src/de_impl.rsdiffbeforeafterboth
--- /dev/null
+++ b/crates/nixlike/src/de_impl.rs
@@ -0,0 +1,368 @@
+use std::{
+	collections::BTreeMap,
+	convert::{TryFrom, TryInto},
+};
+
+use serde::{
+	de::{self, MapAccess, SeqAccess},
+	Deserializer,
+};
+
+use crate::{Error, Value};
+
+struct ObjectAccess {
+	iter: std::collections::btree_map::IntoIter<String, Value>,
+	value: Option<Value>,
+}
+impl ObjectAccess {
+	fn new(v: BTreeMap<String, Value>) -> Self {
+		Self {
+			iter: v.into_iter(),
+			value: None,
+		}
+	}
+}
+
+impl<'de> MapAccess<'de> for ObjectAccess {
+	type Error = Error;
+
+	fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error>
+	where
+		K: de::DeserializeSeed<'de>,
+	{
+		if let Some((k, v)) = self.iter.next() {
+			let _ = self.value.insert(v);
+			Ok(Some(seed.deserialize(Value::String(k))?))
+		} else {
+			Ok(None)
+		}
+	}
+
+	fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Self::Error>
+	where
+		V: de::DeserializeSeed<'de>,
+	{
+		seed.deserialize(self.value.take().unwrap())
+	}
+}
+
+struct ArrayAccess {
+	iter: std::vec::IntoIter<Value>,
+}
+impl ArrayAccess {
+	fn new(v: Vec<Value>) -> Self {
+		Self {
+			iter: v.into_iter(),
+		}
+	}
+}
+
+impl<'de> SeqAccess<'de> for ArrayAccess {
+	type Error = Error;
+
+	fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
+	where
+		T: de::DeserializeSeed<'de>,
+	{
+		if let Some(v) = self.iter.next() {
+			Ok(Some(seed.deserialize(v)?))
+		} else {
+			Ok(None)
+		}
+	}
+}
+
+impl Value {
+	fn parse_int<T: TryFrom<i64>>(&self) -> Result<T, Error> {
+		match self {
+			Value::Number(n) => Ok((*n).try_into().map_err(|_| Error::BadNumber)?),
+			_ => Err(Error::Expected("integer")),
+		}
+	}
+	fn parse_boolean(self) -> Result<bool, Error> {
+		match self {
+			Value::Boolean(b) => Ok(b),
+			_ => Err(Error::Expected("boolean")),
+		}
+	}
+	pub fn parse_string(&self) -> Result<&str, Error> {
+		match self {
+			Value::String(s) => Ok(s),
+			_ => Err(Error::Expected("string")),
+		}
+	}
+	fn parse_char(self) -> Result<char, Error> {
+		match self {
+			Value::String(s) if s.chars().count() == 1 => Ok(s.chars().next().unwrap()),
+			_ => Err(Error::Expected("char")),
+		}
+	}
+	fn parse_array(self) -> Result<Vec<Value>, Error> {
+		match self {
+			Value::Array(s) => Ok(s),
+			_ => Err(Error::Expected("array")),
+		}
+	}
+	fn parse_object(self) -> Result<BTreeMap<String, Value>, Error> {
+		match self {
+			Value::Object(s) => Ok(s),
+			_ => Err(Error::Expected("object")),
+		}
+	}
+	fn parse_null(self) -> Result<(), Error> {
+		match self {
+			Value::Null => Ok(()),
+			_ => Err(Error::Expected("null")),
+		}
+	}
+}
+
+impl de::Error for Error {
+	fn custom<T>(msg: T) -> Self
+	where
+		T: std::fmt::Display,
+	{
+		Self::Custom(format!("{}", msg))
+	}
+}
+
+impl<'de> Deserializer<'de> for Value {
+	type Error = Error;
+
+	fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+	where
+		V: serde::de::Visitor<'de>,
+	{
+		match self {
+			Value::Number(f) => visitor.visit_i64(f),
+			Value::String(s) => visitor.visit_str(&s),
+			Value::Boolean(b) => visitor.visit_bool(b),
+			Value::Object(o) => visitor.visit_map(ObjectAccess::new(o)),
+			Value::Array(a) => visitor.visit_seq(ArrayAccess::new(a)),
+			Value::Null => visitor.visit_none(),
+		}
+	}
+
+	fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+	where
+		V: serde::de::Visitor<'de>,
+	{
+		visitor.visit_bool(self.parse_boolean()?)
+	}
+
+	fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+	where
+		V: serde::de::Visitor<'de>,
+	{
+		visitor.visit_i8(self.parse_int()?)
+	}
+
+	fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+	where
+		V: serde::de::Visitor<'de>,
+	{
+		visitor.visit_i16(self.parse_int()?)
+	}
+
+	fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+	where
+		V: serde::de::Visitor<'de>,
+	{
+		visitor.visit_i32(self.parse_int()?)
+	}
+
+	fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+	where
+		V: serde::de::Visitor<'de>,
+	{
+		visitor.visit_i64(self.parse_int()?)
+	}
+
+	fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+	where
+		V: serde::de::Visitor<'de>,
+	{
+		visitor.visit_u8(self.parse_int()?)
+	}
+
+	fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+	where
+		V: serde::de::Visitor<'de>,
+	{
+		visitor.visit_u16(self.parse_int()?)
+	}
+
+	fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+	where
+		V: serde::de::Visitor<'de>,
+	{
+		visitor.visit_u32(self.parse_int()?)
+	}
+
+	fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+	where
+		V: serde::de::Visitor<'de>,
+	{
+		visitor.visit_u64(self.parse_int()?)
+	}
+
+	fn deserialize_f32<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
+	where
+		V: serde::de::Visitor<'de>,
+	{
+		todo!()
+	}
+
+	fn deserialize_f64<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
+	where
+		V: serde::de::Visitor<'de>,
+	{
+		todo!()
+	}
+
+	fn deserialize_char<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+	where
+		V: serde::de::Visitor<'de>,
+	{
+		visitor.visit_char(self.parse_char()?)
+	}
+
+	fn deserialize_str<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+	where
+		V: serde::de::Visitor<'de>,
+	{
+		visitor.visit_str(self.parse_string()?)
+	}
+
+	fn deserialize_string<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+	where
+		V: serde::de::Visitor<'de>,
+	{
+		visitor.visit_string(self.parse_string()?.to_owned())
+	}
+
+	fn deserialize_bytes<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
+	where
+		V: serde::de::Visitor<'de>,
+	{
+		todo!()
+	}
+
+	fn deserialize_byte_buf<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
+	where
+		V: serde::de::Visitor<'de>,
+	{
+		todo!()
+	}
+
+	fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+	where
+		V: serde::de::Visitor<'de>,
+	{
+		match self {
+			Value::Null => visitor.visit_none(),
+			v => visitor.visit_some(v),
+		}
+	}
+
+	fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+	where
+		V: serde::de::Visitor<'de>,
+	{
+		self.parse_null()?;
+		visitor.visit_unit()
+	}
+
+	fn deserialize_unit_struct<V>(
+		self,
+		_name: &'static str,
+		visitor: V,
+	) -> Result<V::Value, Self::Error>
+	where
+		V: serde::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: serde::de::Visitor<'de>,
+	{
+		visitor.visit_newtype_struct(self)
+	}
+
+	fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+	where
+		V: serde::de::Visitor<'de>,
+	{
+		visitor.visit_seq(self.parse_array().map(ArrayAccess::new)?)
+	}
+
+	fn deserialize_tuple<V>(self, _len: usize, visitor: V) -> Result<V::Value, Self::Error>
+	where
+		V: serde::de::Visitor<'de>,
+	{
+		self.deserialize_seq(visitor)
+	}
+
+	fn deserialize_tuple_struct<V>(
+		self,
+		_name: &'static str,
+		_len: usize,
+		visitor: V,
+	) -> Result<V::Value, Self::Error>
+	where
+		V: serde::de::Visitor<'de>,
+	{
+		self.deserialize_seq(visitor)
+	}
+
+	fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+	where
+		V: serde::de::Visitor<'de>,
+	{
+		visitor.visit_map(self.parse_object().map(ObjectAccess::new)?)
+	}
+
+	fn deserialize_struct<V>(
+		self,
+		_name: &'static str,
+		_fields: &'static [&'static str],
+		visitor: V,
+	) -> Result<V::Value, Self::Error>
+	where
+		V: serde::de::Visitor<'de>,
+	{
+		self.deserialize_map(visitor)
+	}
+
+	fn deserialize_enum<V>(
+		self,
+		_name: &'static str,
+		_variants: &'static [&'static str],
+		_visitor: V,
+	) -> Result<V::Value, Self::Error>
+	where
+		V: serde::de::Visitor<'de>,
+	{
+		todo!()
+	}
+
+	fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+	where
+		V: serde::de::Visitor<'de>,
+	{
+		self.deserialize_str(visitor)
+	}
+
+	fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+	where
+		V: serde::de::Visitor<'de>,
+	{
+		self.deserialize_any(visitor)
+	}
+}
addedcrates/nixlike/src/lib.rsdiffbeforeafterboth

no content

addedcrates/nixlike/src/se_impl.rsdiffbeforeafterboth
--- /dev/null
+++ b/crates/nixlike/src/se_impl.rs
@@ -0,0 +1,362 @@
+use std::{collections::BTreeMap, convert::TryInto};
+
+use serde::{
+	ser::{
+		self, SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant, SerializeTuple,
+		SerializeTupleStruct, SerializeTupleVariant,
+	},
+	Serializer,
+};
+
+use crate::{Error, Value};
+
+impl ser::Error for Error {
+	fn custom<T>(msg: T) -> Self
+	where
+		T: std::fmt::Display,
+	{
+		Self::Custom(format!("{}", msg))
+	}
+}
+
+pub struct MySerializeSeq(Vec<Value>);
+
+impl SerializeSeq for MySerializeSeq {
+	type Ok = Value;
+
+	type Error = Error;
+
+	fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
+	where
+		T: serde::Serialize,
+	{
+		self.0.push(value.serialize(MySerialize)?);
+		Ok(())
+	}
+
+	fn end(self) -> Result<Self::Ok, Self::Error> {
+		Ok(Value::Array(self.0))
+	}
+}
+impl SerializeTuple for MySerializeSeq {
+	type Ok = Value;
+
+	type Error = Error;
+
+	fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
+	where
+		T: serde::Serialize,
+	{
+		self.0.push(value.serialize(MySerialize)?);
+		Ok(())
+	}
+
+	fn end(self) -> Result<Self::Ok, Self::Error> {
+		Ok(Value::Array(self.0))
+	}
+}
+impl SerializeTupleStruct for MySerializeSeq {
+	type Ok = Value;
+
+	type Error = Error;
+
+	fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
+	where
+		T: serde::Serialize,
+	{
+		self.0.push(value.serialize(MySerialize)?);
+		Ok(())
+	}
+
+	fn end(self) -> Result<Self::Ok, Self::Error> {
+		Ok(Value::Array(self.0))
+	}
+}
+
+pub struct MySerializeSeqVariant(String, MySerializeSeq);
+
+impl SerializeTupleVariant for MySerializeSeqVariant {
+	type Ok = Value;
+
+	type Error = Error;
+
+	fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
+	where
+		T: serde::Serialize,
+	{
+		self.1.serialize_field(value)
+	}
+
+	fn end(self) -> Result<Self::Ok, Self::Error> {
+		Ok(Value::Object(
+			vec![(self.0, Value::Array(self.1 .0))]
+				.into_iter()
+				.collect(),
+		))
+	}
+}
+
+pub struct MySerializeMap(BTreeMap<String, Value>, Option<String>);
+
+impl SerializeMap for MySerializeMap {
+	type Ok = Value;
+
+	type Error = Error;
+
+	fn serialize_key<T: ?Sized>(&mut self, key: &T) -> Result<(), Self::Error>
+	where
+		T: serde::Serialize,
+	{
+		let _ = self
+			.1
+			.insert(key.serialize(MySerialize)?.parse_string()?.to_owned());
+		Ok(())
+	}
+
+	fn serialize_value<T: ?Sized>(&mut self, value: &T) -> Result<(), Self::Error>
+	where
+		T: serde::Serialize,
+	{
+		self.0
+			.insert(self.1.take().unwrap(), value.serialize(MySerialize)?);
+		Ok(())
+	}
+
+	fn end(self) -> Result<Self::Ok, Self::Error> {
+		Ok(Value::Object(self.0))
+	}
+}
+
+pub struct MySerializeStruct(BTreeMap<String, Value>);
+
+impl SerializeStruct for MySerializeStruct {
+	type Ok = Value;
+
+	type Error = Error;
+
+	fn serialize_field<T: ?Sized>(&mut self, key: &str, value: &T) -> Result<(), Self::Error>
+	where
+		T: serde::Serialize,
+	{
+		self.0.insert(key.to_owned(), value.serialize(MySerialize)?);
+		Ok(())
+	}
+
+	fn end(self) -> Result<Self::Ok, Self::Error> {
+		Ok(Value::Object(self.0))
+	}
+}
+
+pub struct MySerializeStructVariant(String, BTreeMap<String, Value>);
+
+impl SerializeStructVariant for MySerializeStructVariant {
+	type Ok = Value;
+
+	type Error = Error;
+
+	fn serialize_field<T: ?Sized>(
+		&mut self,
+		key: &'static str,
+		value: &T,
+	) -> Result<(), Self::Error>
+	where
+		T: serde::Serialize,
+	{
+		self.1.insert(key.to_owned(), value.serialize(MySerialize)?);
+		Ok(())
+	}
+
+	fn end(self) -> Result<Self::Ok, Self::Error> {
+		Ok(Value::Object(
+			vec![(self.0, Value::Object(self.1))].into_iter().collect(),
+		))
+	}
+}
+
+pub struct MySerialize;
+
+impl Serializer for MySerialize {
+	type Ok = Value;
+
+	type Error = Error;
+
+	type SerializeSeq = MySerializeSeq;
+
+	type SerializeTuple = MySerializeSeq;
+
+	type SerializeTupleStruct = MySerializeSeq;
+
+	type SerializeTupleVariant = MySerializeSeqVariant;
+
+	type SerializeMap = MySerializeMap;
+
+	type SerializeStruct = MySerializeStruct;
+
+	type SerializeStructVariant = MySerializeStructVariant;
+
+	fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
+		Ok(Value::Boolean(v))
+	}
+
+	fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error> {
+		Ok(Value::Number(v as i64))
+	}
+
+	fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error> {
+		Ok(Value::Number(v as i64))
+	}
+
+	fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error> {
+		Ok(Value::Number(v as i64))
+	}
+
+	fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error> {
+		Ok(Value::Number(v as i64))
+	}
+
+	fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> {
+		Ok(Value::Number(v as i64))
+	}
+
+	fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error> {
+		Ok(Value::Number(v as i64))
+	}
+
+	fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> {
+		Ok(Value::Number(v as i64))
+	}
+
+	fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> {
+		Ok(Value::Number(v.try_into().map_err(|_| Error::BadNumber)?))
+	}
+
+	fn serialize_f32(self, _v: f32) -> Result<Self::Ok, Self::Error> {
+		todo!()
+	}
+
+	fn serialize_f64(self, _v: f64) -> Result<Self::Ok, Self::Error> {
+		todo!()
+	}
+
+	fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error> {
+		Ok(Value::String(v.to_string()))
+	}
+
+	fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
+		Ok(Value::String(v.to_owned()))
+	}
+
+	fn serialize_bytes(self, _v: &[u8]) -> Result<Self::Ok, Self::Error> {
+		todo!()
+	}
+
+	fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
+		Ok(Value::Null)
+	}
+
+	fn serialize_some<T: ?Sized>(self, value: &T) -> Result<Self::Ok, Self::Error>
+	where
+		T: serde::Serialize,
+	{
+		value.serialize(self)
+	}
+
+	fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
+		Ok(Value::Null)
+	}
+
+	fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> {
+		self.serialize_unit()
+	}
+
+	fn serialize_unit_variant(
+		self,
+		_name: &'static str,
+		_variant_index: u32,
+		variant: &'static str,
+	) -> Result<Self::Ok, Self::Error> {
+		Ok(Value::String(variant.to_string()))
+	}
+
+	fn serialize_newtype_struct<T: ?Sized>(
+		self,
+		_name: &'static str,
+		value: &T,
+	) -> Result<Self::Ok, Self::Error>
+	where
+		T: serde::Serialize,
+	{
+		value.serialize(self)
+	}
+
+	fn serialize_newtype_variant<T: ?Sized>(
+		self,
+		_name: &'static str,
+		_variant_index: u32,
+		variant: &'static str,
+		value: &T,
+	) -> Result<Self::Ok, Self::Error>
+	where
+		T: serde::Serialize,
+	{
+		Ok(Value::Object(
+			vec![(variant.to_string(), value.serialize(self)?)]
+				.into_iter()
+				.collect(),
+		))
+	}
+
+	fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
+		Ok(MySerializeSeq(Vec::with_capacity(len.unwrap_or_default())))
+	}
+
+	fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Self::Error> {
+		Ok(MySerializeSeq(Vec::with_capacity(len)))
+	}
+
+	fn serialize_tuple_struct(
+		self,
+		_name: &'static str,
+		len: usize,
+	) -> Result<Self::SerializeTupleStruct, Self::Error> {
+		Ok(MySerializeSeq(Vec::with_capacity(len)))
+	}
+
+	fn serialize_tuple_variant(
+		self,
+		_name: &'static str,
+		_variant_index: u32,
+		variant: &'static str,
+		len: usize,
+	) -> Result<Self::SerializeTupleVariant, Self::Error> {
+		Ok(MySerializeSeqVariant(
+			variant.to_owned(),
+			MySerializeSeq(Vec::with_capacity(len)),
+		))
+	}
+
+	fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
+		Ok(MySerializeMap(BTreeMap::new(), None))
+	}
+
+	fn serialize_struct(
+		self,
+		_name: &'static str,
+		_len: usize,
+	) -> Result<Self::SerializeStruct, Self::Error> {
+		Ok(MySerializeStruct(BTreeMap::new()))
+	}
+
+	fn serialize_struct_variant(
+		self,
+		_name: &'static str,
+		_variant_index: u32,
+		variant: &'static str,
+		_len: usize,
+	) -> Result<Self::SerializeStructVariant, Self::Error> {
+		Ok(MySerializeStructVariant(
+			variant.to_owned(),
+			BTreeMap::new(),
+		))
+	}
+}
addedcrates/nixlike/src/to_string.rsdiffbeforeafterboth
--- /dev/null
+++ b/crates/nixlike/src/to_string.rs
@@ -0,0 +1,74 @@
+use crate::{Error, Value};
+
+fn write_nix_obj_key_buf(
+	k: &str,
+	v: &Value,
+	out: &mut String,
+	indent: &mut String,
+) -> Result<(), Error> {
+	use std::fmt::Write;
+	write!(out, "{}", k)?;
+	match v {
+		Value::Object(o) if o.len() == 1 => {
+			let (k, v) = o.iter().next().unwrap();
+			write!(out, ".")?;
+			write_nix_obj_key_buf(k, v, out, indent)?;
+		}
+		v => {
+			write!(out, " = ")?;
+			write_nix_buf(v, out, indent)?;
+			writeln!(out, ";")?;
+		}
+	}
+	Ok(())
+}
+
+fn write_nix_buf(value: &Value, out: &mut String, indent: &mut String) -> Result<(), Error> {
+	use std::fmt::Write;
+	match value {
+		Value::Null => write!(out, "null")?,
+		Value::Boolean(v) => write!(out, "{:?}", v)?,
+		Value::Number(n) => write!(out, "{}", n)?,
+		Value::String(s) => write!(out, "{:?}", s)?,
+		Value::Array(a) => {
+			if a.is_empty() {
+				write!(out, "[ ]")?;
+			} else {
+				writeln!(out, "[")?;
+				let old_len = indent.len();
+				indent.push_str("  ");
+				for item in a {
+					write!(out, "{}", indent)?;
+					write_nix_buf(item, out, indent)?;
+					writeln!(out)?;
+				}
+				indent.truncate(old_len);
+				write!(out, "{}]", indent)?;
+			}
+		}
+		Value::Object(obj) => {
+			if obj.is_empty() {
+				write!(out, "{{ }}")?;
+			} else {
+				writeln!(out, "{{")?;
+				let old_len = indent.len();
+				indent.push_str("  ");
+				for (k, v) in obj {
+					write!(out, "{}", indent)?;
+					write_nix_obj_key_buf(k, v, out, indent)?;
+				}
+				indent.truncate(old_len);
+				write!(out, "{}}}", indent)?;
+			}
+		}
+	};
+	Ok(())
+}
+
+pub fn write_nix(value: &Value) -> Result<String, Error> {
+	let mut out = String::new();
+	let mut indent = String::new();
+
+	write_nix_buf(value, &mut out, &mut indent)?;
+	Ok(out)
+}