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.rsdiffbeforeafterboth--- a/crates/nixlike/src/lib.rs
+++ b/crates/nixlike/src/lib.rs
@@ -1,5 +1,4 @@
-use std::collections::BTreeMap;
-
+use linked_hash_map::LinkedHashMap;
use peg::str::LineCol;
use se_impl::MySerialize;
use serde::{Deserialize, Serialize};
@@ -29,7 +28,7 @@
Number(i64),
String(String),
Boolean(bool),
- Object(BTreeMap<String, Value>),
+ Object(LinkedHashMap<String, Value>),
Array(Vec<Value>),
Null,
}
@@ -49,11 +48,11 @@
/ "false" {false} } / expected!("<boolean>")
rule indent() -> String
= quiet! { s:$(['a'..='z' | 'A'..='Z' | '0'..='9' | '_' | '-']+) { s.to_owned() } } / expected!("<identifier>")
- rule object() -> BTreeMap<String, Value>
+ rule object() -> LinkedHashMap<String, Value>
= "{" _
e:(k:indent()++(_ "." _) _ "=" _ v:value() _ ";" _ {(k, v)})*
"}" {?
- let mut out = BTreeMap::new();
+ let mut out = LinkedHashMap::new();
for (k, v) in e {
let mut map = &mut out;
for v in k.iter().take(k.len() - 1) {
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.rsdiffbeforeafterboth1use crate::Value;2use dprint_core::formatting::{3 condition_resolvers, conditions, format, ConditionResolverContext, Info, PrintItems,4 PrintOptions, Signal,5};67fn write_nix_obj_key_buf(k: &str, v: &Value, out: &mut PrintItems) {8 out.push_str(k);9 match v {10 Value::Object(o) if o.len() == 1 => {11 let (k, v) = o.iter().next().unwrap();1213 out.push_str(".");14 write_nix_obj_key_buf(k, v, out);15 }16 v => {17 out.push_str(" = ");18 write_nix_buf(v, out);19 out.push_str(";");20 }21 }22}2324fn write_nix_buf(value: &Value, out: &mut PrintItems) {25 match value {26 Value::Null => out.push_str("null"),27 Value::Boolean(v) => out.push_str(if *v { "true" } else { "false" }),28 Value::Number(n) => out.push_str(&format!("{}", n)),29 Value::String(s) => out.push_str(&format!("{:?}", s)),30 Value::Array(a) => {31 if a.is_empty() {32 out.push_str("[ ]");33 } else {34 let start_info = Info::new("start");35 let end_info = Info::new("end");36 let is_multiple_lines = move |ctx: &mut ConditionResolverContext| {37 condition_resolvers::is_multiple_lines(ctx, &start_info, &end_info)38 };39 out.push_str("[");40 out.push_info(start_info);41 out.push_signal(Signal::StartIndent);42 out.push_condition(conditions::if_true_or(43 "array start",44 is_multiple_lines.clone(),45 Signal::NewLine.into(),46 Signal::SpaceOrNewLine.into(),47 ));48 for item in a {49 write_nix_buf(item, out);50 out.push_condition(conditions::if_true_or(51 "element separator",52 is_multiple_lines.clone(),53 Signal::NewLine.into(),54 Signal::SpaceOrNewLine.into(),55 ));56 }57 out.push_signal(Signal::FinishIndent);58 out.push_info(end_info);59 out.push_str("]");60 }61 }62 Value::Object(obj) => {63 if obj.is_empty() {64 out.push_str("{ }")65 } else {66 let start_info = Info::new("start");67 let end_info = Info::new("end");68 let is_multiple_lines = move |ctx: &mut ConditionResolverContext| {69 condition_resolvers::is_multiple_lines(ctx, &start_info, &end_info)70 };71 out.push_str("{");72 out.push_info(start_info);73 out.push_signal(Signal::StartIndent);74 out.push_condition(conditions::if_true_or(75 "object start",76 is_multiple_lines.clone(),77 Signal::NewLine.into(),78 Signal::SpaceOrNewLine.into(),79 ));80 for (k, v) in obj {81 write_nix_obj_key_buf(k, v, out);82 out.push_condition(conditions::if_true_or(83 "element separator",84 is_multiple_lines.clone(),85 Signal::NewLine.into(),86 Signal::SpaceOrNewLine.into(),87 ));88 }89 out.push_signal(Signal::FinishIndent);90 out.push_info(end_info);91 out.push_str("}");92 }93 }94 };95}9697pub fn write_nix(value: &Value) -> String {98 format(99 || {100 let mut items = PrintItems::new();101 write_nix_buf(value, &mut items);102 items103 },104 PrintOptions {105 max_width: 120,106 use_tabs: false,107 indent_width: 2,108 new_line_text: "\n",109 },110 )111}