difftreelog
feat (de)serialize nix imports
in: trunk
5 files changed
Cargo.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",
crates/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"
crates/nixlike/src/de_impl.rsdiffbeforeafterboth1use 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}crates/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");
+ }
}
crates/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;