Repository: ron-rs/ron (rust)

Issue:
Configure the depth limit for item indentation
After a particular depth, don't insert newlines any more, e.g. (from @pyfisch custom format):
```
┌ Display List
│  ├─ ClipScrollNodes
│  │  └─ ClipScrollNode { id: Some(Clip(0, PipelineId(0, 1))), parent_index: ClipScrollNodeIndex(0), clip: ClippingRegion::Empty, content_rect: TypedRect(0.0×0.0 at (0.0,0.0)), node_type: ScrollFrame(ScriptAndInputEvents) }
│  ├─ Items
│  │  ├─ PushStackingContext(StackingContext at TypedRect(1024px×1077.2333333333333px at (0px,0px)) with overflow TypedRect(1024px×1077.2333333333333px at (0px,0px)): StackingContextId(0)) StackingContext: StackingContextId(0) ClippingAndScrolling { scrolling: ClipScrollNodeIndex(0), clipping: None }
│  │  ├─ SolidColor rgba(0, 0, 0, 0) @ TypedRect(1024px×1077.2333333333333px at (0px,0px)) Rect(TypedRect(1024.0×1077.2333 at (0.0,0.0))) StackingContext: StackingContextId(0) ClippingAndScrolling { scrolling: ClipScrollNodeIndex(0), clipping: None }
```

Pull request:
Limit the pretty depth
Fixes #92
cc @pyfisch 

Output of `cargo run --example encode`:
```yaml
(
    float: (2.18,-1.1,),
    tuple: ((),false,),
    map: {0:'1',8:'1',3:'5',1:'2',},
    nested: (a:"Hello from \"RON\"",b:'b',),
    var: A(255,"",),
    array: [(),(),(),],
)
```

Unresolved questions:
  1. should we omit the trailing comma for non-pretty output? Given that it's on the same line, it sort of makes sense. It would just affect more than just the `depth_limit` parameter.
  2. do we still want to put spaces after commas for pretty output behind the depth limit?

Patch:
diff --git a/examples/encode.rs b/examples/encode.rs
index d81bb5d8..389ac507 100644
--- a/examples/encode.rs
+++ b/examples/encode.rs
@@ -4,7 +4,6 @@ extern crate serde;
 
 use std::collections::HashMap;
 use std::default::Default;
-use std::fs::File;
 
 use ron::ser::{to_string_pretty, PrettyConfig};
 
