Skip to content

Commit baf6d0d

Browse files
authored
Get rid of hand-written Struct, Value, and ListValue. (apple#366)
1 parent 1a298bc commit baf6d0d

18 files changed

+957
-707
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ LIBRARY_PROTOS= \
140140
Protos/google/protobuf/empty.proto \
141141
Protos/google/protobuf/field_mask.proto \
142142
Protos/google/protobuf/source_context.proto \
143+
Protos/google/protobuf/struct.proto \
143144
Protos/google/protobuf/timestamp.proto \
144145
Protos/google/protobuf/type.proto \
145146
Protos/google/protobuf/wrappers.proto

Sources/SwiftProtobuf/CustomJSONCodable.swift

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,25 @@
1212
///
1313
// -----------------------------------------------------------------------------
1414

15-
import Foundation
16-
1715
/// Allows WKTs to provide their custom JSON encodings.
1816
internal protocol _CustomJSONCodable {
19-
func encodedJSONString() throws -> String
20-
mutating func decodeJSON(from: inout JSONDecoder) throws
17+
func encodedJSONString() throws -> String
18+
mutating func decodeJSON(from: inout JSONDecoder) throws
19+
20+
/// Called when the JSON `null` literal is encountered in a position where
21+
/// a message of the conforming type is expected. The message type can then
22+
/// handle the `null` value differently, if needed; for example,
23+
/// `Google_Protobuf_Value` returns a special instance whose `kind` is set to
24+
/// `.nullValue(.nullValue)`.
25+
///
26+
/// The default behavior is to return `nil`, which indicates that `null`
27+
/// should be treated as the absence of a message.
28+
static func decodedFromJSONNull() throws -> Self?
29+
}
30+
31+
extension _CustomJSONCodable {
32+
internal static func decodedFromJSONNull() -> Self? {
33+
// Return nil by default. Concrete types can provide custom logic.
34+
return nil
35+
}
2136
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Sources/SwiftProtobuf/Google_Protobuf_ListValue+Extensions.swift - ListValue extensions
2+
//
3+
// Copyright (c) 2014 - 2017 Apple Inc. and the project authors
4+
// Licensed under Apache License v2.0 with Runtime Library Exception
5+
//
6+
// See LICENSE.txt for license information:
7+
// https://github.com/apple/swift-protobuf/blob/master/LICENSE.txt
8+
//
9+
// -----------------------------------------------------------------------------
10+
///
11+
/// ListValue is a well-known message type that can be used to parse or encode
12+
/// arbitrary JSON arrays without a predefined schema.
13+
///
14+
// -----------------------------------------------------------------------------
15+
16+
extension Google_Protobuf_ListValue: ExpressibleByArrayLiteral {
17+
// TODO: Give this a direct array interface by proxying the interesting
18+
// bits down to values
19+
public typealias Element = Google_Protobuf_Value
20+
21+
/// Creates a new `Google_Protobuf_ListValue` from an array literal containing
22+
/// `Google_Protobuf_Value` elements.
23+
public init(arrayLiteral elements: Element...) {
24+
self.init(values: elements)
25+
}
26+
}
27+
28+
extension Google_Protobuf_ListValue: _CustomJSONCodable {
29+
internal func encodedJSONString() throws -> String {
30+
var jsonEncoder = JSONEncoder()
31+
jsonEncoder.append(text: "[")
32+
var separator: StaticString = ""
33+
for v in values {
34+
jsonEncoder.append(staticText: separator)
35+
try v.serializeJSONValue(to: &jsonEncoder)
36+
separator = ","
37+
}
38+
jsonEncoder.append(text: "]")
39+
return jsonEncoder.stringResult
40+
}
41+
42+
internal mutating func decodeJSON(from decoder: inout JSONDecoder) throws {
43+
if decoder.scanner.skipOptionalNull() {
44+
return
45+
}
46+
try decoder.scanner.skipRequiredArrayStart()
47+
if decoder.scanner.skipOptionalArrayEnd() {
48+
return
49+
}
50+
while true {
51+
var v = Google_Protobuf_Value()
52+
try v.decodeJSON(from: &decoder)
53+
values.append(v)
54+
if decoder.scanner.skipOptionalArrayEnd() {
55+
return
56+
}
57+
try decoder.scanner.skipRequiredComma()
58+
}
59+
}
60+
}
61+
62+
extension Google_Protobuf_ListValue {
63+
/// Creates a new `Google_Protobuf_ListValue` from the given array of
64+
/// `Google_Protobuf_Value` elements.
65+
public init(values: [Google_Protobuf_Value]) {
66+
self.init()
67+
self.values = values
68+
}
69+
70+
public subscript(index: Int) -> Google_Protobuf_Value {
71+
get {return values[index]}
72+
set(newValue) {values[index] = newValue}
73+
}
74+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Sources/SwiftProtobuf/Google_Protobuf_Struct+Extensions.swift - Struct extensions
2+
//
3+
// Copyright (c) 2014 - 2017 Apple Inc. and the project authors
4+
// Licensed under Apache License v2.0 with Runtime Library Exception
5+
//
6+
// See LICENSE.txt for license information:
7+
// https://github.com/apple/swift-protobuf/blob/master/LICENSE.txt
8+
//
9+
// -----------------------------------------------------------------------------
10+
///
11+
/// Struct is a well-known message type that can be used to parse or encode
12+
/// arbitrary JSON objects without a predefined schema.
13+
///
14+
// -----------------------------------------------------------------------------
15+
16+
extension Google_Protobuf_Struct: ExpressibleByDictionaryLiteral {
17+
public typealias Key = String
18+
public typealias Value = Google_Protobuf_Value
19+
20+
/// Creates a new `Google_Protobuf_Struct` from a dictionary of string keys to
21+
/// values of type `Google_Protobuf_Value`.
22+
public init(dictionaryLiteral: (String, Google_Protobuf_Value)...) {
23+
self.init()
24+
for (k,v) in dictionaryLiteral {
25+
fields[k] = v
26+
}
27+
}
28+
}
29+
30+
extension Google_Protobuf_Struct: _CustomJSONCodable {
31+
internal func encodedJSONString() throws -> String {
32+
var jsonEncoder = JSONEncoder()
33+
jsonEncoder.startObject()
34+
var mapVisitor = JSONMapEncodingVisitor(encoder: jsonEncoder)
35+
for (k,v) in fields {
36+
try mapVisitor.visitSingularStringField(value: k, fieldNumber: 1)
37+
try mapVisitor.visitSingularMessageField(value: v, fieldNumber: 2)
38+
}
39+
mapVisitor.encoder.endObject()
40+
return mapVisitor.encoder.stringResult
41+
}
42+
43+
internal mutating func decodeJSON(from decoder: inout JSONDecoder) throws {
44+
try decoder.scanner.skipRequiredObjectStart()
45+
if decoder.scanner.skipOptionalObjectEnd() {
46+
return
47+
}
48+
while true {
49+
let key = try decoder.scanner.nextQuotedString()
50+
try decoder.scanner.skipRequiredColon()
51+
var value = Google_Protobuf_Value()
52+
try value.decodeJSON(from: &decoder)
53+
fields[key] = value
54+
if decoder.scanner.skipOptionalObjectEnd() {
55+
return
56+
}
57+
try decoder.scanner.skipRequiredComma()
58+
}
59+
}
60+
}
61+
62+
extension Google_Protobuf_Struct {
63+
/// Creates a new `Google_Protobuf_Struct` from a dictionary of string keys to
64+
/// values of type `Google_Protobuf_Value`.
65+
public init(fields: [String: Google_Protobuf_Value]) {
66+
self.init()
67+
self.fields = fields
68+
}
69+
70+
public subscript(index: String) -> Google_Protobuf_Value? {
71+
get {return fields[index]}
72+
set(newValue) {fields[index] = newValue}
73+
}
74+
}

0 commit comments

Comments
 (0)