difftreelog
feat preserve object field order
in: trunk
5 files changed
crates/nixlike/Cargo.tomldiffbeforeafterboth--- a/crates/nixlike/Cargo.toml
+++ b/crates/nixlike/Cargo.toml
@@ -8,6 +8,7 @@
[dependencies]
anyhow = "1.0.44"
dprint-core = "0.46.2"
+linked-hash-map = "0.5.4"
peg = "0.7.0"
serde = "1.0.130"
serde_json = "1.0.68"
crates/nixlike/src/de_impl.rsdiffbeforeafterboth--- a/crates/nixlike/src/de_impl.rs
+++ b/crates/nixlike/src/de_impl.rs
@@ -1,8 +1,6 @@
-use std::{
- collections::BTreeMap,
- convert::{TryFrom, TryInto},
-};
+use std::convert::{TryFrom, TryInto};
+use linked_hash_map::LinkedHashMap;
use serde::{
de::{self, MapAccess, SeqAccess},
Deserializer,
@@ -11,11 +9,11 @@
use crate::{Error, Value};
struct ObjectAccess {
- iter: std::collections::btree_map::IntoIter<String, Value>,
+ iter: linked_hash_map::IntoIter<String, Value>,
value: Option<Value>,
}
impl ObjectAccess {
- fn new(v: BTreeMap<String, Value>) -> Self {
+ fn new(v: LinkedHashMap<String, Value>) -> Self {
Self {
iter: v.into_iter(),
value: None,
@@ -103,7 +101,7 @@
_ => Err(Error::Expected("array")),
}
}
- fn parse_object(self) -> Result<BTreeMap<String, Value>, Error> {
+ fn parse_object(self) -> Result<LinkedHashMap<String, Value>, Error> {
match self {
Value::Object(s) => Ok(s),
_ => Err(Error::Expected("object")),
crates/nixlike/src/lib.rsdiffbeforeafterboth1use linked_hash_map::LinkedHashMap;2use peg::str::LineCol;3use se_impl::MySerialize;4use serde::{Deserialize, Serialize};56mod de_impl;7mod se_impl;8mod to_string;910#[derive(thiserror::Error, Debug)]11pub enum Error {12 #[error("bad number")]13 BadNumber,14 #[error("expected {0}")]15 Expected(&'static str),16 #[error("parse error")]17 ParseError(#[from] peg::error::ParseError<LineCol>),18 #[error("{0}")]19 Custom(String),20 #[error("io: {0}")]21 Io(#[from] std::io::Error),22 #[error("fmt: {0}")]23 Fmt(#[from] std::fmt::Error),24}2526#[derive(Debug)]27pub enum Value {28 Number(i64),29 String(String),30 Boolean(bool),31 Object(LinkedHashMap<String, Value>),32 Array(Vec<Value>),33 Null,34}3536peg::parser! {37pub grammar nixlike() for str {38 rule number() -> i6439 = quiet! { v:$(['0'..='9' | '+' | '-']+) {? v.parse().map_err(|_| "<number>")} } / expected!("<number>")40 rule string_char() -> &'input str41 = "\\\"" { "\"" }42 / "\\\\" { "\\" }43 / c:$([_]) { c }44 rule string() -> String45 = quiet! { "\"" v:(!"\"" c:string_char() {c})* "\"" { v.into_iter().collect() } } / expected!("<string>")46 rule boolean() -> bool47 = quiet! { "true" {true}48 / "false" {false} } / expected!("<boolean>")49 rule indent() -> String50 = quiet! { s:$(['a'..='z' | 'A'..='Z' | '0'..='9' | '_' | '-']+) { s.to_owned() } } / expected!("<identifier>")51 rule object() -> LinkedHashMap<String, Value>52 = "{" _53 e:(k:indent()++(_ "." _) _ "=" _ v:value() _ ";" _ {(k, v)})*54 "}" {?55 let mut out = LinkedHashMap::new();56 for (k, v) in e {57 let mut map = &mut out;58 for v in k.iter().take(k.len() - 1) {59 map = match map.entry(v.clone()).or_insert_with(|| Value::Object(Default::default())) {60 Value::Object(v) => v,61 _ => return Err("expected object"),62 }63 }6465 let key = k.into_iter().last().unwrap();66 if map.contains_key(&key) {67 return Err("can't override object");68 }69 map.insert(key, v);70 }71 Ok(out)72 }7374 rule array() -> Vec<Value>75 = "[" _ v:value()**_ _ "]" {v}7677 rule value() -> Value78 = o:object() { Value::Object(o) }79 / a:array() { Value::Array(a) }80 / s:string() { Value::String(s) }81 / "null" { Value::Null }82 / b:boolean() { Value::Boolean(b) }83 / n:number() { Value::Number(n) }8485 pub rule root() -> Value86 = _ v:value() _ { v }8788 rule _()89 = ( quiet!{ [' ' | '\t' | '\n']+ }90 / "#" (!['\n'] [_])* "\n" )*91}92}9394pub fn parse_str<'de, D: Deserialize<'de>>(s: &str) -> Result<D, Error> {95 let value = nixlike::root(s)?;96 D::deserialize(value)97}9899pub fn parse_value<'de, D: Deserialize<'de>>(value: Value) -> Result<D, Error> {100 D::deserialize(value)101}102103pub fn serialize_value_pretty(value: Value) -> String {104 to_string::write_nix(&value)105}106107pub fn serialize<S: Serialize>(value: S) -> Result<String, Error> {108 let value: Value = value.serialize(MySerialize)?;109 Ok(serialize_value_pretty(value))110}111112#[test]113fn test() {114 let v: serde_json::Value = parse_str(115 r#"116 {117 b.c = 2;118 b.d = "hello";119 c = {120 k = 123;121 p = 231;122 ll = [1 2 3 [] [[4 5 6]] ];123 };124 }125 "#,126 )127 .unwrap();128 let s: String = serialize(v).unwrap();129 println!("{}", s);130}crates/nixlike/src/se_impl.rsdiffbeforeafterboth--- a/crates/nixlike/src/se_impl.rs
+++ b/crates/nixlike/src/se_impl.rs
@@ -1,5 +1,6 @@
-use std::{collections::BTreeMap, convert::TryInto};
+use std::convert::TryInto;
+use linked_hash_map::LinkedHashMap;
use serde::{
ser::{
self, SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant, SerializeTuple,
@@ -96,7 +97,7 @@
}
}
-pub struct MySerializeMap(BTreeMap<String, Value>, Option<String>);
+pub struct MySerializeMap(LinkedHashMap<String, Value>, Option<String>);
impl SerializeMap for MySerializeMap {
type Ok = Value;
@@ -127,7 +128,7 @@
}
}
-pub struct MySerializeStruct(BTreeMap<String, Value>);
+pub struct MySerializeStruct(LinkedHashMap<String, Value>);
impl SerializeStruct for MySerializeStruct {
type Ok = Value;
@@ -147,7 +148,7 @@
}
}
-pub struct MySerializeStructVariant(String, BTreeMap<String, Value>);
+pub struct MySerializeStructVariant(String, LinkedHashMap<String, Value>);
impl SerializeStructVariant for MySerializeStructVariant {
type Ok = Value;
@@ -336,7 +337,7 @@
}
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
- Ok(MySerializeMap(BTreeMap::new(), None))
+ Ok(MySerializeMap(LinkedHashMap::new(), None))
}
fn serialize_struct(
@@ -344,7 +345,7 @@
_name: &'static str,
_len: usize,
) -> Result<Self::SerializeStruct, Self::Error> {
- Ok(MySerializeStruct(BTreeMap::new()))
+ Ok(MySerializeStruct(LinkedHashMap::new()))
}
fn serialize_struct_variant(
@@ -356,7 +357,7 @@
) -> Result<Self::SerializeStructVariant, Self::Error> {
Ok(MySerializeStructVariant(
variant.to_owned(),
- BTreeMap::new(),
+ LinkedHashMap::new(),
))
}
}
crates/nixlike/src/to_string.rsdiffbeforeafterboth--- a/crates/nixlike/src/to_string.rs
+++ b/crates/nixlike/src/to_string.rs
@@ -41,7 +41,7 @@
out.push_signal(Signal::StartIndent);
out.push_condition(conditions::if_true_or(
"array start",
- is_multiple_lines.clone(),
+ is_multiple_lines,
Signal::NewLine.into(),
Signal::SpaceOrNewLine.into(),
));
@@ -49,7 +49,7 @@
write_nix_buf(item, out);
out.push_condition(conditions::if_true_or(
"element separator",
- is_multiple_lines.clone(),
+ is_multiple_lines,
Signal::NewLine.into(),
Signal::SpaceOrNewLine.into(),
));
@@ -73,7 +73,7 @@
out.push_signal(Signal::StartIndent);
out.push_condition(conditions::if_true_or(
"object start",
- is_multiple_lines.clone(),
+ is_multiple_lines,
Signal::NewLine.into(),
Signal::SpaceOrNewLine.into(),
));
@@ -81,7 +81,7 @@
write_nix_obj_key_buf(k, v, out);
out.push_condition(conditions::if_true_or(
"element separator",
- is_multiple_lines.clone(),
+ is_multiple_lines,
Signal::NewLine.into(),
Signal::SpaceOrNewLine.into(),
));