@@ -33,11 +32,8 @@ struct Nested {
 }
 
 fn main() {
-    use std::io::Write;
     use std::iter::FromIterator;
 
-    let mut file = File::create("config.ron").expect("Failed to create file");
-
     let data = Config {
         float: (2.18, -1.1),
         tuple: TupleStruct((), false),
@@ -51,12 +47,12 @@ fn main() {
     };
 
     let pretty = PrettyConfig {
+        depth_limit: 2,
         separate_tuple_members: true,
         enumerate_arrays: true,
         ..PrettyConfig::default()
     };
     let s = to_string_pretty(&data, pretty).expect("Serialization failed");
 
-    file.write(s.as_bytes())
-        .expect("Failed to write data to file");
+    println!("{}", s);
 }
diff --git a/src/ser/mod.rs b/src/ser/mod.rs
index 9b6da53f..7182eb6b 100644
--- a/src/ser/mod.rs
+++ b/src/ser/mod.rs
@@ -78,6 +78,8 @@ struct Pretty {
 /// Pretty serializer configuration
 #[derive(Clone, Debug, Serialize, Deserialize)]
 pub struct PrettyConfig {
+    /// Limit the pretty-ness up to the given depth.
+    pub depth_limit: usize,
     /// New line string
     pub new_line: String,
     /// Indentation string
@@ -91,6 +93,7 @@ pub struct PrettyConfig {
 impl Default for PrettyConfig {
     fn default() -> Self {
         PrettyConfig {
+            depth_limit: !0,
             #[cfg(not(target_os = "windows"))]
             new_line: "\n".to_string(),
             #[cfg(target_os = "windows")]
@@ -129,6 +132,13 @@ impl Serializer {
         self.output
     }
 
+    fn is_pretty(&self) -> bool {
+        match self.pretty {
+            Some((ref config, ref pretty)) => pretty.indent < config.depth_limit,
+            None => false,
+        }
+    }
+
     fn separate_tuple_members(&self) -> bool {
         self.pretty
             .as_ref()
@@ -139,22 +149,28 @@ impl Serializer {
     fn start_indent(&mut self) {
         if let Some((ref config, ref mut pretty)) = self.pretty {
             pretty.indent += 1;
-            self.output += &config.new_line;
+            if pretty.indent < config.depth_limit {
+                self.output += &config.new_line;
+            }
         }
     }
 
     fn indent(&mut self) {
         if let Some((ref config, ref pretty)) = self.pretty {
-            self.output
-                .extend((0..pretty.indent).map(|_| config.indentor.as_str()));
+            if pretty.indent < config.depth_limit {
+                self.output
+                    .extend((0..pretty.indent).map(|_| config.indentor.as_str()));
+            }
         }
     }
 
     fn end_indent(&mut self) {
         if let Some((ref config, ref mut pretty)) = self.pretty {
+            if pretty.indent < config.depth_limit {
+                self.output
+                    .extend((1..pretty.indent).map(|_| config.indentor.as_str()));
+            }
             pretty.indent -= 1;
-            self.output
-                .extend((0..pretty.indent).map(|_| config.indentor.as_str()));
         }
     }
 }
@@ -425,15 +441,17 @@ impl<'a> ser::SerializeSeq for &'a mut Serializer {
         self.output += ",";
 
         if let Some((ref config, ref mut pretty)) = self.pretty {
-            if config.enumerate_arrays {
-                assert!(config.new_line.contains('\n'));
-                let index = pretty.sequence_index.last_mut().unwrap();
-                //TODO: when /**/ comments are supported, prepend the index
-                // to an element instead of appending it.
-                write!(self.output, "// [{}]", index).unwrap();
-                *index += 1;
+            if pretty.indent < config.depth_limit {
+                if config.enumerate_arrays {
+                    assert!(config.new_line.contains('\n'));
+                    let index = pretty.sequence_index.last_mut().unwrap();
+                    //TODO: when /**/ comments are supported, prepend the index
+                    // to an element instead of appending it.
+                    write!(self.output, "// [{}]", index).unwrap();
+                    *index += 1;
+                }
+                self.output += &config.new_line;
             }
-            self.output += &config.new_line;
         }
 
         Ok(())
@@ -466,25 +484,25 @@ impl<'a> ser::SerializeTuple for &'a mut Serializer {
         value.serialize(&mut **self)?;
         self.output += ",";
 
-        if let Some((ref config, _)) = self.pretty {
-            self.output += if self.separate_tuple_members() {
-                &config.new_line
-            } else {
-                " "
-            };
+        if let Some((ref config, ref pretty)) = self.pretty {
+            if pretty.indent < config.depth_limit {
+                self.output += if self.separate_tuple_members() {
+                    &config.new_line
+                } else {
+                    " "
+                };
+            }
         }
 
         Ok(())
     }
 
     fn end(self) -> Result<()> {
-        if self.pretty.is_some() {
-            if self.separate_tuple_members() {
-                self.end_indent();
-            } else {
-                self.output.pop();
-                self.output.pop();
-            }
+        if self.separate_tuple_members() {
+            self.end_indent();
+        } else if self.is_pretty() {
+            self.output.pop();
+            self.output.pop();
         }
 
         self.output += ")";
@@ -545,15 +563,17 @@ impl<'a> ser::SerializeMap for &'a mut Serializer {
     {
         self.output += ":";
 
-        if self.pretty.is_some() {
+        if self.is_pretty() {
             self.output += " ";
         }
 
         value.serialize(&mut **self)?;
         self.output += ",";
 
-        if let Some((ref config, _)) = self.pretty {
-            self.output += &config.new_line;
+        if let Some((ref config, ref pretty)) = self.pretty {
+            if pretty.indent < config.depth_limit {
+                self.output += &config.new_line;
+            }
         }
 
         Ok(())
@@ -580,15 +600,17 @@ impl<'a> ser::SerializeStruct for &'a mut Serializer {
         self.output += key;
         self.output += ":";
 
-        if self.pretty.is_some() {
+        if self.is_pretty() {
             self.output += " ";
         }
 
         value.serialize(&mut **self)?;
         self.output += ",";
 
-        if let Some((ref config, _)) = self.pretty {
-            self.output += &config.new_line;
+        if let Some((ref config, ref pretty)) = self.pretty {
+            if pretty.indent < config.depth_limit {
+                self.output += &config.new_line;
+            }
         }
 
         Ok(())


Test patch:
diff --git a/tests/depth_limit.rs b/tests/depth_limit.rs
new file mode 100644
index 00000000..8dd157b1
--- /dev/null
+++ b/tests/depth_limit.rs
@@ -0,0 +1,63 @@
+extern crate ron;
+#[macro_use]
+extern crate serde;
+
+use std::collections::HashMap;
+
+#[derive(Serialize)]
+struct Config {
+    float: (f32, f64),
+    tuple: TupleStruct,
+    map: HashMap<u8, char>,
+    nested: Nested,
+    var: Variant,
+    array: Vec<()>,
+}
+
+#[derive(Serialize)]
+struct TupleStruct((), bool);
+
+#[derive(Serialize)]
+enum Variant {
+    A(u8, &'static str),
+}
+
+#[derive(Serialize)]
+struct Nested {
+    a: String,
+    b: char,
+}
+
+const EXPECTED: &str = "(
+    float: (2.18,-1.1,),
+    tuple: ((),false,),
+    map: {8:'1',},
+    nested: (a:\"a\",b:'b',),
+    var: A(255,\"\",),
+    array: [(),(),(),],
+)";
+
+#[test]
+fn depth_limit() {
+    let data = Config {
+        float: (2.18, -1.1),
+        tuple: TupleStruct((), false),
+        map: vec![(8, '1')].into_iter().collect(),
+        nested: Nested {
+            a: "a".to_owned(),
+            b: 'b',
+        },
+        var: Variant::A(!0, ""),
+        array: vec![(); 3],
+    };
+
+    let pretty = ron::ser::PrettyConfig {
+        depth_limit: 2,
+        separate_tuple_members: true,
+        enumerate_arrays: true,
+        .. Default::default()
+    };
+    let s = ron::ser::to_string_pretty(&data, pretty);
+
+    assert_eq!(s, Ok(EXPECTED.to_string()));
+}