Repository: jpsim/Yams (swift)

Issue:
Yams.dump dumps numerical String values as Number
this seems to be similar to https://github.com/jpsim/Yams/issues/174 though i'm not sure it's the same issue.

```swift
let dict = ["1.0": "2."]
print(try! Yams.dump(object: dict))
```

expected output: `'1.0': '2.'`
actual output: `1.0: 2.`

maybe this is an accepted limitation?

if so, i'd love to know if there is a sane way to correctly dump dicts of an unknown structure. i can only think of deep-mapping them to Nodes and setting styles based on the Strings' contents...

Pull request description:
Fixes #197

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 95d14c97..3ae898dd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,11 @@
 * Accurately represent `Date`s with nanosecond components in Swift 4.x.  
   [Norio Nomura](https://github.com/norio-nomura)
 
+* Change to apply single quoted style to YAML representation of `String`, if 
+  that contents will be resolved to other than `.str` by default `Resolver`.  
+  [Norio Nomura](https://github.com/norio-nomura)
+  [#197](https://github.com/jpsim/Yams/issues/197)
+
 ##### Bug Fixes
 
 * Fix a bug where `YAMLEncoder` would delay `Date`s by 1 second when encoding
diff --git a/Sources/Yams/Representer.swift b/Sources/Yams/Representer.swift
index 92ab078a..e01b4972 100644
--- a/Sources/Yams/Representer.swift
+++ b/Sources/Yams/Representer.swift
@@ -56,9 +56,7 @@ extension Dictionary: NodeRepresentable {
 }
 
 private func represent(_ value: Any) throws -> Node {
-    if let string = value as? String {
-        return Node(string)
-    } else if let representable = value as? NodeRepresentable {
+    if let representable = value as? NodeRepresentable {
         return try representable.represented()
     }
     throw YamlError.representer(problem: "Failed to represent \(value)")
@@ -230,7 +228,8 @@ extension URL: ScalarRepresentable {
 extension String: ScalarRepresentable {
     /// This value's `Node.scalar` representation.
     public func represented() -> Node.Scalar {
-        return .init(self)
+        let scalar = Node.Scalar(self)
+        return scalar.resolvedTag.name == .str ? scalar : .init(self, Tag(.str), .singleQuoted)
     }
 }
 


Test patch:
diff --git a/Tests/YamsTests/EmitterTests.swift b/Tests/YamsTests/EmitterTests.swift
index 8afb7572..e96c016d 100644
--- a/Tests/YamsTests/EmitterTests.swift
+++ b/Tests/YamsTests/EmitterTests.swift
@@ -122,6 +122,24 @@ class EmitterTests: XCTestCase {
         let expectedSorted = "key1: value1\nkey2: value2\nkey3: value3\n"
         XCTAssertEqual(yamlSorted, expectedSorted)
     }
+
+    func testSmartQuotedString() throws {
+        let samples: [(string: String, tag: Tag.Name, expected: String, line: UInt)] = [
+            ("string", .str, "string", #line),
+            ("true", .bool, "'true'", #line),
+            ("1", .int, "'1'", #line),
+            ("1.0", .float, "'1.0'", #line),
+            ("null", .null, "'null'", #line),
+            ("2019-07-06", .timestamp, "'2019-07-06'", #line),
+        ]
+        let resolver = Resolver.default
+        for (string, tag, expected, line) in samples {
+            let resolvedTag = resolver.resolveTag(of: Node(string))
+            XCTAssertEqual(resolvedTag, tag, "Resolver resolves unexpected tag", line: line)
+            let yaml = try Yams.dump(object: string)
+            XCTAssertEqual(yaml, "\(expected)\n", line: line)
+        }
+    }
 }
 
 extension EmitterTests {
@@ -132,7 +150,8 @@ extension EmitterTests {
             ("testMapping", testMapping),
             ("testLineBreaks", testLineBreaks),
             ("testAllowUnicode", testAllowUnicode),
-            ("testSortKeys", testSortKeys)
+            ("testSortKeys", testSortKeys),
+            ("testSmartQuotedString", testSmartQuotedString)
         ]
     }
 }
diff --git a/Tests/YamsTests/SpecTests.swift b/Tests/YamsTests/SpecTests.swift
index f3adeaa2..95d0c88c 100644
--- a/Tests/YamsTests/SpecTests.swift
+++ b/Tests/YamsTests/SpecTests.swift
@@ -703,7 +703,7 @@ class SpecTests: XCTestCase { // swiftlint:disable:this type_body_length
               different documents.
 
               '
-            not-date: 2002-04-28
+            not-date: '2002-04-28'
             picture: R0lGODlhDAAMAIQAAP//9/X17unp5WZmZgAAAOfn515eXvPz7Y6OjuDg4J+fn5OTk6enp56enmleECcgggoBADs=
 
             """