Skip to content

Commit

Permalink
SWIFT-1064 Rename ExtendedJSONEncoder.mode and make it a struct (#61)
Browse files Browse the repository at this point in the history
  • Loading branch information
kmahar authored Feb 10, 2021
1 parent e2b2dc0 commit 8a9d60c
Show file tree
Hide file tree
Showing 7 changed files with 29 additions and 17 deletions.
1 change: 1 addition & 0 deletions .swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ disabled_rules:
- inclusive_language # disabled until we complete work for the "remove offensive terminology" project.
- cyclomatic_complexity
- opening_brace # conflicts with SwiftFormat wrapMultilineStatementBraces
- nesting

opt_in_rules:
- array_init
Expand Down
2 changes: 1 addition & 1 deletion Guides/JSON-Interop.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ The `ExtendedJSONEncoder` produces relaxed Extended JSON by default, but can be
```swift
let bob = Person(name: "Bob", age: 25)
let encoder = ExtendedJSONEncoder()
encoder.mode = .canonical
encoder.format = .canonical
let canonicalEncoded = try encoder.encode(bob) // "{\"name\":\"Bob\",\"age\":{\"$numberInt\":\"25\"}}"
```
The `ExtendedJSONDecoder` accepts either format, or a mix of both:
Expand Down
2 changes: 1 addition & 1 deletion Sources/SwiftBSON/BSONDocument.swift
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ public struct BSONDocument {
/// On error, an empty string will be returned.
public func toCanonicalExtendedJSONString() -> String {
let encoder = ExtendedJSONEncoder()
encoder.mode = .canonical
encoder.format = .canonical
guard let encoded = try? encoder.encode(self) else {
return ""
}
Expand Down
32 changes: 22 additions & 10 deletions Sources/SwiftBSON/ExtendedJSONEncoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,31 @@ import NIO

/// Facilitates the encoding of `Encodable` values into ExtendedJSON.
public class ExtendedJSONEncoder {
/// An enum representing one of the two supported string formats based on the JSON standard
/// that describe how to represent BSON documents in JSON using standard JSON types and/or type wrapper objects.
public enum Mode {
/// A struct representing the supported string formats based on the JSON standard that describe how to represent
/// BSON documents in JSON using standard JSON types and/or type wrapper objects.
public struct Format {
/// Canonical Extended JSON Format: Emphasizes type preservation
/// at the expense of readability and interoperability.
case canonical
public static let canonical = Format(.canonical)

/// Relaxed Extended JSON Format: Emphasizes readability and interoperability
/// at the expense of type preservation.
case relaxed
public static let relaxed = Format(.relaxed)

/// Internal representation of extJSON format.
fileprivate enum _Format {
case canonical, relaxed
}

fileprivate var _format: _Format

private init(_ _format: _Format) {
self._format = _format
}
}

/// Determines whether to encode to canonical or relaxed extended JSON. Default is relaxed.
public var mode: Mode = .relaxed
public var format: Format = .relaxed

/// Contextual user-provided information for use during encoding.
public var userInfo: [CodingUserInfoKey: Any] = [:]
Expand All @@ -29,14 +40,14 @@ public class ExtendedJSONEncoder {
// T --> BSON --> JSONValue --> Data
// Takes in any encodable type `T`, converts it to an instance of the `BSON` enum via the `BSONDecoder`.
// The `BSON` is converted to an instance of the `JSON` enum via the `toRelaxedExtendedJSON`
// or `toCanonicalExtendedJSON` methods on `BSONValue`s (depending on the `mode`).
// or `toCanonicalExtendedJSON` methods on `BSONValue`s (depending on the `format`).
// The `JSON` is then passed through a `JSONEncoder` and outputted as `Data`.
let encoder = BSONEncoder()
encoder.userInfo = self.userInfo
let bson: BSON = try encoder.encodeFragment(value)

let json: JSON
switch self.mode {
switch self.format._format {
case .canonical:
json = bson.bsonValue.toCanonicalExtendedJSON()
case .relaxed:
Expand All @@ -49,7 +60,8 @@ public class ExtendedJSONEncoder {
}

/// Encodes an instance of the Encodable Type `T` into Data representing canonical or relaxed extended JSON.
/// The value of `self.mode` will determine which format is used. If it is not set explicitly, relaxed will be used.
/// The value of `self.format` will determine which format is used. If it is not set explicitly, relaxed will
/// be used.
///
/// - SeeAlso: https://docs.mongodb.com/manual/reference/mongodb-extended-json/
///
Expand All @@ -62,7 +74,7 @@ public class ExtendedJSONEncoder {
}

/// Encodes an instance of the Encodable Type `T` into a `ByteBuffer` representing canonical or relaxed extended
/// JSON. The value of `self.mode` will determine which format is used. If it is not set explicitly, relaxed will
/// JSON. The value of `self.format` will determine which format is used. If it is not set explicitly, relaxed will
/// be used.
///
/// - SeeAlso: https://docs.mongodb.com/manual/reference/mongodb-extended-json/
Expand Down
1 change: 0 additions & 1 deletion Tests/.swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ disabled_rules:
- force_cast
- force_try
- force_unwrapping
- nesting
- explicit_acl
- missing_docs
- cyclomatic_complexity
4 changes: 2 additions & 2 deletions Tests/SwiftBSONTests/BSONCorpusTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -166,12 +166,12 @@ final class BSONCorpusTests: BSONTestCase {

// native_to_canonical_extended_json( bson_to_native(cB) ) = cEJ
let canonicalEncoder = ExtendedJSONEncoder()
canonicalEncoder.mode = .canonical
canonicalEncoder.format = .canonical
expect(try canonicalEncoder.encode(docFromCB))
.to(cleanEqual(test.canonicalExtJSON), description: test.description)

// native_to_relaxed_extended_json( bson_to_native(cB) ) = rEJ (if rEJ exists)
let relaxedEncoder = ExtendedJSONEncoder() // default mode is .relaxed
let relaxedEncoder = ExtendedJSONEncoder() // default format is .relaxed
if let rEJ = test.relaxedExtJSON {
expect(try relaxedEncoder.encode(docFromCB))
.to(cleanEqual(rEJ), description: test.description)
Expand Down
4 changes: 2 additions & 2 deletions Tests/SwiftBSONTests/ExtendedJSONConversionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ open class ExtendedJSONConversionTestCase: BSONTestCase {

// Test canonical encoder
let encoder = ExtendedJSONEncoder()
encoder.mode = .canonical
encoder.format = .canonical
let encoded: Data = try encoder.encode(test)
expect(encoded).to(cleanEqual(canonicalExtJSON))
let encodedBuffer = try encoder.encodeBuffer(test)
expect(Data(encodedBuffer.readableBytesView)).to(cleanEqual(canonicalExtJSON))

// Test relaxed encoder
encoder.mode = .relaxed
encoder.format = .relaxed
let relaxedEncoded: Data = try encoder.encode(test)
let relaxedExtJSON = "{\"x\":true,\"y\":5,\"z\":\(regexStr)}"
expect(relaxedEncoded).to(cleanEqual(relaxedExtJSON))
Expand Down

0 comments on commit 8a9d60c

Please sign in to comment.