git.delta.rocks / jrsonnet / refs/commits / 9b70922a5a83

difftreelog

feat (de)serialize nix imports

upkxyxktYaroslav Bolyukin2026-01-22parent: #3bdc221.patch.diff
in: trunk

5 files changed

modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2208,6 +2208,7 @@
 name = "nixlike"
 version = "0.1.0"
 dependencies = [
+ "itertools 0.14.0",
  "linked-hash-map",
  "peg",
  "ron",
modifiedcrates/nixlike/Cargo.tomldiffbeforeafterboth
--- a/crates/nixlike/Cargo.toml
+++ b/crates/nixlike/Cargo.toml
@@ -10,6 +10,7 @@
 linked-hash-map = "0.5.6"
 peg = "0.8.5"
 ron = "0.11.0"
-serde = "1.0.219"
+serde = { version = "1.0.219", features = ["derive"] }
 serde-transcode = "1.1.1"
 serde_json = "1.0.140"
+itertools = "0.14.0"
modifiedcrates/nixlike/src/de_impl.rsdiffbeforeafterboth
before · crates/nixlike/src/de_impl.rs
1use std::convert::{TryFrom, TryInto};23use linked_hash_map::LinkedHashMap;4use serde::{5	Deserializer,6	de::{self, MapAccess, SeqAccess},7};89use crate::{Error, Value};1011struct ObjectAccess {12	iter: linked_hash_map::IntoIter<String, Value>,13	value: Option<Value>,14}15impl ObjectAccess {16	fn new(v: LinkedHashMap<String, Value>) -> Self {17		Self {18			iter: v.into_iter(),19			value: None,20		}21	}22}2324impl<'de> MapAccess<'de> for ObjectAccess {25	type Error = Error;2627	fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error>28	where29		K: de::DeserializeSeed<'de>,30	{31		match self.iter.next() {32			Some((k, v)) => {33				let _ = self.value.insert(v);34				Ok(Some(seed.deserialize(Value::String(k))?))35			}36			_ => Ok(None),37		}38	}3940	fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Self::Error>41	where42		V: de::DeserializeSeed<'de>,43	{44		seed.deserialize(self.value.take().unwrap())45	}46}4748struct ArrayAccess {49	iter: std::vec::IntoIter<Value>,50}51impl ArrayAccess {52	fn new(v: Vec<Value>) -> Self {53		Self {54			iter: v.into_iter(),55		}56	}57}5859impl<'de> SeqAccess<'de> for ArrayAccess {60	type Error = Error;6162	fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>63	where64		T: de::DeserializeSeed<'de>,65	{66		match self.iter.next() {67			Some(v) => Ok(Some(seed.deserialize(v)?)),68			_ => Ok(None),69		}70	}71}7273impl Value {74	fn parse_int<T: TryFrom<i64>>(&self) -> Result<T, Error> {75		match self {76			Value::Number(n) => Ok((*n).try_into().map_err(|_| Error::BadNumber)?),77			_ => Err(Error::Expected("integer")),78		}79	}80	fn parse_boolean(self) -> Result<bool, Error> {81		match self {82			Value::Boolean(b) => Ok(b),83			_ => Err(Error::Expected("boolean")),84		}85	}86	pub fn parse_string(&self) -> Result<&str, Error> {87		match self {88			Value::String(s) => Ok(s),89			_ => Err(Error::Expected("string")),90		}91	}92	fn parse_char(self) -> Result<char, Error> {93		match self {94			Value::String(s) if s.chars().count() == 1 => Ok(s.chars().next().unwrap()),95			_ => Err(Error::Expected("char")),96		}97	}98	fn parse_array(self) -> Result<Vec<Value>, Error> {99		match self {100			Value::Array(s) => Ok(s),101			_ => Err(Error::Expected("array")),102		}103	}104	fn parse_object(self) -> Result<LinkedHashMap<String, Value>, Error> {105		match self {106			Value::Object(s) => Ok(s),107			_ => Err(Error::Expected("object")),108		}109	}110	fn parse_null(self) -> Result<(), Error> {111		match self {112			Value::Null => Ok(()),113			_ => Err(Error::Expected("null")),114		}115	}116}117118impl de::Error for Error {119	fn custom<T>(msg: T) -> Self120	where121		T: std::fmt::Display,122	{123		Self::Custom(format!("{}", msg))124	}125}126127impl<'de> Deserializer<'de> for Value {128	type Error = Error;129130	fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>131	where132		V: serde::de::Visitor<'de>,133	{134		match self {135			Value::Number(f) => visitor.visit_i64(f),136			Value::String(s) => visitor.visit_str(&s),137			Value::Boolean(b) => visitor.visit_bool(b),138			Value::Object(o) => visitor.visit_map(ObjectAccess::new(o)),139			Value::Array(a) => visitor.visit_seq(ArrayAccess::new(a)),140			Value::Null => visitor.visit_none(),141		}142	}143144	fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Self::Error>145	where146		V: serde::de::Visitor<'de>,147	{148		visitor.visit_bool(self.parse_boolean()?)149	}150151	fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value, Self::Error>152	where153		V: serde::de::Visitor<'de>,154	{155		visitor.visit_i8(self.parse_int()?)156	}157158	fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value, Self::Error>159	where160		V: serde::de::Visitor<'de>,161	{162		visitor.visit_i16(self.parse_int()?)163	}164165	fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value, Self::Error>166	where167		V: serde::de::Visitor<'de>,168	{169		visitor.visit_i32(self.parse_int()?)170	}171172	fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value, Self::Error>173	where174		V: serde::de::Visitor<'de>,175	{176		visitor.visit_i64(self.parse_int()?)177	}178179	fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value, Self::Error>180	where181		V: serde::de::Visitor<'de>,182	{183		visitor.visit_u8(self.parse_int()?)184	}185186	fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value, Self::Error>187	where188		V: serde::de::Visitor<'de>,189	{190		visitor.visit_u16(self.parse_int()?)191	}192193	fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value, Self::Error>194	where195		V: serde::de::Visitor<'de>,196	{197		visitor.visit_u32(self.parse_int()?)198	}199200	fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value, Self::Error>201	where202		V: serde::de::Visitor<'de>,203	{204		visitor.visit_u64(self.parse_int()?)205	}206207	fn deserialize_f32<V>(self, _visitor: V) -> Result<V::Value, Self::Error>208	where209		V: serde::de::Visitor<'de>,210	{211		todo!()212	}213214	fn deserialize_f64<V>(self, _visitor: V) -> Result<V::Value, Self::Error>215	where216		V: serde::de::Visitor<'de>,217	{218		todo!()219	}220221	fn deserialize_char<V>(self, visitor: V) -> Result<V::Value, Self::Error>222	where223		V: serde::de::Visitor<'de>,224	{225		visitor.visit_char(self.parse_char()?)226	}227228	fn deserialize_str<V>(self, visitor: V) -> Result<V::Value, Self::Error>229	where230		V: serde::de::Visitor<'de>,231	{232		visitor.visit_str(self.parse_string()?)233	}234235	fn deserialize_string<V>(self, visitor: V) -> Result<V::Value, Self::Error>236	where237		V: serde::de::Visitor<'de>,238	{239		visitor.visit_string(self.parse_string()?.to_owned())240	}241242	fn deserialize_bytes<V>(self, _visitor: V) -> Result<V::Value, Self::Error>243	where244		V: serde::de::Visitor<'de>,245	{246		todo!()247	}248249	fn deserialize_byte_buf<V>(self, _visitor: V) -> Result<V::Value, Self::Error>250	where251		V: serde::de::Visitor<'de>,252	{253		todo!()254	}255256	fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>257	where258		V: serde::de::Visitor<'de>,259	{260		match self {261			Value::Null => visitor.visit_none(),262			v => visitor.visit_some(v),263		}264	}265266	fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Self::Error>267	where268		V: serde::de::Visitor<'de>,269	{270		self.parse_null()?;271		visitor.visit_unit()272	}273274	fn deserialize_unit_struct<V>(275		self,276		_name: &'static str,277		visitor: V,278	) -> Result<V::Value, Self::Error>279	where280		V: serde::de::Visitor<'de>,281	{282		self.deserialize_unit(visitor)283	}284285	fn deserialize_newtype_struct<V>(286		self,287		_name: &'static str,288		visitor: V,289	) -> Result<V::Value, Self::Error>290	where291		V: serde::de::Visitor<'de>,292	{293		visitor.visit_newtype_struct(self)294	}295296	fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>297	where298		V: serde::de::Visitor<'de>,299	{300		visitor.visit_seq(self.parse_array().map(ArrayAccess::new)?)301	}302303	fn deserialize_tuple<V>(self, _len: usize, visitor: V) -> Result<V::Value, Self::Error>304	where305		V: serde::de::Visitor<'de>,306	{307		self.deserialize_seq(visitor)308	}309310	fn deserialize_tuple_struct<V>(311		self,312		_name: &'static str,313		_len: usize,314		visitor: V,315	) -> Result<V::Value, Self::Error>316	where317		V: serde::de::Visitor<'de>,318	{319		self.deserialize_seq(visitor)320	}321322	fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Self::Error>323	where324		V: serde::de::Visitor<'de>,325	{326		visitor.visit_map(self.parse_object().map(ObjectAccess::new)?)327	}328329	fn deserialize_struct<V>(330		self,331		_name: &'static str,332		_fields: &'static [&'static str],333		visitor: V,334	) -> Result<V::Value, Self::Error>335	where336		V: serde::de::Visitor<'de>,337	{338		self.deserialize_map(visitor)339	}340341	fn deserialize_enum<V>(342		self,343		_name: &'static str,344		_variants: &'static [&'static str],345		_visitor: V,346	) -> Result<V::Value, Self::Error>347	where348		V: serde::de::Visitor<'de>,349	{350		todo!()351	}352353	fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value, Self::Error>354	where355		V: serde::de::Visitor<'de>,356	{357		self.deserialize_str(visitor)358	}359360	fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>361	where362		V: serde::de::Visitor<'de>,363	{364		self.deserialize_any(visitor)365	}366}
after · crates/nixlike/src/de_impl.rs
1use std::convert::{TryFrom, TryInto};23use linked_hash_map::LinkedHashMap;4use serde::{5	Deserializer, Serialize,6	de::{self, MapAccess, SeqAccess},7};89use crate::{Error, Value};1011struct ObjectAccess {12	iter: linked_hash_map::IntoIter<String, Value>,13	value: Option<Value>,14}15impl ObjectAccess {16	fn new(v: LinkedHashMap<String, Value>) -> Self {17		Self {18			iter: v.into_iter(),19			value: None,20		}21	}22}2324impl<'de> MapAccess<'de> for ObjectAccess {25	type Error = Error;2627	fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error>28	where29		K: de::DeserializeSeed<'de>,30	{31		match self.iter.next() {32			Some((k, v)) => {33				let _ = self.value.insert(v);34				Ok(Some(seed.deserialize(Value::String(k))?))35			}36			_ => Ok(None),37		}38	}3940	fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Self::Error>41	where42		V: de::DeserializeSeed<'de>,43	{44		seed.deserialize(self.value.take().unwrap())45	}46}4748struct ArrayAccess {49	iter: std::vec::IntoIter<Value>,50}51impl ArrayAccess {52	fn new(v: Vec<Value>) -> Self {53		Self {54			iter: v.into_iter(),55		}56	}57}5859impl<'de> SeqAccess<'de> for ArrayAccess {60	type Error = Error;6162	fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>63	where64		T: de::DeserializeSeed<'de>,65	{66		match self.iter.next() {67			Some(v) => Ok(Some(seed.deserialize(v)?)),68			_ => Ok(None),69		}70	}71}7273impl Value {74	fn parse_int<T: TryFrom<i64>>(&self) -> Result<T, Error> {75		match self {76			Value::Number(n) => Ok((*n).try_into().map_err(|_| Error::BadNumber)?),77			_ => Err(Error::Expected("integer")),78		}79	}80	fn parse_boolean(self) -> Result<bool, Error> {81		match self {82			Value::Boolean(b) => Ok(b),83			_ => Err(Error::Expected("boolean")),84		}85	}86	pub fn parse_string(&self) -> Result<&str, Error> {87		match self {88			Value::String(s) => Ok(s),89			_ => Err(Error::Expected("string")),90		}91	}92	fn parse_char(self) -> Result<char, Error> {93		match self {94			Value::String(s) if s.chars().count() == 1 => Ok(s.chars().next().unwrap()),95			_ => Err(Error::Expected("char")),96		}97	}98	fn parse_array(self) -> Result<Vec<Value>, Error> {99		match self {100			Value::Array(s) => Ok(s),101			_ => Err(Error::Expected("array")),102		}103	}104	fn parse_object(self) -> Result<LinkedHashMap<String, Value>, Error> {105		match self {106			Value::Object(s) => Ok(s),107			_ => Err(Error::Expected("object")),108		}109	}110	fn parse_null(self) -> Result<(), Error> {111		match self {112			Value::Null => Ok(()),113			_ => Err(Error::Expected("null")),114		}115	}116}117118impl de::Error for Error {119	fn custom<T>(msg: T) -> Self120	where121		T: std::fmt::Display,122	{123		Self::Custom(format!("{}", msg))124	}125}126127impl<'de> Deserializer<'de> for Value {128	type Error = Error;129130	fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>131	where132		V: serde::de::Visitor<'de>,133	{134		match self {135			Value::Number(f) => visitor.visit_i64(f),136			Value::String(s) => visitor.visit_str(&s),137			Value::Boolean(b) => visitor.visit_bool(b),138			Value::Object(o) => visitor.visit_map(ObjectAccess::new(o)),139			Value::Array(a) => visitor.visit_seq(ArrayAccess::new(a)),140			Value::Null => visitor.visit_none(),141			Value::Import(d) => {142				let value = d.serialize(crate::se_impl::MySerialize)?;143				value.deserialize_any(visitor)144			}145		}146	}147148	fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Self::Error>149	where150		V: serde::de::Visitor<'de>,151	{152		visitor.visit_bool(self.parse_boolean()?)153	}154155	fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value, Self::Error>156	where157		V: serde::de::Visitor<'de>,158	{159		visitor.visit_i8(self.parse_int()?)160	}161162	fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value, Self::Error>163	where164		V: serde::de::Visitor<'de>,165	{166		visitor.visit_i16(self.parse_int()?)167	}168169	fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value, Self::Error>170	where171		V: serde::de::Visitor<'de>,172	{173		visitor.visit_i32(self.parse_int()?)174	}175176	fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value, Self::Error>177	where178		V: serde::de::Visitor<'de>,179	{180		visitor.visit_i64(self.parse_int()?)181	}182183	fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value, Self::Error>184	where185		V: serde::de::Visitor<'de>,186	{187		visitor.visit_u8(self.parse_int()?)188	}189190	fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value, Self::Error>191	where192		V: serde::de::Visitor<'de>,193	{194		visitor.visit_u16(self.parse_int()?)195	}196197	fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value, Self::Error>198	where199		V: serde::de::Visitor<'de>,200	{201		visitor.visit_u32(self.parse_int()?)202	}203204	fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value, Self::Error>205	where206		V: serde::de::Visitor<'de>,207	{208		visitor.visit_u64(self.parse_int()?)209	}210211	fn deserialize_f32<V>(self, _visitor: V) -> Result<V::Value, Self::Error>212	where213		V: serde::de::Visitor<'de>,214	{215		todo!()216	}217218	fn deserialize_f64<V>(self, _visitor: V) -> Result<V::Value, Self::Error>219	where220		V: serde::de::Visitor<'de>,221	{222		todo!()223	}224225	fn deserialize_char<V>(self, visitor: V) -> Result<V::Value, Self::Error>226	where227		V: serde::de::Visitor<'de>,228	{229		visitor.visit_char(self.parse_char()?)230	}231232	fn deserialize_str<V>(self, visitor: V) -> Result<V::Value, Self::Error>233	where234		V: serde::de::Visitor<'de>,235	{236		visitor.visit_str(self.parse_string()?)237	}238239	fn deserialize_string<V>(self, visitor: V) -> Result<V::Value, Self::Error>240	where241		V: serde::de::Visitor<'de>,242	{243		visitor.visit_string(self.parse_string()?.to_owned())244	}245246	fn deserialize_bytes<V>(self, _visitor: V) -> Result<V::Value, Self::Error>247	where248		V: serde::de::Visitor<'de>,249	{250		todo!()251	}252253	fn deserialize_byte_buf<V>(self, _visitor: V) -> Result<V::Value, Self::Error>254	where255		V: serde::de::Visitor<'de>,256	{257		todo!()258	}259260	fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>261	where262		V: serde::de::Visitor<'de>,263	{264		match self {265			Value::Null => visitor.visit_none(),266			v => visitor.visit_some(v),267		}268	}269270	fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Self::Error>271	where272		V: serde::de::Visitor<'de>,273	{274		self.parse_null()?;275		visitor.visit_unit()276	}277278	fn deserialize_unit_struct<V>(279		self,280		_name: &'static str,281		visitor: V,282	) -> Result<V::Value, Self::Error>283	where284		V: serde::de::Visitor<'de>,285	{286		self.deserialize_unit(visitor)287	}288289	fn deserialize_newtype_struct<V>(290		self,291		_name: &'static str,292		visitor: V,293	) -> Result<V::Value, Self::Error>294	where295		V: serde::de::Visitor<'de>,296	{297		visitor.visit_newtype_struct(self)298	}299300	fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>301	where302		V: serde::de::Visitor<'de>,303	{304		visitor.visit_seq(self.parse_array().map(ArrayAccess::new)?)305	}306307	fn deserialize_tuple<V>(self, _len: usize, visitor: V) -> Result<V::Value, Self::Error>308	where309		V: serde::de::Visitor<'de>,310	{311		self.deserialize_seq(visitor)312	}313314	fn deserialize_tuple_struct<V>(315		self,316		_name: &'static str,317		_len: usize,318		visitor: V,319	) -> Result<V::Value, Self::Error>320	where321		V: serde::de::Visitor<'de>,322	{323		self.deserialize_seq(visitor)324	}325326	fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Self::Error>327	where328		V: serde::de::Visitor<'de>,329	{330		match self {331			Value::Import(d) => {332				let value = d.serialize(crate::se_impl::MySerialize)?;333				value.deserialize_map(visitor)334			}335			v => visitor.visit_map(v.parse_object().map(ObjectAccess::new)?),336		}337	}338339	fn deserialize_struct<V>(340		self,341		_name: &'static str,342		_fields: &'static [&'static str],343		visitor: V,344	) -> Result<V::Value, Self::Error>345	where346		V: serde::de::Visitor<'de>,347	{348		self.deserialize_map(visitor)349	}350351	fn deserialize_enum<V>(352		self,353		_name: &'static str,354		_variants: &'static [&'static str],355		_visitor: V,356	) -> Result<V::Value, Self::Error>357	where358		V: serde::de::Visitor<'de>,359	{360		todo!()361	}362363	fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value, Self::Error>364	where365		V: serde::de::Visitor<'de>,366	{367		self.deserialize_str(visitor)368	}369370	fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>371	where372		V: serde::de::Visitor<'de>,373	{374		self.deserialize_any(visitor)375	}376}
modifiedcrates/nixlike/src/lib.rsdiffbeforeafterboth
--- a/crates/nixlike/src/lib.rs
+++ b/crates/nixlike/src/lib.rs
@@ -5,6 +5,8 @@
 //! expressions and expect it to work, only basic primitives are supported, and there is no
 //! variables/recursive records, interpolation, e.t.c.
 
+use std::marker::PhantomData;
+
 use linked_hash_map::LinkedHashMap;
 use peg::str::LineCol;
 use se_impl::MySerialize;
@@ -39,9 +41,28 @@
 	Boolean(bool),
 	Object(LinkedHashMap<String, Value>),
 	Array(Vec<Value>),
+	Import(NixImport),
 	Null,
 }
 
