Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support SE-0166 Swift Archival & Serialization #43

Merged
merged 49 commits into from
Oct 30, 2017
Merged

Conversation

norio-nomura
Copy link
Collaborator

@norio-nomura norio-nomura commented May 7, 2017

Support SE-0166 Swift Archival & Serialization
Fixes #38

This uses local built toolchain https://github.com/norio-nomura/swift-dev/releases/tag/20170501a

TODOs for merging:

  • [ ] Add text encoding detection to YAMLDecoder.decode(_:from:)
  • Revise throwing errors on failing Encode and Decode
  • Add more tests
  • Support first snapshot of Swift 4.0

@jpsim
Copy link
Owner

jpsim commented May 8, 2017

This is very exciting!

if let expectedYAML = yaml {
XCTAssertEqual(expectedYAML, payload, "Produced YAML not identical to expected YAML.")
if let expectedYAML = yamlString {
let producedYAML = String(data: payload, encoding: .utf8)! // swiftlint:disable:this force_unwrapping
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can probably skip the force unwrap here as I think that XCTAssertEqual can take arguments of (String?, String)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup. But I prefer failing output as

XCTAssertEqual failed: ("{}
") is not equal to ("{ }
") - Produced YAML not identical to expected YAML.

to

XCTAssertEqual failed: ("Optional("{}\n")") is not equal to ("Optional("{ }\n")") - Produced YAML not identical to expected YAML.

Especially in long strings, I feel the former is easier to see.

// MARK: - Date Strategy Tests
func testEncodingDate() {
#if os(Linux)
print("'Date' does not conform to 'Codable' on Linux yet.")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By the way, this is now fixed 🎉

@norio-nomura norio-nomura mentioned this pull request Oct 20, 2017
@norio-nomura norio-nomura force-pushed the nn-SE-0166 branch 6 times, most recently from 7f64904 to 42727ed Compare October 25, 2017 01:05
@norio-nomura norio-nomura changed the title [WIP][Experimental] Support SE-0166 Swift Archival & Serialization Support SE-0166 Swift Archival & Serialization Oct 25, 2017
@norio-nomura
Copy link
Collaborator Author

Sorry for being late.
This is ready for review.

Copy link
Owner

@jpsim jpsim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First round of review.

@@ -185,50 +187,94 @@ extension Double: ScalarConstructible {
}
}

extension Float: ScalarConstructible {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think it'd be possible to generalize this to the FloatingPoint protocol? I ran into several issues while trying to do it, but if you can find a way, seems like that'd be pretty powerful.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll try.

var scalar = node.scalar!.string
switch scalar {
case ".inf", ".Inf", ".INF", "+.inf", "+.Inf", "+.INF":
return Float.infinity
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can omit the type here and below: (return .infinity, return -.infinity, return .nan). Same with the Double implementation.

}
#else
fileprivate init?(_ value: String) {
self.init(value, radix: 10)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation


// MARK: - CodingKey for `_UnkeyedEncodingContainer` and `superEncoders`

struct _YAMLEncodingKey: CodingKey { // swiftlint:disable:this type_name
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shares an implementation with _YAMLDecodingKey. Why not merge them into a _YAMLCodingKey?

}
}

extension Double: ScalarRepresentableCustomizedForCodable {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be able to combine these two implementations:

diff --git a/Sources/Yams/Representer.swift b/Sources/Yams/Representer.swift
index 09e05f9..c79eb44 100644
--- a/Sources/Yams/Representer.swift
+++ b/Sources/Yams/Representer.swift
@@ -225,23 +225,25 @@ extension Date: ScalarRepresentableCustomizedForCodable {
     }
 }
 
-extension Double: ScalarRepresentableCustomizedForCodable {
+extension Double: ScalarRepresentableCustomizedForCodable {}
+extension Float: ScalarRepresentableCustomizedForCodable {}
+
+extension FloatingPoint where Self: CVarArg {
     public func representedForCodable() -> Node {
-    #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
-        return Node(formattedStringForCodable, Tag(.float))
-    #else
+#if os(Linux)
         return Node(doubleFormatter.string(for: self)!.replacingOccurrences(of: "+-", with: "-"), Tag(.float))
-    #endif
+#else
+        return Node(formattedStringForCodable, Tag(.float))
+#endif
     }
 
     private var formattedStringForCodable: String {
         // Since `NumberFormatter` creates a string with insufficient precision for Decode,
         // it uses with `String(format:...)`
-    #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
+#if os(Linux)
+        let DBL_DECIMAL_DIG = 9
+#endif
         let string = String(format: "%.*g", DBL_DECIMAL_DIG, self)
-    #else
-        let string = String(format: "%.*g", 9, self)
-    #endif
         // "%*.g" does not use scientific notation if the exponent is less than –4.
         // So fallback to using `NumberFormatter` if string does not uses scientific notation.
         guard string.lazy.suffix(5).contains("e") else {
@@ -251,30 +253,4 @@ extension Double: ScalarRepresentableCustomizedForCodable {
     }
 }
 
-extension Float: ScalarRepresentableCustomizedForCodable {
-    public func representedForCodable() -> Node {
-    #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
-        return Node(formattedStringForCodable, Tag(.float))
-    #else
-        return Node(floatFormatter.string(for: self)!.replacingOccurrences(of: "+-", with: "-"), Tag(.float))
-    #endif
-    }
-
-    private var formattedStringForCodable: String {
-        // Since `NumberFormatter` creates a string with insufficient precision for Decode,
-        // it uses with `String(format:...)`
-    #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
-        let string = String(format: "%.*g", FLT_DECIMAL_DIG, self)
-    #else
-        let string = String(format: "%.*g", 9, self)
-    #endif
-        // "%*.g" does not use scientific notation if the exponent is less than –4.
-        // So fallback to using `NumberFormatter` if string does not uses scientific notation.
-        guard string.lazy.suffix(6).contains("e") else {
-            return floatFormatter.string(for: self)!.replacingOccurrences(of: "+-", with: "-")
-        }
-        return string
-    }
-}
-
 #endif

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for patch.
By applying this, I noticed that my commits has mistake:

+#if os(Linux)
+        let DBL_DECIMAL_DIG = 9
+#endif

DBL_DECIMAL_DIG should be 17.
That caused errors that fixed on 587c382.

_testRoundTrip(of: numbers, expectedYAML: "- 4\n- 8\n- 15\n- 16\n- 23\n- 42\n")
}

func testEncodingTopLevelStructuredSingleClass() {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test is flaky on Linux in my testing, where the order of keys doesn't appear to be stable across invocations.

EncoderTests.testEncodingTopLevelStructuredSingleClass : XCTAssertEqual failed: ("localhost:
  relative: http://127.0.0.1
Apple:
  relative: http://apple.com
") is not equal to ("Apple:
  relative: http://apple.com
localhost:
  relative: http://127.0.0.1
") - Produced YAML not identical to expected YAML.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add sortedKeys option to Emitter.Options.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be PR against master.

@jpsim
Copy link
Owner

jpsim commented Oct 25, 2017

I should say that this is super exciting. I think we'll be able to point to this to demonstrate a good implementation of Swift 4's custom encoders when this merges.

@norio-nomura
Copy link
Collaborator Author

Thank you for reviews!

FYI, I opened a custom encoder implementation of Swift 4 that I created when refactoring this PR.
https://github.com/norio-nomura/ObjectEncoder
That uses Any, [Any] and [String: Any] as payload instead of Yams.Node.

@norio-nomura
Copy link
Collaborator Author

I don't have any further points of review

Is this approved?

@jpsim
Copy link
Owner

jpsim commented Oct 30, 2017

Yes! 🎉

@norio-nomura norio-nomura merged commit 1c69640 into master Oct 30, 2017
@norio-nomura norio-nomura deleted the nn-SE-0166 branch October 30, 2017 03:28
@norio-nomura
Copy link
Collaborator Author

Thanks! 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants