Repository: alexcrichton/toml-rs
Pull request #208 (2017-08-13 22:19:37)

add pretty_string_literal to be able to disable literal strings
We should be able to disable literal `'` and `'''` strings and just use `"` or `"""` depending on whether there are newlines.

In the process I found a bug with the deserialization module that I added a FIXME for.

@killercup requested this in killercup/cargo-edit#163

vitiral: let me resolve the conflicts

Patch:
diff --git a/src/ser.rs b/src/ser.rs
index 3c4c220..ef037ed 100644
--- a/src/ser.rs
+++ b/src/ser.rs
@@ -169,9 +169,19 @@ impl ArraySettings {
 
 #[doc(hidden)]
 #[derive(Debug, Default, Clone)]
-/// String settings. Currently empty but may contain settings
-/// eventually.
-struct StringSettings();
+/// String settings
+struct StringSettings {
+    /// Whether to use literal strings when possible
+    literal: bool,
+}
+
+impl StringSettings {
+    fn pretty() -> StringSettings {
+        StringSettings {
+            literal: true,
+        }
+    }
+}
 
 #[derive(Debug, Default, Clone)]
 #[doc(hidden)]
@@ -259,7 +269,7 @@ impl<'a> Serializer<'a> {
             state: State::End,
             settings: Rc::new(Settings {
                 array: Some(ArraySettings::pretty()),
-                string: Some(StringSettings()),
+                string: Some(StringSettings::pretty()),
             }),
         }
     }
@@ -289,13 +299,56 @@ impl<'a> Serializer<'a> {
     /// ```
     pub fn pretty_string(&mut self, value: bool) -> &mut Self {
         Rc::get_mut(&mut self.settings).unwrap().string = if value {
-            Some(StringSettings())
+            Some(StringSettings::pretty())
         } else {
             None
         };
         self
     }
 
+    /// Enable or Disable Literal strings for pretty strings 
+    ///
+    /// If enabled, literal strings will be used when possible and strings with
+    /// one or more newlines will use triple quotes (i.e.: `'''` or `"""`)
+    ///
+    /// If disabled, literal strings will NEVER be used and strings with one or
+    /// more newlines will use `"""`
+    ///
+    /// # Examples
+    ///
+    /// Instead of:
+    ///
+    /// ```toml,ignore
+    /// single = "no newlines"
+    /// text = "\nfoo\nbar\n"
+    /// ```
+    ///
+    /// You will have:
+    ///
+    /// ```toml,ignore
+    /// single = "no newlines"
+    /// text = """
+    /// foo
+    /// bar
+    /// """
+    /// ```
+    pub fn pretty_string_literal(&mut self, value: bool) -> &mut Self {
+        let use_default = if let &mut Some(ref mut s) = &mut Rc::get_mut(&mut self.settings)
+                .unwrap().string {
+            s.literal = value;
+            false
+        } else {
+            true
+        };
+
+        if use_default {
+            let mut string = StringSettings::pretty();
+            string.literal = value;
+            Rc::get_mut(&mut self.settings).unwrap().string = Some(string);
+        }
+        self
+    }
+
     /// Enable or Disable pretty arrays
     ///
     /// If enabled, arrays will always have each item on their own line.
@@ -548,7 +601,11 @@ impl<'a> Serializer<'a> {
         }
 
         let repr = if !is_key && self.settings.string.is_some() {
-            do_pretty(value)
+            match (&self.settings.string, do_pretty(value)) {
+                (&Some(StringSettings { literal: false, .. }), Repr::Literal(_, ty)) => 
+                    Repr::Std(ty),
+                (_, r @ _) => r,
+            }
         } else {
             Repr::Std(Type::OnelineSingle)
         };
@@ -569,8 +626,11 @@ impl<'a> Serializer<'a> {
             Repr::Std(ty) => {
                 match ty {
                     Type::NewlineTripple =>  self.dst.push_str("\"\"\"\n"),
-                    Type::OnelineSingle =>  self.dst.push('"'),
-                    _ => unreachable!(),
+                    // note: OnelineTripple can happen if do_pretty wants to do
+                    // '''it's one line''' 
+                    // but settings.string.literal == false
+                    Type::OnelineSingle | 
+                        Type::OnelineTripple =>  self.dst.push('"'),
                 }
                 for ch in value.chars() {
                     match ch {
@@ -593,8 +653,7 @@ impl<'a> Serializer<'a> {
                 }
                 match ty {
                     Type::NewlineTripple =>  self.dst.push_str("\"\"\""),
-                    Type::OnelineSingle =>  self.dst.push('"'),
-                    _ => unreachable!(),
+                    Type::OnelineSingle | Type::OnelineTripple => self.dst.push('"'),
                 }
             },
         }


Test patch:
diff --git a/tests/pretty.rs b/tests/pretty.rs
index 3a9777c..ce906d9 100644
--- a/tests/pretty.rs
+++ b/tests/pretty.rs
@@ -198,7 +198,7 @@ text = """
 this is the first line.
 This has a ''' in it and \"\"\" cuz it's tricky yo
 Also ' and \" because why not
-this is the third line
+this is the fourth line
 """
 "##;
 
@@ -260,3 +260,45 @@ fn table_array() {
     println!("\nRESULT:\n{}", result);
     assert_eq!(toml, &result);
 }
+
+// FIXME: add the `glass` line to this. Unfortunately there seems to
+// be an issue with the deserialization module that treats the first \n
+// as a real newline in that case (not cool)
+const PRETTY_TRICKY_NON_LITERAL: &'static str = r##"[example]
+f = "\f"
+plain = """
+This has a couple of lines
+Because it likes to.
+"""
+r = "\r"
+r_newline = """
+\r
+"""
+single = "this is a single line but has '' cuz it's tricky"
+single_tricky = "single line with ''' in it"
+tabs = """
+this is pretty standard
+\texcept for some \ttabs right here
+"""
+text = """
+this is the first line.
+This has a ''' in it and \"\"\" cuz it's tricky yo
+Also ' and \" because why not
+this is the fourth line
+"""
+"##;
+
+#[test]
+fn pretty_tricky_non_literal() {
+    let toml = PRETTY_TRICKY_NON_LITERAL;
+    let value: toml::Value = toml::from_str(toml).unwrap();
+    let mut result = String::with_capacity(128);
+    {
+        let mut serializer = toml::Serializer::pretty(&mut result);
+        serializer.pretty_string_literal(false);
+        value.serialize(&mut serializer).unwrap();
+    }
+    println!("EXPECTED:\n{}", toml);
+    println!("\nRESULT:\n{}", result);
+    assert_eq!(toml, &result);
+}