+#[derive(Debug, Serialize, Deserialize)]
+pub struct NixImport {
+	#[serde(rename = "__magic_import")]
+	import: String,
+	// Magic values should have exactly two values to avoid pretty-printing
+	// as nix inline object value
+	__magic_marker: PhantomData<()>,
+}
+
+impl NixImport {
+	pub fn new(import: impl AsRef<str>) -> Self {
+		Self {
+			import: import.as_ref().to_string(),
+			__magic_marker: PhantomData,
+		}
+	}
+}
+
 fn count_spaces(l: &str) -> usize {
 	l.chars().take_while(|&c| c == ' ').count()
 }
@@ -150,8 +171,12 @@
 	rule array() -> Vec<Value>
 		= "[" _ v:value()**_ _ "]" {v}
 
+	rule import() -> NixImport
+		= "import" _ s:string() {NixImport::new(s)}
+
 	rule value() -> Value
-		= o:object() { Value::Object(o) }
+		= i:import() { Value::Import(i) }
+		/ o:object() { Value::Object(o) }
 		/ a:array() { Value::Array(a) }
 		/ s:string() { Value::String(s) }
 		/ "null" { Value::Null }
@@ -191,26 +216,43 @@
 	out
 }
 
-#[test]
-fn test() {
-	assert_eq!(serialize("Hello\nworld").unwrap(), "\"Hello\\nworld\"\n");
-}
 pub fn format_nix(value: &String) -> String {
 	// TODO
 	value.to_owned()
 }
 
-#[test]
-fn parse_multiline() {
-	// First line is ignored, unless there is a significant characters.
-	assert_eq!(nixlike::multiline_string("''\n''").expect("parse"), "");
-	// Rest of the lines are processed normally.
-	assert_eq!(nixlike::multiline_string("''\n\n''").expect("parse"), "\n");
-	// Example with significant character on first line.
-	assert_eq!(nixlike::multiline_string("''t\n''").expect("parse"), "t\n");
-	// There might be nothing in multiline string block.
-	assert_eq!(nixlike::multiline_string("''''").expect("parse"), "");
-	// And there also might just be spaces, they are removed due to dedent, and output is empty because
-	// first line was also ignored due to missing significant characters.
-	assert_eq!(nixlike::multiline_string("''    ''").expect("parse"), "");
+#[cfg(test)]
+mod tests {
+	use super::*;
+
+	#[test]
+	fn test() {
+		assert_eq!(serialize("Hello\nworld").unwrap(), "\"Hello\\nworld\"");
+	}
+
+	#[test]
+	fn parse_multiline() {
+		// First line is ignored, unless there is a significant characters.
+		assert_eq!(nixlike::multiline_string("''\n''").expect("parse"), "");
+		// Rest of the lines are processed normally.
+		assert_eq!(nixlike::multiline_string("''\n\n''").expect("parse"), "\n");
+		// Example with significant character on first line.
+		assert_eq!(nixlike::multiline_string("''t\n''").expect("parse"), "t\n");
+		// There might be nothing in multiline string block.
+		assert_eq!(nixlike::multiline_string("''''").expect("parse"), "");
+		// And there also might just be spaces, they are removed due to dedent, and output is empty because
+		// first line was also ignored due to missing significant characters.
+		assert_eq!(nixlike::multiline_string("''    ''").expect("parse"), "");
+	}
+
+	#[test]
+	fn test_nix_import_roundtrip() {
+		let import = NixImport::new("./some/path.nix");
+
+		let serialized = serialize(&import).expect("serialize");
+		assert_eq!(serialized, "import \"./some/path.nix\"");
+
+		let deserialized: NixImport = parse_str(&serialized).expect("deserialize");
+		assert_eq!(deserialized.import, "./some/path.nix");
+	}
 }
modifiedcrates/nixlike/src/to_string.rsdiffbeforeafterboth
--- a/crates/nixlike/src/to_string.rs
+++ b/crates/nixlike/src/to_string.rs
@@ -1,3 +1,5 @@
+use itertools::Itertools;
+
 use crate::Value;
 
 pub fn write_identifier(k: &str, out: &mut String) {
@@ -76,6 +78,10 @@
 	}
 }
 
+fn write_nix_import(import: &str, out: &mut String, padding: &mut usize) {
+	out.push_str("import ");
+	write_nix_str(import, out, padding)
+}
 fn write_nix_buf(value: &Value, out: &mut String, padding: &mut usize) {
 	match value {
 		Value::Null => out.push_str("null"),
@@ -98,9 +104,17 @@
 				out.push(']');
 			}
 		}
+		Value::Import(i) => write_nix_import(&i.import, out, padding),
 		Value::Object(obj) => {
 			if obj.is_empty() {
-				out.push_str("{ }")
+				out.push_str("{ }");
+			} else if obj.len() == 2
+				&& let Some([(importk, Value::String(importv)), (markerk, Value::Null)]) =
+					obj.iter().next_array::<2>()
+				&& markerk == "__magic_marker"
+				&& importk == "__magic_import"
+			{
+				write_nix_import(importv, out, padding)
 			} else {
 				out.push_str("{\n");
 				*padding += 1;