From ac8da77c937f3c98c3959f72e76184b6b577cf38 Mon Sep 17 00:00:00 2001 From: Thomas Van Lenten Date: Mon, 9 Sep 2024 12:38:17 -0400 Subject: [PATCH] Run `swift-format -p -r -i .` --- .../InternalImportsByDefault/Package.swift | 10 +- CompileTests/MultiModule/Package.swift | 2 +- .../MultiModule/Tests/Test1/test1.swift | 46 +- .../MultiModule/Tests/Test2/test2.swift | 46 +- FuzzTesting/Package.swift | 25 +- .../FuzzAsyncMessageSequence/main.swift | 67 +- FuzzTesting/Sources/FuzzBinary/main.swift | 34 +- .../Sources/FuzzBinaryDelimited/main.swift | 59 +- FuzzTesting/Sources/FuzzCommon/Options.swift | 7 +- FuzzTesting/Sources/FuzzJSON/main.swift | 35 +- FuzzTesting/Sources/FuzzTextFormat/main.swift | 35 +- .../Tests/FuzzCommonTests/Test_Options.swift | 44 +- Package.swift | 162 +- Performance/Harness.swift | 249 +- Performance/main.swift | 3 +- PluginExamples/Package.swift | 18 +- .../Sources/ExampleTests/ExampleTests.swift | 5 +- Plugins/SwiftProtobufPlugin/plugin.swift | 14 +- Sources/Conformance/main.swift | 68 +- Sources/SwiftProtobuf/AnyMessageStorage.swift | 832 ++--- Sources/SwiftProtobuf/AnyUnpackError.swift | 20 +- .../SwiftProtobuf/AsyncMessageSequence.swift | 344 +- Sources/SwiftProtobuf/BinaryDecoder.swift | 224 +- .../SwiftProtobuf/BinaryDecodingError.swift | 42 +- .../SwiftProtobuf/BinaryDecodingOptions.swift | 40 +- Sources/SwiftProtobuf/BinaryDelimited.swift | 424 +-- Sources/SwiftProtobuf/BinaryEncoder.swift | 8 +- .../SwiftProtobuf/BinaryEncodingError.swift | 14 +- .../BinaryEncodingSizeVisitor.swift | 910 +++--- .../SwiftProtobuf/BinaryEncodingVisitor.swift | 721 +++-- Sources/SwiftProtobuf/CustomJSONCodable.swift | 30 +- Sources/SwiftProtobuf/Decoder.swift | 343 +- Sources/SwiftProtobuf/DoubleParser.swift | 12 +- Sources/SwiftProtobuf/Enum.swift | 114 +- Sources/SwiftProtobuf/ExtensibleMessage.swift | 15 +- .../ExtensionFieldValueSet.swift | 111 +- Sources/SwiftProtobuf/ExtensionFields.swift | 1119 +++---- Sources/SwiftProtobuf/FieldTag.swift | 76 +- Sources/SwiftProtobuf/FieldTypes.swift | 32 +- .../Google_Protobuf_Any+Extensions.swift | 259 +- .../Google_Protobuf_Any+Registry.swift | 99 +- .../Google_Protobuf_Duration+Extensions.swift | 350 +- ...Google_Protobuf_FieldMask+Extensions.swift | 561 ++-- ...Google_Protobuf_ListValue+Extensions.swift | 114 +- ...Google_Protobuf_NullValue+Extensions.swift | 20 +- .../Google_Protobuf_Struct+Extensions.swift | 112 +- ...Google_Protobuf_Timestamp+Extensions.swift | 539 ++-- .../Google_Protobuf_Value+Extensions.swift | 258 +- .../Google_Protobuf_Wrappers+Extensions.swift | 358 ++- Sources/SwiftProtobuf/HashVisitor.swift | 384 +-- Sources/SwiftProtobuf/Internal.swift | 59 +- Sources/SwiftProtobuf/JSONDecoder.swift | 1453 ++++----- .../SwiftProtobuf/JSONDecodingOptions.swift | 24 +- Sources/SwiftProtobuf/JSONEncoder.swift | 71 +- .../SwiftProtobuf/JSONEncodingOptions.swift | 46 +- .../SwiftProtobuf/JSONEncodingVisitor.swift | 816 ++--- .../JSONMapEncodingVisitor.swift | 322 +- Sources/SwiftProtobuf/JSONScanner.swift | 2491 +++++++------- Sources/SwiftProtobuf/MathUtils.swift | 4 +- .../SwiftProtobuf/Message+AnyAdditions.swift | 58 +- .../Message+BinaryAdditions.swift | 256 +- .../Message+BinaryAdditions_Data.swift | 404 +-- Sources/SwiftProtobuf/Message+FieldMask.swift | 181 +- .../SwiftProtobuf/Message+JSONAdditions.swift | 244 +- .../Message+JSONAdditions_Data.swift | 85 +- .../Message+JSONArrayAdditions.swift | 238 +- .../Message+JSONArrayAdditions_Data.swift | 104 +- .../Message+TextFormatAdditions.swift | 174 +- Sources/SwiftProtobuf/Message.swift | 304 +- Sources/SwiftProtobuf/MessageExtension.swift | 2 +- Sources/SwiftProtobuf/NameMap.swift | 446 +-- Sources/SwiftProtobuf/PathDecoder.swift | 805 ++--- Sources/SwiftProtobuf/PathVisitor.swift | 452 +-- .../SwiftProtobuf/ProtoNameProviding.swift | 7 +- .../ProtobufAPIVersionCheck.swift | 3 +- Sources/SwiftProtobuf/ProtobufMap.swift | 15 +- Sources/SwiftProtobuf/SelectiveVisitor.swift | 425 +-- .../SwiftProtobuf/SimpleExtensionMap.swift | 9 +- Sources/SwiftProtobuf/StringUtils.swift | 80 +- .../SwiftProtobufContiguousBytes.swift | 2 +- .../SwiftProtobuf/SwiftProtobufError.swift | 118 +- Sources/SwiftProtobuf/TextFormatDecoder.swift | 58 +- .../TextFormatDecodingOptions.swift | 38 +- Sources/SwiftProtobuf/TextFormatEncoder.swift | 65 +- .../TextFormatEncodingOptions.swift | 6 +- .../TextFormatEncodingVisitor.swift | 1309 ++++---- Sources/SwiftProtobuf/TextFormatScanner.swift | 870 ++--- Sources/SwiftProtobuf/TimeUtils.swift | 24 +- Sources/SwiftProtobuf/UnknownStorage.swift | 26 +- .../UnsafeRawPointer+Shims.swift | 5 +- Sources/SwiftProtobuf/Varint.swift | 159 +- Sources/SwiftProtobuf/Version.swift | 16 +- Sources/SwiftProtobuf/Visitor.swift | 1381 ++++---- Sources/SwiftProtobuf/WireFormat.swift | 92 +- Sources/SwiftProtobuf/ZigZag.swift | 9 +- .../CodeGenerator.swift | 523 +-- .../CodeGeneratorParameter.swift | 32 +- .../CodePrinter.swift | 402 +-- .../Descriptor+Extensions.swift | 270 +- .../Descriptor.swift | 2859 +++++++++-------- .../FeatureResolver.swift | 297 +- .../FieldNumbers.swift | 40 +- .../GeneratorOutputs.swift | 24 +- ...ler_CodeGeneratorResponse+Extensions.swift | 52 +- .../Google_Protobuf_Edition+Extensions.swift | 7 +- ...e_Protobuf_SourceCodeInfo+Extensions.swift | 100 +- .../NamingUtils.swift | 1023 +++--- .../ProtoCompilerContext.swift | 6 +- .../ProtoFileToModuleMappings.swift | 294 +- .../ProvidesDeprecationComment.swift | 82 +- .../ProvidesLocationPath.swift | 12 +- .../ProvidesSourceCodeLocation.swift | 32 +- .../StandardErrorOutputStream.swift | 12 +- .../StringUtils.swift | 4 +- .../SwiftLanguage.swift | 27 +- .../SwiftProtobufInfo.swift | 52 +- .../SwiftProtobufNamer.swift | 627 ++-- .../UnicodeScalar+Extensions.swift | 66 +- .../Descriptor+TestHelpers.swift | 56 +- .../CommandLine+Extensions.swift | 26 +- .../Descriptor+Extensions.swift | 943 +++--- Sources/protoc-gen-swift/EnumGenerator.swift | 406 +-- .../ExtensionSetGenerator.swift | 170 +- Sources/protoc-gen-swift/FieldGenerator.swift | 160 +- Sources/protoc-gen-swift/FileGenerator.swift | 106 +- Sources/protoc-gen-swift/FileIo.swift | 10 +- .../protoc-gen-swift/GenerationError.swift | 38 +- .../protoc-gen-swift/GeneratorOptions.swift | 369 ++- ...tobuf_FileDescriptorProto+Extensions.swift | 12 +- .../MessageFieldGenerator.swift | 76 +- .../protoc-gen-swift/MessageGenerator.swift | 995 +++--- .../MessageStorageClassGenerator.swift | 191 +- .../MessageStorageDecision.swift | 282 +- Sources/protoc-gen-swift/OneofGenerator.swift | 263 +- ...rovidesDeprecationComment+Extensions.swift | 27 +- ...rovidesSourceCodeLocation+Extensions.swift | 29 +- .../protoc-gen-swift/Range+Extensions.swift | 76 +- Sources/protoc-gen-swift/StringUtils.swift | 186 +- .../SwiftGeneratorPlugin.swift | 128 +- .../SwiftProtobufNamer+Extensions.swift | 52 +- .../Test_Descriptor.swift | 844 ++--- .../Test_Descriptor_FeatureResolution.swift | 1374 ++++---- .../Test_FeatureResolver.swift | 465 +-- .../Test_NamingUtils.swift | 688 ++-- .../Test_ProtoFileToModuleMappings.swift | 565 ++-- .../Test_SwiftLanguage.swift | 48 +- .../Test_SwiftProtobufNamer.swift | 476 +-- .../SwiftProtobufTests/Data+TestHelpers.swift | 2 +- Tests/SwiftProtobufTests/TestHelpers.swift | 356 +- Tests/SwiftProtobufTests/Test_AllTypes.swift | 974 +++--- .../Test_AllTypes_Proto3.swift | 622 ++-- .../Test_AllTypes_Proto3_Optional.swift | 374 ++- Tests/SwiftProtobufTests/Test_Any.swift | 563 ++-- Tests/SwiftProtobufTests/Test_Api.swift | 10 +- .../Test_AsyncMessageSequence.swift | 420 +-- .../Test_BasicFields_Access_Proto2.swift | 1690 +++++----- .../Test_BasicFields_Access_Proto3.swift | 740 ++--- .../Test_BinaryDecodingOptions.swift | 184 +- .../Test_BinaryDelimited.swift | 273 +- .../Test_BinaryEncodingOptions.swift | 100 +- .../SwiftProtobufTests/Test_Conformance.swift | 42 +- Tests/SwiftProtobufTests/Test_Duration.swift | 170 +- Tests/SwiftProtobufTests/Test_Empty.swift | 2 +- Tests/SwiftProtobufTests/Test_Enum.swift | 62 +- .../Test_EnumWithAliases.swift | 58 +- .../SwiftProtobufTests/Test_Enum_Proto2.swift | 60 +- .../SwiftProtobufTests/Test_Extensions.swift | 71 +- .../Test_ExtremeDefaultValues.swift | 14 +- Tests/SwiftProtobufTests/Test_FieldMask.swift | 65 +- .../Test_FieldOrdering.swift | 16 +- Tests/SwiftProtobufTests/Test_FuzzTests.swift | 320 +- .../Test_GroupWithGroups.swift | 76 +- Tests/SwiftProtobufTests/Test_JSON.swift | 828 ++--- .../Test_JSONDecodingOptions.swift | 32 +- .../Test_JSONEncodingOptions.swift | 646 ++-- .../SwiftProtobufTests/Test_JSON_Array.swift | 97 +- .../Test_JSON_Conformance.swift | 83 +- .../Test_JSON_Extensions.swift | 115 +- .../SwiftProtobufTests/Test_JSON_Group.swift | 20 +- .../Test_JSON_Performance.swift | 90 +- Tests/SwiftProtobufTests/Test_Map.swift | 80 +- .../Test_MapFields_Access_Proto2.swift | 426 +-- .../Test_MapFields_Access_Proto3.swift | 426 +-- Tests/SwiftProtobufTests/Test_Map_JSON.swift | 151 +- Tests/SwiftProtobufTests/Test_Merge.swift | 76 +- .../SwiftProtobufTests/Test_MessageSet.swift | 476 +-- Tests/SwiftProtobufTests/Test_Naming.swift | 1392 ++++---- .../Test_OneofFields_Access_Proto2.swift | 1050 +++--- .../Test_OneofFields_Access_Proto3.swift | 918 +++--- Tests/SwiftProtobufTests/Test_Packed.swift | 307 +- .../Test_ParsingMerge.swift | 9 +- Tests/SwiftProtobufTests/Test_Required.swift | 68 +- Tests/SwiftProtobufTests/Test_Reserved.swift | 21 +- .../Test_SimpleExtensionMap.swift | 316 +- Tests/SwiftProtobufTests/Test_Struct.swift | 115 +- .../Test_TextFormatDecodingOptions.swift | 242 +- .../Test_TextFormat_Map_proto3.swift | 225 +- .../Test_TextFormat_Performance.swift | 64 +- .../Test_TextFormat_Unknown.swift | 44 +- .../Test_TextFormat_WKT_proto3.swift | 58 +- .../Test_TextFormat_proto2.swift | 27 +- .../Test_TextFormat_proto2_extensions.swift | 20 +- .../Test_TextFormat_proto3.swift | 1042 +++--- Tests/SwiftProtobufTests/Test_Timestamp.swift | 168 +- Tests/SwiftProtobufTests/Test_Type.swift | 8 +- .../Test_Unknown_proto2.swift | 59 +- .../Test_Unknown_proto3.swift | 53 +- Tests/SwiftProtobufTests/Test_Wrappers.swift | 113 +- .../Test_DescriptorExtensions.swift | 959 +++--- .../Test_SwiftProtobufNamerExtensions.swift | 573 ++-- 210 files changed, 30053 insertions(+), 27682 deletions(-) diff --git a/CompileTests/InternalImportsByDefault/Package.swift b/CompileTests/InternalImportsByDefault/Package.swift index f34374727..f7d5e8759 100644 --- a/CompileTests/InternalImportsByDefault/Package.swift +++ b/CompileTests/InternalImportsByDefault/Package.swift @@ -20,23 +20,23 @@ let package = Package( .executableTarget( name: "InternalImportsByDefault", dependencies: [ - .product(name: "SwiftProtobuf", package: "swift-protobuf"), + .product(name: "SwiftProtobuf", package: "swift-protobuf") ], exclude: [ "Protos/SomeProtoWithBytes.proto", - "Protos/ServiceOnly.proto" + "Protos/ServiceOnly.proto", ], swiftSettings: [ .enableExperimentalFeature("InternalImportsByDefault"), .enableExperimentalFeature("AccessLevelOnImport"), // Enable warnings as errors so the build fails if warnings are // present in generated code. - .unsafeFlags(["-warnings-as-errors"]) + .unsafeFlags(["-warnings-as-errors"]), ], plugins: [ .plugin(name: "SwiftProtobufPlugin", package: "swift-protobuf") ] - ), + ) ] ) #else @@ -48,7 +48,7 @@ let package = Package( exclude: [ "swift-protobuf-config.json", "Protos/SomeProtoWithBytes.proto", - "Protos/ServiceOnly.proto" + "Protos/ServiceOnly.proto", ] ) ] diff --git a/CompileTests/MultiModule/Package.swift b/CompileTests/MultiModule/Package.swift index 4be7b44d2..497376b36 100644 --- a/CompileTests/MultiModule/Package.swift +++ b/CompileTests/MultiModule/Package.swift @@ -19,7 +19,7 @@ let package = Package( .target( name: "ModuleA", dependencies: [ - .product(name: "SwiftProtobuf", package: "swift-protobuf"), + .product(name: "SwiftProtobuf", package: "swift-protobuf") ] ), .target( diff --git a/CompileTests/MultiModule/Tests/Test1/test1.swift b/CompileTests/MultiModule/Tests/Test1/test1.swift index 8ccbb81b6..f9cefc125 100644 --- a/CompileTests/MultiModule/Tests/Test1/test1.swift +++ b/CompileTests/MultiModule/Tests/Test1/test1.swift @@ -1,31 +1,31 @@ import ImportsAPublicly -// Don't need to import ModuleA because of the file being a `import public` - import XCTest -final class ExampleTests: XCTestCase { - func testA() { - let anA = A.with { $0.e = .a } - XCTAssertEqual(anA.e, .a) - } +// Don't need to import ModuleA because of the file being a `import public` - func testImportsAPublicly() { - let imports = ImportsAPublicly.with { $0.a.e = .a } - XCTAssertEqual(imports.a.e, .a) - } +final class ExampleTests: XCTestCase { + func testA() { + let anA = A.with { $0.e = .a } + XCTAssertEqual(anA.e, .a) + } - func testInterop() { - let anA = A.with { $0.e = .b } - let imports = ImportsAPublicly.with { - $0.a = anA - $0.e = .b + func testImportsAPublicly() { + let imports = ImportsAPublicly.with { $0.a.e = .a } + XCTAssertEqual(imports.a.e, .a) } - XCTAssertEqual(imports.a.e, imports.e) - let transitively = UsesATransitively.with { - $0.a = anA - $0.e = imports.e + + func testInterop() { + let anA = A.with { $0.e = .b } + let imports = ImportsAPublicly.with { + $0.a = anA + $0.e = .b + } + XCTAssertEqual(imports.a.e, imports.e) + let transitively = UsesATransitively.with { + $0.a = anA + $0.e = imports.e + } + XCTAssertEqual(transitively.a, anA) + XCTAssertEqual(transitively.e, imports.e) } - XCTAssertEqual(transitively.a, anA) - XCTAssertEqual(transitively.e, imports.e) - } } diff --git a/CompileTests/MultiModule/Tests/Test2/test2.swift b/CompileTests/MultiModule/Tests/Test2/test2.swift index d12060a12..3e47bbeae 100644 --- a/CompileTests/MultiModule/Tests/Test2/test2.swift +++ b/CompileTests/MultiModule/Tests/Test2/test2.swift @@ -1,31 +1,31 @@ import ImportsImportsAPublicly -// Don't need to import ModuleA because of the file being a `import public` - import XCTest -final class ExampleTests: XCTestCase { - func testA() { - let anA = A.with { $0.e = .a } - XCTAssertEqual(anA.e, .a) - } +// Don't need to import ModuleA because of the file being a `import public` - func testImportsImportsAPublicly() { - let imports = ImportsImportsAPublicly.with { $0.a.e = .a } - XCTAssertEqual(imports.a.e, .a) - } +final class ExampleTests: XCTestCase { + func testA() { + let anA = A.with { $0.e = .a } + XCTAssertEqual(anA.e, .a) + } - func testInterop() { - let anA = A.with { $0.e = .b } - let imports = ImportsImportsAPublicly.with { - $0.a = anA - $0.e = .b + func testImportsImportsAPublicly() { + let imports = ImportsImportsAPublicly.with { $0.a.e = .a } + XCTAssertEqual(imports.a.e, .a) } - XCTAssertEqual(imports.a.e, imports.e) - let transitively = UsesATransitively2.with { - $0.a = anA - $0.e = imports.e + + func testInterop() { + let anA = A.with { $0.e = .b } + let imports = ImportsImportsAPublicly.with { + $0.a = anA + $0.e = .b + } + XCTAssertEqual(imports.a.e, imports.e) + let transitively = UsesATransitively2.with { + $0.a = anA + $0.e = imports.e + } + XCTAssertEqual(transitively.a, anA) + XCTAssertEqual(transitively.e, imports.e) } - XCTAssertEqual(transitively.a, anA) - XCTAssertEqual(transitively.e, imports.e) - } } diff --git a/FuzzTesting/Package.swift b/FuzzTesting/Package.swift index b2ea9c13c..075310bf9 100644 --- a/FuzzTesting/Package.swift +++ b/FuzzTesting/Package.swift @@ -6,32 +6,39 @@ import PackageDescription let package = Package( name: "FuzzTesting", platforms: [ - .macOS(.v10_15), + .macOS(.v10_15) ], dependencies: [ - .package(name: "SwiftProtobuf", path: ".."), + .package(name: "SwiftProtobuf", path: "..") ], targets: [ .target( name: "FuzzCommon", - dependencies: ["SwiftProtobuf"]), + dependencies: ["SwiftProtobuf"] + ), .target( name: "FuzzBinary", - dependencies: ["SwiftProtobuf", "FuzzCommon"]), + dependencies: ["SwiftProtobuf", "FuzzCommon"] + ), .target( name: "FuzzBinaryDelimited", - dependencies: ["SwiftProtobuf", "FuzzCommon"]), + dependencies: ["SwiftProtobuf", "FuzzCommon"] + ), .target( name: "FuzzAsyncMessageSequence", - dependencies: ["SwiftProtobuf", "FuzzCommon"]), + dependencies: ["SwiftProtobuf", "FuzzCommon"] + ), .target( name: "FuzzJSON", - dependencies: ["SwiftProtobuf", "FuzzCommon"]), + dependencies: ["SwiftProtobuf", "FuzzCommon"] + ), .target( name: "FuzzTextFormat", - dependencies: ["SwiftProtobuf", "FuzzCommon"]), + dependencies: ["SwiftProtobuf", "FuzzCommon"] + ), .testTarget( name: "FuzzCommonTests", - dependencies: ["FuzzCommon"]), + dependencies: ["FuzzCommon"] + ), ] ) diff --git a/FuzzTesting/Sources/FuzzAsyncMessageSequence/main.swift b/FuzzTesting/Sources/FuzzAsyncMessageSequence/main.swift index 8fa2bda89..27ccb1a4f 100644 --- a/FuzzTesting/Sources/FuzzAsyncMessageSequence/main.swift +++ b/FuzzTesting/Sources/FuzzAsyncMessageSequence/main.swift @@ -7,49 +7,48 @@ // ----------------------------------------------------------------------------- import Foundation - import FuzzCommon - import SwiftProtobuf -fileprivate func asyncByteStream(bytes: UnsafeRawBufferPointer) -> AsyncStream { - AsyncStream(UInt8.self) { continuation in - for i in 0.. AsyncStream { + AsyncStream(UInt8.self) { continuation in + for i in 0.. CInt { - // No decoding options here, a leading zero is actually valid (zero length message), - // so we rely on the other Binary fuzz tester to test options, and just let this - // one focus on issue around framing of the messages on the stream. - let bytes = UnsafeRawBufferPointer(start: start, count: count) - let asyncBytes = asyncByteStream(bytes: bytes) - let decoding = asyncBytes.binaryProtobufDelimitedMessages( - of: SwiftProtoTesting_Fuzz_Message.self, - extensions: SwiftProtoTesting_Fuzz_FuzzTesting_Extensions) + // No decoding options here, a leading zero is actually valid (zero length message), + // so we rely on the other Binary fuzz tester to test options, and just let this + // one focus on issue around framing of the messages on the stream. + let bytes = UnsafeRawBufferPointer(start: start, count: count) + let asyncBytes = asyncByteStream(bytes: bytes) + let decoding = asyncBytes.binaryProtobufDelimitedMessages( + of: SwiftProtoTesting_Fuzz_Message.self, + extensions: SwiftProtoTesting_Fuzz_FuzzTesting_Extensions + ) - let semaphore = DispatchSemaphore(value: 0) - Task { - do { - for try await _ in decoding { - // TODO: Test serialization for completeness. - // We could serialize individual messages like this: - // let _: [UInt8] = try! msg.serializedBytes() - // but we don't have a stream writer which is what - // we really want to exercise here. + let semaphore = DispatchSemaphore(value: 0) + Task { + do { + for try await _ in decoding { + // TODO: Test serialization for completeness. + // We could serialize individual messages like this: + // let _: [UInt8] = try! msg.serializedBytes() + // but we don't have a stream writer which is what + // we really want to exercise here. - // Also, serialization here more than doubles the total - // run time, leading to timeouts for the fuzz tester. :( - } - } catch { - // Error parsing are to be expected since not all input will be well formed. + // Also, serialization here more than doubles the total + // run time, leading to timeouts for the fuzz tester. :( + } + } catch { + // Error parsing are to be expected since not all input will be well formed. + } + semaphore.signal() } - semaphore.signal() - } - semaphore.wait() - return 0 + semaphore.wait() + return 0 } diff --git a/FuzzTesting/Sources/FuzzBinary/main.swift b/FuzzTesting/Sources/FuzzBinary/main.swift index fd49428c0..a442a4ba5 100644 --- a/FuzzTesting/Sources/FuzzBinary/main.swift +++ b/FuzzTesting/Sources/FuzzBinary/main.swift @@ -7,26 +7,26 @@ // ----------------------------------------------------------------------------- import FuzzCommon - import SwiftProtobuf @_cdecl("LLVMFuzzerTestOneInput") public func FuzzBinary(_ start: UnsafeRawPointer, _ count: Int) -> CInt { - guard let (options, bytes) = BinaryDecodingOptions.extractOptions(start, count) else { - return 1 - } - var msg: SwiftProtoTesting_Fuzz_Message? - do { - msg = try SwiftProtoTesting_Fuzz_Message( - serializedBytes: Array(bytes), - extensions: SwiftProtoTesting_Fuzz_FuzzTesting_Extensions, - options: options) - } catch { - // Error parsing are to be expected since not all input will be well formed. - } - // Test serialization for completeness. - // If a message was parsed, it should not fail to serialize, so assert as such. - let _: [UInt8]? = try! msg?.serializedBytes() + guard let (options, bytes) = BinaryDecodingOptions.extractOptions(start, count) else { + return 1 + } + var msg: SwiftProtoTesting_Fuzz_Message? + do { + msg = try SwiftProtoTesting_Fuzz_Message( + serializedBytes: Array(bytes), + extensions: SwiftProtoTesting_Fuzz_FuzzTesting_Extensions, + options: options + ) + } catch { + // Error parsing are to be expected since not all input will be well formed. + } + // Test serialization for completeness. + // If a message was parsed, it should not fail to serialize, so assert as such. + let _: [UInt8]? = try! msg?.serializedBytes() - return 0 + return 0 } diff --git a/FuzzTesting/Sources/FuzzBinaryDelimited/main.swift b/FuzzTesting/Sources/FuzzBinaryDelimited/main.swift index 4f7f580cd..3fdee5a6a 100644 --- a/FuzzTesting/Sources/FuzzBinaryDelimited/main.swift +++ b/FuzzTesting/Sources/FuzzBinaryDelimited/main.swift @@ -7,41 +7,40 @@ // ----------------------------------------------------------------------------- import Foundation - import FuzzCommon - import SwiftProtobuf @_cdecl("LLVMFuzzerTestOneInput") public func FuzzDelimited(_ start: UnsafeRawPointer, _ count: Int) -> CInt { - // No decoding options here, a leading zero is actually valid (zero length message), - // so we rely on the other Binary fuzz tester to test options, and just let this - // one focus on issue around framing of the messages on the stream. - let bytes = UnsafeRawBufferPointer(start: start, count: count) - let istream = InputStream(data: Data(bytes)) - istream.open() - while true { - let msg: SwiftProtoTesting_Fuzz_Message? - do { - msg = try BinaryDelimited.parse( - messageType: SwiftProtoTesting_Fuzz_Message.self, - from: istream, - extensions: SwiftProtoTesting_Fuzz_FuzzTesting_Extensions) - } catch { - // Error parsing are to be expected since not all input will be well formed. - break - } - // Test serialization for completeness. - // If a message was parsed, it should not fail to serialize, so assert as such. - if let msg = msg { - // Could use one stream for all messages, but since fuzz tests have - // memory limits, attempt to avoid hitting that limit with a new stream - // for each output attempt. - let ostream = OutputStream.toMemory() - ostream.open() - try! BinaryDelimited.serialize(message: msg, to: ostream) + // No decoding options here, a leading zero is actually valid (zero length message), + // so we rely on the other Binary fuzz tester to test options, and just let this + // one focus on issue around framing of the messages on the stream. + let bytes = UnsafeRawBufferPointer(start: start, count: count) + let istream = InputStream(data: Data(bytes)) + istream.open() + while true { + let msg: SwiftProtoTesting_Fuzz_Message? + do { + msg = try BinaryDelimited.parse( + messageType: SwiftProtoTesting_Fuzz_Message.self, + from: istream, + extensions: SwiftProtoTesting_Fuzz_FuzzTesting_Extensions + ) + } catch { + // Error parsing are to be expected since not all input will be well formed. + break + } + // Test serialization for completeness. + // If a message was parsed, it should not fail to serialize, so assert as such. + if let msg = msg { + // Could use one stream for all messages, but since fuzz tests have + // memory limits, attempt to avoid hitting that limit with a new stream + // for each output attempt. + let ostream = OutputStream.toMemory() + ostream.open() + try! BinaryDelimited.serialize(message: msg, to: ostream) + } } - } - return 0 + return 0 } diff --git a/FuzzTesting/Sources/FuzzCommon/Options.swift b/FuzzTesting/Sources/FuzzCommon/Options.swift index 36367f41b..c886597a0 100644 --- a/FuzzTesting/Sources/FuzzCommon/Options.swift +++ b/FuzzTesting/Sources/FuzzCommon/Options.swift @@ -7,7 +7,6 @@ // ----------------------------------------------------------------------------- import Foundation - import SwiftProtobuf public enum FuzzOption { @@ -105,7 +104,7 @@ extension SupportsFuzzOptions { extension BinaryDecodingOptions: SupportsFuzzOptions { public static var fuzzOptionsList: [FuzzOption] { - return [ + [ // NOTE: Do not reorder these in the future as it invalidates all // existing cases. @@ -119,7 +118,7 @@ extension BinaryDecodingOptions: SupportsFuzzOptions { extension JSONDecodingOptions: SupportsFuzzOptions { public static var fuzzOptionsList: [FuzzOption] { - return [ + [ // NOTE: Do not reorder these in the future as it invalidates all // existing cases. @@ -133,7 +132,7 @@ extension JSONDecodingOptions: SupportsFuzzOptions { extension TextFormatDecodingOptions: SupportsFuzzOptions { public static var fuzzOptionsList: [FuzzOption] { - return [ + [ // NOTE: Do not reorder these in the future as it invalidates all // existing cases. diff --git a/FuzzTesting/Sources/FuzzJSON/main.swift b/FuzzTesting/Sources/FuzzJSON/main.swift index dfa2501f3..898e09f4b 100644 --- a/FuzzTesting/Sources/FuzzJSON/main.swift +++ b/FuzzTesting/Sources/FuzzJSON/main.swift @@ -7,27 +7,26 @@ // ----------------------------------------------------------------------------- import Foundation - import FuzzCommon - import SwiftProtobuf @_cdecl("LLVMFuzzerTestOneInput") public func FuzzJSON(_ start: UnsafeRawPointer, _ count: Int) -> CInt { - guard let (options, bytes) = JSONDecodingOptions.extractOptions(start, count) else { - return 1 - } - var msg: SwiftProtoTesting_Fuzz_Message? - do { - msg = try SwiftProtoTesting_Fuzz_Message( - jsonUTF8Data: Data(bytes), - extensions: SwiftProtoTesting_Fuzz_FuzzTesting_Extensions, - options: options) - } catch { - // Error parsing are to be expected since not all input will be well formed. - } - // Test serialization for completeness. - // If a message was parsed, it should not fail to serialize, so assert as such. - let _ = try! msg?.jsonString() - return 0 + guard let (options, bytes) = JSONDecodingOptions.extractOptions(start, count) else { + return 1 + } + var msg: SwiftProtoTesting_Fuzz_Message? + do { + msg = try SwiftProtoTesting_Fuzz_Message( + jsonUTF8Data: Data(bytes), + extensions: SwiftProtoTesting_Fuzz_FuzzTesting_Extensions, + options: options + ) + } catch { + // Error parsing are to be expected since not all input will be well formed. + } + // Test serialization for completeness. + // If a message was parsed, it should not fail to serialize, so assert as such. + let _ = try! msg?.jsonString() + return 0 } diff --git a/FuzzTesting/Sources/FuzzTextFormat/main.swift b/FuzzTesting/Sources/FuzzTextFormat/main.swift index efb361939..8f7531b2b 100644 --- a/FuzzTesting/Sources/FuzzTextFormat/main.swift +++ b/FuzzTesting/Sources/FuzzTextFormat/main.swift @@ -7,28 +7,27 @@ // ----------------------------------------------------------------------------- import Foundation - import FuzzCommon - import SwiftProtobuf @_cdecl("LLVMFuzzerTestOneInput") public func FuzzTextFormat(_ start: UnsafeRawPointer, _ count: Int) -> CInt { - guard let (options, bytes) = TextFormatDecodingOptions.extractOptions(start, count) else { - return 1 - } - guard let str = String(data: Data(bytes), encoding: .utf8) else { return 0 } - var msg: SwiftProtoTesting_Fuzz_Message? - do { - msg = try SwiftProtoTesting_Fuzz_Message( - textFormatString: str, - options: options, - extensions: SwiftProtoTesting_Fuzz_FuzzTesting_Extensions) - } catch { - // Error parsing are to be expected since not all input will be well formed. - } - // Test serialization for completeness. - let _ = msg?.textFormatString() + guard let (options, bytes) = TextFormatDecodingOptions.extractOptions(start, count) else { + return 1 + } + guard let str = String(data: Data(bytes), encoding: .utf8) else { return 0 } + var msg: SwiftProtoTesting_Fuzz_Message? + do { + msg = try SwiftProtoTesting_Fuzz_Message( + textFormatString: str, + options: options, + extensions: SwiftProtoTesting_Fuzz_FuzzTesting_Extensions + ) + } catch { + // Error parsing are to be expected since not all input will be well formed. + } + // Test serialization for completeness. + let _ = msg?.textFormatString() - return 0 + return 0 } diff --git a/FuzzTesting/Tests/FuzzCommonTests/Test_Options.swift b/FuzzTesting/Tests/FuzzCommonTests/Test_Options.swift index 0864264c1..02b84f00f 100644 --- a/FuzzTesting/Tests/FuzzCommonTests/Test_Options.swift +++ b/FuzzTesting/Tests/FuzzCommonTests/Test_Options.swift @@ -7,11 +7,10 @@ // ----------------------------------------------------------------------------- import Foundation -import XCTest - import FuzzCommon +import XCTest -struct TestOptions : SupportsFuzzOptions { +struct TestOptions: SupportsFuzzOptions { var bool1: Bool = false { didSet { sets.append("bool1:\(bool1)") } @@ -40,7 +39,7 @@ struct TestOptions : SupportsFuzzOptions { init() {} } -struct TestOptionsLarge : SupportsFuzzOptions { +struct TestOptionsLarge: SupportsFuzzOptions { var bool1: Bool = false { didSet { sets.append("bool1:\(bool1)") } @@ -95,7 +94,7 @@ final class Test_FuzzOptions: XCTestCase { func testOptionBasics_noOptionsSignal() throws { // Claim no bytes passed. - let bytes: [UInt8] = [ ] + let bytes: [UInt8] = [] XCTAssertEqual(bytes.count, 0) try bytes.withUnsafeBytes { ptr in let result = TestOptions.extractOptions(ptr.baseAddress!, bytes.count) @@ -106,7 +105,7 @@ final class Test_FuzzOptions: XCTestCase { // Try with no leading zero, so no options. for x: UInt8 in 1...UInt8.max { - let bytes: [UInt8] = [ x ] + let bytes: [UInt8] = [x] XCTAssertEqual(bytes.count, 1) try bytes.withUnsafeBytes { ptr in let result = TestOptions.extractOptions(ptr.baseAddress!, bytes.count) @@ -120,7 +119,7 @@ final class Test_FuzzOptions: XCTestCase { } func testOptionBasics_optionsSignalNoBytes() throws { - let bytes: [UInt8] = [ 0 ] // Options signal, then nothing + let bytes: [UInt8] = [0] // Options signal, then nothing XCTAssertEqual(bytes.count, 1) try bytes.withUnsafeBytes { ptr in let result = TestOptions.extractOptions(ptr.baseAddress!, bytes.count) @@ -140,7 +139,7 @@ final class Test_FuzzOptions: XCTestCase { (0x3, true, true, ["bool1:true", "bool2:true"]), ] for test in testCases { - let bytes: [UInt8] = [ 0, test.byte] + let bytes: [UInt8] = [0, test.byte] XCTAssertEqual(bytes.count, 2) try bytes.withUnsafeBytes { ptr in let result = TestOptions.extractOptions(ptr.baseAddress!, bytes.count) @@ -162,7 +161,7 @@ final class Test_FuzzOptions: XCTestCase { ([0xC, 3, 20], 3, 4, ["int1:3", "int2:4"]), // int2 has a mod applied ] for test in testCases { - let bytes: [UInt8] = [ 0 ] + test.bytes + let bytes: [UInt8] = [0] + test.bytes try bytes.withUnsafeBytes { ptr in let result = TestOptions.extractOptions(ptr.baseAddress!, bytes.count) let (opts, bytes) = try XCTUnwrap(result) @@ -183,7 +182,7 @@ final class Test_FuzzOptions: XCTestCase { [0xC, 20], // int1 & int2, data for only int1 ] for test in testCases { - let bytes: [UInt8] = [ 0 ] + test + let bytes: [UInt8] = [0] + test bytes.withUnsafeBytes { ptr in XCTAssertNil(TestOptions.extractOptions(ptr.baseAddress!, bytes.count)) } @@ -194,7 +193,7 @@ final class Test_FuzzOptions: XCTestCase { // Try every value that will have at least one bit set above the valid ones // to ensure it causing parsing failure. for x: UInt8 in 0x10...UInt8.max { - let bytes: [UInt8] = [ 0, x ] + let bytes: [UInt8] = [0, x] bytes.withUnsafeBytes { ptr in XCTAssertNil(TestOptions.extractOptions(ptr.baseAddress!, bytes.count)) } @@ -205,7 +204,7 @@ final class Test_FuzzOptions: XCTestCase { // For the first byte of optionBits, just signal that there is a second, but // then set all the expected zero bits to ensure it fails. for x: UInt8 in 0x8...UInt8.max { - let bytes: [UInt8] = [ 0, 0x80, x ] + let bytes: [UInt8] = [0, 0x80, x] bytes.withUnsafeBytes { ptr in XCTAssertNil(TestOptions.extractOptions(ptr.baseAddress!, bytes.count)) } @@ -213,7 +212,7 @@ final class Test_FuzzOptions: XCTestCase { } func testOptionBasics_bytesAfterOptsComeThrough() throws { - let bytes: [UInt8] = [ 0, 0, 1, 2, 3] + let bytes: [UInt8] = [0, 0, 1, 2, 3] XCTAssertEqual(bytes.count, 5) try bytes.withUnsafeBytes { ptr in let result = TestOptions.extractOptions(ptr.baseAddress!, bytes.count) @@ -227,7 +226,7 @@ final class Test_FuzzOptions: XCTestCase { } // Make sure data is right after a bytes value also - let bytes2: [UInt8] = [ 0, 0x4, 20, 4, 15, 26] + let bytes2: [UInt8] = [0, 0x4, 20, 4, 15, 26] try bytes2.withUnsafeBytes { ptr in let result = TestOptions.extractOptions(ptr.baseAddress!, bytes2.count) let (opts, bytes) = try XCTUnwrap(result) @@ -242,12 +241,15 @@ final class Test_FuzzOptions: XCTestCase { // Options that can spill to two bytes for the optionBits. // Only one byte of optionsBits - let bytes3: [UInt8] = [ 0, 0, 1, 2, 3] + let bytes3: [UInt8] = [0, 0, 1, 2, 3] XCTAssertEqual(bytes3.count, 5) try bytes3.withUnsafeBytes { ptr in let result = TestOptionsLarge.extractOptions(ptr.baseAddress!, bytes3.count) let (opts, bytes) = try XCTUnwrap(result) - XCTAssertEqual(opts.sets, ["bool1:false", "bool2:false", "bool3:false", "bool4:false", "bool5:false", "bool6:false"]) + XCTAssertEqual( + opts.sets, + ["bool1:false", "bool2:false", "bool3:false", "bool4:false", "bool5:false", "bool6:false"] + ) XCTAssertEqual(bytes.count, 3) XCTAssertNotEqual(bytes.baseAddress, ptr.baseAddress) XCTAssertEqual(bytes.loadUnaligned(fromByteOffset: 0, as: UInt8.self), 1) @@ -256,12 +258,18 @@ final class Test_FuzzOptions: XCTestCase { } // Two bytes of optionsBits with a `byte` value - let bytes4: [UInt8] = [ 0, 0x90, 123, 0x4, 20, 81, 92, 103] + let bytes4: [UInt8] = [0, 0x90, 123, 0x4, 20, 81, 92, 103] XCTAssertEqual(bytes4.count, 8) try bytes4.withUnsafeBytes { ptr in let result = TestOptionsLarge.extractOptions(ptr.baseAddress!, bytes4.count) let (opts, bytes) = try XCTUnwrap(result) - XCTAssertEqual(opts.sets, ["bool1:false", "bool2:false", "bool3:false", "bool4:false", "int1:123", "bool5:false", "bool6:false", "bool7:false", "bool8:false", "int2:20"]) + XCTAssertEqual( + opts.sets, + [ + "bool1:false", "bool2:false", "bool3:false", "bool4:false", "int1:123", "bool5:false", + "bool6:false", "bool7:false", "bool8:false", "int2:20", + ] + ) XCTAssertEqual(bytes.count, 3) XCTAssertNotEqual(bytes.baseAddress, ptr.baseAddress) XCTAssertEqual(bytes.loadUnaligned(fromByteOffset: 0, as: UInt8.self), 81) diff --git a/Package.swift b/Package.swift index e8de0369c..77de89c99 100644 --- a/Package.swift +++ b/Package.swift @@ -12,90 +12,90 @@ import PackageDescription let package = Package( - name: "SwiftProtobuf", - products: [ - .executable( - name: "protoc-gen-swift", - targets: ["protoc-gen-swift"] - ), - .library( - name: "SwiftProtobuf", - targets: ["SwiftProtobuf"] - ), - .library( - name: "SwiftProtobufPluginLibrary", - targets: ["SwiftProtobufPluginLibrary"] - ), - .plugin( - name: "SwiftProtobufPlugin", - targets: ["SwiftProtobufPlugin"] - ), - ], - dependencies: [ - .package(url: "https://github.com/swiftlang/swift-docc-plugin", from: "1.0.0"), - ], - targets: [ - .target( - name: "SwiftProtobuf", - exclude: ["CMakeLists.txt"], - resources: [.copy("PrivacyInfo.xcprivacy")], - swiftSettings: .packageSettings - ), - .target( - name: "SwiftProtobufPluginLibrary", - dependencies: ["SwiftProtobuf"], - exclude: ["CMakeLists.txt"], - resources: [.copy("PrivacyInfo.xcprivacy")], - swiftSettings: .packageSettings - ), - .target( - name: "SwiftProtobufTestHelpers", - dependencies: ["SwiftProtobuf"], - swiftSettings: .packageSettings - ), - .executableTarget( - name: "protoc-gen-swift", - dependencies: ["SwiftProtobufPluginLibrary", "SwiftProtobuf"], - exclude: ["CMakeLists.txt"], - swiftSettings: .packageSettings - ), - .executableTarget( - name: "Conformance", - dependencies: ["SwiftProtobuf"], - exclude: ["failure_list_swift.txt", "text_format_failure_list_swift.txt"], - swiftSettings: .packageSettings - ), - .plugin( - name: "SwiftProtobufPlugin", - capability: .buildTool(), - dependencies: ["protoc-gen-swift"] - ), - .testTarget( - name: "SwiftProtobufTests", - dependencies: ["SwiftProtobuf"], - swiftSettings: .packageSettings - ), - .testTarget( - name: "SwiftProtobufPluginLibraryTests", - dependencies: ["SwiftProtobufPluginLibrary", "SwiftProtobufTestHelpers"], - swiftSettings: .packageSettings - ), - .testTarget( - name: "protoc-gen-swiftTests", - dependencies: ["protoc-gen-swift", "SwiftProtobufTestHelpers"], - swiftSettings: .packageSettings - ), - ], - swiftLanguageVersions: [.v5] + name: "SwiftProtobuf", + products: [ + .executable( + name: "protoc-gen-swift", + targets: ["protoc-gen-swift"] + ), + .library( + name: "SwiftProtobuf", + targets: ["SwiftProtobuf"] + ), + .library( + name: "SwiftProtobufPluginLibrary", + targets: ["SwiftProtobufPluginLibrary"] + ), + .plugin( + name: "SwiftProtobufPlugin", + targets: ["SwiftProtobufPlugin"] + ), + ], + dependencies: [ + .package(url: "https://github.com/swiftlang/swift-docc-plugin", from: "1.0.0") + ], + targets: [ + .target( + name: "SwiftProtobuf", + exclude: ["CMakeLists.txt"], + resources: [.copy("PrivacyInfo.xcprivacy")], + swiftSettings: .packageSettings + ), + .target( + name: "SwiftProtobufPluginLibrary", + dependencies: ["SwiftProtobuf"], + exclude: ["CMakeLists.txt"], + resources: [.copy("PrivacyInfo.xcprivacy")], + swiftSettings: .packageSettings + ), + .target( + name: "SwiftProtobufTestHelpers", + dependencies: ["SwiftProtobuf"], + swiftSettings: .packageSettings + ), + .executableTarget( + name: "protoc-gen-swift", + dependencies: ["SwiftProtobufPluginLibrary", "SwiftProtobuf"], + exclude: ["CMakeLists.txt"], + swiftSettings: .packageSettings + ), + .executableTarget( + name: "Conformance", + dependencies: ["SwiftProtobuf"], + exclude: ["failure_list_swift.txt", "text_format_failure_list_swift.txt"], + swiftSettings: .packageSettings + ), + .plugin( + name: "SwiftProtobufPlugin", + capability: .buildTool(), + dependencies: ["protoc-gen-swift"] + ), + .testTarget( + name: "SwiftProtobufTests", + dependencies: ["SwiftProtobuf"], + swiftSettings: .packageSettings + ), + .testTarget( + name: "SwiftProtobufPluginLibraryTests", + dependencies: ["SwiftProtobufPluginLibrary", "SwiftProtobufTestHelpers"], + swiftSettings: .packageSettings + ), + .testTarget( + name: "protoc-gen-swiftTests", + dependencies: ["protoc-gen-swift", "SwiftProtobufTestHelpers"], + swiftSettings: .packageSettings + ), + ], + swiftLanguageVersions: [.v5] ) // Settings for every Swift target in this package, like project-level settings // in an Xcode project. extension Array where Element == PackageDescription.SwiftSetting { - static var packageSettings: Self { - [ - .enableExperimentalFeature("StrictConcurrency=complete"), - .enableUpcomingFeature("ExistentialAny"), - ] - } + static var packageSettings: Self { + [ + .enableExperimentalFeature("StrictConcurrency=complete"), + .enableUpcomingFeature("ExistentialAny"), + ] + } } diff --git a/Performance/Harness.swift b/Performance/Harness.swift index 47d693c8f..c7e32038a 100644 --- a/Performance/Harness.swift +++ b/Performance/Harness.swift @@ -15,17 +15,17 @@ import Foundation private func padded(_ input: String, to width: Int) -> String { - return input + String(repeating: " ", count: max(0, width - input.count)) + input + String(repeating: " ", count: max(0, width - input.count)) } /// It is expected that the generator will provide these in an extension. protocol GeneratedHarnessMembers { - /// The number of times to loop the body of the run() method. - /// Increase this to get better precision. - var runCount: Int { get } + /// The number of times to loop the body of the run() method. + /// Increase this to get better precision. + var runCount: Int { get } - /// The main body of the performance harness. - func run() + /// The main body of the performance harness. + func run() } /// Harness used for performance tests. @@ -34,135 +34,134 @@ protocol GeneratedHarnessMembers { /// run() method, which the main.swift file calls. class Harness: GeneratedHarnessMembers { - /// The number of times to execute the block passed to measure(). - var measurementCount = 10 - - /// The number of times to call append() for repeated fields. - let repeatedCount: Int32 = 10 - - /// Ordered list of task names - var taskNames = [String]() - - /// The times taken by subtasks during each measured attempt. - var subtaskTimings = [String: [TimeInterval]]() - - /// Times for the subtasks in the current attempt. - var currentSubtasks = [String: TimeInterval]() - - /// The file to which results should be written. - let resultsFile: FileHandle? - - /// Creates a new harness that writes its statistics to the given file - /// (as well as to stdout). - init(resultsFile: FileHandle?) { - self.resultsFile = resultsFile - } - - /// Measures the time it takes to execute the given block. The block is - /// executed five times and the mean/standard deviation are computed. - func measure(block: () throws -> Void) { - var timings = [TimeInterval]() - subtaskTimings.removeAll() - print("Running each check \(runCount) times, times in µs") - - var headingsDisplayed = false - - do { - // Do each measurement multiple times and collect the means and standard - // deviation to account for noise. - for attempt in 1...measurementCount { - currentSubtasks.removeAll() - taskNames.removeAll() - let start = Date() - for _ in 0.. Void) { + var timings = [TimeInterval]() + subtaskTimings.removeAll() + print("Running each check \(runCount) times, times in µs") + + var headingsDisplayed = false + + do { + // Do each measurement multiple times and collect the means and standard + // deviation to account for noise. + for attempt in 1...measurementCount { + currentSubtasks.removeAll() + taskNames.removeAll() + let start = Date() + for _ in 0..( + _ name: String, + block: () throws -> Result + ) rethrows -> Result { + try autoreleasepool { () -> Result in + taskNames.append(name) + let start = Date() + let result = try block() + let end = Date() + let diff = end.timeIntervalSince(start) / Double(runCount) * 1000000.0 + currentSubtasks[name] = (currentSubtasks[name] ?? 0) + diff + return result + } } - let (mean, stddev) = statistics(timings) - let stats = - String(format: "Relative stddev = %.1f%%\n", (stddev / mean) * 100.0) - print(stats) - } - - /// Measure an individual subtask whose timing will be printed separately - /// from the main results. - func measureSubtask( - _ name: String, - block: () throws -> Result - ) rethrows -> Result { - return try autoreleasepool { () -> Result in - taskNames.append(name) - let start = Date() - let result = try block() - let end = Date() - let diff = end.timeIntervalSince(start) / Double(runCount) * 1000000.0 - currentSubtasks[name] = (currentSubtasks[name] ?? 0) + diff - return result - } - } - - /// Compute the mean and standard deviation of the given time intervals. - private func statistics(_ timings: [TimeInterval]) -> - (mean: TimeInterval, stddev: TimeInterval) { - var sum: TimeInterval = 0 - var sqsum: TimeInterval = 0 - for timing in timings { - sum += timing - sqsum += timing * timing + /// Compute the mean and standard deviation of the given time intervals. + private func statistics(_ timings: [TimeInterval]) -> (mean: TimeInterval, stddev: TimeInterval) { + var sum: TimeInterval = 0 + var sqsum: TimeInterval = 0 + for timing in timings { + sum += timing + sqsum += timing * timing + } + let n = TimeInterval(timings.count) + let mean = sum / n + let variance = sqsum / n - mean * mean + return (mean: mean, stddev: sqrt(variance)) } - let n = TimeInterval(timings.count) - let mean = sum / n - let variance = sqsum / n - mean * mean - return (mean: mean, stddev: sqrt(variance)) - } - - /// Writes a string to the data results file that will be parsed by the - /// calling script to produce visualizations. - private func writeToLog(_ string: String) { - if let resultsFile = resultsFile { - let utf8 = Data(string.utf8) - resultsFile.write(utf8) + + /// Writes a string to the data results file that will be parsed by the + /// calling script to produce visualizations. + private func writeToLog(_ string: String) { + if let resultsFile = resultsFile { + let utf8 = Data(string.utf8) + resultsFile.write(utf8) + } } - } } diff --git a/Performance/main.swift b/Performance/main.swift index 922fd72ad..40f93d468 100644 --- a/Performance/main.swift +++ b/Performance/main.swift @@ -15,8 +15,7 @@ import Foundation let args = CommandLine.arguments -let resultsFile = args.count > 1 ? - FileHandle(forWritingAtPath: args[1]) : nil +let resultsFile = args.count > 1 ? FileHandle(forWritingAtPath: args[1]) : nil resultsFile?.seekToEndOfFile() let harness = Harness(resultsFile: resultsFile) diff --git a/PluginExamples/Package.swift b/PluginExamples/Package.swift index 1fe9d23f4..dc59afbab 100644 --- a/PluginExamples/Package.swift +++ b/PluginExamples/Package.swift @@ -16,9 +16,9 @@ private func targets() -> [Target] { .target(name: "Nested"), .target(name: "Import"), ] -#if compiler(>=5.9) + #if compiler(>=5.9) testDependencies.append(.target(name: "AccessLevelOnImport")) -#endif + #endif var targets: [Target] = [ .testTarget( name: "ExampleTests", @@ -27,7 +27,7 @@ private func targets() -> [Target] { .target( name: "Simple", dependencies: [ - .product(name: "SwiftProtobuf", package: "swift-protobuf"), + .product(name: "SwiftProtobuf", package: "swift-protobuf") ], plugins: [ .plugin(name: "SwiftProtobufPlugin", package: "swift-protobuf") @@ -36,7 +36,7 @@ private func targets() -> [Target] { .target( name: "Nested", dependencies: [ - .product(name: "SwiftProtobuf", package: "swift-protobuf"), + .product(name: "SwiftProtobuf", package: "swift-protobuf") ], plugins: [ .plugin(name: "SwiftProtobufPlugin", package: "swift-protobuf") @@ -45,28 +45,28 @@ private func targets() -> [Target] { .target( name: "Import", dependencies: [ - .product(name: "SwiftProtobuf", package: "swift-protobuf"), + .product(name: "SwiftProtobuf", package: "swift-protobuf") ], plugins: [ .plugin(name: "SwiftProtobufPlugin", package: "swift-protobuf") ] ), ] -#if compiler(>=5.9) + #if compiler(>=5.9) targets.append( .target( name: "AccessLevelOnImport", dependencies: [ - .product(name: "SwiftProtobuf", package: "swift-protobuf"), + .product(name: "SwiftProtobuf", package: "swift-protobuf") ], swiftSettings: [ - .enableExperimentalFeature("AccessLevelOnImport"), + .enableExperimentalFeature("AccessLevelOnImport") ], plugins: [ .plugin(name: "SwiftProtobufPlugin", package: "swift-protobuf") ] ) ) -#endif + #endif return targets } diff --git a/PluginExamples/Sources/ExampleTests/ExampleTests.swift b/PluginExamples/Sources/ExampleTests/ExampleTests.swift index ef334a389..1bd2d5354 100644 --- a/PluginExamples/Sources/ExampleTests/ExampleTests.swift +++ b/PluginExamples/Sources/ExampleTests/ExampleTests.swift @@ -1,7 +1,6 @@ -import Simple -import Nested import Import - +import Nested +import Simple import XCTest final class ExampleTests: XCTestCase { diff --git a/Plugins/SwiftProtobufPlugin/plugin.swift b/Plugins/SwiftProtobufPlugin/plugin.swift index ccc84a491..1a644bd2f 100644 --- a/Plugins/SwiftProtobufPlugin/plugin.swift +++ b/Plugins/SwiftProtobufPlugin/plugin.swift @@ -112,11 +112,13 @@ struct SwiftProtobufPlugin { sourceFiles: FileList, tool: (String) throws -> PackagePlugin.PluginContext.Tool ) throws -> [Command] { - guard let configurationFilePath = sourceFiles.first( - where: { - $0.path.lastComponent == Self.configurationFileName - } - )?.path else { + guard + let configurationFilePath = sourceFiles.first( + where: { + $0.path.lastComponent == Self.configurationFileName + } + )?.path + else { throw PluginError.noConfigFound(Self.configurationFileName) } let data = try Data(contentsOf: URL(fileURLWithPath: "\(configurationFilePath)")) @@ -262,7 +264,7 @@ extension SwiftProtobufPlugin: XcodeBuildToolPlugin { context: XcodePluginContext, target: XcodeTarget ) throws -> [Command] { - return try createBuildCommands( + try createBuildCommands( pluginWorkDirectory: context.pluginWorkDirectory, sourceFiles: target.inputFiles, tool: context.tool diff --git a/Sources/Conformance/main.swift b/Sources/Conformance/main.swift index 44b5de15e..dc384d6a4 100644 --- a/Sources/Conformance/main.swift +++ b/Sources/Conformance/main.swift @@ -16,29 +16,29 @@ // ----------------------------------------------------------------------------- import Foundation - import SwiftProtobuf extension FileHandle { - fileprivate func _read(count: Int) -> Data? { - if #available(macOS 10.15.4, *) { - do { - guard let result = try read(upToCount: count), - result.count == count else { - return nil + fileprivate func _read(count: Int) -> Data? { + if #available(macOS 10.15.4, *) { + do { + guard let result = try read(upToCount: count), + result.count == count + else { + return nil + } + return result + } catch { + return nil + } + } else { + let result = readData(ofLength: count) + guard result.count == count else { + return nil + } + return result } - return result - } catch { - return nil - } - } else { - let result = readData(ofLength: count) - guard result.count == count else { - return nil - } - return result } - } } func readRequest() -> Data? { @@ -51,7 +51,8 @@ func readRequest() -> Data? { } let count = UInt32(littleEndian: countLE) guard count < Int.max, - let result = stdIn._read(count: Int(count)) else { + let result = stdIn._read(count: Int(count)) + else { return nil } return result @@ -96,8 +97,8 @@ func buildResponse(serializedData: Data) -> Conformance_ConformanceResponse { return response case .UNRECOGNIZED(let x): response.runtimeError = - "ConformanceRequest had a new testCategory (\(x)); regenerate conformance.pb.swift" - + " and see what support needs to be added." + "ConformanceRequest had a new testCategory (\(x)); regenerate conformance.pb.swift" + + " and see what support needs to be added." return response } @@ -141,9 +142,11 @@ func buildResponse(serializedData: Data) -> Conformance_ConformanceResponse { var options = JSONDecodingOptions() options.ignoreUnknownFields = (request.testCategory == .jsonIgnoreUnknownParsingTest) do { - testMessage = try msgType.init(jsonString: json, - extensions: extensions, - options: options) + testMessage = try msgType.init( + jsonString: json, + extensions: extensions, + options: options + ) } catch let e { response.parseError = "JSON failed to parse: \(e)" return response @@ -195,14 +198,14 @@ func buildResponse(serializedData: Data) -> Conformance_ConformanceResponse { } func singleTest() throws -> Bool { - if let indata = readRequest() { - let response = buildResponse(serializedData: indata) - let outdata: Data = try response.serializedData() - writeResponse(data: outdata) - return true - } else { - return false - } + if let indata = readRequest() { + let response = buildResponse(serializedData: indata) + let outdata: Data = try response.serializedData() + writeResponse(data: outdata) + return true + } else { + return false + } } Google_Protobuf_Any.register(messageType: ProtobufTestMessages_Proto3_TestAllTypesProto3.self) @@ -210,4 +213,3 @@ Google_Protobuf_Any.register(messageType: ProtobufTestMessages_Editions_Proto3_T while try singleTest() { } - diff --git a/Sources/SwiftProtobuf/AnyMessageStorage.swift b/Sources/SwiftProtobuf/AnyMessageStorage.swift index e510a9630..3c4b3a450 100644 --- a/Sources/SwiftProtobuf/AnyMessageStorage.swift +++ b/Sources/SwiftProtobuf/AnyMessageStorage.swift @@ -15,302 +15,317 @@ import Foundation -fileprivate func serializeAnyJSON( - for message: any Message, - typeURL: String, - options: JSONEncodingOptions +private func serializeAnyJSON( + for message: any Message, + typeURL: String, + options: JSONEncodingOptions ) throws -> String { - var visitor = try JSONEncodingVisitor(type: type(of: message), options: options) - visitor.startObject(message: message) - visitor.encodeField(name: "@type", stringValue: typeURL) - if let m = message as? (any _CustomJSONCodable) { - let value = try m.encodedJSONString(options: options) - visitor.encodeField(name: "value", jsonText: value) - } else { - try message.traverse(visitor: &visitor) - } - visitor.endObject() - return visitor.stringResult + var visitor = try JSONEncodingVisitor(type: type(of: message), options: options) + visitor.startObject(message: message) + visitor.encodeField(name: "@type", stringValue: typeURL) + if let m = message as? (any _CustomJSONCodable) { + let value = try m.encodedJSONString(options: options) + visitor.encodeField(name: "value", jsonText: value) + } else { + try message.traverse(visitor: &visitor) + } + visitor.endObject() + return visitor.stringResult } -fileprivate func emitVerboseTextForm(visitor: inout TextFormatEncodingVisitor, message: any Message, typeURL: String) { - let url: String - if typeURL.isEmpty { - url = buildTypeURL(forMessage: message, typePrefix: defaultAnyTypeURLPrefix) - } else { - url = typeURL - } - visitor.visitAnyVerbose(value: message, typeURL: url) +private func emitVerboseTextForm(visitor: inout TextFormatEncodingVisitor, message: any Message, typeURL: String) { + let url: String + if typeURL.isEmpty { + url = buildTypeURL(forMessage: message, typePrefix: defaultAnyTypeURLPrefix) + } else { + url = typeURL + } + visitor.visitAnyVerbose(value: message, typeURL: url) } -fileprivate func asJSONObject(body: [UInt8]) -> Data { - let asciiOpenCurlyBracket = UInt8(ascii: "{") - let asciiCloseCurlyBracket = UInt8(ascii: "}") - var result = [asciiOpenCurlyBracket] - result.append(contentsOf: body) - result.append(asciiCloseCurlyBracket) - return Data(result) +private func asJSONObject(body: [UInt8]) -> Data { + let asciiOpenCurlyBracket = UInt8(ascii: "{") + let asciiCloseCurlyBracket = UInt8(ascii: "}") + var result = [asciiOpenCurlyBracket] + result.append(contentsOf: body) + result.append(asciiCloseCurlyBracket) + return Data(result) } -fileprivate func unpack(contentJSON: [UInt8], - extensions: any ExtensionMap, - options: JSONDecodingOptions, - as messageType: any Message.Type) throws -> any Message { - guard messageType is any _CustomJSONCodable.Type else { - let contentJSONAsObject = asJSONObject(body: contentJSON) - return try messageType.init(jsonUTF8Bytes: contentJSONAsObject, extensions: extensions, options: options) - } - - var value = String() - try contentJSON.withUnsafeBytes { (body: UnsafeRawBufferPointer) in - if body.count > 0 { - // contentJSON will be the valid JSON for inside an object (everything but - // the '{' and '}', so minimal validation is needed. - var scanner = JSONScanner(source: body, options: options, extensions: extensions) - while !scanner.complete { - let key = try scanner.nextQuotedString() - try scanner.skipRequiredColon() - if key == "value" { - value = try scanner.skip() - break - } - if !options.ignoreUnknownFields { - // The only thing within a WKT should be "value". - throw AnyUnpackError.malformedWellKnownTypeJSON +private func unpack( + contentJSON: [UInt8], + extensions: any ExtensionMap, + options: JSONDecodingOptions, + as messageType: any Message.Type +) throws -> any Message { + guard messageType is any _CustomJSONCodable.Type else { + let contentJSONAsObject = asJSONObject(body: contentJSON) + return try messageType.init(jsonUTF8Bytes: contentJSONAsObject, extensions: extensions, options: options) + } + + var value = String() + try contentJSON.withUnsafeBytes { (body: UnsafeRawBufferPointer) in + if body.count > 0 { + // contentJSON will be the valid JSON for inside an object (everything but + // the '{' and '}', so minimal validation is needed. + var scanner = JSONScanner(source: body, options: options, extensions: extensions) + while !scanner.complete { + let key = try scanner.nextQuotedString() + try scanner.skipRequiredColon() + if key == "value" { + value = try scanner.skip() + break + } + if !options.ignoreUnknownFields { + // The only thing within a WKT should be "value". + throw AnyUnpackError.malformedWellKnownTypeJSON + } + let _ = try scanner.skip() + try scanner.skipRequiredComma() + } + if !options.ignoreUnknownFields && !scanner.complete { + // If that wasn't the end, then there was another key, and WKTs should + // only have the one when not skipping unknowns. + throw AnyUnpackError.malformedWellKnownTypeJSON + } } - let _ = try scanner.skip() - try scanner.skipRequiredComma() - } - if !options.ignoreUnknownFields && !scanner.complete { - // If that wasn't the end, then there was another key, and WKTs should - // only have the one when not skipping unknowns. - throw AnyUnpackError.malformedWellKnownTypeJSON - } } - } - return try messageType.init(jsonString: value, extensions: extensions, options: options) + return try messageType.init(jsonString: value, extensions: extensions, options: options) } internal class AnyMessageStorage { - // The two properties generated Google_Protobuf_Any will reference. - var _typeURL = String() - var _value: Data { - // Remapped to the internal `state`. - get { - switch state { - case .binary(let value): - return Data(value) - case .message(let message): - do { - return try message.serializedBytes(partial: true) - } catch { - return Data() - } - case .contentJSON(let contentJSON, let options): - guard let messageType = Google_Protobuf_Any.messageType(forTypeURL: _typeURL) else { - return Data() + // The two properties generated Google_Protobuf_Any will reference. + var _typeURL = String() + var _value: Data { + // Remapped to the internal `state`. + get { + switch state { + case .binary(let value): + return Data(value) + case .message(let message): + do { + return try message.serializedBytes(partial: true) + } catch { + return Data() + } + case .contentJSON(let contentJSON, let options): + guard let messageType = Google_Protobuf_Any.messageType(forTypeURL: _typeURL) else { + return Data() + } + do { + let m = try unpack( + contentJSON: contentJSON, + extensions: SimpleExtensionMap(), + options: options, + as: messageType + ) + return try m.serializedBytes(partial: true) + } catch { + return Data() + } + } } - do { - let m = try unpack(contentJSON: contentJSON, - extensions: SimpleExtensionMap(), - options: options, - as: messageType) - return try m.serializedBytes(partial: true) - } catch { - return Data() + set { + state = .binary(newValue) } - } } - set { - state = .binary(newValue) + + enum InternalState { + // a serialized binary + // Note: Unlike contentJSON below, binary does not bother to capture the + // decoding options. This is because the actual binary format is the binary + // blob, i.e. - when decoding from binary, the spec doesn't include decoding + // the binary blob, it is pass through. Instead there is a public api for + // unpacking that takes new options when a developer decides to decode it. + case binary(Data) + // a message + case message(any Message) + // parsed JSON with the @type removed and the decoding options. + case contentJSON([UInt8], JSONDecodingOptions) } - } - - enum InternalState { - // a serialized binary - // Note: Unlike contentJSON below, binary does not bother to capture the - // decoding options. This is because the actual binary format is the binary - // blob, i.e. - when decoding from binary, the spec doesn't include decoding - // the binary blob, it is pass through. Instead there is a public api for - // unpacking that takes new options when a developer decides to decode it. - case binary(Data) - // a message - case message(any Message) - // parsed JSON with the @type removed and the decoding options. - case contentJSON([UInt8], JSONDecodingOptions) - } - var state: InternalState = .binary(Data()) - - #if swift(>=5.10) + var state: InternalState = .binary(Data()) + + #if swift(>=5.10) // This property is used as the initial default value for new instances of the type. // The type itself is protecting the reference to its storage via CoW semantics. // This will force a copy to be made of this reference when the first mutation occurs; // hence, it is safe to mark this as `nonisolated(unsafe)`. static nonisolated(unsafe) let defaultInstance = AnyMessageStorage() - #else + #else static let defaultInstance = AnyMessageStorage() - #endif - - private init() {} + #endif - init(copying source: AnyMessageStorage) { - _typeURL = source._typeURL - state = source.state - } + private init() {} - func isA(_ type: M.Type) -> Bool { - if _typeURL.isEmpty { - return false + init(copying source: AnyMessageStorage) { + _typeURL = source._typeURL + state = source.state } - let encodedType = typeName(fromURL: _typeURL) - return encodedType == M.protoMessageName - } - - // This is only ever called with the expectation that target will be fully - // replaced during the unpacking and never as a merge. - func unpackTo( - target: inout M, - extensions: (any ExtensionMap)?, - options: BinaryDecodingOptions - ) throws { - guard isA(M.self) else { - throw AnyUnpackError.typeMismatch + + func isA(_ type: M.Type) -> Bool { + if _typeURL.isEmpty { + return false + } + let encodedType = typeName(fromURL: _typeURL) + return encodedType == M.protoMessageName } - switch state { - case .binary(let data): - target = try M(serializedBytes: data, extensions: extensions, partial: true, options: options) - - case .message(let msg): - if let message = msg as? M { - // Already right type, copy it over. - target = message - } else { - // Different type, serialize and parse. - let bytes: [UInt8] = try msg.serializedBytes(partial: true) - target = try M(serializedBytes: bytes, extensions: extensions, partial: true) - } - - case .contentJSON(let contentJSON, let options): - target = try unpack(contentJSON: contentJSON, - extensions: extensions ?? SimpleExtensionMap(), - options: options, - as: M.self) as! M + // This is only ever called with the expectation that target will be fully + // replaced during the unpacking and never as a merge. + func unpackTo( + target: inout M, + extensions: (any ExtensionMap)?, + options: BinaryDecodingOptions + ) throws { + guard isA(M.self) else { + throw AnyUnpackError.typeMismatch + } + + switch state { + case .binary(let data): + target = try M(serializedBytes: data, extensions: extensions, partial: true, options: options) + + case .message(let msg): + if let message = msg as? M { + // Already right type, copy it over. + target = message + } else { + // Different type, serialize and parse. + let bytes: [UInt8] = try msg.serializedBytes(partial: true) + target = try M(serializedBytes: bytes, extensions: extensions, partial: true) + } + + case .contentJSON(let contentJSON, let options): + target = + try unpack( + contentJSON: contentJSON, + extensions: extensions ?? SimpleExtensionMap(), + options: options, + as: M.self + ) as! M + } } - } - - // Called before the message is traversed to do any error preflights. - // Since traverse() will use _value, this is our chance to throw - // when _value can't. - func preTraverse() throws { - switch state { - case .binary: - // Nothing to be checked. - break - - case .message: - // When set from a developer provided message, partial support - // is done. Any message that comes in from another format isn't - // checked, and transcoding the isInitialized requirement is - // never inserted. - break - - case .contentJSON(let contentJSON, let options): - // contentJSON requires we have the type available for decoding. - guard let messageType = Google_Protobuf_Any.messageType(forTypeURL: _typeURL) else { - throw BinaryEncodingError.anyTranscodeFailure - } - do { - // Decodes the full JSON and then discard the result. - // The regular traversal will decode this again by querying the - // `value` field, but that has no way to fail. As a result, - // we need this to accurately handle decode errors. - _ = try unpack(contentJSON: contentJSON, - extensions: SimpleExtensionMap(), - options: options, - as: messageType) - } catch { - throw BinaryEncodingError.anyTranscodeFailure - } + + // Called before the message is traversed to do any error preflights. + // Since traverse() will use _value, this is our chance to throw + // when _value can't. + func preTraverse() throws { + switch state { + case .binary: + // Nothing to be checked. + break + + case .message: + // When set from a developer provided message, partial support + // is done. Any message that comes in from another format isn't + // checked, and transcoding the isInitialized requirement is + // never inserted. + break + + case .contentJSON(let contentJSON, let options): + // contentJSON requires we have the type available for decoding. + guard let messageType = Google_Protobuf_Any.messageType(forTypeURL: _typeURL) else { + throw BinaryEncodingError.anyTranscodeFailure + } + do { + // Decodes the full JSON and then discard the result. + // The regular traversal will decode this again by querying the + // `value` field, but that has no way to fail. As a result, + // we need this to accurately handle decode errors. + _ = try unpack( + contentJSON: contentJSON, + extensions: SimpleExtensionMap(), + options: options, + as: messageType + ) + } catch { + throw BinaryEncodingError.anyTranscodeFailure + } + } } - } } /// Custom handling for Text format. extension AnyMessageStorage { - func decodeTextFormat(typeURL url: String, decoder: inout TextFormatDecoder) throws { - // Decoding the verbose form requires knowing the type. - _typeURL = url - guard let messageType = Google_Protobuf_Any.messageType(forTypeURL: url) else { - // The type wasn't registered, can't parse it. - throw TextFormatDecodingError.malformedText - } - let terminator = try decoder.scanner.skipObjectStart() - var subDecoder = try TextFormatDecoder(messageType: messageType, scanner: decoder.scanner, terminator: terminator) - if messageType == Google_Protobuf_Any.self { - var any = Google_Protobuf_Any() - try any.decodeTextFormat(decoder: &subDecoder) - state = .message(any) - } else { - var m = messageType.init() - try m.decodeMessage(decoder: &subDecoder) - state = .message(m) - } - decoder.scanner = subDecoder.scanner - if try decoder.nextFieldNumber() != nil { - // Verbose any can never have additional keys. - throw TextFormatDecodingError.malformedText - } - } - - // Specialized traverse for writing out a Text form of the Any. - // This prefers the more-legible "verbose" format if it can - // use it, otherwise will fall back to simpler forms. - internal func textTraverse(visitor: inout TextFormatEncodingVisitor) { - switch state { - case .binary(let valueData): - if let messageType = Google_Protobuf_Any.messageType(forTypeURL: _typeURL) { - // If we can decode it, we can write the readable verbose form: - do { - let m = try messageType.init(serializedBytes: valueData, partial: true) - emitVerboseTextForm(visitor: &visitor, message: m, typeURL: _typeURL) - return - } catch { - // Fall through to just print the type and raw binary data. + func decodeTextFormat(typeURL url: String, decoder: inout TextFormatDecoder) throws { + // Decoding the verbose form requires knowing the type. + _typeURL = url + guard let messageType = Google_Protobuf_Any.messageType(forTypeURL: url) else { + // The type wasn't registered, can't parse it. + throw TextFormatDecodingError.malformedText + } + let terminator = try decoder.scanner.skipObjectStart() + var subDecoder = try TextFormatDecoder( + messageType: messageType, + scanner: decoder.scanner, + terminator: terminator + ) + if messageType == Google_Protobuf_Any.self { + var any = Google_Protobuf_Any() + try any.decodeTextFormat(decoder: &subDecoder) + state = .message(any) + } else { + var m = messageType.init() + try m.decodeMessage(decoder: &subDecoder) + state = .message(m) + } + decoder.scanner = subDecoder.scanner + if try decoder.nextFieldNumber() != nil { + // Verbose any can never have additional keys. + throw TextFormatDecodingError.malformedText } - } - if !_typeURL.isEmpty { - try! visitor.visitSingularStringField(value: _typeURL, fieldNumber: 1) - } - if !valueData.isEmpty { - try! visitor.visitSingularBytesField(value: valueData, fieldNumber: 2) - } - - case .message(let msg): - emitVerboseTextForm(visitor: &visitor, message: msg, typeURL: _typeURL) - - case .contentJSON(let contentJSON, let options): - // If we can decode it, we can write the readable verbose form: - if let messageType = Google_Protobuf_Any.messageType(forTypeURL: _typeURL) { - do { - let m = try unpack(contentJSON: contentJSON, - extensions: SimpleExtensionMap(), - options: options, - as: messageType) - emitVerboseTextForm(visitor: &visitor, message: m, typeURL: _typeURL) - return - } catch { - // Fall through to just print the raw JSON data + } + + // Specialized traverse for writing out a Text form of the Any. + // This prefers the more-legible "verbose" format if it can + // use it, otherwise will fall back to simpler forms. + internal func textTraverse(visitor: inout TextFormatEncodingVisitor) { + switch state { + case .binary(let valueData): + if let messageType = Google_Protobuf_Any.messageType(forTypeURL: _typeURL) { + // If we can decode it, we can write the readable verbose form: + do { + let m = try messageType.init(serializedBytes: valueData, partial: true) + emitVerboseTextForm(visitor: &visitor, message: m, typeURL: _typeURL) + return + } catch { + // Fall through to just print the type and raw binary data. + } + } + if !_typeURL.isEmpty { + try! visitor.visitSingularStringField(value: _typeURL, fieldNumber: 1) + } + if !valueData.isEmpty { + try! visitor.visitSingularBytesField(value: valueData, fieldNumber: 2) + } + + case .message(let msg): + emitVerboseTextForm(visitor: &visitor, message: msg, typeURL: _typeURL) + + case .contentJSON(let contentJSON, let options): + // If we can decode it, we can write the readable verbose form: + if let messageType = Google_Protobuf_Any.messageType(forTypeURL: _typeURL) { + do { + let m = try unpack( + contentJSON: contentJSON, + extensions: SimpleExtensionMap(), + options: options, + as: messageType + ) + emitVerboseTextForm(visitor: &visitor, message: m, typeURL: _typeURL) + return + } catch { + // Fall through to just print the raw JSON data + } + } + if !_typeURL.isEmpty { + try! visitor.visitSingularStringField(value: _typeURL, fieldNumber: 1) + } + // Build a readable form of the JSON: + let contentJSONAsObject = asJSONObject(body: contentJSON) + visitor.visitAnyJSONBytesField(value: contentJSONAsObject) } - } - if !_typeURL.isEmpty { - try! visitor.visitSingularStringField(value: _typeURL, fieldNumber: 1) - } - // Build a readable form of the JSON: - let contentJSONAsObject = asJSONObject(body: contentJSON) - visitor.visitAnyJSONBytesField(value: contentJSONAsObject) } - } } /// The obvious goal for Hashable/Equatable conformance would be for @@ -326,165 +341,168 @@ extension AnyMessageStorage { /// test. Of course, regardless of the above, we must guarantee that /// hashValue is compatible with equality. extension AnyMessageStorage { - // Can't use _valueData for a few reasons: - // 1. Since decode is done on demand, two objects could be equal - // but created differently (one from JSON, one for Message, etc.), - // and the hash values have to be equal even if we don't have data - // yet. - // 2. map<> serialization order is undefined. At the time of writing - // the Swift, Objective-C, and Go runtimes all tend to have random - // orders, so the messages could be identical, but in binary form - // they could differ. - public func hash(into hasher: inout Hasher) { - if !_typeURL.isEmpty { - hasher.combine(_typeURL) + // Can't use _valueData for a few reasons: + // 1. Since decode is done on demand, two objects could be equal + // but created differently (one from JSON, one for Message, etc.), + // and the hash values have to be equal even if we don't have data + // yet. + // 2. map<> serialization order is undefined. At the time of writing + // the Swift, Objective-C, and Go runtimes all tend to have random + // orders, so the messages could be identical, but in binary form + // they could differ. + public func hash(into hasher: inout Hasher) { + if !_typeURL.isEmpty { + hasher.combine(_typeURL) + } } - } - func isEqualTo(other: AnyMessageStorage) -> Bool { - if (_typeURL != other._typeURL) { - return false - } + func isEqualTo(other: AnyMessageStorage) -> Bool { + if _typeURL != other._typeURL { + return false + } - // Since the library does lazy Any decode, equality is a very hard problem. - // It things exactly match, that's pretty easy, otherwise, one ends up having - // to error on saying they aren't equal. - // - // The best option would be to have Message forms and compare those, as that - // removes issues like map<> serialization order, some other protocol buffer - // implementation details/bugs around serialized form order, etc.; but that - // would also greatly slow down equality tests. - // - // Do our best to compare what is present have... - - // If both have messages, check if they are the same. - if case .message(let myMsg) = state, case .message(let otherMsg) = other.state, type(of: myMsg) == type(of: otherMsg) { - // Since the messages are known to be same type, we can claim both equal and - // not equal based on the equality comparison. - return myMsg.isEqualTo(message: otherMsg) - } + // Since the library does lazy Any decode, equality is a very hard problem. + // It things exactly match, that's pretty easy, otherwise, one ends up having + // to error on saying they aren't equal. + // + // The best option would be to have Message forms and compare those, as that + // removes issues like map<> serialization order, some other protocol buffer + // implementation details/bugs around serialized form order, etc.; but that + // would also greatly slow down equality tests. + // + // Do our best to compare what is present have... + + // If both have messages, check if they are the same. + if case .message(let myMsg) = state, case .message(let otherMsg) = other.state, + type(of: myMsg) == type(of: otherMsg) + { + // Since the messages are known to be same type, we can claim both equal and + // not equal based on the equality comparison. + return myMsg.isEqualTo(message: otherMsg) + } - // If both have serialized data, and they exactly match; the messages are equal. - // Because there could be map in the message, the fact that the data isn't the - // same doesn't always mean the messages aren't equal. Likewise, the binary could - // have been created by a library that doesn't order the fields, or the binary was - // created using the appending ability in of the binary format. - if case .binary(let myValue) = state, case .binary(let otherValue) = other.state, myValue == otherValue { - return true - } + // If both have serialized data, and they exactly match; the messages are equal. + // Because there could be map in the message, the fact that the data isn't the + // same doesn't always mean the messages aren't equal. Likewise, the binary could + // have been created by a library that doesn't order the fields, or the binary was + // created using the appending ability in of the binary format. + if case .binary(let myValue) = state, case .binary(let otherValue) = other.state, myValue == otherValue { + return true + } - // If both have contentJSON, and they exactly match; the messages are equal. - // Because there could be map in the message (or the JSON could just be in a different - // order), the fact that the JSON isn't the same doesn't always mean the messages - // aren't equal. - if case .contentJSON(let myJSON, _) = state, - case .contentJSON(let otherJSON, _) = other.state, - myJSON == otherJSON { - return true - } + // If both have contentJSON, and they exactly match; the messages are equal. + // Because there could be map in the message (or the JSON could just be in a different + // order), the fact that the JSON isn't the same doesn't always mean the messages + // aren't equal. + if case .contentJSON(let myJSON, _) = state, + case .contentJSON(let otherJSON, _) = other.state, + myJSON == otherJSON + { + return true + } - // Out of options. To do more compares, the states conversions would have to be - // done to do comparisons; and since equality can be used somewhat removed from - // a developer (if they put protos in a Set, use them as keys to a Dictionary, etc), - // the conversion cost might be to high for those uses. Give up and say they aren't equal. - return false - } + // Out of options. To do more compares, the states conversions would have to be + // done to do comparisons; and since equality can be used somewhat removed from + // a developer (if they put protos in a Set, use them as keys to a Dictionary, etc), + // the conversion cost might be to high for those uses. Give up and say they aren't equal. + return false + } } // _CustomJSONCodable support for Google_Protobuf_Any extension AnyMessageStorage { - // Override the traversal-based JSON encoding - // This builds an Any JSON representation from one of: - // * The message we were initialized with, - // * The JSON fields we last deserialized, or - // * The protobuf field we were deserialized from. - // The last case requires locating the type, deserializing - // into an object, then reserializing back to JSON. - func encodedJSONString(options: JSONEncodingOptions) throws -> String { - switch state { - case .binary(let valueData): - // Follow the C++ protostream_objectsource.cc's - // ProtoStreamObjectSource::RenderAny() special casing of an empty value. - guard !valueData.isEmpty else { - if _typeURL.isEmpty { - return "{}" + // Override the traversal-based JSON encoding + // This builds an Any JSON representation from one of: + // * The message we were initialized with, + // * The JSON fields we last deserialized, or + // * The protobuf field we were deserialized from. + // The last case requires locating the type, deserializing + // into an object, then reserializing back to JSON. + func encodedJSONString(options: JSONEncodingOptions) throws -> String { + switch state { + case .binary(let valueData): + // Follow the C++ protostream_objectsource.cc's + // ProtoStreamObjectSource::RenderAny() special casing of an empty value. + guard !valueData.isEmpty else { + if _typeURL.isEmpty { + return "{}" + } + var jsonEncoder = JSONEncoder() + jsonEncoder.startField(name: "@type") + jsonEncoder.putStringValue(value: _typeURL) + jsonEncoder.endObject() + return jsonEncoder.stringResult + } + // Transcode by decoding the binary data to a message object + // and then recode back into JSON. + guard let messageType = Google_Protobuf_Any.messageType(forTypeURL: _typeURL) else { + // If we don't have the type available, we can't decode the + // binary value, so we're stuck. (The Google spec does not + // provide a way to just package the binary value for someone + // else to decode later.) + throw JSONEncodingError.anyTranscodeFailure + } + let m = try messageType.init(serializedBytes: valueData, partial: true) + return try serializeAnyJSON(for: m, typeURL: _typeURL, options: options) + + case .message(let msg): + // We should have been initialized with a typeURL, but + // ensure it wasn't cleared. + let url = !_typeURL.isEmpty ? _typeURL : buildTypeURL(forMessage: msg, typePrefix: defaultAnyTypeURLPrefix) + return try serializeAnyJSON(for: msg, typeURL: url, options: options) + + case .contentJSON(let contentJSON, _): + var jsonEncoder = JSONEncoder() + jsonEncoder.startObject() + jsonEncoder.startField(name: "@type") + jsonEncoder.putStringValue(value: _typeURL) + if !contentJSON.isEmpty { + jsonEncoder.append(staticText: ",") + // NOTE: This doesn't really take `options` into account since it is + // just reflecting out what was taken in originally. + jsonEncoder.append(utf8Bytes: contentJSON) + } + jsonEncoder.endObject() + return jsonEncoder.stringResult } - var jsonEncoder = JSONEncoder() - jsonEncoder.startField(name: "@type") - jsonEncoder.putStringValue(value: _typeURL) - jsonEncoder.endObject() - return jsonEncoder.stringResult - } - // Transcode by decoding the binary data to a message object - // and then recode back into JSON. - guard let messageType = Google_Protobuf_Any.messageType(forTypeURL: _typeURL) else { - // If we don't have the type available, we can't decode the - // binary value, so we're stuck. (The Google spec does not - // provide a way to just package the binary value for someone - // else to decode later.) - throw JSONEncodingError.anyTranscodeFailure - } - let m = try messageType.init(serializedBytes: valueData, partial: true) - return try serializeAnyJSON(for: m, typeURL: _typeURL, options: options) - - case .message(let msg): - // We should have been initialized with a typeURL, but - // ensure it wasn't cleared. - let url = !_typeURL.isEmpty ? _typeURL : buildTypeURL(forMessage: msg, typePrefix: defaultAnyTypeURLPrefix) - return try serializeAnyJSON(for: msg, typeURL: url, options: options) - - case .contentJSON(let contentJSON, _): - var jsonEncoder = JSONEncoder() - jsonEncoder.startObject() - jsonEncoder.startField(name: "@type") - jsonEncoder.putStringValue(value: _typeURL) - if !contentJSON.isEmpty { - jsonEncoder.append(staticText: ",") - // NOTE: This doesn't really take `options` into account since it is - // just reflecting out what was taken in originally. - jsonEncoder.append(utf8Bytes: contentJSON) - } - jsonEncoder.endObject() - return jsonEncoder.stringResult - } - } - - // TODO: If the type is well-known or has already been registered, - // we should consider decoding eagerly. Eager decoding would - // catch certain errors earlier (good) but would probably be - // a performance hit if the Any contents were never accessed (bad). - // Of course, we can't always decode eagerly (we don't always have the - // message type available), so the deferred logic here is still needed. - func decodeJSON(from decoder: inout JSONDecoder) throws { - try decoder.scanner.skipRequiredObjectStart() - // Reset state - _typeURL = String() - state = .binary(Data()) - if decoder.scanner.skipOptionalObjectEnd() { - return } - var jsonEncoder = JSONEncoder() - while true { - let key = try decoder.scanner.nextQuotedString() - try decoder.scanner.skipRequiredColon() - if key == "@type" { - _typeURL = try decoder.scanner.nextQuotedString() - } else { - jsonEncoder.startField(name: key) - let keyValueJSON = try decoder.scanner.skip() - jsonEncoder.append(text: keyValueJSON) - } - if decoder.scanner.skipOptionalObjectEnd() { - // Capture the options, but set the messageDepthLimit to be what - // was left right now, as that is the limit when the JSON is finally - // parsed. - var updatedOptions = decoder.options - updatedOptions.messageDepthLimit = decoder.scanner.recursionBudget - state = .contentJSON(Array(jsonEncoder.dataResult), updatedOptions) - return - } - try decoder.scanner.skipRequiredComma() + // TODO: If the type is well-known or has already been registered, + // we should consider decoding eagerly. Eager decoding would + // catch certain errors earlier (good) but would probably be + // a performance hit if the Any contents were never accessed (bad). + // Of course, we can't always decode eagerly (we don't always have the + // message type available), so the deferred logic here is still needed. + func decodeJSON(from decoder: inout JSONDecoder) throws { + try decoder.scanner.skipRequiredObjectStart() + // Reset state + _typeURL = String() + state = .binary(Data()) + if decoder.scanner.skipOptionalObjectEnd() { + return + } + + var jsonEncoder = JSONEncoder() + while true { + let key = try decoder.scanner.nextQuotedString() + try decoder.scanner.skipRequiredColon() + if key == "@type" { + _typeURL = try decoder.scanner.nextQuotedString() + } else { + jsonEncoder.startField(name: key) + let keyValueJSON = try decoder.scanner.skip() + jsonEncoder.append(text: keyValueJSON) + } + if decoder.scanner.skipOptionalObjectEnd() { + // Capture the options, but set the messageDepthLimit to be what + // was left right now, as that is the limit when the JSON is finally + // parsed. + var updatedOptions = decoder.options + updatedOptions.messageDepthLimit = decoder.scanner.recursionBudget + state = .contentJSON(Array(jsonEncoder.dataResult), updatedOptions) + return + } + try decoder.scanner.skipRequiredComma() + } } - } } diff --git a/Sources/SwiftProtobuf/AnyUnpackError.swift b/Sources/SwiftProtobuf/AnyUnpackError.swift index 738b0fe9a..a67f4055a 100644 --- a/Sources/SwiftProtobuf/AnyUnpackError.swift +++ b/Sources/SwiftProtobuf/AnyUnpackError.swift @@ -22,16 +22,16 @@ /// regular decoding operation. There are also other errors that can occur due /// to problems with the `Any` value's structure. public enum AnyUnpackError: Error { - /// The `type_url` field in the `Google_Protobuf_Any` message did not match - /// the message type provided to the `unpack()` method. - case typeMismatch + /// The `type_url` field in the `Google_Protobuf_Any` message did not match + /// the message type provided to the `unpack()` method. + case typeMismatch - /// Well-known types being decoded from JSON must have only two fields: the - /// `@type` field and a `value` field containing the specialized JSON coding - /// of the well-known type. - case malformedWellKnownTypeJSON + /// Well-known types being decoded from JSON must have only two fields: the + /// `@type` field and a `value` field containing the specialized JSON coding + /// of the well-known type. + case malformedWellKnownTypeJSON - /// The `Google_Protobuf_Any` message was malformed in some other way not - /// covered by the other error cases. - case malformedAnyField + /// The `Google_Protobuf_Any` message was malformed in some other way not + /// covered by the other error cases. + case malformedAnyField } diff --git a/Sources/SwiftProtobuf/AsyncMessageSequence.swift b/Sources/SwiftProtobuf/AsyncMessageSequence.swift index 503e85623..c54ecce33 100644 --- a/Sources/SwiftProtobuf/AsyncMessageSequence.swift +++ b/Sources/SwiftProtobuf/AsyncMessageSequence.swift @@ -15,203 +15,203 @@ @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) extension AsyncSequence where Element == UInt8 { - /// Creates an asynchronous sequence of size-delimited messages from this sequence of bytes. - /// Delimited format allows a single file or stream to contain multiple messages. A delimited message - /// is a varint encoding the message size followed by a message of exactly that size. - /// - /// - Parameters: - /// - messageType: The type of message to read. - /// - extensions: An ``ExtensionMap`` used to look up and decode any extensions in - /// messages encoded by this sequence, or in messages nested within these messages. - /// - partial: If `false` (the default), after decoding a message, ``Message/isInitialized-6abgi` - /// will be checked to ensure all fields are present. - /// - options: The ``BinaryDecodingOptions`` to use. - /// - Returns: An asynchronous sequence of messages read from the `AsyncSequence` of bytes. - @inlinable - public func binaryProtobufDelimitedMessages( - of messageType: M.Type = M.self, - extensions: (any ExtensionMap)? = nil, - partial: Bool = false, - options: BinaryDecodingOptions = BinaryDecodingOptions() - ) -> AsyncMessageSequence { - AsyncMessageSequence( - base: self, - extensions: extensions, - partial: partial, - options: options - ) - } + /// Creates an asynchronous sequence of size-delimited messages from this sequence of bytes. + /// Delimited format allows a single file or stream to contain multiple messages. A delimited message + /// is a varint encoding the message size followed by a message of exactly that size. + /// + /// - Parameters: + /// - messageType: The type of message to read. + /// - extensions: An ``ExtensionMap`` used to look up and decode any extensions in + /// messages encoded by this sequence, or in messages nested within these messages. + /// - partial: If `false` (the default), after decoding a message, ``Message/isInitialized-6abgi` + /// will be checked to ensure all fields are present. + /// - options: The ``BinaryDecodingOptions`` to use. + /// - Returns: An asynchronous sequence of messages read from the `AsyncSequence` of bytes. + @inlinable + public func binaryProtobufDelimitedMessages( + of messageType: M.Type = M.self, + extensions: (any ExtensionMap)? = nil, + partial: Bool = false, + options: BinaryDecodingOptions = BinaryDecodingOptions() + ) -> AsyncMessageSequence { + AsyncMessageSequence( + base: self, + extensions: extensions, + partial: partial, + options: options + ) + } } /// An asynchronous sequence of messages decoded from an asynchronous sequence of bytes. @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) public struct AsyncMessageSequence< - Base: AsyncSequence, - M: Message + Base: AsyncSequence, + M: Message >: AsyncSequence where Base.Element == UInt8 { - /// The message type in this asynchronous sequence. - public typealias Element = M + /// The message type in this asynchronous sequence. + public typealias Element = M - private let base: Base - private let extensions: (any ExtensionMap)? - private let partial: Bool - private let options: BinaryDecodingOptions + private let base: Base + private let extensions: (any ExtensionMap)? + private let partial: Bool + private let options: BinaryDecodingOptions - /// Reads size-delimited messages from the given sequence of bytes. Delimited - /// format allows a single file or stream to contain multiple messages. A delimited message - /// is a varint encoding the message size followed by a message of exactly that size. - /// - /// - Parameters: - /// - baseSequence: The `AsyncSequence` to read messages from. - /// - extensions: An ``ExtensionMap`` used to look up and decode any extensions in - /// messages encoded by this sequence, or in messages nested within these messages. - /// - partial: If `false` (the default), after decoding a message, ``Message/isInitialized-6abgi`` - /// will be checked to ensure all fields are present. - /// - options: The ``BinaryDecodingOptions`` to use. - /// - Returns: An asynchronous sequence of messages read from the `AsyncSequence` of bytes. - public init( - base: Base, - extensions: (any ExtensionMap)? = nil, - partial: Bool = false, - options: BinaryDecodingOptions = BinaryDecodingOptions() - ) { - self.base = base - self.extensions = extensions - self.partial = partial - self.options = options - } - - /// An asynchronous iterator that produces the messages of this asynchronous sequence. - public struct AsyncIterator: AsyncIteratorProtocol { - @usableFromInline - var iterator: Base.AsyncIterator? - @usableFromInline - let extensions: (any ExtensionMap)? - @usableFromInline - let partial: Bool - @usableFromInline - let options: BinaryDecodingOptions - - init( - iterator: Base.AsyncIterator, - extensions: (any ExtensionMap)?, - partial: Bool, - options: BinaryDecodingOptions + /// Reads size-delimited messages from the given sequence of bytes. Delimited + /// format allows a single file or stream to contain multiple messages. A delimited message + /// is a varint encoding the message size followed by a message of exactly that size. + /// + /// - Parameters: + /// - baseSequence: The `AsyncSequence` to read messages from. + /// - extensions: An ``ExtensionMap`` used to look up and decode any extensions in + /// messages encoded by this sequence, or in messages nested within these messages. + /// - partial: If `false` (the default), after decoding a message, ``Message/isInitialized-6abgi`` + /// will be checked to ensure all fields are present. + /// - options: The ``BinaryDecodingOptions`` to use. + /// - Returns: An asynchronous sequence of messages read from the `AsyncSequence` of bytes. + public init( + base: Base, + extensions: (any ExtensionMap)? = nil, + partial: Bool = false, + options: BinaryDecodingOptions = BinaryDecodingOptions() ) { - self.iterator = iterator - self.extensions = extensions - self.partial = partial - self.options = options + self.base = base + self.extensions = extensions + self.partial = partial + self.options = options } - /// Asynchronously reads the next varint. - @inlinable - mutating func nextVarInt() async throws -> UInt64? { - var messageSize: UInt64 = 0 - var shift: UInt64 = 0 + /// An asynchronous iterator that produces the messages of this asynchronous sequence. + public struct AsyncIterator: AsyncIteratorProtocol { + @usableFromInline + var iterator: Base.AsyncIterator? + @usableFromInline + let extensions: (any ExtensionMap)? + @usableFromInline + let partial: Bool + @usableFromInline + let options: BinaryDecodingOptions - while let byte = try await iterator?.next() { - messageSize |= UInt64(byte & 0x7f) << shift - shift += UInt64(7) - if shift > 35 { - iterator = nil - throw SwiftProtobufError.BinaryStreamDecoding.malformedLength() + init( + iterator: Base.AsyncIterator, + extensions: (any ExtensionMap)?, + partial: Bool, + options: BinaryDecodingOptions + ) { + self.iterator = iterator + self.extensions = extensions + self.partial = partial + self.options = options } - if (byte & 0x80 == 0) { - return messageSize + + /// Asynchronously reads the next varint. + @inlinable + mutating func nextVarInt() async throws -> UInt64? { + var messageSize: UInt64 = 0 + var shift: UInt64 = 0 + + while let byte = try await iterator?.next() { + messageSize |= UInt64(byte & 0x7f) << shift + shift += UInt64(7) + if shift > 35 { + iterator = nil + throw SwiftProtobufError.BinaryStreamDecoding.malformedLength() + } + if byte & 0x80 == 0 { + return messageSize + } + } + if shift > 0 { + // The stream has ended inside a varint. + iterator = nil + throw BinaryDelimited.Error.truncated + } + return nil // End of stream reached. } - } - if (shift > 0) { - // The stream has ended inside a varint. - iterator = nil - throw BinaryDelimited.Error.truncated - } - return nil // End of stream reached. - } - /// Helper to read the given number of bytes. - @usableFromInline - mutating func readBytes(_ size: Int) async throws -> [UInt8] { - // Even though the bytes are read in chunks, things can still hard fail if - // there isn't enough memory to append to have all the bytes at once for - // parsing; but this at least catches some possible OOM attacks. - var bytesNeeded = size - var buffer = [UInt8]() - let kChunkSize = 16 * 1024 * 1024 - var chunk = [UInt8](repeating: 0, count: Swift.min(bytesNeeded, kChunkSize)) - while bytesNeeded > 0 { - var consumedBytes = 0 - let maxLength = Swift.min(bytesNeeded, chunk.count) - while consumedBytes < maxLength { - guard let byte = try await iterator?.next() else { - // The iterator hit the end, but the chunk wasn't filled, so the full - // payload wasn't read. - throw BinaryDelimited.Error.truncated - } - chunk[consumedBytes] = byte - consumedBytes += 1 + /// Helper to read the given number of bytes. + @usableFromInline + mutating func readBytes(_ size: Int) async throws -> [UInt8] { + // Even though the bytes are read in chunks, things can still hard fail if + // there isn't enough memory to append to have all the bytes at once for + // parsing; but this at least catches some possible OOM attacks. + var bytesNeeded = size + var buffer = [UInt8]() + let kChunkSize = 16 * 1024 * 1024 + var chunk = [UInt8](repeating: 0, count: Swift.min(bytesNeeded, kChunkSize)) + while bytesNeeded > 0 { + var consumedBytes = 0 + let maxLength = Swift.min(bytesNeeded, chunk.count) + while consumedBytes < maxLength { + guard let byte = try await iterator?.next() else { + // The iterator hit the end, but the chunk wasn't filled, so the full + // payload wasn't read. + throw BinaryDelimited.Error.truncated + } + chunk[consumedBytes] = byte + consumedBytes += 1 + } + if consumedBytes < chunk.count { + buffer += chunk[0.. M? { + guard let messageSize = try await nextVarInt() else { + iterator = nil + return nil + } + guard messageSize <= UInt64(0x7fff_ffff) else { + iterator = nil + throw SwiftProtobufError.BinaryDecoding.tooLarge() + } + if messageSize == 0 { + return try M( + serializedBytes: [], + extensions: extensions, + partial: partial, + options: options + ) + } + let buffer = try await readBytes(Int(messageSize)) + return try M( + serializedBytes: buffer, + extensions: extensions, + partial: partial, + options: options + ) } - bytesNeeded -= maxLength - } - return buffer } - /// Asynchronously advances to the next message and returns it, or ends the - /// sequence if there is no next message. + /// Creates the asynchronous iterator that produces elements of this + /// asynchronous sequence. /// - /// - Returns: The next message, if it exists, or `nil` to signal the end of - /// the sequence. - @inlinable - public mutating func next() async throws -> M? { - guard let messageSize = try await nextVarInt() else { - iterator = nil - return nil - } - guard messageSize <= UInt64(0x7fffffff) else { - iterator = nil - throw SwiftProtobufError.BinaryDecoding.tooLarge() - } - if messageSize == 0 { - return try M( - serializedBytes: [], - extensions: extensions, - partial: partial, - options: options + /// - Returns: An instance of the `AsyncIterator` type used to produce + /// messages in the asynchronous sequence. + public func makeAsyncIterator() -> AsyncMessageSequence.AsyncIterator { + AsyncIterator( + iterator: base.makeAsyncIterator(), + extensions: extensions, + partial: partial, + options: options ) - } - let buffer = try await readBytes(Int(messageSize)) - return try M( - serializedBytes: buffer, - extensions: extensions, - partial: partial, - options: options - ) } - } - - /// Creates the asynchronous iterator that produces elements of this - /// asynchronous sequence. - /// - /// - Returns: An instance of the `AsyncIterator` type used to produce - /// messages in the asynchronous sequence. - public func makeAsyncIterator() -> AsyncMessageSequence.AsyncIterator { - AsyncIterator( - iterator: base.makeAsyncIterator(), - extensions: extensions, - partial: partial, - options: options - ) - } } @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -extension AsyncMessageSequence: Sendable where Base: Sendable { } +extension AsyncMessageSequence: Sendable where Base: Sendable {} @available(*, unavailable) -extension AsyncMessageSequence.AsyncIterator: Sendable { } +extension AsyncMessageSequence.AsyncIterator: Sendable {} diff --git a/Sources/SwiftProtobuf/BinaryDecoder.swift b/Sources/SwiftProtobuf/BinaryDecoder.swift index fa84c25a9..073beebcc 100644 --- a/Sources/SwiftProtobuf/BinaryDecoder.swift +++ b/Sources/SwiftProtobuf/BinaryDecoder.swift @@ -19,13 +19,13 @@ import Foundation internal struct BinaryDecoder: Decoder { // Current position - private var p : UnsafeRawPointer + private var p: UnsafeRawPointer // Remaining bytes in input. - private var available : Int + private var available: Int // Position of start of field currently being parsed - private var fieldStartP : UnsafeRawPointer + private var fieldStartP: UnsafeRawPointer // Position of end of field currently being parsed, nil if we don't know. - private var fieldEndP : UnsafeRawPointer? + private var fieldEndP: UnsafeRawPointer? // Whether or not the field value has actually been parsed private var consumed = true // Wire format for last-examined field @@ -48,13 +48,13 @@ internal struct BinaryDecoder: Decoder { // packed repeated enums; see below private var unknownOverride: Data? - private var complete: Bool {return available == 0} + private var complete: Bool { available == 0 } internal init( - forReadingFrom pointer: UnsafeRawPointer, - count: Int, - options: BinaryDecodingOptions, - extensions: (any ExtensionMap)? = nil + forReadingFrom pointer: UnsafeRawPointer, + count: Int, + options: BinaryDecodingOptions, + extensions: (any ExtensionMap)? = nil ) { // Assuming baseAddress is not nil. p = pointer @@ -66,15 +66,17 @@ internal struct BinaryDecoder: Decoder { } internal init( - forReadingFrom pointer: UnsafeRawPointer, - count: Int, - parent: BinaryDecoder + forReadingFrom pointer: UnsafeRawPointer, + count: Int, + parent: BinaryDecoder ) { - self.init(forReadingFrom: pointer, - count: count, - options: parent.options, - extensions: parent.extensions) - recursionBudget = parent.recursionBudget + self.init( + forReadingFrom: pointer, + count: count, + options: parent.options, + extensions: parent.extensions + ) + recursionBudget = parent.recursionBudget } private mutating func incrementRecursionDepth() throws { @@ -789,18 +791,18 @@ internal struct BinaryDecoder: Decoder { internal mutating func decodeSingularEnumField(value: inout E?) throws where E.RawValue == Int { guard fieldWireFormat == WireFormat.varint else { - return - } + return + } let varint = try decodeVarint() if let v = E(rawValue: Int(Int32(truncatingIfNeeded: varint))) { value = v consumed = true } - } + } internal mutating func decodeSingularEnumField(value: inout E) throws where E.RawValue == Int { guard fieldWireFormat == WireFormat.varint else { - return + return } let varint = try decodeVarint() if let v = E(rawValue: Int(Int32(truncatingIfNeeded: varint))) { @@ -839,15 +841,16 @@ internal struct BinaryDecoder: Decoder { if let extras = extras { let fieldTag = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited) let bodySize = extras.reduce(0) { $0 + Varint.encodedSize(of: Int64($1)) } - let fieldSize = Varint.encodedSize(of: fieldTag.rawValue) + Varint.encodedSize(of: Int64(bodySize)) + bodySize + let fieldSize = + Varint.encodedSize(of: fieldTag.rawValue) + Varint.encodedSize(of: Int64(bodySize)) + bodySize var field = Data(count: fieldSize) field.withUnsafeMutableBytes { (body: UnsafeMutableRawBufferPointer) in - var encoder = BinaryEncoder(forWritingInto: body) - encoder.startField(tag: fieldTag) - encoder.putVarInt(value: Int64(bodySize)) - for v in extras { - encoder.putVarInt(value: Int64(v)) - } + var encoder = BinaryEncoder(forWritingInto: body) + encoder.startField(tag: fieldTag) + encoder.putVarInt(value: Int64(bodySize)) + for v in extras { + encoder.putVarInt(value: Int64(v)) + } } unknownOverride = field } @@ -885,16 +888,16 @@ internal struct BinaryDecoder: Decoder { } internal mutating func decodeFullMessage(message: inout M) throws { - assert(unknownData == nil) - try incrementRecursionDepth() - try message.decodeMessage(decoder: &self) - decrementRecursionDepth() - guard complete else { - throw BinaryDecodingError.trailingGarbage - } - if let unknownData = unknownData { - message.unknownFields.append(protobufData: unknownData) - } + assert(unknownData == nil) + try incrementRecursionDepth() + try message.decodeMessage(decoder: &self) + decrementRecursionDepth() + guard complete else { + throw BinaryDecodingError.trailingGarbage + } + if let unknownData = unknownData { + message.unknownFields.append(protobufData: unknownData) + } } internal mutating func decodeSingularGroupField(value: inout G?) throws { @@ -947,7 +950,10 @@ internal struct BinaryDecoder: Decoder { return true } - internal mutating func decodeMapField(fieldType: _ProtobufMap.Type, value: inout _ProtobufMap.BaseType) throws { + internal mutating func decodeMapField( + fieldType: _ProtobufMap.Type, + value: inout _ProtobufMap.BaseType + ) throws { guard fieldWireFormat == WireFormat.lengthDelimited else { return } @@ -966,7 +972,7 @@ internal struct BinaryDecoder: Decoder { try KeyType.decodeSingular(value: &k, from: &subdecoder) case 2: try ValueType.decodeSingular(value: &v, from: &subdecoder) - default: // Skip any other fields within the map entry object + default: // Skip any other fields within the map entry object try subdecoder.skip() } } @@ -982,7 +988,10 @@ internal struct BinaryDecoder: Decoder { consumed = true } - internal mutating func decodeMapField(fieldType: _ProtobufEnumMap.Type, value: inout _ProtobufEnumMap.BaseType) throws where ValueType.RawValue == Int { + internal mutating func decodeMapField( + fieldType: _ProtobufEnumMap.Type, + value: inout _ProtobufEnumMap.BaseType + ) throws where ValueType.RawValue == Int { guard fieldWireFormat == WireFormat.lengthDelimited else { return } @@ -997,9 +1006,9 @@ internal struct BinaryDecoder: Decoder { } let fieldNumber = tag.fieldNumber switch fieldNumber { - case 1: // Keys are basic types + case 1: // Keys are basic types try KeyType.decodeSingular(value: &k, from: &subdecoder) - case 2: // Value is an Enum type + case 2: // Value is an Enum type try subdecoder.decodeSingularEnumField(value: &v) if v == nil && tag.wireFormat == .varint { // Enum decode fail and wire format was varint, so this had to @@ -1009,7 +1018,7 @@ internal struct BinaryDecoder: Decoder { // the map entry. return } - default: // Skip any other fields within the map entry object + default: // Skip any other fields within the map entry object try subdecoder.skip() } } @@ -1022,7 +1031,10 @@ internal struct BinaryDecoder: Decoder { consumed = true } - internal mutating func decodeMapField(fieldType: _ProtobufMessageMap.Type, value: inout _ProtobufMessageMap.BaseType) throws { + internal mutating func decodeMapField( + fieldType: _ProtobufMessageMap.Type, + value: inout _ProtobufMessageMap.BaseType + ) throws { guard fieldWireFormat == WireFormat.lengthDelimited else { return } @@ -1037,11 +1049,11 @@ internal struct BinaryDecoder: Decoder { } let fieldNumber = tag.fieldNumber switch fieldNumber { - case 1: // Keys are basic types + case 1: // Keys are basic types try KeyType.decodeSingular(value: &k, from: &subdecoder) - case 2: // Value is a message type + case 2: // Value is a message type try subdecoder.decodeSingularMessageField(value: &v) - default: // Skip any other fields within the map entry object + default: // Skip any other fields within the map entry object try subdecoder.skip() } } @@ -1055,24 +1067,26 @@ internal struct BinaryDecoder: Decoder { } internal mutating func decodeExtensionField( - values: inout ExtensionFieldValueSet, - messageType: any Message.Type, - fieldNumber: Int + values: inout ExtensionFieldValueSet, + messageType: any Message.Type, + fieldNumber: Int ) throws { if let ext = extensions?[messageType, fieldNumber] { - try decodeExtensionField(values: &values, - messageType: messageType, - fieldNumber: fieldNumber, - messageExtension: ext) + try decodeExtensionField( + values: &values, + messageType: messageType, + fieldNumber: fieldNumber, + messageExtension: ext + ) } } /// Helper to reuse between Extension decoding and MessageSet Extension decoding. private mutating func decodeExtensionField( - values: inout ExtensionFieldValueSet, - messageType: any Message.Type, - fieldNumber: Int, - messageExtension ext: any AnyMessageExtension + values: inout ExtensionFieldValueSet, + messageType: any Message.Type, + fieldNumber: Int, + messageExtension ext: any AnyMessageExtension ) throws { assert(!consumed) assert(fieldNumber == ext.fieldNumber) @@ -1096,13 +1110,13 @@ internal struct BinaryDecoder: Decoder { } internal mutating func decodeExtensionFieldsAsMessageSet( - values: inout ExtensionFieldValueSet, - messageType: any Message.Type + values: inout ExtensionFieldValueSet, + messageType: any Message.Type ) throws { // Spin looking for the Item group, everything else will end up in unknown fields. while let fieldNumber = try self.nextFieldNumber() { - guard fieldNumber == WireFormat.MessageSet.FieldNumbers.item && - fieldWireFormat == WireFormat.startGroup else { + guard fieldNumber == WireFormat.MessageSet.FieldNumbers.item && fieldWireFormat == WireFormat.startGroup + else { continue } @@ -1113,19 +1127,21 @@ internal struct BinaryDecoder: Decoder { subDecoder.groupFieldNumber = fieldNumber subDecoder.consumed = true - let itemResult = try subDecoder.decodeMessageSetItem(values: &values, - messageType: messageType) + let itemResult = try subDecoder.decodeMessageSetItem( + values: &values, + messageType: messageType + ) switch itemResult { case .success: - // Advance over what was parsed. - consume(length: available - subDecoder.available) - consumed = true + // Advance over what was parsed. + consume(length: available - subDecoder.available) + consumed = true case .handleAsUnknown: - // Nothing to do. - break + // Nothing to do. + break case .malformed: - throw BinaryDecodingError.malformedProtobuf + throw BinaryDecodingError.malformedProtobuf } assert(recursionBudget == subDecoder.recursionBudget) @@ -1134,21 +1150,21 @@ internal struct BinaryDecoder: Decoder { } private enum DecodeMessageSetItemResult { - case success - case handleAsUnknown - case malformed + case success + case handleAsUnknown + case malformed } private mutating func decodeMessageSetItem( - values: inout ExtensionFieldValueSet, - messageType: any Message.Type + values: inout ExtensionFieldValueSet, + messageType: any Message.Type ) throws -> DecodeMessageSetItemResult { // This is loosely based on the C++: // ExtensionSet::ParseMessageSetItem() // WireFormat::ParseAndMergeMessageSetItem() // (yes, there have two versions that are almost the same) - var msgExtension: (any AnyMessageExtension)? + var msgExtension: (any AnyMessageExtension)? var fieldData: Data? // In this loop, if wire types are wrong, things don't decode, @@ -1170,19 +1186,23 @@ internal struct BinaryDecoder: Decoder { if let data = fieldData { var wasDecoded = false try data.withUnsafeBytes { (body: UnsafeRawBufferPointer) in - if let baseAddress = body.baseAddress, body.count > 0 { - var extDecoder = BinaryDecoder(forReadingFrom: baseAddress, - count: body.count, - parent: self) - // Prime the decode to be correct. - extDecoder.consumed = false - extDecoder.fieldWireFormat = .lengthDelimited - try extDecoder.decodeExtensionField(values: &values, - messageType: messageType, - fieldNumber: ext.fieldNumber, - messageExtension: ext) - wasDecoded = extDecoder.consumed - } + if let baseAddress = body.baseAddress, body.count > 0 { + var extDecoder = BinaryDecoder( + forReadingFrom: baseAddress, + count: body.count, + parent: self + ) + // Prime the decode to be correct. + extDecoder.consumed = false + extDecoder.fieldWireFormat = .lengthDelimited + try extDecoder.decodeExtensionField( + values: &values, + messageType: messageType, + fieldNumber: ext.fieldNumber, + messageExtension: ext + ) + wasDecoded = extDecoder.consumed + } } if !wasDecoded { return .malformed @@ -1193,10 +1213,12 @@ internal struct BinaryDecoder: Decoder { case WireFormat.MessageSet.FieldNumbers.message: if let ext = msgExtension { assert(consumed == false) - try decodeExtensionField(values: &values, - messageType: messageType, - fieldNumber: ext.fieldNumber, - messageExtension: ext) + try decodeExtensionField( + values: &values, + messageType: messageType, + fieldNumber: ext.fieldNumber, + messageExtension: ext + ) if !consumed { return .malformed } @@ -1213,8 +1235,8 @@ internal struct BinaryDecoder: Decoder { let payloadSize = Varint.encodedSize(of: Int64(data.count)) + data.count var payload = Data(count: payloadSize) payload.withUnsafeMutableBytes { (body: UnsafeMutableRawBufferPointer) in - var encoder = BinaryEncoder(forWritingInto: body) - encoder.putBytesValue(value: data) + var encoder = BinaryEncoder(forWritingInto: body) + encoder.putBytesValue(value: data) } fieldData = payload } else { @@ -1394,7 +1416,7 @@ internal struct BinaryDecoder: Decoder { private mutating func decodeLittleEndianInteger() throws -> T { let size = MemoryLayout.size assert(size == 4 || size == 8) - guard available >= size else {throw BinaryDecodingError.truncated} + guard available >= size else { throw BinaryDecodingError.truncated } defer { consume(length: size) } return T(littleEndian: p.loadUnaligned(as: T.self)) } @@ -1423,13 +1445,13 @@ internal struct BinaryDecoder: Decoder { // This function does get called in some package decode handling, but // that is length delimited on the wire, so the spec would imply // the limit still applies. - guard length < 0x7fffffff else { - // Reuse existing error to avoid breaking change of changing thrown error - throw BinaryDecodingError.malformedProtobuf + guard length < 0x7fff_ffff else { + // Reuse existing error to avoid breaking change of changing thrown error + throw BinaryDecodingError.malformedProtobuf } guard length <= UInt64(available) else { - throw BinaryDecodingError.truncated + throw BinaryDecodingError.truncated } count = Int(length) diff --git a/Sources/SwiftProtobuf/BinaryDecodingError.swift b/Sources/SwiftProtobuf/BinaryDecodingError.swift index 016d8ad70..3b19f8303 100644 --- a/Sources/SwiftProtobuf/BinaryDecodingError.swift +++ b/Sources/SwiftProtobuf/BinaryDecodingError.swift @@ -14,31 +14,31 @@ /// Describes errors that can occur when decoding a message from binary format. public enum BinaryDecodingError: Error { - /// Extraneous data remained after decoding should have been complete. - case trailingGarbage + /// Extraneous data remained after decoding should have been complete. + case trailingGarbage - /// The decoder unexpectedly reached the end of the data before it was - /// expected. - case truncated + /// The decoder unexpectedly reached the end of the data before it was + /// expected. + case truncated - /// A string field was not encoded as valid UTF-8. - case invalidUTF8 + /// A string field was not encoded as valid UTF-8. + case invalidUTF8 - /// The binary data was malformed in some way, such as an invalid wire format - /// or field tag. - case malformedProtobuf + /// The binary data was malformed in some way, such as an invalid wire format + /// or field tag. + case malformedProtobuf - /// The definition of the message or one of its nested messages has required - /// fields but the binary data did not include values for them. You must pass - /// `partial: true` during decoding if you wish to explicitly ignore missing - /// required fields. - case missingRequiredFields + /// The definition of the message or one of its nested messages has required + /// fields but the binary data did not include values for them. You must pass + /// `partial: true` during decoding if you wish to explicitly ignore missing + /// required fields. + case missingRequiredFields - /// An internal error happened while decoding. If this is ever encountered, - /// please file an issue with SwiftProtobuf with as much details as possible - /// for what happened (proto definitions, bytes being decoded (if possible)). - case internalExtensionError + /// An internal error happened while decoding. If this is ever encountered, + /// please file an issue with SwiftProtobuf with as much details as possible + /// for what happened (proto definitions, bytes being decoded (if possible)). + case internalExtensionError - /// Reached the nesting limit for messages within messages while decoding. - case messageDepthLimit + /// Reached the nesting limit for messages within messages while decoding. + case messageDepthLimit } diff --git a/Sources/SwiftProtobuf/BinaryDecodingOptions.swift b/Sources/SwiftProtobuf/BinaryDecodingOptions.swift index caa4cffea..d78f9080c 100644 --- a/Sources/SwiftProtobuf/BinaryDecodingOptions.swift +++ b/Sources/SwiftProtobuf/BinaryDecodingOptions.swift @@ -14,26 +14,26 @@ /// Options for binary decoding. public struct BinaryDecodingOptions: Sendable { - /// The maximum nesting of message with messages. The default is 100. - /// - /// To prevent corrupt or malicious messages from causing stack overflows, - /// this controls how deep messages can be nested within other messages - /// while parsing. - public var messageDepthLimit: Int = 100 + /// The maximum nesting of message with messages. The default is 100. + /// + /// To prevent corrupt or malicious messages from causing stack overflows, + /// this controls how deep messages can be nested within other messages + /// while parsing. + public var messageDepthLimit: Int = 100 - /// Discard unknown fields while parsing. The default is false, so parsering - /// does not discard unknown fields. - /// - /// The Protobuf binary format allows unknown fields to be still parsed - /// so the schema can be expanded without requiring all readers to be updated. - /// This works in part by having any unknown fields preserved so they can - /// be relayed on without loss. For a while the proto3 syntax definition - /// called for unknown fields to be dropped, but that lead to problems in - /// some case. The default is to follow the spec and keep them, but setting - /// this option to `true` allows a developer to strip them during a parse - /// in case they have a specific need to drop the unknown fields from the - /// object graph being created. - public var discardUnknownFields: Bool = false + /// Discard unknown fields while parsing. The default is false, so parsering + /// does not discard unknown fields. + /// + /// The Protobuf binary format allows unknown fields to be still parsed + /// so the schema can be expanded without requiring all readers to be updated. + /// This works in part by having any unknown fields preserved so they can + /// be relayed on without loss. For a while the proto3 syntax definition + /// called for unknown fields to be dropped, but that lead to problems in + /// some case. The default is to follow the spec and keep them, but setting + /// this option to `true` allows a developer to strip them during a parse + /// in case they have a specific need to drop the unknown fields from the + /// object graph being created. + public var discardUnknownFields: Bool = false - public init() {} + public init() {} } diff --git a/Sources/SwiftProtobuf/BinaryDelimited.swift b/Sources/SwiftProtobuf/BinaryDelimited.swift index 5b4ea9ebd..2b46c40cb 100644 --- a/Sources/SwiftProtobuf/BinaryDelimited.swift +++ b/Sources/SwiftProtobuf/BinaryDelimited.swift @@ -18,194 +18,198 @@ import Foundation /// Helper methods for reading/writing messages with a length prefix. public enum BinaryDelimited { - /// Additional errors for delimited message handing. - public enum Error: Swift.Error { - /// If a read/write to the stream fails, but the stream's `streamError` is nil, - /// this error will be throw instead since the stream didn't provide anything - /// more specific. A common cause for this can be failing to open the stream - /// before trying to read/write to it. - case unknownStreamError + /// Additional errors for delimited message handing. + public enum Error: Swift.Error { + /// If a read/write to the stream fails, but the stream's `streamError` is nil, + /// this error will be throw instead since the stream didn't provide anything + /// more specific. A common cause for this can be failing to open the stream + /// before trying to read/write to it. + case unknownStreamError - /// While reading/writing to the stream, less than the expected bytes was - /// read/written. - case truncated - } - -#if !os(WASI) - /// Serialize a single size-delimited message to the given stream. Delimited - /// format allows a single file or stream to contain multiple messages, - /// whereas normally writing multiple non-delimited messages to the same - /// stream would cause them to be merged. A delimited message is a varint - /// encoding the message size followed by a message of exactly that size. - /// - /// - Parameters: - /// - message: The message to be written. - /// - to: The `OutputStream` to write the message to. The stream is - /// is assumed to be ready to be written to. - /// - partial: If `false` (the default), this method will check - /// ``Message/isInitialized-6abgi`` before encoding to verify that all required - /// fields are present. If any are missing, this method throws - /// ``BinaryDelimited/Error/missingRequiredFields``. - /// - Throws: ``BinaryDelimited/Error`` if encoding fails or some writing errors occur; or the - /// underlying `OutputStream.streamError` for a stream error. - public static func serialize( - message: any Message, - to stream: OutputStream, - partial: Bool = false - ) throws { - // TODO: Revisit to avoid the extra buffering when encoding is streamed in general. - let serialized: [UInt8] = try message.serializedBytes(partial: partial) - let totalSize = Varint.encodedSize(of: UInt64(serialized.count)) + serialized.count - var bytes: [UInt8] = Array(repeating: 0, count: totalSize) - bytes.withUnsafeMutableBytes { (body: UnsafeMutableRawBufferPointer) in - var encoder = BinaryEncoder(forWritingInto: body) - encoder.putBytesValue(value: serialized) - } - - var written: Int = 0 - bytes.withUnsafeBytes { (body: UnsafeRawBufferPointer) in - if let baseAddress = body.baseAddress, body.count > 0 { - // This assumingMemoryBound is technically unsafe, but without SR-11078 - // (https://bugs.swift.org/browse/SR-11087) we don't have another option. - // It should be "safe enough". - let pointer = baseAddress.assumingMemoryBound(to: UInt8.self) - written = stream.write(pointer, maxLength: totalSize) - } + /// While reading/writing to the stream, less than the expected bytes was + /// read/written. + case truncated } - if written != totalSize { - if written == -1 { - if let streamError = stream.streamError { - throw streamError + #if !os(WASI) + /// Serialize a single size-delimited message to the given stream. Delimited + /// format allows a single file or stream to contain multiple messages, + /// whereas normally writing multiple non-delimited messages to the same + /// stream would cause them to be merged. A delimited message is a varint + /// encoding the message size followed by a message of exactly that size. + /// + /// - Parameters: + /// - message: The message to be written. + /// - to: The `OutputStream` to write the message to. The stream is + /// is assumed to be ready to be written to. + /// - partial: If `false` (the default), this method will check + /// ``Message/isInitialized-6abgi`` before encoding to verify that all required + /// fields are present. If any are missing, this method throws + /// ``BinaryDelimited/Error/missingRequiredFields``. + /// - Throws: ``BinaryDelimited/Error`` if encoding fails or some writing errors occur; or the + /// underlying `OutputStream.streamError` for a stream error. + public static func serialize( + message: any Message, + to stream: OutputStream, + partial: Bool = false + ) throws { + // TODO: Revisit to avoid the extra buffering when encoding is streamed in general. + let serialized: [UInt8] = try message.serializedBytes(partial: partial) + let totalSize = Varint.encodedSize(of: UInt64(serialized.count)) + serialized.count + var bytes: [UInt8] = Array(repeating: 0, count: totalSize) + bytes.withUnsafeMutableBytes { (body: UnsafeMutableRawBufferPointer) in + var encoder = BinaryEncoder(forWritingInto: body) + encoder.putBytesValue(value: serialized) } - throw BinaryDelimited.Error.unknownStreamError - } - throw BinaryDelimited.Error.truncated - } - } - /// Reads a single size-delimited message from the given stream. Delimited - /// format allows a single file or stream to contain multiple messages, - /// whereas normally parsing consumes the entire input. A delimited message - /// is a varint encoding the message size followed by a message of exactly - /// exactly that size. - /// - /// - Parameters: - /// - messageType: The type of message to read. - /// - from: The `InputStream` to read the data from. The stream is assumed - /// to be ready to read from. - /// - extensions: An ``ExtensionMap`` used to look up and decode any - /// extensions in this message or messages nested within this message's - /// fields. - /// - partial: If `false` (the default), this method will check - /// ``Message/isInitialized-6abgi`` after decoding to verify that all required - /// fields are present. If any are missing, this method throws - /// ``BinaryDecodingError/missingRequiredFields``. - /// - options: The ``BinaryDecodingOptions`` to use. - /// - Returns: The message read. - /// - Throws: ``BinaryDelimited/Error`` or ``SwiftProtobufError`` if decoding fails, - /// some reading errors; or the underlying `InputStream.streamError` for a stream error. - public static func parse( - messageType: M.Type, - from stream: InputStream, - extensions: (any ExtensionMap)? = nil, - partial: Bool = false, - options: BinaryDecodingOptions = BinaryDecodingOptions() - ) throws -> M { - var message = M() - try merge(into: &message, - from: stream, - extensions: extensions, - partial: partial, - options: options) - return message - } + var written: Int = 0 + bytes.withUnsafeBytes { (body: UnsafeRawBufferPointer) in + if let baseAddress = body.baseAddress, body.count > 0 { + // This assumingMemoryBound is technically unsafe, but without SR-11078 + // (https://bugs.swift.org/browse/SR-11087) we don't have another option. + // It should be "safe enough". + let pointer = baseAddress.assumingMemoryBound(to: UInt8.self) + written = stream.write(pointer, maxLength: totalSize) + } + } - /// Updates the message by reading a single size-delimited message from - /// the given stream. Delimited format allows a single file or stream to - /// contain multiple messages, whereas normally parsing consumes the entire - /// input. A delimited message is a varint encoding the message size - /// followed by a message of exactly that size. - /// - /// - Note: If this method throws an error, the message may still have been - /// partially mutated by the binary data that was decoded before the error - /// occurred. - /// - /// - Parameters: - /// - mergingTo: The message to merge the data into. - /// - from: The `InputStream` to read the data from. The stream is assumed - /// to be ready to read from. - /// - extensions: An ``ExtensionMap`` used to look up and decode any - /// extensions in this message or messages nested within this message's - /// fields. - /// - partial: If `false` (the default), this method will check - /// ``Message/isInitialized-6abgi`` after decoding to verify that all required - /// fields are present. If any are missing, this method throws - /// ``BinaryDelimited/Error/missingRequiredFields``. - /// - options: The BinaryDecodingOptions to use. - /// - Throws: ``BinaryDelimited/Error`` or ``SwiftProtobufError`` if decoding fails, - /// and for some reading errors; or the underlying `InputStream.streamError` for a stream error. - public static func merge( - into message: inout M, - from stream: InputStream, - extensions: (any ExtensionMap)? = nil, - partial: Bool = false, - options: BinaryDecodingOptions = BinaryDecodingOptions() - ) throws { - let unsignedLength = try decodeVarint(stream) - if unsignedLength == 0 { - // The message was all defaults, nothing to actually read. - return - } - guard unsignedLength <= 0x7fffffff else { - // Adding a new case is a breaking change, reuse malformedProtobuf. - throw BinaryDecodingError.malformedProtobuf + if written != totalSize { + if written == -1 { + if let streamError = stream.streamError { + throw streamError + } + throw BinaryDelimited.Error.unknownStreamError + } + throw BinaryDelimited.Error.truncated + } } - let length = Int(unsignedLength) - // TODO: Consider doing a version with getBuffer:length: if the InputStream - // support it and thus avoiding this local copy. + /// Reads a single size-delimited message from the given stream. Delimited + /// format allows a single file or stream to contain multiple messages, + /// whereas normally parsing consumes the entire input. A delimited message + /// is a varint encoding the message size followed by a message of exactly + /// exactly that size. + /// + /// - Parameters: + /// - messageType: The type of message to read. + /// - from: The `InputStream` to read the data from. The stream is assumed + /// to be ready to read from. + /// - extensions: An ``ExtensionMap`` used to look up and decode any + /// extensions in this message or messages nested within this message's + /// fields. + /// - partial: If `false` (the default), this method will check + /// ``Message/isInitialized-6abgi`` after decoding to verify that all required + /// fields are present. If any are missing, this method throws + /// ``BinaryDecodingError/missingRequiredFields``. + /// - options: The ``BinaryDecodingOptions`` to use. + /// - Returns: The message read. + /// - Throws: ``BinaryDelimited/Error`` or ``SwiftProtobufError`` if decoding fails, + /// some reading errors; or the underlying `InputStream.streamError` for a stream error. + public static func parse( + messageType: M.Type, + from stream: InputStream, + extensions: (any ExtensionMap)? = nil, + partial: Bool = false, + options: BinaryDecodingOptions = BinaryDecodingOptions() + ) throws -> M { + var message = M() + try merge( + into: &message, + from: stream, + extensions: extensions, + partial: partial, + options: options + ) + return message + } - // Even though the bytes are read in chunks, things can still hard fail if - // there isn't enough memory to append to have all the bytes at once for - // parsing. - var data = [UInt8]() - let kChunkSize = 16 * 1024 * 1024 - var chunk = [UInt8](repeating: 0, count: min(length, kChunkSize)) - var bytesNeeded = length - while bytesNeeded > 0 { - let maxLength = min(bytesNeeded, chunk.count) - var bytesRead: Int = 0 - chunk.withUnsafeMutableBytes { (body: UnsafeMutableRawBufferPointer) in - if let baseAddress = body.baseAddress, body.count > 0 { - let pointer = baseAddress.assumingMemoryBound(to: UInt8.self) - bytesRead = stream.read(pointer, maxLength: maxLength) + /// Updates the message by reading a single size-delimited message from + /// the given stream. Delimited format allows a single file or stream to + /// contain multiple messages, whereas normally parsing consumes the entire + /// input. A delimited message is a varint encoding the message size + /// followed by a message of exactly that size. + /// + /// - Note: If this method throws an error, the message may still have been + /// partially mutated by the binary data that was decoded before the error + /// occurred. + /// + /// - Parameters: + /// - mergingTo: The message to merge the data into. + /// - from: The `InputStream` to read the data from. The stream is assumed + /// to be ready to read from. + /// - extensions: An ``ExtensionMap`` used to look up and decode any + /// extensions in this message or messages nested within this message's + /// fields. + /// - partial: If `false` (the default), this method will check + /// ``Message/isInitialized-6abgi`` after decoding to verify that all required + /// fields are present. If any are missing, this method throws + /// ``BinaryDelimited/Error/missingRequiredFields``. + /// - options: The BinaryDecodingOptions to use. + /// - Throws: ``BinaryDelimited/Error`` or ``SwiftProtobufError`` if decoding fails, + /// and for some reading errors; or the underlying `InputStream.streamError` for a stream error. + public static func merge( + into message: inout M, + from stream: InputStream, + extensions: (any ExtensionMap)? = nil, + partial: Bool = false, + options: BinaryDecodingOptions = BinaryDecodingOptions() + ) throws { + let unsignedLength = try decodeVarint(stream) + if unsignedLength == 0 { + // The message was all defaults, nothing to actually read. + return } - } - if bytesRead == -1 { - if let streamError = stream.streamError { - throw streamError + guard unsignedLength <= 0x7fff_ffff else { + // Adding a new case is a breaking change, reuse malformedProtobuf. + throw BinaryDecodingError.malformedProtobuf } - throw BinaryDelimited.Error.unknownStreamError - } - if bytesRead == 0 { - // Hit the end of the stream - throw BinaryDelimited.Error.truncated - } - if bytesRead < chunk.count { - data += chunk[0.. 0 { + let maxLength = min(bytesNeeded, chunk.count) + var bytesRead: Int = 0 + chunk.withUnsafeMutableBytes { (body: UnsafeMutableRawBufferPointer) in + if let baseAddress = body.baseAddress, body.count > 0 { + let pointer = baseAddress.assumingMemoryBound(to: UInt8.self) + bytesRead = stream.read(pointer, maxLength: maxLength) + } + } + if bytesRead == -1 { + if let streamError = stream.streamError { + throw streamError + } + throw BinaryDelimited.Error.unknownStreamError + } + if bytesRead == 0 { + // Hit the end of the stream + throw BinaryDelimited.Error.truncated + } + if bytesRead < chunk.count { + data += chunk[0.. UInt64 { - // Buffer to reuse within nextByte. - let readBuffer = UnsafeMutablePointer.allocate(capacity: 1) - defer { readBuffer.deallocate() } + // Buffer to reuse within nextByte. + let readBuffer = UnsafeMutablePointer.allocate(capacity: 1) + defer { readBuffer.deallocate() } - func nextByte() throws -> UInt8? { - let bytesRead = stream.read(readBuffer, maxLength: 1) - switch bytesRead { - case 1: - return readBuffer[0] - case 0: - return nil - default: - precondition(bytesRead == -1) - if let streamError = stream.streamError { - throw streamError - } - throw BinaryDelimited.Error.unknownStreamError + func nextByte() throws -> UInt8? { + let bytesRead = stream.read(readBuffer, maxLength: 1) + switch bytesRead { + case 1: + return readBuffer[0] + case 0: + return nil + default: + precondition(bytesRead == -1) + if let streamError = stream.streamError { + throw streamError + } + throw BinaryDelimited.Error.unknownStreamError + } } - } - var value: UInt64 = 0 - var shift: UInt64 = 0 - while true { - guard let c = try nextByte() else { - if shift == 0 { - throw SwiftProtobufError.BinaryStreamDecoding.noBytesAvailable() - } - throw BinaryDelimited.Error.truncated - } - value |= UInt64(c & 0x7f) << shift - if c & 0x80 == 0 { - return value - } - shift += 7 - if shift > 63 { - throw BinaryDecodingError.malformedProtobuf + var value: UInt64 = 0 + var shift: UInt64 = 0 + while true { + guard let c = try nextByte() else { + if shift == 0 { + throw SwiftProtobufError.BinaryStreamDecoding.noBytesAvailable() + } + throw BinaryDelimited.Error.truncated + } + value |= UInt64(c & 0x7f) << shift + if c & 0x80 == 0 { + return value + } + shift += 7 + if shift > 63 { + throw BinaryDecodingError.malformedProtobuf + } } - } } #endif // !os(WASI) diff --git a/Sources/SwiftProtobuf/BinaryEncoder.swift b/Sources/SwiftProtobuf/BinaryEncoder.swift index 4f0ba310d..40c0703da 100644 --- a/Sources/SwiftProtobuf/BinaryEncoder.swift +++ b/Sources/SwiftProtobuf/BinaryEncoder.swift @@ -40,12 +40,14 @@ internal struct BinaryEncoder { } internal var used: Int { - return buffer.baseAddress!.distance(to: pointer) + buffer.baseAddress!.distance(to: pointer) } internal var remainder: UnsafeMutableRawBufferPointer { - return UnsafeMutableRawBufferPointer(start: pointer, - count: buffer.count - used) + UnsafeMutableRawBufferPointer( + start: pointer, + count: buffer.count - used + ) } internal mutating func advance(_ bytes: Int) { diff --git a/Sources/SwiftProtobuf/BinaryEncodingError.swift b/Sources/SwiftProtobuf/BinaryEncodingError.swift index 9dd433018..68f1eb62b 100644 --- a/Sources/SwiftProtobuf/BinaryEncodingError.swift +++ b/Sources/SwiftProtobuf/BinaryEncodingError.swift @@ -14,11 +14,11 @@ /// Describes errors that can occur when decoding a message from binary format. public enum BinaryEncodingError: Error, Hashable { - /// An unexpected failure when deserializing a `Google_Protobuf_Any`. - case anyTranscodeFailure - /// The definition of the message or one of its nested messages has required - /// fields but the message being encoded did not include values for them. You - /// must pass `partial: true` during encoding if you wish to explicitly ignore - /// missing required fields. - case missingRequiredFields + /// An unexpected failure when deserializing a `Google_Protobuf_Any`. + case anyTranscodeFailure + /// The definition of the message or one of its nested messages has required + /// fields but the message being encoded did not include values for them. You + /// must pass `partial: true` during encoding if you wish to explicitly ignore + /// missing required fields. + case missingRequiredFields } diff --git a/Sources/SwiftProtobuf/BinaryEncodingSizeVisitor.swift b/Sources/SwiftProtobuf/BinaryEncodingSizeVisitor.swift index 2db485d27..e3e8714fb 100644 --- a/Sources/SwiftProtobuf/BinaryEncodingSizeVisitor.swift +++ b/Sources/SwiftProtobuf/BinaryEncodingSizeVisitor.swift @@ -20,454 +20,486 @@ import Foundation /// serialization. internal struct BinaryEncodingSizeVisitor: Visitor { - /// Accumulates the required size of the message during traversal. - var serializedSize: Int = 0 - - init() {} - - mutating func visitUnknown(bytes: Data) throws { - serializedSize += bytes.count - } - - mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws { - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed32).encodedSize - serializedSize += tagSize + MemoryLayout.size - } - - mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws { - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed64).encodedSize - serializedSize += tagSize + MemoryLayout.size - } - - mutating func visitSingularInt32Field(value: Int32, fieldNumber: Int) throws { - try visitSingularInt64Field(value: Int64(value), fieldNumber: fieldNumber) - } - - mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws { - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize - serializedSize += tagSize + Varint.encodedSize(of: value) - } - - mutating func visitSingularUInt32Field(value: UInt32, fieldNumber: Int) throws { - try visitSingularUInt64Field(value: UInt64(value), fieldNumber: fieldNumber) - } - - mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws { - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize - serializedSize += tagSize + Varint.encodedSize(of: value) - } - - mutating func visitSingularSInt32Field(value: Int32, fieldNumber: Int) throws { - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize - serializedSize += tagSize + Varint.encodedSize(of: ZigZag.encoded(value)) - } - - mutating func visitSingularSInt64Field(value: Int64, fieldNumber: Int) throws { - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize - serializedSize += tagSize + Varint.encodedSize(of: ZigZag.encoded(value)) - } - - mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws { - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed32).encodedSize - serializedSize += tagSize + MemoryLayout.size - } - - mutating func visitSingularFixed64Field(value: UInt64, fieldNumber: Int) throws { - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed64).encodedSize - serializedSize += tagSize + MemoryLayout.size - } - - mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws { - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed32).encodedSize - serializedSize += tagSize + MemoryLayout.size - } - - mutating func visitSingularSFixed64Field(value: Int64, fieldNumber: Int) throws { - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed64).encodedSize - serializedSize += tagSize + MemoryLayout.size - } - - mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws { - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize - serializedSize += tagSize + 1 - } - - mutating func visitSingularStringField(value: String, fieldNumber: Int) throws { - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize - let count = value.utf8.count - serializedSize += tagSize + Varint.encodedSize(of: Int64(count)) + count - } - - mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws { - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize - let count = value.count - serializedSize += tagSize + Varint.encodedSize(of: Int64(count)) + count - } - - // The default impls for visitRepeated*Field would work, but by implementing - // these directly, the calculation for the tag overhead can be optimized and - // the fixed width fields can be simple multiplication. - - mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws { - assert(!value.isEmpty) - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed32).encodedSize - serializedSize += tagSize * value.count + MemoryLayout.size * value.count - } - - mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws { - assert(!value.isEmpty) - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed64).encodedSize - serializedSize += tagSize * value.count + MemoryLayout.size * value.count - } - - mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws { - assert(!value.isEmpty) - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize - let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) } - serializedSize += tagSize * value.count + dataSize - } - - mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws { - assert(!value.isEmpty) - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize - let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) } - serializedSize += tagSize * value.count + dataSize - } - - mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws { - assert(!value.isEmpty) - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize - let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) } - serializedSize += tagSize * value.count + dataSize - } - - mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws { - assert(!value.isEmpty) - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize - let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) } - serializedSize += tagSize * value.count + dataSize - } - - mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws { - assert(!value.isEmpty) - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize - let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: ZigZag.encoded($1)) } - serializedSize += tagSize * value.count + dataSize - } - - mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws { - assert(!value.isEmpty) - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize - let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: ZigZag.encoded($1)) } - serializedSize += tagSize * value.count + dataSize - } - - mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws { - assert(!value.isEmpty) - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed32).encodedSize - serializedSize += tagSize * value.count + MemoryLayout.size * value.count - } - - mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws { - assert(!value.isEmpty) - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed64).encodedSize - serializedSize += tagSize * value.count + MemoryLayout.size * value.count - } - - mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws { - assert(!value.isEmpty) - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed32).encodedSize - serializedSize += tagSize * value.count + MemoryLayout.size * value.count - } - - mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws { - assert(!value.isEmpty) - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed64).encodedSize - serializedSize += tagSize * value.count + MemoryLayout.size * value.count - } - - mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws { - assert(!value.isEmpty) - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize - serializedSize += tagSize * value.count + 1 * value.count - } - - mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws { - assert(!value.isEmpty) - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize - let dataSize = value.reduce(0) { - let count = $1.utf8.count - return $0 + Varint.encodedSize(of: Int64(count)) + count - } - serializedSize += tagSize * value.count + dataSize - } - - mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws { - assert(!value.isEmpty) - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize - let dataSize = value.reduce(0) { - let count = $1.count - return $0 + Varint.encodedSize(of: Int64(count)) + count - } - serializedSize += tagSize * value.count + dataSize - } - - // Packed field handling. - - mutating func visitPackedFloatField(value: [Float], fieldNumber: Int) throws { - assert(!value.isEmpty) - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize - let dataSize = value.count * MemoryLayout.size - serializedSize += tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize - } - - mutating func visitPackedDoubleField(value: [Double], fieldNumber: Int) throws { - assert(!value.isEmpty) - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize - let dataSize = value.count * MemoryLayout.size - serializedSize += tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize - } - - mutating func visitPackedInt32Field(value: [Int32], fieldNumber: Int) throws { - assert(!value.isEmpty) - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize - let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) } - serializedSize += - tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize - } - - mutating func visitPackedInt64Field(value: [Int64], fieldNumber: Int) throws { - assert(!value.isEmpty) - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize - let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) } - serializedSize += - tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize - } - - mutating func visitPackedSInt32Field(value: [Int32], fieldNumber: Int) throws { - assert(!value.isEmpty) - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize - let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: ZigZag.encoded($1)) } - serializedSize += - tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize - } - - mutating func visitPackedSInt64Field(value: [Int64], fieldNumber: Int) throws { - assert(!value.isEmpty) - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize - let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: ZigZag.encoded($1)) } - serializedSize += - tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize - } - - mutating func visitPackedUInt32Field(value: [UInt32], fieldNumber: Int) throws { - assert(!value.isEmpty) - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize - let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) } - serializedSize += - tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize - } - - mutating func visitPackedUInt64Field(value: [UInt64], fieldNumber: Int) throws { - assert(!value.isEmpty) - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize - let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) } - serializedSize += - tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize - } - - mutating func visitPackedFixed32Field(value: [UInt32], fieldNumber: Int) throws { - assert(!value.isEmpty) - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize - let dataSize = value.count * MemoryLayout.size - serializedSize += tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize - } - - mutating func visitPackedFixed64Field(value: [UInt64], fieldNumber: Int) throws { - assert(!value.isEmpty) - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize - let dataSize = value.count * MemoryLayout.size - serializedSize += tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize - } - - mutating func visitPackedSFixed32Field(value: [Int32], fieldNumber: Int) throws { - assert(!value.isEmpty) - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize - let dataSize = value.count * MemoryLayout.size - serializedSize += tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize - } - - mutating func visitPackedSFixed64Field(value: [Int64], fieldNumber: Int) throws { - assert(!value.isEmpty) - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize - let dataSize = value.count * MemoryLayout.size - serializedSize += tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize - } - - mutating func visitPackedBoolField(value: [Bool], fieldNumber: Int) throws { - assert(!value.isEmpty) - let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize - let dataSize = value.count - serializedSize += tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize - } - - mutating func visitSingularEnumField(value: E, - fieldNumber: Int) throws { - let tagSize = FieldTag(fieldNumber: fieldNumber, - wireFormat: .varint).encodedSize - serializedSize += tagSize - let dataSize = Varint.encodedSize(of: Int32(truncatingIfNeeded: value.rawValue)) - serializedSize += dataSize - } - - mutating func visitRepeatedEnumField(value: [E], - fieldNumber: Int) throws { - assert(!value.isEmpty) - let tagSize = FieldTag(fieldNumber: fieldNumber, - wireFormat: .varint).encodedSize - serializedSize += value.count * tagSize - let dataSize = value.reduce(0) { - $0 + Varint.encodedSize(of: Int32(truncatingIfNeeded: $1.rawValue)) - } - serializedSize += dataSize - } - - mutating func visitPackedEnumField(value: [E], - fieldNumber: Int) throws { - assert(!value.isEmpty) - let tagSize = FieldTag(fieldNumber: fieldNumber, - wireFormat: .varint).encodedSize - serializedSize += tagSize - let dataSize = value.reduce(0) { - $0 + Varint.encodedSize(of: Int32(truncatingIfNeeded: $1.rawValue)) - } - serializedSize += Varint.encodedSize(of: Int64(dataSize)) + dataSize - } - - mutating func visitSingularMessageField(value: M, - fieldNumber: Int) throws { - let tagSize = FieldTag(fieldNumber: fieldNumber, - wireFormat: .lengthDelimited).encodedSize - let messageSize = try value.serializedDataSize() - serializedSize += - tagSize + Varint.encodedSize(of: UInt64(messageSize)) + messageSize - } - - mutating func visitRepeatedMessageField(value: [M], - fieldNumber: Int) throws { - assert(!value.isEmpty) - let tagSize = FieldTag(fieldNumber: fieldNumber, - wireFormat: .lengthDelimited).encodedSize - serializedSize += value.count * tagSize - let dataSize = try value.reduce(0) { - let messageSize = try $1.serializedDataSize() - return $0 + Varint.encodedSize(of: UInt64(messageSize)) + messageSize - } - serializedSize += dataSize - } - - mutating func visitSingularGroupField(value: G, fieldNumber: Int) throws { - // The wire format doesn't matter here because the encoded size of the - // integer won't change based on the low three bits. - let tagSize = FieldTag(fieldNumber: fieldNumber, - wireFormat: .startGroup).encodedSize - serializedSize += 2 * tagSize - try value.traverse(visitor: &self) - } - - mutating func visitRepeatedGroupField(value: [G], - fieldNumber: Int) throws { - assert(!value.isEmpty) - let tagSize = FieldTag(fieldNumber: fieldNumber, - wireFormat: .startGroup).encodedSize - serializedSize += 2 * value.count * tagSize - for v in value { - try v.traverse(visitor: &self) - } - } - - mutating func visitMapField( - fieldType: _ProtobufMap.Type, - value: _ProtobufMap.BaseType, - fieldNumber: Int - ) throws { - let tagSize = FieldTag(fieldNumber: fieldNumber, - wireFormat: .lengthDelimited).encodedSize - for (k,v) in value { - var sizer = BinaryEncodingSizeVisitor() - try KeyType.visitSingular(value: k, fieldNumber: 1, with: &sizer) - try ValueType.visitSingular(value: v, fieldNumber: 2, with: &sizer) - let entrySize = sizer.serializedSize - serializedSize += Varint.encodedSize(of: Int64(entrySize)) + entrySize - } - serializedSize += value.count * tagSize - } - - mutating func visitMapField( - fieldType: _ProtobufEnumMap.Type, - value: _ProtobufEnumMap.BaseType, - fieldNumber: Int - ) throws where ValueType.RawValue == Int { - let tagSize = FieldTag(fieldNumber: fieldNumber, - wireFormat: .lengthDelimited).encodedSize - for (k,v) in value { - var sizer = BinaryEncodingSizeVisitor() - try KeyType.visitSingular(value: k, fieldNumber: 1, with: &sizer) - try sizer.visitSingularEnumField(value: v, fieldNumber: 2) - let entrySize = sizer.serializedSize - serializedSize += Varint.encodedSize(of: Int64(entrySize)) + entrySize - } - serializedSize += value.count * tagSize - } - - mutating func visitMapField( - fieldType: _ProtobufMessageMap.Type, - value: _ProtobufMessageMap.BaseType, - fieldNumber: Int - ) throws { - let tagSize = FieldTag(fieldNumber: fieldNumber, - wireFormat: .lengthDelimited).encodedSize - for (k,v) in value { - var sizer = BinaryEncodingSizeVisitor() - try KeyType.visitSingular(value: k, fieldNumber: 1, with: &sizer) - try sizer.visitSingularMessageField(value: v, fieldNumber: 2) - let entrySize = sizer.serializedSize - serializedSize += Varint.encodedSize(of: Int64(entrySize)) + entrySize - } - serializedSize += value.count * tagSize - } - - mutating func visitExtensionFieldsAsMessageSet( - fields: ExtensionFieldValueSet, - start: Int, - end: Int - ) throws { - var sizer = BinaryEncodingMessageSetSizeVisitor() - try fields.traverse(visitor: &sizer, start: start, end: end) - serializedSize += sizer.serializedSize - } + /// Accumulates the required size of the message during traversal. + var serializedSize: Int = 0 + + init() {} + + mutating func visitUnknown(bytes: Data) throws { + serializedSize += bytes.count + } + + mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws { + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed32).encodedSize + serializedSize += tagSize + MemoryLayout.size + } + + mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws { + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed64).encodedSize + serializedSize += tagSize + MemoryLayout.size + } + + mutating func visitSingularInt32Field(value: Int32, fieldNumber: Int) throws { + try visitSingularInt64Field(value: Int64(value), fieldNumber: fieldNumber) + } + + mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws { + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize + serializedSize += tagSize + Varint.encodedSize(of: value) + } + + mutating func visitSingularUInt32Field(value: UInt32, fieldNumber: Int) throws { + try visitSingularUInt64Field(value: UInt64(value), fieldNumber: fieldNumber) + } + + mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws { + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize + serializedSize += tagSize + Varint.encodedSize(of: value) + } + + mutating func visitSingularSInt32Field(value: Int32, fieldNumber: Int) throws { + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize + serializedSize += tagSize + Varint.encodedSize(of: ZigZag.encoded(value)) + } + + mutating func visitSingularSInt64Field(value: Int64, fieldNumber: Int) throws { + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize + serializedSize += tagSize + Varint.encodedSize(of: ZigZag.encoded(value)) + } + + mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws { + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed32).encodedSize + serializedSize += tagSize + MemoryLayout.size + } + + mutating func visitSingularFixed64Field(value: UInt64, fieldNumber: Int) throws { + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed64).encodedSize + serializedSize += tagSize + MemoryLayout.size + } + + mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws { + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed32).encodedSize + serializedSize += tagSize + MemoryLayout.size + } + + mutating func visitSingularSFixed64Field(value: Int64, fieldNumber: Int) throws { + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed64).encodedSize + serializedSize += tagSize + MemoryLayout.size + } + + mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws { + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize + serializedSize += tagSize + 1 + } + + mutating func visitSingularStringField(value: String, fieldNumber: Int) throws { + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize + let count = value.utf8.count + serializedSize += tagSize + Varint.encodedSize(of: Int64(count)) + count + } + + mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws { + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize + let count = value.count + serializedSize += tagSize + Varint.encodedSize(of: Int64(count)) + count + } + + // The default impls for visitRepeated*Field would work, but by implementing + // these directly, the calculation for the tag overhead can be optimized and + // the fixed width fields can be simple multiplication. + + mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws { + assert(!value.isEmpty) + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed32).encodedSize + serializedSize += tagSize * value.count + MemoryLayout.size * value.count + } + + mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws { + assert(!value.isEmpty) + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed64).encodedSize + serializedSize += tagSize * value.count + MemoryLayout.size * value.count + } + + mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws { + assert(!value.isEmpty) + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize + let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) } + serializedSize += tagSize * value.count + dataSize + } + + mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws { + assert(!value.isEmpty) + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize + let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) } + serializedSize += tagSize * value.count + dataSize + } + + mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws { + assert(!value.isEmpty) + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize + let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) } + serializedSize += tagSize * value.count + dataSize + } + + mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws { + assert(!value.isEmpty) + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize + let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) } + serializedSize += tagSize * value.count + dataSize + } + + mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws { + assert(!value.isEmpty) + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize + let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: ZigZag.encoded($1)) } + serializedSize += tagSize * value.count + dataSize + } + + mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws { + assert(!value.isEmpty) + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize + let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: ZigZag.encoded($1)) } + serializedSize += tagSize * value.count + dataSize + } + + mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws { + assert(!value.isEmpty) + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed32).encodedSize + serializedSize += tagSize * value.count + MemoryLayout.size * value.count + } + + mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws { + assert(!value.isEmpty) + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed64).encodedSize + serializedSize += tagSize * value.count + MemoryLayout.size * value.count + } + + mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws { + assert(!value.isEmpty) + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed32).encodedSize + serializedSize += tagSize * value.count + MemoryLayout.size * value.count + } + + mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws { + assert(!value.isEmpty) + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .fixed64).encodedSize + serializedSize += tagSize * value.count + MemoryLayout.size * value.count + } + + mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws { + assert(!value.isEmpty) + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint).encodedSize + serializedSize += tagSize * value.count + 1 * value.count + } + + mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws { + assert(!value.isEmpty) + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize + let dataSize = value.reduce(0) { + let count = $1.utf8.count + return $0 + Varint.encodedSize(of: Int64(count)) + count + } + serializedSize += tagSize * value.count + dataSize + } + + mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws { + assert(!value.isEmpty) + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize + let dataSize = value.reduce(0) { + let count = $1.count + return $0 + Varint.encodedSize(of: Int64(count)) + count + } + serializedSize += tagSize * value.count + dataSize + } + + // Packed field handling. + + mutating func visitPackedFloatField(value: [Float], fieldNumber: Int) throws { + assert(!value.isEmpty) + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize + let dataSize = value.count * MemoryLayout.size + serializedSize += tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize + } + + mutating func visitPackedDoubleField(value: [Double], fieldNumber: Int) throws { + assert(!value.isEmpty) + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize + let dataSize = value.count * MemoryLayout.size + serializedSize += tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize + } + + mutating func visitPackedInt32Field(value: [Int32], fieldNumber: Int) throws { + assert(!value.isEmpty) + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize + let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) } + serializedSize += + tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize + } + + mutating func visitPackedInt64Field(value: [Int64], fieldNumber: Int) throws { + assert(!value.isEmpty) + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize + let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) } + serializedSize += + tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize + } + + mutating func visitPackedSInt32Field(value: [Int32], fieldNumber: Int) throws { + assert(!value.isEmpty) + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize + let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: ZigZag.encoded($1)) } + serializedSize += + tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize + } + + mutating func visitPackedSInt64Field(value: [Int64], fieldNumber: Int) throws { + assert(!value.isEmpty) + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize + let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: ZigZag.encoded($1)) } + serializedSize += + tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize + } + + mutating func visitPackedUInt32Field(value: [UInt32], fieldNumber: Int) throws { + assert(!value.isEmpty) + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize + let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) } + serializedSize += + tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize + } + + mutating func visitPackedUInt64Field(value: [UInt64], fieldNumber: Int) throws { + assert(!value.isEmpty) + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize + let dataSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) } + serializedSize += + tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize + } + + mutating func visitPackedFixed32Field(value: [UInt32], fieldNumber: Int) throws { + assert(!value.isEmpty) + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize + let dataSize = value.count * MemoryLayout.size + serializedSize += tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize + } + + mutating func visitPackedFixed64Field(value: [UInt64], fieldNumber: Int) throws { + assert(!value.isEmpty) + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize + let dataSize = value.count * MemoryLayout.size + serializedSize += tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize + } + + mutating func visitPackedSFixed32Field(value: [Int32], fieldNumber: Int) throws { + assert(!value.isEmpty) + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize + let dataSize = value.count * MemoryLayout.size + serializedSize += tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize + } + + mutating func visitPackedSFixed64Field(value: [Int64], fieldNumber: Int) throws { + assert(!value.isEmpty) + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize + let dataSize = value.count * MemoryLayout.size + serializedSize += tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize + } + + mutating func visitPackedBoolField(value: [Bool], fieldNumber: Int) throws { + assert(!value.isEmpty) + let tagSize = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited).encodedSize + let dataSize = value.count + serializedSize += tagSize + Varint.encodedSize(of: Int64(dataSize)) + dataSize + } + + mutating func visitSingularEnumField( + value: E, + fieldNumber: Int + ) throws { + let tagSize = FieldTag( + fieldNumber: fieldNumber, + wireFormat: .varint + ).encodedSize + serializedSize += tagSize + let dataSize = Varint.encodedSize(of: Int32(truncatingIfNeeded: value.rawValue)) + serializedSize += dataSize + } + + mutating func visitRepeatedEnumField( + value: [E], + fieldNumber: Int + ) throws { + assert(!value.isEmpty) + let tagSize = FieldTag( + fieldNumber: fieldNumber, + wireFormat: .varint + ).encodedSize + serializedSize += value.count * tagSize + let dataSize = value.reduce(0) { + $0 + Varint.encodedSize(of: Int32(truncatingIfNeeded: $1.rawValue)) + } + serializedSize += dataSize + } + + mutating func visitPackedEnumField( + value: [E], + fieldNumber: Int + ) throws { + assert(!value.isEmpty) + let tagSize = FieldTag( + fieldNumber: fieldNumber, + wireFormat: .varint + ).encodedSize + serializedSize += tagSize + let dataSize = value.reduce(0) { + $0 + Varint.encodedSize(of: Int32(truncatingIfNeeded: $1.rawValue)) + } + serializedSize += Varint.encodedSize(of: Int64(dataSize)) + dataSize + } + + mutating func visitSingularMessageField( + value: M, + fieldNumber: Int + ) throws { + let tagSize = FieldTag( + fieldNumber: fieldNumber, + wireFormat: .lengthDelimited + ).encodedSize + let messageSize = try value.serializedDataSize() + serializedSize += + tagSize + Varint.encodedSize(of: UInt64(messageSize)) + messageSize + } + + mutating func visitRepeatedMessageField( + value: [M], + fieldNumber: Int + ) throws { + assert(!value.isEmpty) + let tagSize = FieldTag( + fieldNumber: fieldNumber, + wireFormat: .lengthDelimited + ).encodedSize + serializedSize += value.count * tagSize + let dataSize = try value.reduce(0) { + let messageSize = try $1.serializedDataSize() + return $0 + Varint.encodedSize(of: UInt64(messageSize)) + messageSize + } + serializedSize += dataSize + } + + mutating func visitSingularGroupField(value: G, fieldNumber: Int) throws { + // The wire format doesn't matter here because the encoded size of the + // integer won't change based on the low three bits. + let tagSize = FieldTag( + fieldNumber: fieldNumber, + wireFormat: .startGroup + ).encodedSize + serializedSize += 2 * tagSize + try value.traverse(visitor: &self) + } + + mutating func visitRepeatedGroupField( + value: [G], + fieldNumber: Int + ) throws { + assert(!value.isEmpty) + let tagSize = FieldTag( + fieldNumber: fieldNumber, + wireFormat: .startGroup + ).encodedSize + serializedSize += 2 * value.count * tagSize + for v in value { + try v.traverse(visitor: &self) + } + } + + mutating func visitMapField( + fieldType: _ProtobufMap.Type, + value: _ProtobufMap.BaseType, + fieldNumber: Int + ) throws { + let tagSize = FieldTag( + fieldNumber: fieldNumber, + wireFormat: .lengthDelimited + ).encodedSize + for (k, v) in value { + var sizer = BinaryEncodingSizeVisitor() + try KeyType.visitSingular(value: k, fieldNumber: 1, with: &sizer) + try ValueType.visitSingular(value: v, fieldNumber: 2, with: &sizer) + let entrySize = sizer.serializedSize + serializedSize += Varint.encodedSize(of: Int64(entrySize)) + entrySize + } + serializedSize += value.count * tagSize + } + + mutating func visitMapField( + fieldType: _ProtobufEnumMap.Type, + value: _ProtobufEnumMap.BaseType, + fieldNumber: Int + ) throws where ValueType.RawValue == Int { + let tagSize = FieldTag( + fieldNumber: fieldNumber, + wireFormat: .lengthDelimited + ).encodedSize + for (k, v) in value { + var sizer = BinaryEncodingSizeVisitor() + try KeyType.visitSingular(value: k, fieldNumber: 1, with: &sizer) + try sizer.visitSingularEnumField(value: v, fieldNumber: 2) + let entrySize = sizer.serializedSize + serializedSize += Varint.encodedSize(of: Int64(entrySize)) + entrySize + } + serializedSize += value.count * tagSize + } + + mutating func visitMapField( + fieldType: _ProtobufMessageMap.Type, + value: _ProtobufMessageMap.BaseType, + fieldNumber: Int + ) throws { + let tagSize = FieldTag( + fieldNumber: fieldNumber, + wireFormat: .lengthDelimited + ).encodedSize + for (k, v) in value { + var sizer = BinaryEncodingSizeVisitor() + try KeyType.visitSingular(value: k, fieldNumber: 1, with: &sizer) + try sizer.visitSingularMessageField(value: v, fieldNumber: 2) + let entrySize = sizer.serializedSize + serializedSize += Varint.encodedSize(of: Int64(entrySize)) + entrySize + } + serializedSize += value.count * tagSize + } + + mutating func visitExtensionFieldsAsMessageSet( + fields: ExtensionFieldValueSet, + start: Int, + end: Int + ) throws { + var sizer = BinaryEncodingMessageSetSizeVisitor() + try fields.traverse(visitor: &sizer, start: start, end: end) + serializedSize += sizer.serializedSize + } } extension BinaryEncodingSizeVisitor { - // Helper Visitor to compute the sizes when writing out the extensions as MessageSets. - internal struct BinaryEncodingMessageSetSizeVisitor: SelectiveVisitor { - var serializedSize: Int = 0 + // Helper Visitor to compute the sizes when writing out the extensions as MessageSets. + internal struct BinaryEncodingMessageSetSizeVisitor: SelectiveVisitor { + var serializedSize: Int = 0 - init() {} + init() {} - mutating func visitSingularMessageField(value: M, fieldNumber: Int) throws { - var groupSize = WireFormat.MessageSet.itemTagsEncodedSize + mutating func visitSingularMessageField(value: M, fieldNumber: Int) throws { + var groupSize = WireFormat.MessageSet.itemTagsEncodedSize - groupSize += Varint.encodedSize(of: Int32(fieldNumber)) + groupSize += Varint.encodedSize(of: Int32(fieldNumber)) - let messageSize = try value.serializedDataSize() - groupSize += Varint.encodedSize(of: UInt64(messageSize)) + messageSize + let messageSize = try value.serializedDataSize() + groupSize += Varint.encodedSize(of: UInt64(messageSize)) + messageSize - serializedSize += groupSize - } + serializedSize += groupSize + } - // SelectiveVisitor handles the rest. - } + // SelectiveVisitor handles the rest. + } } diff --git a/Sources/SwiftProtobuf/BinaryEncodingVisitor.swift b/Sources/SwiftProtobuf/BinaryEncodingVisitor.swift index 6ce63ec4d..81fd97dcd 100644 --- a/Sources/SwiftProtobuf/BinaryEncodingVisitor.swift +++ b/Sources/SwiftProtobuf/BinaryEncodingVisitor.swift @@ -17,371 +17,386 @@ import Foundation /// Visitor that encodes a message graph in the protobuf binary wire format. internal struct BinaryEncodingVisitor: Visitor { - private let options: BinaryEncodingOptions - - var encoder: BinaryEncoder - - /// Creates a new visitor that writes the binary-coded message into the memory - /// at the given pointer. - /// - /// - Precondition: `pointer` must point to an allocated block of memory that - /// is large enough to hold the entire encoded message. For performance - /// reasons, the encoder does not make any attempts to verify this. - init(forWritingInto buffer: UnsafeMutableRawBufferPointer, options: BinaryEncodingOptions) { - self.encoder = BinaryEncoder(forWritingInto: buffer) - self.options = options - } - - mutating func visitUnknown(bytes: Data) throws { - encoder.appendUnknown(data: bytes) - } - - mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws { - encoder.startField(fieldNumber: fieldNumber, wireFormat: .fixed32) - encoder.putFloatValue(value: value) - } - - mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws { - encoder.startField(fieldNumber: fieldNumber, wireFormat: .fixed64) - encoder.putDoubleValue(value: value) - } - - mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws { - try visitSingularUInt64Field(value: UInt64(bitPattern: value), fieldNumber: fieldNumber) - } - - mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws { - encoder.startField(fieldNumber: fieldNumber, wireFormat: .varint) - encoder.putVarInt(value: value) - } - - mutating func visitSingularSInt32Field(value: Int32, fieldNumber: Int) throws { - try visitSingularSInt64Field(value: Int64(value), fieldNumber: fieldNumber) - } - - mutating func visitSingularSInt64Field(value: Int64, fieldNumber: Int) throws { - try visitSingularUInt64Field(value: ZigZag.encoded(value), fieldNumber: fieldNumber) - } - - mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws { - encoder.startField(fieldNumber: fieldNumber, wireFormat: .fixed32) - encoder.putFixedUInt32(value: value) - } - - mutating func visitSingularFixed64Field(value: UInt64, fieldNumber: Int) throws { - encoder.startField(fieldNumber: fieldNumber, wireFormat: .fixed64) - encoder.putFixedUInt64(value: value) - } - - mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws { - try visitSingularFixed32Field(value: UInt32(bitPattern: value), fieldNumber: fieldNumber) - } - - mutating func visitSingularSFixed64Field(value: Int64, fieldNumber: Int) throws { - try visitSingularFixed64Field(value: UInt64(bitPattern: value), fieldNumber: fieldNumber) - } - - mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws { - try visitSingularUInt64Field(value: value ? 1 : 0, fieldNumber: fieldNumber) - } - - mutating func visitSingularStringField(value: String, fieldNumber: Int) throws { - encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited) - encoder.putStringValue(value: value) - } - - mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws { - encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited) - encoder.putBytesValue(value: value) - } - - mutating func visitSingularEnumField(value: E, - fieldNumber: Int) throws { - try visitSingularUInt64Field(value: UInt64(bitPattern: Int64(value.rawValue)), - fieldNumber: fieldNumber) - } - - mutating func visitSingularMessageField(value: M, - fieldNumber: Int) throws { - encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited) - let length = try value.serializedDataSize() - encoder.putVarInt(value: length) - try value.traverse(visitor: &self) - } - - mutating func visitSingularGroupField(value: G, fieldNumber: Int) throws { - encoder.startField(fieldNumber: fieldNumber, wireFormat: .startGroup) - try value.traverse(visitor: &self) - encoder.startField(fieldNumber: fieldNumber, wireFormat: .endGroup) - } - - // Repeated fields are handled by the default implementations in Visitor.swift - - - // Packed Fields - - mutating func visitPackedFloatField(value: [Float], fieldNumber: Int) throws { - assert(!value.isEmpty) - encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited) - encoder.putVarInt(value: value.count * MemoryLayout.size) - for v in value { - encoder.putFloatValue(value: v) - } - } - - mutating func visitPackedDoubleField(value: [Double], fieldNumber: Int) throws { - assert(!value.isEmpty) - encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited) - encoder.putVarInt(value: value.count * MemoryLayout.size) - for v in value { - encoder.putDoubleValue(value: v) - } - } - - mutating func visitPackedInt32Field(value: [Int32], fieldNumber: Int) throws { - assert(!value.isEmpty) - encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited) - let packedSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) } - encoder.putVarInt(value: packedSize) - for v in value { - encoder.putVarInt(value: Int64(v)) - } - } - - mutating func visitPackedInt64Field(value: [Int64], fieldNumber: Int) throws { - assert(!value.isEmpty) - encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited) - let packedSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) } - encoder.putVarInt(value: packedSize) - for v in value { - encoder.putVarInt(value: v) - } - } - - mutating func visitPackedSInt32Field(value: [Int32], fieldNumber: Int) throws { - assert(!value.isEmpty) - encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited) - let packedSize = value.reduce(0) { $0 + Varint.encodedSize(of: ZigZag.encoded($1)) } - encoder.putVarInt(value: packedSize) - for v in value { - encoder.putZigZagVarInt(value: Int64(v)) - } - } - - mutating func visitPackedSInt64Field(value: [Int64], fieldNumber: Int) throws { - assert(!value.isEmpty) - encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited) - let packedSize = value.reduce(0) { $0 + Varint.encodedSize(of: ZigZag.encoded($1)) } - encoder.putVarInt(value: packedSize) - for v in value { - encoder.putZigZagVarInt(value: v) - } - } - - mutating func visitPackedUInt32Field(value: [UInt32], fieldNumber: Int) throws { - assert(!value.isEmpty) - encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited) - let packedSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) } - encoder.putVarInt(value: packedSize) - for v in value { - encoder.putVarInt(value: UInt64(v)) - } - } - - mutating func visitPackedUInt64Field(value: [UInt64], fieldNumber: Int) throws { - assert(!value.isEmpty) - encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited) - let packedSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) } - encoder.putVarInt(value: packedSize) - for v in value { - encoder.putVarInt(value: v) - } - } - - mutating func visitPackedFixed32Field(value: [UInt32], fieldNumber: Int) throws { - assert(!value.isEmpty) - encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited) - encoder.putVarInt(value: value.count * MemoryLayout.size) - for v in value { - encoder.putFixedUInt32(value: v) - } - } - - mutating func visitPackedFixed64Field(value: [UInt64], fieldNumber: Int) throws { - assert(!value.isEmpty) - encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited) - encoder.putVarInt(value: value.count * MemoryLayout.size) - for v in value { - encoder.putFixedUInt64(value: v) - } - } - - mutating func visitPackedSFixed32Field(value: [Int32], fieldNumber: Int) throws { - assert(!value.isEmpty) - encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited) - encoder.putVarInt(value: value.count * MemoryLayout.size) - for v in value { - encoder.putFixedUInt32(value: UInt32(bitPattern: v)) - } - } - - mutating func visitPackedSFixed64Field(value: [Int64], fieldNumber: Int) throws { - assert(!value.isEmpty) - encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited) - encoder.putVarInt(value: value.count * MemoryLayout.size) - for v in value { - encoder.putFixedUInt64(value: UInt64(bitPattern: v)) - } - } - - mutating func visitPackedBoolField(value: [Bool], fieldNumber: Int) throws { - assert(!value.isEmpty) - encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited) - encoder.putVarInt(value: value.count) - for v in value { - encoder.putVarInt(value: v ? 1 : 0) - } - } - - mutating func visitPackedEnumField(value: [E], fieldNumber: Int) throws { - assert(!value.isEmpty) - encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited) - let packedSize = value.reduce(0) { - $0 + Varint.encodedSize(of: Int32(truncatingIfNeeded: $1.rawValue)) - } - encoder.putVarInt(value: packedSize) - for v in value { - encoder.putVarInt(value: v.rawValue) - } - } - - mutating func visitMapField( - fieldType: _ProtobufMap.Type, - value: _ProtobufMap.BaseType, - fieldNumber: Int - ) throws { - try iterateAndEncode( - map: value, fieldNumber: fieldNumber, isOrderedBefore: KeyType._lessThan, - encodeWithSizer: { sizer, key, value in - try KeyType.visitSingular(value: key, fieldNumber: 1, with: &sizer) - try ValueType.visitSingular(value: value, fieldNumber: 2, with: &sizer) - }, encodeWithVisitor: { visitor, key, value in - try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor) - try ValueType.visitSingular(value: value, fieldNumber: 2, with: &visitor) - } - ) - } - - mutating func visitMapField( - fieldType: _ProtobufEnumMap.Type, - value: _ProtobufEnumMap.BaseType, - fieldNumber: Int - ) throws where ValueType.RawValue == Int { - try iterateAndEncode( - map: value, fieldNumber: fieldNumber, isOrderedBefore: KeyType._lessThan, - encodeWithSizer: { sizer, key, value in - try KeyType.visitSingular(value: key, fieldNumber: 1, with: &sizer) - try sizer.visitSingularEnumField(value: value, fieldNumber: 2) - }, encodeWithVisitor: { visitor, key, value in - try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor) - try visitor.visitSingularEnumField(value: value, fieldNumber: 2) - } - ) - } - - mutating func visitMapField( - fieldType: _ProtobufMessageMap.Type, - value: _ProtobufMessageMap.BaseType, - fieldNumber: Int - ) throws { - try iterateAndEncode( - map: value, fieldNumber: fieldNumber, isOrderedBefore: KeyType._lessThan, - encodeWithSizer: { sizer, key, value in - try KeyType.visitSingular(value: key, fieldNumber: 1, with: &sizer) - try sizer.visitSingularMessageField(value: value, fieldNumber: 2) - }, encodeWithVisitor: { visitor, key, value in - try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor) - try visitor.visitSingularMessageField(value: value, fieldNumber: 2) - } - ) - } - - /// Helper to encapsulate the common structure of iterating over a map - /// and encoding the keys and values. - private mutating func iterateAndEncode( - map: Dictionary, - fieldNumber: Int, - isOrderedBefore: (K, K) -> Bool, - encodeWithSizer: (inout BinaryEncodingSizeVisitor, K, V) throws -> (), - encodeWithVisitor: (inout BinaryEncodingVisitor, K, V) throws -> () - ) throws { - if options.useDeterministicOrdering { - for (k,v) in map.sorted(by: { isOrderedBefore( $0.0, $1.0) }) { + private let options: BinaryEncodingOptions + + var encoder: BinaryEncoder + + /// Creates a new visitor that writes the binary-coded message into the memory + /// at the given pointer. + /// + /// - Precondition: `pointer` must point to an allocated block of memory that + /// is large enough to hold the entire encoded message. For performance + /// reasons, the encoder does not make any attempts to verify this. + init(forWritingInto buffer: UnsafeMutableRawBufferPointer, options: BinaryEncodingOptions) { + self.encoder = BinaryEncoder(forWritingInto: buffer) + self.options = options + } + + mutating func visitUnknown(bytes: Data) throws { + encoder.appendUnknown(data: bytes) + } + + mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws { + encoder.startField(fieldNumber: fieldNumber, wireFormat: .fixed32) + encoder.putFloatValue(value: value) + } + + mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws { + encoder.startField(fieldNumber: fieldNumber, wireFormat: .fixed64) + encoder.putDoubleValue(value: value) + } + + mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws { + try visitSingularUInt64Field(value: UInt64(bitPattern: value), fieldNumber: fieldNumber) + } + + mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws { + encoder.startField(fieldNumber: fieldNumber, wireFormat: .varint) + encoder.putVarInt(value: value) + } + + mutating func visitSingularSInt32Field(value: Int32, fieldNumber: Int) throws { + try visitSingularSInt64Field(value: Int64(value), fieldNumber: fieldNumber) + } + + mutating func visitSingularSInt64Field(value: Int64, fieldNumber: Int) throws { + try visitSingularUInt64Field(value: ZigZag.encoded(value), fieldNumber: fieldNumber) + } + + mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws { + encoder.startField(fieldNumber: fieldNumber, wireFormat: .fixed32) + encoder.putFixedUInt32(value: value) + } + + mutating func visitSingularFixed64Field(value: UInt64, fieldNumber: Int) throws { + encoder.startField(fieldNumber: fieldNumber, wireFormat: .fixed64) + encoder.putFixedUInt64(value: value) + } + + mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws { + try visitSingularFixed32Field(value: UInt32(bitPattern: value), fieldNumber: fieldNumber) + } + + mutating func visitSingularSFixed64Field(value: Int64, fieldNumber: Int) throws { + try visitSingularFixed64Field(value: UInt64(bitPattern: value), fieldNumber: fieldNumber) + } + + mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws { + try visitSingularUInt64Field(value: value ? 1 : 0, fieldNumber: fieldNumber) + } + + mutating func visitSingularStringField(value: String, fieldNumber: Int) throws { encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited) - var sizer = BinaryEncodingSizeVisitor() - try encodeWithSizer(&sizer, k, v) - let entrySize = sizer.serializedSize - encoder.putVarInt(value: entrySize) - try encodeWithVisitor(&self, k, v) - } - } else { - for (k,v) in map { + encoder.putStringValue(value: value) + } + + mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws { encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited) - var sizer = BinaryEncodingSizeVisitor() - try encodeWithSizer(&sizer, k, v) - let entrySize = sizer.serializedSize - encoder.putVarInt(value: entrySize) - try encodeWithVisitor(&self, k, v) - } - } - } - - mutating func visitExtensionFieldsAsMessageSet( - fields: ExtensionFieldValueSet, - start: Int, - end: Int - ) throws { - var subVisitor = BinaryEncodingMessageSetVisitor(encoder: encoder, options: options) - try fields.traverse(visitor: &subVisitor, start: start, end: end) - encoder = subVisitor.encoder - } -} + encoder.putBytesValue(value: value) + } -extension BinaryEncodingVisitor { + mutating func visitSingularEnumField( + value: E, + fieldNumber: Int + ) throws { + try visitSingularUInt64Field( + value: UInt64(bitPattern: Int64(value.rawValue)), + fieldNumber: fieldNumber + ) + } - // Helper Visitor to when writing out the extensions as MessageSets. - internal struct BinaryEncodingMessageSetVisitor: SelectiveVisitor { - private let options: BinaryEncodingOptions + mutating func visitSingularMessageField( + value: M, + fieldNumber: Int + ) throws { + encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited) + let length = try value.serializedDataSize() + encoder.putVarInt(value: length) + try value.traverse(visitor: &self) + } - var encoder: BinaryEncoder + mutating func visitSingularGroupField(value: G, fieldNumber: Int) throws { + encoder.startField(fieldNumber: fieldNumber, wireFormat: .startGroup) + try value.traverse(visitor: &self) + encoder.startField(fieldNumber: fieldNumber, wireFormat: .endGroup) + } - init(encoder: BinaryEncoder, options: BinaryEncodingOptions) { - self.options = options - self.encoder = encoder + // Repeated fields are handled by the default implementations in Visitor.swift + + // Packed Fields + + mutating func visitPackedFloatField(value: [Float], fieldNumber: Int) throws { + assert(!value.isEmpty) + encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited) + encoder.putVarInt(value: value.count * MemoryLayout.size) + for v in value { + encoder.putFloatValue(value: v) + } } - mutating func visitSingularMessageField(value: M, fieldNumber: Int) throws { - encoder.putVarInt(value: Int64(WireFormat.MessageSet.Tags.itemStart.rawValue)) + mutating func visitPackedDoubleField(value: [Double], fieldNumber: Int) throws { + assert(!value.isEmpty) + encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited) + encoder.putVarInt(value: value.count * MemoryLayout.size) + for v in value { + encoder.putDoubleValue(value: v) + } + } - encoder.putVarInt(value: Int64(WireFormat.MessageSet.Tags.typeId.rawValue)) - encoder.putVarInt(value: fieldNumber) + mutating func visitPackedInt32Field(value: [Int32], fieldNumber: Int) throws { + assert(!value.isEmpty) + encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited) + let packedSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) } + encoder.putVarInt(value: packedSize) + for v in value { + encoder.putVarInt(value: Int64(v)) + } + } - encoder.putVarInt(value: Int64(WireFormat.MessageSet.Tags.message.rawValue)) + mutating func visitPackedInt64Field(value: [Int64], fieldNumber: Int) throws { + assert(!value.isEmpty) + encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited) + let packedSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) } + encoder.putVarInt(value: packedSize) + for v in value { + encoder.putVarInt(value: v) + } + } - // Use a normal BinaryEncodingVisitor so any message fields end up in the - // normal wire format (instead of MessageSet format). - let length = try value.serializedDataSize() - encoder.putVarInt(value: length) - // Create the sub encoder after writing the length. - var subVisitor = BinaryEncodingVisitor( - forWritingInto: encoder.remainder, options: options - ) - try value.traverse(visitor: &subVisitor) - encoder.advance(subVisitor.encoder.used) + mutating func visitPackedSInt32Field(value: [Int32], fieldNumber: Int) throws { + assert(!value.isEmpty) + encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited) + let packedSize = value.reduce(0) { $0 + Varint.encodedSize(of: ZigZag.encoded($1)) } + encoder.putVarInt(value: packedSize) + for v in value { + encoder.putZigZagVarInt(value: Int64(v)) + } + } - encoder.putVarInt(value: Int64(WireFormat.MessageSet.Tags.itemEnd.rawValue)) + mutating func visitPackedSInt64Field(value: [Int64], fieldNumber: Int) throws { + assert(!value.isEmpty) + encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited) + let packedSize = value.reduce(0) { $0 + Varint.encodedSize(of: ZigZag.encoded($1)) } + encoder.putVarInt(value: packedSize) + for v in value { + encoder.putZigZagVarInt(value: v) + } } - // SelectiveVisitor handles the rest. - } + mutating func visitPackedUInt32Field(value: [UInt32], fieldNumber: Int) throws { + assert(!value.isEmpty) + encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited) + let packedSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) } + encoder.putVarInt(value: packedSize) + for v in value { + encoder.putVarInt(value: UInt64(v)) + } + } + + mutating func visitPackedUInt64Field(value: [UInt64], fieldNumber: Int) throws { + assert(!value.isEmpty) + encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited) + let packedSize = value.reduce(0) { $0 + Varint.encodedSize(of: $1) } + encoder.putVarInt(value: packedSize) + for v in value { + encoder.putVarInt(value: v) + } + } + + mutating func visitPackedFixed32Field(value: [UInt32], fieldNumber: Int) throws { + assert(!value.isEmpty) + encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited) + encoder.putVarInt(value: value.count * MemoryLayout.size) + for v in value { + encoder.putFixedUInt32(value: v) + } + } + + mutating func visitPackedFixed64Field(value: [UInt64], fieldNumber: Int) throws { + assert(!value.isEmpty) + encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited) + encoder.putVarInt(value: value.count * MemoryLayout.size) + for v in value { + encoder.putFixedUInt64(value: v) + } + } + + mutating func visitPackedSFixed32Field(value: [Int32], fieldNumber: Int) throws { + assert(!value.isEmpty) + encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited) + encoder.putVarInt(value: value.count * MemoryLayout.size) + for v in value { + encoder.putFixedUInt32(value: UInt32(bitPattern: v)) + } + } + + mutating func visitPackedSFixed64Field(value: [Int64], fieldNumber: Int) throws { + assert(!value.isEmpty) + encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited) + encoder.putVarInt(value: value.count * MemoryLayout.size) + for v in value { + encoder.putFixedUInt64(value: UInt64(bitPattern: v)) + } + } + + mutating func visitPackedBoolField(value: [Bool], fieldNumber: Int) throws { + assert(!value.isEmpty) + encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited) + encoder.putVarInt(value: value.count) + for v in value { + encoder.putVarInt(value: v ? 1 : 0) + } + } + + mutating func visitPackedEnumField(value: [E], fieldNumber: Int) throws { + assert(!value.isEmpty) + encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited) + let packedSize = value.reduce(0) { + $0 + Varint.encodedSize(of: Int32(truncatingIfNeeded: $1.rawValue)) + } + encoder.putVarInt(value: packedSize) + for v in value { + encoder.putVarInt(value: v.rawValue) + } + } + + mutating func visitMapField( + fieldType: _ProtobufMap.Type, + value: _ProtobufMap.BaseType, + fieldNumber: Int + ) throws { + try iterateAndEncode( + map: value, + fieldNumber: fieldNumber, + isOrderedBefore: KeyType._lessThan, + encodeWithSizer: { sizer, key, value in + try KeyType.visitSingular(value: key, fieldNumber: 1, with: &sizer) + try ValueType.visitSingular(value: value, fieldNumber: 2, with: &sizer) + }, + encodeWithVisitor: { visitor, key, value in + try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor) + try ValueType.visitSingular(value: value, fieldNumber: 2, with: &visitor) + } + ) + } + + mutating func visitMapField( + fieldType: _ProtobufEnumMap.Type, + value: _ProtobufEnumMap.BaseType, + fieldNumber: Int + ) throws where ValueType.RawValue == Int { + try iterateAndEncode( + map: value, + fieldNumber: fieldNumber, + isOrderedBefore: KeyType._lessThan, + encodeWithSizer: { sizer, key, value in + try KeyType.visitSingular(value: key, fieldNumber: 1, with: &sizer) + try sizer.visitSingularEnumField(value: value, fieldNumber: 2) + }, + encodeWithVisitor: { visitor, key, value in + try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor) + try visitor.visitSingularEnumField(value: value, fieldNumber: 2) + } + ) + } + + mutating func visitMapField( + fieldType: _ProtobufMessageMap.Type, + value: _ProtobufMessageMap.BaseType, + fieldNumber: Int + ) throws { + try iterateAndEncode( + map: value, + fieldNumber: fieldNumber, + isOrderedBefore: KeyType._lessThan, + encodeWithSizer: { sizer, key, value in + try KeyType.visitSingular(value: key, fieldNumber: 1, with: &sizer) + try sizer.visitSingularMessageField(value: value, fieldNumber: 2) + }, + encodeWithVisitor: { visitor, key, value in + try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor) + try visitor.visitSingularMessageField(value: value, fieldNumber: 2) + } + ) + } + + /// Helper to encapsulate the common structure of iterating over a map + /// and encoding the keys and values. + private mutating func iterateAndEncode( + map: [K: V], + fieldNumber: Int, + isOrderedBefore: (K, K) -> Bool, + encodeWithSizer: (inout BinaryEncodingSizeVisitor, K, V) throws -> Void, + encodeWithVisitor: (inout BinaryEncodingVisitor, K, V) throws -> Void + ) throws { + if options.useDeterministicOrdering { + for (k, v) in map.sorted(by: { isOrderedBefore($0.0, $1.0) }) { + encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited) + var sizer = BinaryEncodingSizeVisitor() + try encodeWithSizer(&sizer, k, v) + let entrySize = sizer.serializedSize + encoder.putVarInt(value: entrySize) + try encodeWithVisitor(&self, k, v) + } + } else { + for (k, v) in map { + encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited) + var sizer = BinaryEncodingSizeVisitor() + try encodeWithSizer(&sizer, k, v) + let entrySize = sizer.serializedSize + encoder.putVarInt(value: entrySize) + try encodeWithVisitor(&self, k, v) + } + } + } + + mutating func visitExtensionFieldsAsMessageSet( + fields: ExtensionFieldValueSet, + start: Int, + end: Int + ) throws { + var subVisitor = BinaryEncodingMessageSetVisitor(encoder: encoder, options: options) + try fields.traverse(visitor: &subVisitor, start: start, end: end) + encoder = subVisitor.encoder + } +} + +extension BinaryEncodingVisitor { + + // Helper Visitor to when writing out the extensions as MessageSets. + internal struct BinaryEncodingMessageSetVisitor: SelectiveVisitor { + private let options: BinaryEncodingOptions + + var encoder: BinaryEncoder + + init(encoder: BinaryEncoder, options: BinaryEncodingOptions) { + self.options = options + self.encoder = encoder + } + + mutating func visitSingularMessageField(value: M, fieldNumber: Int) throws { + encoder.putVarInt(value: Int64(WireFormat.MessageSet.Tags.itemStart.rawValue)) + + encoder.putVarInt(value: Int64(WireFormat.MessageSet.Tags.typeId.rawValue)) + encoder.putVarInt(value: fieldNumber) + + encoder.putVarInt(value: Int64(WireFormat.MessageSet.Tags.message.rawValue)) + + // Use a normal BinaryEncodingVisitor so any message fields end up in the + // normal wire format (instead of MessageSet format). + let length = try value.serializedDataSize() + encoder.putVarInt(value: length) + // Create the sub encoder after writing the length. + var subVisitor = BinaryEncodingVisitor( + forWritingInto: encoder.remainder, + options: options + ) + try value.traverse(visitor: &subVisitor) + encoder.advance(subVisitor.encoder.used) + + encoder.putVarInt(value: Int64(WireFormat.MessageSet.Tags.itemEnd.rawValue)) + } + + // SelectiveVisitor handles the rest. + } } diff --git a/Sources/SwiftProtobuf/CustomJSONCodable.swift b/Sources/SwiftProtobuf/CustomJSONCodable.swift index 2e1fd3401..32be7f568 100644 --- a/Sources/SwiftProtobuf/CustomJSONCodable.swift +++ b/Sources/SwiftProtobuf/CustomJSONCodable.swift @@ -14,23 +14,23 @@ /// Allows WKTs to provide their custom JSON encodings. internal protocol _CustomJSONCodable { - func encodedJSONString(options: JSONEncodingOptions) throws -> String - mutating func decodeJSON(from: inout JSONDecoder) throws + func encodedJSONString(options: JSONEncodingOptions) throws -> String + mutating func decodeJSON(from: inout JSONDecoder) throws - /// Called when the JSON `null` literal is encountered in a position where - /// a message of the conforming type is expected. The message type can then - /// handle the `null` value differently, if needed; for example, - /// `Google_Protobuf_Value` returns a special instance whose `kind` is set to - /// `.nullValue(.nullValue)`. - /// - /// The default behavior is to return `nil`, which indicates that `null` - /// should be treated as the absence of a message. - static func decodedFromJSONNull() throws -> Self? + /// Called when the JSON `null` literal is encountered in a position where + /// a message of the conforming type is expected. The message type can then + /// handle the `null` value differently, if needed; for example, + /// `Google_Protobuf_Value` returns a special instance whose `kind` is set to + /// `.nullValue(.nullValue)`. + /// + /// The default behavior is to return `nil`, which indicates that `null` + /// should be treated as the absence of a message. + static func decodedFromJSONNull() throws -> Self? } extension _CustomJSONCodable { - internal static func decodedFromJSONNull() -> Self? { - // Return nil by default. Concrete types can provide custom logic. - return nil - } + internal static func decodedFromJSONNull() -> Self? { + // Return nil by default. Concrete types can provide custom logic. + nil + } } diff --git a/Sources/SwiftProtobuf/Decoder.swift b/Sources/SwiftProtobuf/Decoder.swift index 9fee303e0..7b4d489a3 100644 --- a/Sources/SwiftProtobuf/Decoder.swift +++ b/Sources/SwiftProtobuf/Decoder.swift @@ -55,174 +55,191 @@ import Foundation /// polluting the generated `Enum` and `Message` types with all of the /// necessary generic methods to support this. public protocol Decoder { - /// Called by a `oneof` when it already has a value and is being asked to - /// accept a new value. Some formats require `oneof` decoding to fail in this - /// case. - mutating func handleConflictingOneOf() throws - - /// Returns the next field number, or nil when the end of the input is - /// reached. - /// - /// For JSON and text format, the decoder translates the field name to a - /// number at this point, based on information it obtained from the message - /// when it was initialized. - mutating func nextFieldNumber() throws -> Int? - - /// Decode a float value to non-`Optional` field storage - mutating func decodeSingularFloatField(value: inout Float) throws - /// Decode a float value to `Optional` field storage - mutating func decodeSingularFloatField(value: inout Float?) throws - /// Decode float values to repeated field storage - mutating func decodeRepeatedFloatField(value: inout [Float]) throws - /// Decode a double value to non-`Optional` field storage - mutating func decodeSingularDoubleField(value: inout Double) throws - /// Decode a double value to `Optional` field storage - mutating func decodeSingularDoubleField(value: inout Double?) throws - /// Decode double values to repeated field storage - mutating func decodeRepeatedDoubleField(value: inout [Double]) throws - /// Decode an int32 value to non-`Optional` field storage - mutating func decodeSingularInt32Field(value: inout Int32) throws - /// Decode an int32 value to `Optional` field storage - mutating func decodeSingularInt32Field(value: inout Int32?) throws - /// Decode int32 values to repeated field storage - mutating func decodeRepeatedInt32Field(value: inout [Int32]) throws - /// Decode an int64 value to non-`Optional` field storage - mutating func decodeSingularInt64Field(value: inout Int64) throws - /// Decode an int64 value to `Optional` field storage - mutating func decodeSingularInt64Field(value: inout Int64?) throws - /// Decode int64 values to repeated field storage - mutating func decodeRepeatedInt64Field(value: inout [Int64]) throws - /// Decode a uint32 value to non-`Optional` field storage - mutating func decodeSingularUInt32Field(value: inout UInt32) throws - /// Decode a uint32 value to `Optional` field storage - mutating func decodeSingularUInt32Field(value: inout UInt32?) throws - /// Decode uint32 values to repeated field storage - mutating func decodeRepeatedUInt32Field(value: inout [UInt32]) throws - /// Decode a uint64 value to non-`Optional` field storage - mutating func decodeSingularUInt64Field(value: inout UInt64) throws - /// Decode a uint64 value to `Optional` field storage - mutating func decodeSingularUInt64Field(value: inout UInt64?) throws - /// Decode uint64 values to repeated field storage - mutating func decodeRepeatedUInt64Field(value: inout [UInt64]) throws - /// Decode an sint32 value to non-`Optional` field storage - mutating func decodeSingularSInt32Field(value: inout Int32) throws - /// Decode an sint32 value to `Optional` field storage - mutating func decodeSingularSInt32Field(value: inout Int32?) throws - /// Decode sint32 values to repeated field storage - mutating func decodeRepeatedSInt32Field(value: inout [Int32]) throws - /// Decode an sint64 value to non-`Optional` field storage - mutating func decodeSingularSInt64Field(value: inout Int64) throws - /// Decode an sint64 value to `Optional` field storage - mutating func decodeSingularSInt64Field(value: inout Int64?) throws - /// Decode sint64 values to repeated field storage - mutating func decodeRepeatedSInt64Field(value: inout [Int64]) throws - /// Decode a fixed32 value to non-`Optional` field storage - mutating func decodeSingularFixed32Field(value: inout UInt32) throws - /// Decode a fixed32 value to `Optional` field storage - mutating func decodeSingularFixed32Field(value: inout UInt32?) throws - /// Decode fixed32 values to repeated field storage - mutating func decodeRepeatedFixed32Field(value: inout [UInt32]) throws - /// Decode a fixed64 value to non-`Optional` field storage - mutating func decodeSingularFixed64Field(value: inout UInt64) throws - /// Decode a fixed64 value to `Optional` field storage - mutating func decodeSingularFixed64Field(value: inout UInt64?) throws - /// Decode fixed64 values to repeated field storage - mutating func decodeRepeatedFixed64Field(value: inout [UInt64]) throws - /// Decode an sfixed32 value to non-`Optional` field storage - mutating func decodeSingularSFixed32Field(value: inout Int32) throws - /// Decode an sfixed32 value to `Optional` field storage - mutating func decodeSingularSFixed32Field(value: inout Int32?) throws - /// Decode sfixed32 values to repeated field storage - mutating func decodeRepeatedSFixed32Field(value: inout [Int32]) throws - /// Decode an sfixed64 value to non-`Optional` field storage - mutating func decodeSingularSFixed64Field(value: inout Int64) throws - /// Decode an sfixed64 value to `Optional` field storage - mutating func decodeSingularSFixed64Field(value: inout Int64?) throws - /// Decode sfixed64 values to repeated field storage - mutating func decodeRepeatedSFixed64Field(value: inout [Int64]) throws - /// Decode a bool value to non-`Optional` field storage - mutating func decodeSingularBoolField(value: inout Bool) throws - /// Decode a bool value to `Optional` field storage - mutating func decodeSingularBoolField(value: inout Bool?) throws - /// Decode bool values to repeated field storage - mutating func decodeRepeatedBoolField(value: inout [Bool]) throws - /// Decode a string value to non-`Optional` field storage - mutating func decodeSingularStringField(value: inout String) throws - /// Decode a string value to `Optional` field storage - mutating func decodeSingularStringField(value: inout String?) throws - /// Decode string values to repeated field storage - mutating func decodeRepeatedStringField(value: inout [String]) throws - /// Decode a bytes value to non-`Optional` field storage - mutating func decodeSingularBytesField(value: inout Data) throws - /// Decode a bytes value to `Optional` field storage - mutating func decodeSingularBytesField(value: inout Data?) throws - /// Decode bytes values to repeated field storage - mutating func decodeRepeatedBytesField(value: inout [Data]) throws - - // Decode Enum fields - - /// Decode an enum value to non-`Optional` field storage - mutating func decodeSingularEnumField(value: inout E) throws where E.RawValue == Int - /// Decode an enum value to `Optional` field storage - mutating func decodeSingularEnumField(value: inout E?) throws where E.RawValue == Int - /// Decode enum values to repeated field storage - mutating func decodeRepeatedEnumField(value: inout [E]) throws where E.RawValue == Int - - // Decode Message fields - - /// Decode a message value to `Optional` field storage. - /// - /// Unlike the primitive types, message fields are always stored - /// as Swift `Optional` values. - mutating func decodeSingularMessageField(value: inout M?) throws - /// Decode message values to repeated field storage - mutating func decodeRepeatedMessageField(value: inout [M]) throws - - // Decode Group fields - - /// Decode a group value to `Optional` field storage. - /// - /// Unlike the primitive types, message fields are always stored - /// as Swift `Optional` values. - /// Note that groups are only used in proto2. - mutating func decodeSingularGroupField(value: inout G?) throws - /// Decode group values to repeated field storage - mutating func decodeRepeatedGroupField(value: inout [G]) throws - - // Decode Map fields. - // This is broken into separate methods depending on whether the value - // type is primitive (_ProtobufMap), enum (_ProtobufEnumMap), or message - // (_ProtobufMessageMap) - - /// Decode a map whose values are primitive types (including string and bytes) - mutating func decodeMapField(fieldType: _ProtobufMap.Type, value: inout _ProtobufMap.BaseType) throws - /// Decode a map whose values are protobuf enum types - mutating func decodeMapField(fieldType: _ProtobufEnumMap.Type, value: inout _ProtobufEnumMap.BaseType) throws where ValueType.RawValue == Int - /// Decode a map whose values are protobuf message types - mutating func decodeMapField(fieldType: _ProtobufMessageMap.Type, value: inout _ProtobufMessageMap.BaseType) throws - - // Decode extension fields - - /// Decode an extension field - mutating func decodeExtensionField(values: inout ExtensionFieldValueSet, messageType: any Message.Type, fieldNumber: Int) throws - - // Run a decode loop decoding the MessageSet format for Extensions. - mutating func decodeExtensionFieldsAsMessageSet(values: inout ExtensionFieldValueSet, - messageType: any Message.Type) throws + /// Called by a `oneof` when it already has a value and is being asked to + /// accept a new value. Some formats require `oneof` decoding to fail in this + /// case. + mutating func handleConflictingOneOf() throws + + /// Returns the next field number, or nil when the end of the input is + /// reached. + /// + /// For JSON and text format, the decoder translates the field name to a + /// number at this point, based on information it obtained from the message + /// when it was initialized. + mutating func nextFieldNumber() throws -> Int? + + /// Decode a float value to non-`Optional` field storage + mutating func decodeSingularFloatField(value: inout Float) throws + /// Decode a float value to `Optional` field storage + mutating func decodeSingularFloatField(value: inout Float?) throws + /// Decode float values to repeated field storage + mutating func decodeRepeatedFloatField(value: inout [Float]) throws + /// Decode a double value to non-`Optional` field storage + mutating func decodeSingularDoubleField(value: inout Double) throws + /// Decode a double value to `Optional` field storage + mutating func decodeSingularDoubleField(value: inout Double?) throws + /// Decode double values to repeated field storage + mutating func decodeRepeatedDoubleField(value: inout [Double]) throws + /// Decode an int32 value to non-`Optional` field storage + mutating func decodeSingularInt32Field(value: inout Int32) throws + /// Decode an int32 value to `Optional` field storage + mutating func decodeSingularInt32Field(value: inout Int32?) throws + /// Decode int32 values to repeated field storage + mutating func decodeRepeatedInt32Field(value: inout [Int32]) throws + /// Decode an int64 value to non-`Optional` field storage + mutating func decodeSingularInt64Field(value: inout Int64) throws + /// Decode an int64 value to `Optional` field storage + mutating func decodeSingularInt64Field(value: inout Int64?) throws + /// Decode int64 values to repeated field storage + mutating func decodeRepeatedInt64Field(value: inout [Int64]) throws + /// Decode a uint32 value to non-`Optional` field storage + mutating func decodeSingularUInt32Field(value: inout UInt32) throws + /// Decode a uint32 value to `Optional` field storage + mutating func decodeSingularUInt32Field(value: inout UInt32?) throws + /// Decode uint32 values to repeated field storage + mutating func decodeRepeatedUInt32Field(value: inout [UInt32]) throws + /// Decode a uint64 value to non-`Optional` field storage + mutating func decodeSingularUInt64Field(value: inout UInt64) throws + /// Decode a uint64 value to `Optional` field storage + mutating func decodeSingularUInt64Field(value: inout UInt64?) throws + /// Decode uint64 values to repeated field storage + mutating func decodeRepeatedUInt64Field(value: inout [UInt64]) throws + /// Decode an sint32 value to non-`Optional` field storage + mutating func decodeSingularSInt32Field(value: inout Int32) throws + /// Decode an sint32 value to `Optional` field storage + mutating func decodeSingularSInt32Field(value: inout Int32?) throws + /// Decode sint32 values to repeated field storage + mutating func decodeRepeatedSInt32Field(value: inout [Int32]) throws + /// Decode an sint64 value to non-`Optional` field storage + mutating func decodeSingularSInt64Field(value: inout Int64) throws + /// Decode an sint64 value to `Optional` field storage + mutating func decodeSingularSInt64Field(value: inout Int64?) throws + /// Decode sint64 values to repeated field storage + mutating func decodeRepeatedSInt64Field(value: inout [Int64]) throws + /// Decode a fixed32 value to non-`Optional` field storage + mutating func decodeSingularFixed32Field(value: inout UInt32) throws + /// Decode a fixed32 value to `Optional` field storage + mutating func decodeSingularFixed32Field(value: inout UInt32?) throws + /// Decode fixed32 values to repeated field storage + mutating func decodeRepeatedFixed32Field(value: inout [UInt32]) throws + /// Decode a fixed64 value to non-`Optional` field storage + mutating func decodeSingularFixed64Field(value: inout UInt64) throws + /// Decode a fixed64 value to `Optional` field storage + mutating func decodeSingularFixed64Field(value: inout UInt64?) throws + /// Decode fixed64 values to repeated field storage + mutating func decodeRepeatedFixed64Field(value: inout [UInt64]) throws + /// Decode an sfixed32 value to non-`Optional` field storage + mutating func decodeSingularSFixed32Field(value: inout Int32) throws + /// Decode an sfixed32 value to `Optional` field storage + mutating func decodeSingularSFixed32Field(value: inout Int32?) throws + /// Decode sfixed32 values to repeated field storage + mutating func decodeRepeatedSFixed32Field(value: inout [Int32]) throws + /// Decode an sfixed64 value to non-`Optional` field storage + mutating func decodeSingularSFixed64Field(value: inout Int64) throws + /// Decode an sfixed64 value to `Optional` field storage + mutating func decodeSingularSFixed64Field(value: inout Int64?) throws + /// Decode sfixed64 values to repeated field storage + mutating func decodeRepeatedSFixed64Field(value: inout [Int64]) throws + /// Decode a bool value to non-`Optional` field storage + mutating func decodeSingularBoolField(value: inout Bool) throws + /// Decode a bool value to `Optional` field storage + mutating func decodeSingularBoolField(value: inout Bool?) throws + /// Decode bool values to repeated field storage + mutating func decodeRepeatedBoolField(value: inout [Bool]) throws + /// Decode a string value to non-`Optional` field storage + mutating func decodeSingularStringField(value: inout String) throws + /// Decode a string value to `Optional` field storage + mutating func decodeSingularStringField(value: inout String?) throws + /// Decode string values to repeated field storage + mutating func decodeRepeatedStringField(value: inout [String]) throws + /// Decode a bytes value to non-`Optional` field storage + mutating func decodeSingularBytesField(value: inout Data) throws + /// Decode a bytes value to `Optional` field storage + mutating func decodeSingularBytesField(value: inout Data?) throws + /// Decode bytes values to repeated field storage + mutating func decodeRepeatedBytesField(value: inout [Data]) throws + + // Decode Enum fields + + /// Decode an enum value to non-`Optional` field storage + mutating func decodeSingularEnumField(value: inout E) throws where E.RawValue == Int + /// Decode an enum value to `Optional` field storage + mutating func decodeSingularEnumField(value: inout E?) throws where E.RawValue == Int + /// Decode enum values to repeated field storage + mutating func decodeRepeatedEnumField(value: inout [E]) throws where E.RawValue == Int + + // Decode Message fields + + /// Decode a message value to `Optional` field storage. + /// + /// Unlike the primitive types, message fields are always stored + /// as Swift `Optional` values. + mutating func decodeSingularMessageField(value: inout M?) throws + /// Decode message values to repeated field storage + mutating func decodeRepeatedMessageField(value: inout [M]) throws + + // Decode Group fields + + /// Decode a group value to `Optional` field storage. + /// + /// Unlike the primitive types, message fields are always stored + /// as Swift `Optional` values. + /// Note that groups are only used in proto2. + mutating func decodeSingularGroupField(value: inout G?) throws + /// Decode group values to repeated field storage + mutating func decodeRepeatedGroupField(value: inout [G]) throws + + // Decode Map fields. + // This is broken into separate methods depending on whether the value + // type is primitive (_ProtobufMap), enum (_ProtobufEnumMap), or message + // (_ProtobufMessageMap) + + /// Decode a map whose values are primitive types (including string and bytes) + mutating func decodeMapField( + fieldType: _ProtobufMap.Type, + value: inout _ProtobufMap.BaseType + ) throws + /// Decode a map whose values are protobuf enum types + mutating func decodeMapField( + fieldType: _ProtobufEnumMap.Type, + value: inout _ProtobufEnumMap.BaseType + ) throws where ValueType.RawValue == Int + /// Decode a map whose values are protobuf message types + mutating func decodeMapField( + fieldType: _ProtobufMessageMap.Type, + value: inout _ProtobufMessageMap.BaseType + ) throws + + // Decode extension fields + + /// Decode an extension field + mutating func decodeExtensionField( + values: inout ExtensionFieldValueSet, + messageType: any Message.Type, + fieldNumber: Int + ) throws + + // Run a decode loop decoding the MessageSet format for Extensions. + mutating func decodeExtensionFieldsAsMessageSet( + values: inout ExtensionFieldValueSet, + messageType: any Message.Type + ) throws } /// Most Decoders won't care about Extension handing as in MessageSet /// format, so provide a default implementation simply looping on the /// fieldNumbers and feeding through to extension decoding. extension Decoder { - public mutating func decodeExtensionFieldsAsMessageSet( - values: inout ExtensionFieldValueSet, - messageType: any Message.Type - ) throws { - while let fieldNumber = try self.nextFieldNumber() { - try self.decodeExtensionField(values: &values, - messageType: messageType, - fieldNumber: fieldNumber) + public mutating func decodeExtensionFieldsAsMessageSet( + values: inout ExtensionFieldValueSet, + messageType: any Message.Type + ) throws { + while let fieldNumber = try self.nextFieldNumber() { + try self.decodeExtensionField( + values: &values, + messageType: messageType, + fieldNumber: fieldNumber + ) + } } - } } diff --git a/Sources/SwiftProtobuf/DoubleParser.swift b/Sources/SwiftProtobuf/DoubleParser.swift index 3af8b5506..e7960c6b1 100644 --- a/Sources/SwiftProtobuf/DoubleParser.swift +++ b/Sources/SwiftProtobuf/DoubleParser.swift @@ -25,16 +25,18 @@ internal class DoubleParser { // parse if someone crafts something really long (especially for // TextFormat due to overflows (see below)). private var work = - UnsafeMutableBufferPointer.allocate(capacity: 128) + UnsafeMutableBufferPointer.allocate(capacity: 128) deinit { work.deallocate() } - func utf8ToDouble(bytes: UnsafeRawBufferPointer, - start: UnsafeRawBufferPointer.Index, - end: UnsafeRawBufferPointer.Index) -> Double? { - return utf8ToDouble(bytes: UnsafeRawBufferPointer(rebasing: bytes[start.. Double? { + utf8ToDouble(bytes: UnsafeRawBufferPointer(rebasing: bytes[start.. Double? { diff --git a/Sources/SwiftProtobuf/Enum.swift b/Sources/SwiftProtobuf/Enum.swift index 44da865ea..a236ae86b 100644 --- a/Sources/SwiftProtobuf/Enum.swift +++ b/Sources/SwiftProtobuf/Enum.swift @@ -18,71 +18,73 @@ /// Generated enum types conform to this protocol. @preconcurrency public protocol Enum: RawRepresentable, Hashable, Sendable { - /// Creates a new instance of the enum initialized to its default value. - init() + /// Creates a new instance of the enum initialized to its default value. + init() - /// Creates a new instance of the enum from the given raw integer value. - /// - /// For proto2 enums, this initializer will fail if the raw value does not - /// correspond to a valid enum value. For proto3 enums, this initializer never - /// fails; unknown values are created as instances of the `UNRECOGNIZED` case. - /// - /// - Parameter rawValue: The raw integer value from which to create the enum - /// value. - init?(rawValue: Int) + /// Creates a new instance of the enum from the given raw integer value. + /// + /// For proto2 enums, this initializer will fail if the raw value does not + /// correspond to a valid enum value. For proto3 enums, this initializer never + /// fails; unknown values are created as instances of the `UNRECOGNIZED` case. + /// + /// - Parameter rawValue: The raw integer value from which to create the enum + /// value. + init?(rawValue: Int) - /// The raw integer value of the enum value. - /// - /// For a recognized enum case, this is the integer value of the case as - /// defined in the .proto file. For `UNRECOGNIZED` cases in proto3, this is - /// the value that was originally decoded. - var rawValue: Int { get } + /// The raw integer value of the enum value. + /// + /// For a recognized enum case, this is the integer value of the case as + /// defined in the .proto file. For `UNRECOGNIZED` cases in proto3, this is + /// the value that was originally decoded. + var rawValue: Int { get } } extension Enum { - public func hash(into hasher: inout Hasher) { - hasher.combine(rawValue) - } + public func hash(into hasher: inout Hasher) { + hasher.combine(rawValue) + } - /// Internal convenience property representing the name of the enum value (or - /// `nil` if it is an `UNRECOGNIZED` value or doesn't provide names). - /// - /// Since the text format and JSON names are always identical, we don't need - /// to distinguish them. - internal var name: _NameMap.Name? { - guard let nameProviding = Self.self as? any _ProtoNameProviding.Type else { - return nil + /// Internal convenience property representing the name of the enum value (or + /// `nil` if it is an `UNRECOGNIZED` value or doesn't provide names). + /// + /// Since the text format and JSON names are always identical, we don't need + /// to distinguish them. + internal var name: _NameMap.Name? { + guard let nameProviding = Self.self as? any _ProtoNameProviding.Type else { + return nil + } + return nameProviding._protobuf_nameMap.names(for: rawValue)?.proto } - return nameProviding._protobuf_nameMap.names(for: rawValue)?.proto - } - /// Internal convenience initializer that returns the enum value with the - /// given name, if it provides names. - /// - /// Since the text format and JSON names are always identical, we don't need - /// to distinguish them. - /// - /// - Parameter name: The name of the enum case. - internal init?(name: String) { - guard let nameProviding = Self.self as? any _ProtoNameProviding.Type, - let number = nameProviding._protobuf_nameMap.number(forJSONName: name) else { - return nil + /// Internal convenience initializer that returns the enum value with the + /// given name, if it provides names. + /// + /// Since the text format and JSON names are always identical, we don't need + /// to distinguish them. + /// + /// - Parameter name: The name of the enum case. + internal init?(name: String) { + guard let nameProviding = Self.self as? any _ProtoNameProviding.Type, + let number = nameProviding._protobuf_nameMap.number(forJSONName: name) + else { + return nil + } + self.init(rawValue: number) } - self.init(rawValue: number) - } - /// Internal convenience initializer that returns the enum value with the - /// given name, if it provides names. - /// - /// Since the text format and JSON names are always identical, we don't need - /// to distinguish them. - /// - /// - Parameter name: Buffer holding the UTF-8 bytes of the desired name. - internal init?(rawUTF8: UnsafeRawBufferPointer) { - guard let nameProviding = Self.self as? any _ProtoNameProviding.Type, - let number = nameProviding._protobuf_nameMap.number(forJSONName: rawUTF8) else { - return nil + /// Internal convenience initializer that returns the enum value with the + /// given name, if it provides names. + /// + /// Since the text format and JSON names are always identical, we don't need + /// to distinguish them. + /// + /// - Parameter name: Buffer holding the UTF-8 bytes of the desired name. + internal init?(rawUTF8: UnsafeRawBufferPointer) { + guard let nameProviding = Self.self as? any _ProtoNameProviding.Type, + let number = nameProviding._protobuf_nameMap.number(forJSONName: rawUTF8) + else { + return nil + } + self.init(rawValue: number) } - self.init(rawValue: number) - } } diff --git a/Sources/SwiftProtobuf/ExtensibleMessage.swift b/Sources/SwiftProtobuf/ExtensibleMessage.swift index 0ade0a5cf..4fbe56cce 100644 --- a/Sources/SwiftProtobuf/ExtensibleMessage.swift +++ b/Sources/SwiftProtobuf/ExtensibleMessage.swift @@ -25,13 +25,13 @@ extension ExtensibleMessage { public func getExtensionValue(ext: MessageExtension) -> F.ValueType? { if let fieldValue = _protobuf_extensionFieldValues[ext.fieldNumber] as? F { - return fieldValue.value + return fieldValue.value } return nil } public func hasExtensionValue(ext: MessageExtension) -> Bool { - return _protobuf_extensionFieldValues[ext.fieldNumber] is F + _protobuf_extensionFieldValues[ext.fieldNumber] is F } public mutating func clearExtensionValue(ext: MessageExtension) { @@ -42,12 +42,16 @@ extension ExtensibleMessage { // Additional specializations for the different types of repeated fields so // setting them to an empty array clears them from the map. extension ExtensibleMessage { - public mutating func setExtensionValue(ext: MessageExtension, Self>, value: [T.BaseType]) { + public mutating func setExtensionValue( + ext: MessageExtension, Self>, + value: [T.BaseType] + ) { _protobuf_extensionFieldValues[ext.fieldNumber] = value.isEmpty ? nil : RepeatedExtensionField(protobufExtension: ext, value: value) } - public mutating func setExtensionValue(ext: MessageExtension, Self>, value: [T.BaseType]) { + public mutating func setExtensionValue(ext: MessageExtension, Self>, value: [T.BaseType]) + { _protobuf_extensionFieldValues[ext.fieldNumber] = value.isEmpty ? nil : PackedExtensionField(protobufExtension: ext, value: value) } @@ -62,7 +66,8 @@ extension ExtensibleMessage { value.isEmpty ? nil : PackedEnumExtensionField(protobufExtension: ext, value: value) } - public mutating func setExtensionValue(ext: MessageExtension, Self>, value: [M]) { + public mutating func setExtensionValue(ext: MessageExtension, Self>, value: [M]) + { _protobuf_extensionFieldValues[ext.fieldNumber] = value.isEmpty ? nil : RepeatedMessageExtensionField(protobufExtension: ext, value: value) } diff --git a/Sources/SwiftProtobuf/ExtensionFieldValueSet.swift b/Sources/SwiftProtobuf/ExtensionFieldValueSet.swift index f429da4b4..69d8450b2 100644 --- a/Sources/SwiftProtobuf/ExtensionFieldValueSet.swift +++ b/Sources/SwiftProtobuf/ExtensionFieldValueSet.swift @@ -15,70 +15,75 @@ // ----------------------------------------------------------------------------- public struct ExtensionFieldValueSet: Hashable, Sendable { - fileprivate var values = [Int : any AnyExtensionField]() + fileprivate var values = [Int: any AnyExtensionField]() - public static func ==(lhs: ExtensionFieldValueSet, - rhs: ExtensionFieldValueSet) -> Bool { - guard lhs.values.count == rhs.values.count else { - return false - } - for (index, l) in lhs.values { - if let r = rhs.values[index] { - if type(of: l) != type(of: r) { - return false + public static func == ( + lhs: ExtensionFieldValueSet, + rhs: ExtensionFieldValueSet + ) -> Bool { + guard lhs.values.count == rhs.values.count else { + return false } - if !l.isEqual(other: r) { - return false + for (index, l) in lhs.values { + if let r = rhs.values[index] { + if type(of: l) != type(of: r) { + return false + } + if !l.isEqual(other: r) { + return false + } + } else { + return false + } } - } else { - return false - } + return true } - return true - } - public init() {} + public init() {} - public func hash(into hasher: inout Hasher) { - // AnyExtensionField is not Hashable, and the Self constraint that would - // add breaks some of the uses of it; so the only choice is to manually - // mix things in. However, one must remember to do things in an order - // independent manner. - var hash = 16777619 - for (fieldNumber, v) in values { - var localHasher = hasher - localHasher.combine(fieldNumber) - v.hash(into: &localHasher) - hash = hash &+ localHasher.finalize() + public func hash(into hasher: inout Hasher) { + // AnyExtensionField is not Hashable, and the Self constraint that would + // add breaks some of the uses of it; so the only choice is to manually + // mix things in. However, one must remember to do things in an order + // independent manner. + var hash = 16_777_619 + for (fieldNumber, v) in values { + var localHasher = hasher + localHasher.combine(fieldNumber) + v.hash(into: &localHasher) + hash = hash &+ localHasher.finalize() + } + hasher.combine(hash) } - hasher.combine(hash) - } - public func traverse(visitor: inout V, start: Int, end: Int) throws { - let validIndexes = values.keys.filter {$0 >= start && $0 < end} - for i in validIndexes.sorted() { - let value = values[i]! - try value.traverse(visitor: &visitor) + public func traverse(visitor: inout V, start: Int, end: Int) throws { + let validIndexes = values.keys.filter { $0 >= start && $0 < end } + for i in validIndexes.sorted() { + let value = values[i]! + try value.traverse(visitor: &visitor) + } } - } - public subscript(index: Int) -> (any AnyExtensionField)? { - get { return values[index] } - set { values[index] = newValue } - } + public subscript(index: Int) -> (any AnyExtensionField)? { + get { values[index] } + set { values[index] = newValue } + } - mutating func modify(index: Int, _ modifier: (inout (any AnyExtensionField)?) throws -> ReturnType) rethrows -> ReturnType { - // This internal helper exists to invoke the _modify accessor on Dictionary for the given operation, which can avoid CoWs - // during the modification operation. - return try modifier(&values[index]) - } + mutating func modify( + index: Int, + _ modifier: (inout (any AnyExtensionField)?) throws -> ReturnType + ) rethrows -> ReturnType { + // This internal helper exists to invoke the _modify accessor on Dictionary for the given operation, which can avoid CoWs + // during the modification operation. + try modifier(&values[index]) + } - public var isInitialized: Bool { - for (_, v) in values { - if !v.isInitialized { - return false - } + public var isInitialized: Bool { + for (_, v) in values { + if !v.isInitialized { + return false + } + } + return true } - return true - } } diff --git a/Sources/SwiftProtobuf/ExtensionFields.swift b/Sources/SwiftProtobuf/ExtensionFields.swift index c1696041a..17c7ff0f2 100644 --- a/Sources/SwiftProtobuf/ExtensionFields.swift +++ b/Sources/SwiftProtobuf/ExtensionFields.swift @@ -24,24 +24,24 @@ // @preconcurrency public protocol AnyExtensionField: Sendable, CustomDebugStringConvertible { - func hash(into hasher: inout Hasher) - var protobufExtension: any AnyMessageExtension { get } - func isEqual(other: any AnyExtensionField) -> Bool + func hash(into hasher: inout Hasher) + var protobufExtension: any AnyMessageExtension { get } + func isEqual(other: any AnyExtensionField) -> Bool - /// Merging field decoding - mutating func decodeExtensionField(decoder: inout T) throws + /// Merging field decoding + mutating func decodeExtensionField(decoder: inout T) throws - /// Fields know their own type, so can dispatch to a visitor - func traverse(visitor: inout V) throws + /// Fields know their own type, so can dispatch to a visitor + func traverse(visitor: inout V) throws - /// Check if the field is initialized. - var isInitialized: Bool { get } + /// Check if the field is initialized. + var isInitialized: Bool { get } } extension AnyExtensionField { - // Default implementation for extensions fields. The message types below provide - // custom versions. - public var isInitialized: Bool { return true } + // Default implementation for extensions fields. The message types below provide + // custom versions. + public var isInitialized: Bool { true } } /// @@ -49,122 +49,126 @@ extension AnyExtensionField { /// @preconcurrency public protocol ExtensionField: AnyExtensionField, Hashable { - associatedtype ValueType - var value: ValueType { get set } - init(protobufExtension: any AnyMessageExtension, value: ValueType) - init?(protobufExtension: any AnyMessageExtension, decoder: inout D) throws + associatedtype ValueType + var value: ValueType { get set } + init(protobufExtension: any AnyMessageExtension, value: ValueType) + init?(protobufExtension: any AnyMessageExtension, decoder: inout D) throws } /// /// Singular field /// public struct OptionalExtensionField: ExtensionField { - public typealias BaseType = T.BaseType - public typealias ValueType = BaseType - public var value: ValueType - public var protobufExtension: any AnyMessageExtension - - public static func ==(lhs: OptionalExtensionField, - rhs: OptionalExtensionField) -> Bool { - return lhs.value == rhs.value - } - - public init(protobufExtension: any AnyMessageExtension, value: ValueType) { - self.protobufExtension = protobufExtension - self.value = value - } - - public var debugDescription: String { - #if DEBUG - return String(reflecting: value) - #else - return String(reflecting: type(of: self)) - #endif - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(value) - } - - public func isEqual(other: any AnyExtensionField) -> Bool { - let o = other as! OptionalExtensionField - return self == o - } - - public mutating func decodeExtensionField(decoder: inout D) throws { - var v: ValueType? - try T.decodeSingular(value: &v, from: &decoder) - if let v = v { - value = v - } - } - - public init?(protobufExtension: any AnyMessageExtension, decoder: inout D) throws { - var v: ValueType? - try T.decodeSingular(value: &v, from: &decoder) - if let v = v { - self.init(protobufExtension: protobufExtension, value: v) - } else { - return nil - } - } - - public func traverse(visitor: inout V) throws { - try T.visitSingular(value: value, fieldNumber: protobufExtension.fieldNumber, with: &visitor) - } + public typealias BaseType = T.BaseType + public typealias ValueType = BaseType + public var value: ValueType + public var protobufExtension: any AnyMessageExtension + + public static func == ( + lhs: OptionalExtensionField, + rhs: OptionalExtensionField + ) -> Bool { + lhs.value == rhs.value + } + + public init(protobufExtension: any AnyMessageExtension, value: ValueType) { + self.protobufExtension = protobufExtension + self.value = value + } + + public var debugDescription: String { + #if DEBUG + return String(reflecting: value) + #else + return String(reflecting: type(of: self)) + #endif + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(value) + } + + public func isEqual(other: any AnyExtensionField) -> Bool { + let o = other as! OptionalExtensionField + return self == o + } + + public mutating func decodeExtensionField(decoder: inout D) throws { + var v: ValueType? + try T.decodeSingular(value: &v, from: &decoder) + if let v = v { + value = v + } + } + + public init?(protobufExtension: any AnyMessageExtension, decoder: inout D) throws { + var v: ValueType? + try T.decodeSingular(value: &v, from: &decoder) + if let v = v { + self.init(protobufExtension: protobufExtension, value: v) + } else { + return nil + } + } + + public func traverse(visitor: inout V) throws { + try T.visitSingular(value: value, fieldNumber: protobufExtension.fieldNumber, with: &visitor) + } } /// /// Repeated fields /// public struct RepeatedExtensionField: ExtensionField { - public typealias BaseType = T.BaseType - public typealias ValueType = [BaseType] - public var value: ValueType - public var protobufExtension: any AnyMessageExtension - - public static func ==(lhs: RepeatedExtensionField, - rhs: RepeatedExtensionField) -> Bool { - return lhs.value == rhs.value - } - - public init(protobufExtension: any AnyMessageExtension, value: ValueType) { - self.protobufExtension = protobufExtension - self.value = value - } - - public var debugDescription: String { - #if DEBUG - return "[" + value.map{String(reflecting: $0)}.joined(separator: ",") + "]" - #else - return String(reflecting: type(of: self)) - #endif - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(value) - } - - public func isEqual(other: any AnyExtensionField) -> Bool { - let o = other as! RepeatedExtensionField - return self == o - } - - public mutating func decodeExtensionField(decoder: inout D) throws { - try T.decodeRepeated(value: &value, from: &decoder) - } - - public init?(protobufExtension: any AnyMessageExtension, decoder: inout D) throws { - var v: ValueType = [] - try T.decodeRepeated(value: &v, from: &decoder) - self.init(protobufExtension: protobufExtension, value: v) - } - - public func traverse(visitor: inout V) throws { - if value.count > 0 { - try T.visitRepeated(value: value, fieldNumber: protobufExtension.fieldNumber, with: &visitor) - } - } + public typealias BaseType = T.BaseType + public typealias ValueType = [BaseType] + public var value: ValueType + public var protobufExtension: any AnyMessageExtension + + public static func == ( + lhs: RepeatedExtensionField, + rhs: RepeatedExtensionField + ) -> Bool { + lhs.value == rhs.value + } + + public init(protobufExtension: any AnyMessageExtension, value: ValueType) { + self.protobufExtension = protobufExtension + self.value = value + } + + public var debugDescription: String { + #if DEBUG + return "[" + value.map { String(reflecting: $0) }.joined(separator: ",") + "]" + #else + return String(reflecting: type(of: self)) + #endif + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(value) + } + + public func isEqual(other: any AnyExtensionField) -> Bool { + let o = other as! RepeatedExtensionField + return self == o + } + + public mutating func decodeExtensionField(decoder: inout D) throws { + try T.decodeRepeated(value: &value, from: &decoder) + } + + public init?(protobufExtension: any AnyMessageExtension, decoder: inout D) throws { + var v: ValueType = [] + try T.decodeRepeated(value: &v, from: &decoder) + self.init(protobufExtension: protobufExtension, value: v) + } + + public func traverse(visitor: inout V) throws { + if value.count > 0 { + try T.visitRepeated(value: value, fieldNumber: protobufExtension.fieldNumber, with: &visitor) + } + } } /// @@ -174,169 +178,177 @@ public struct RepeatedExtensionField: ExtensionField { /// find a way to collapse the implementations. /// public struct PackedExtensionField: ExtensionField { - public typealias BaseType = T.BaseType - public typealias ValueType = [BaseType] - public var value: ValueType - public var protobufExtension: any AnyMessageExtension - - public static func ==(lhs: PackedExtensionField, - rhs: PackedExtensionField) -> Bool { - return lhs.value == rhs.value - } - - public init(protobufExtension: any AnyMessageExtension, value: ValueType) { - self.protobufExtension = protobufExtension - self.value = value - } - - public var debugDescription: String { - #if DEBUG - return "[" + value.map{String(reflecting: $0)}.joined(separator: ",") + "]" - #else - return String(reflecting: type(of: self)) - #endif - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(value) - } - - public func isEqual(other: any AnyExtensionField) -> Bool { - let o = other as! PackedExtensionField - return self == o - } - - public mutating func decodeExtensionField(decoder: inout D) throws { - try T.decodeRepeated(value: &value, from: &decoder) - } - - public init?(protobufExtension: any AnyMessageExtension, decoder: inout D) throws { - var v: ValueType = [] - try T.decodeRepeated(value: &v, from: &decoder) - self.init(protobufExtension: protobufExtension, value: v) - } - - public func traverse(visitor: inout V) throws { - if value.count > 0 { - try T.visitPacked(value: value, fieldNumber: protobufExtension.fieldNumber, with: &visitor) - } - } + public typealias BaseType = T.BaseType + public typealias ValueType = [BaseType] + public var value: ValueType + public var protobufExtension: any AnyMessageExtension + + public static func == ( + lhs: PackedExtensionField, + rhs: PackedExtensionField + ) -> Bool { + lhs.value == rhs.value + } + + public init(protobufExtension: any AnyMessageExtension, value: ValueType) { + self.protobufExtension = protobufExtension + self.value = value + } + + public var debugDescription: String { + #if DEBUG + return "[" + value.map { String(reflecting: $0) }.joined(separator: ",") + "]" + #else + return String(reflecting: type(of: self)) + #endif + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(value) + } + + public func isEqual(other: any AnyExtensionField) -> Bool { + let o = other as! PackedExtensionField + return self == o + } + + public mutating func decodeExtensionField(decoder: inout D) throws { + try T.decodeRepeated(value: &value, from: &decoder) + } + + public init?(protobufExtension: any AnyMessageExtension, decoder: inout D) throws { + var v: ValueType = [] + try T.decodeRepeated(value: &v, from: &decoder) + self.init(protobufExtension: protobufExtension, value: v) + } + + public func traverse(visitor: inout V) throws { + if value.count > 0 { + try T.visitPacked(value: value, fieldNumber: protobufExtension.fieldNumber, with: &visitor) + } + } } /// /// Enum extensions /// public struct OptionalEnumExtensionField: ExtensionField where E.RawValue == Int { - public typealias BaseType = E - public typealias ValueType = E - public var value: ValueType - public var protobufExtension: any AnyMessageExtension - - public static func ==(lhs: OptionalEnumExtensionField, - rhs: OptionalEnumExtensionField) -> Bool { - return lhs.value == rhs.value - } - - public init(protobufExtension: any AnyMessageExtension, value: ValueType) { - self.protobufExtension = protobufExtension - self.value = value - } - - public var debugDescription: String { - #if DEBUG - return String(reflecting: value) - #else - return String(reflecting: type(of: self)) - #endif - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(value) - } - - public func isEqual(other: any AnyExtensionField) -> Bool { - let o = other as! OptionalEnumExtensionField - return self == o - } - - public mutating func decodeExtensionField(decoder: inout D) throws { - var v: ValueType? - try decoder.decodeSingularEnumField(value: &v) - if let v = v { - value = v - } - } - - public init?(protobufExtension: any AnyMessageExtension, decoder: inout D) throws { - var v: ValueType? - try decoder.decodeSingularEnumField(value: &v) - if let v = v { - self.init(protobufExtension: protobufExtension, value: v) - } else { - return nil - } - } - - public func traverse(visitor: inout V) throws { - try visitor.visitSingularEnumField( - value: value, - fieldNumber: protobufExtension.fieldNumber) - } + public typealias BaseType = E + public typealias ValueType = E + public var value: ValueType + public var protobufExtension: any AnyMessageExtension + + public static func == ( + lhs: OptionalEnumExtensionField, + rhs: OptionalEnumExtensionField + ) -> Bool { + lhs.value == rhs.value + } + + public init(protobufExtension: any AnyMessageExtension, value: ValueType) { + self.protobufExtension = protobufExtension + self.value = value + } + + public var debugDescription: String { + #if DEBUG + return String(reflecting: value) + #else + return String(reflecting: type(of: self)) + #endif + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(value) + } + + public func isEqual(other: any AnyExtensionField) -> Bool { + let o = other as! OptionalEnumExtensionField + return self == o + } + + public mutating func decodeExtensionField(decoder: inout D) throws { + var v: ValueType? + try decoder.decodeSingularEnumField(value: &v) + if let v = v { + value = v + } + } + + public init?(protobufExtension: any AnyMessageExtension, decoder: inout D) throws { + var v: ValueType? + try decoder.decodeSingularEnumField(value: &v) + if let v = v { + self.init(protobufExtension: protobufExtension, value: v) + } else { + return nil + } + } + + public func traverse(visitor: inout V) throws { + try visitor.visitSingularEnumField( + value: value, + fieldNumber: protobufExtension.fieldNumber + ) + } } /// /// Repeated Enum fields /// public struct RepeatedEnumExtensionField: ExtensionField where E.RawValue == Int { - public typealias BaseType = E - public typealias ValueType = [E] - public var value: ValueType - public var protobufExtension: any AnyMessageExtension - - public static func ==(lhs: RepeatedEnumExtensionField, - rhs: RepeatedEnumExtensionField) -> Bool { - return lhs.value == rhs.value - } - - public init(protobufExtension: any AnyMessageExtension, value: ValueType) { - self.protobufExtension = protobufExtension - self.value = value - } - - public var debugDescription: String { - #if DEBUG - return "[" + value.map{String(reflecting: $0)}.joined(separator: ",") + "]" - #else - return String(reflecting: type(of: self)) - #endif - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(value) - } - - public func isEqual(other: any AnyExtensionField) -> Bool { - let o = other as! RepeatedEnumExtensionField - return self == o - } - - public mutating func decodeExtensionField(decoder: inout D) throws { - try decoder.decodeRepeatedEnumField(value: &value) - } - - public init?(protobufExtension: any AnyMessageExtension, decoder: inout D) throws { - var v: ValueType = [] - try decoder.decodeRepeatedEnumField(value: &v) - self.init(protobufExtension: protobufExtension, value: v) - } - - public func traverse(visitor: inout V) throws { - if value.count > 0 { - try visitor.visitRepeatedEnumField( - value: value, - fieldNumber: protobufExtension.fieldNumber) - } - } + public typealias BaseType = E + public typealias ValueType = [E] + public var value: ValueType + public var protobufExtension: any AnyMessageExtension + + public static func == ( + lhs: RepeatedEnumExtensionField, + rhs: RepeatedEnumExtensionField + ) -> Bool { + lhs.value == rhs.value + } + + public init(protobufExtension: any AnyMessageExtension, value: ValueType) { + self.protobufExtension = protobufExtension + self.value = value + } + + public var debugDescription: String { + #if DEBUG + return "[" + value.map { String(reflecting: $0) }.joined(separator: ",") + "]" + #else + return String(reflecting: type(of: self)) + #endif + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(value) + } + + public func isEqual(other: any AnyExtensionField) -> Bool { + let o = other as! RepeatedEnumExtensionField + return self == o + } + + public mutating func decodeExtensionField(decoder: inout D) throws { + try decoder.decodeRepeatedEnumField(value: &value) + } + + public init?(protobufExtension: any AnyMessageExtension, decoder: inout D) throws { + var v: ValueType = [] + try decoder.decodeRepeatedEnumField(value: &v) + self.init(protobufExtension: protobufExtension, value: v) + } + + public func traverse(visitor: inout V) throws { + if value.count > 0 { + try visitor.visitRepeatedEnumField( + value: value, + fieldNumber: protobufExtension.fieldNumber + ) + } + } } /// @@ -346,178 +358,191 @@ public struct RepeatedEnumExtensionField: ExtensionField where E.RawVal /// find a way to collapse the implementations. /// public struct PackedEnumExtensionField: ExtensionField where E.RawValue == Int { - public typealias BaseType = E - public typealias ValueType = [E] - public var value: ValueType - public var protobufExtension: any AnyMessageExtension - - public static func ==(lhs: PackedEnumExtensionField, - rhs: PackedEnumExtensionField) -> Bool { - return lhs.value == rhs.value - } - - public init(protobufExtension: any AnyMessageExtension, value: ValueType) { - self.protobufExtension = protobufExtension - self.value = value - } - - public var debugDescription: String { - #if DEBUG - return "[" + value.map{String(reflecting: $0)}.joined(separator: ",") + "]" - #else - return String(reflecting: type(of: self)) - #endif - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(value) - } - - public func isEqual(other: any AnyExtensionField) -> Bool { - let o = other as! PackedEnumExtensionField - return self == o - } - - public mutating func decodeExtensionField(decoder: inout D) throws { - try decoder.decodeRepeatedEnumField(value: &value) - } - - public init?(protobufExtension: any AnyMessageExtension, decoder: inout D) throws { - var v: ValueType = [] - try decoder.decodeRepeatedEnumField(value: &v) - self.init(protobufExtension: protobufExtension, value: v) - } - - public func traverse(visitor: inout V) throws { - if value.count > 0 { - try visitor.visitPackedEnumField( - value: value, - fieldNumber: protobufExtension.fieldNumber) - } - } + public typealias BaseType = E + public typealias ValueType = [E] + public var value: ValueType + public var protobufExtension: any AnyMessageExtension + + public static func == ( + lhs: PackedEnumExtensionField, + rhs: PackedEnumExtensionField + ) -> Bool { + lhs.value == rhs.value + } + + public init(protobufExtension: any AnyMessageExtension, value: ValueType) { + self.protobufExtension = protobufExtension + self.value = value + } + + public var debugDescription: String { + #if DEBUG + return "[" + value.map { String(reflecting: $0) }.joined(separator: ",") + "]" + #else + return String(reflecting: type(of: self)) + #endif + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(value) + } + + public func isEqual(other: any AnyExtensionField) -> Bool { + let o = other as! PackedEnumExtensionField + return self == o + } + + public mutating func decodeExtensionField(decoder: inout D) throws { + try decoder.decodeRepeatedEnumField(value: &value) + } + + public init?(protobufExtension: any AnyMessageExtension, decoder: inout D) throws { + var v: ValueType = [] + try decoder.decodeRepeatedEnumField(value: &v) + self.init(protobufExtension: protobufExtension, value: v) + } + + public func traverse(visitor: inout V) throws { + if value.count > 0 { + try visitor.visitPackedEnumField( + value: value, + fieldNumber: protobufExtension.fieldNumber + ) + } + } } // // ========== Message ========== // public struct OptionalMessageExtensionField: - ExtensionField { - public typealias BaseType = M - public typealias ValueType = BaseType - public var value: ValueType - public var protobufExtension: any AnyMessageExtension - - public static func ==(lhs: OptionalMessageExtensionField, - rhs: OptionalMessageExtensionField) -> Bool { - return lhs.value == rhs.value - } - - public init(protobufExtension: any AnyMessageExtension, value: ValueType) { - self.protobufExtension = protobufExtension - self.value = value - } - - public var debugDescription: String { - #if DEBUG - return String(reflecting: value) - #else - return String(reflecting: type(of: self)) - #endif - } - - public func hash(into hasher: inout Hasher) { - value.hash(into: &hasher) - } - - public func isEqual(other: any AnyExtensionField) -> Bool { - let o = other as! OptionalMessageExtensionField - return self == o - } - - public mutating func decodeExtensionField(decoder: inout D) throws { - var v: ValueType? = value - try decoder.decodeSingularMessageField(value: &v) - if let v = v { - self.value = v - } - } - - public init?(protobufExtension: any AnyMessageExtension, decoder: inout D) throws { - var v: ValueType? - try decoder.decodeSingularMessageField(value: &v) - if let v = v { - self.init(protobufExtension: protobufExtension, value: v) - } else { - return nil - } - } - - public func traverse(visitor: inout V) throws { - try visitor.visitSingularMessageField( - value: value, fieldNumber: protobufExtension.fieldNumber) - } - - public var isInitialized: Bool { - return value.isInitialized - } + ExtensionField +{ + public typealias BaseType = M + public typealias ValueType = BaseType + public var value: ValueType + public var protobufExtension: any AnyMessageExtension + + public static func == ( + lhs: OptionalMessageExtensionField, + rhs: OptionalMessageExtensionField + ) -> Bool { + lhs.value == rhs.value + } + + public init(protobufExtension: any AnyMessageExtension, value: ValueType) { + self.protobufExtension = protobufExtension + self.value = value + } + + public var debugDescription: String { + #if DEBUG + return String(reflecting: value) + #else + return String(reflecting: type(of: self)) + #endif + } + + public func hash(into hasher: inout Hasher) { + value.hash(into: &hasher) + } + + public func isEqual(other: any AnyExtensionField) -> Bool { + let o = other as! OptionalMessageExtensionField + return self == o + } + + public mutating func decodeExtensionField(decoder: inout D) throws { + var v: ValueType? = value + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + self.value = v + } + } + + public init?(protobufExtension: any AnyMessageExtension, decoder: inout D) throws { + var v: ValueType? + try decoder.decodeSingularMessageField(value: &v) + if let v = v { + self.init(protobufExtension: protobufExtension, value: v) + } else { + return nil + } + } + + public func traverse(visitor: inout V) throws { + try visitor.visitSingularMessageField( + value: value, + fieldNumber: protobufExtension.fieldNumber + ) + } + + public var isInitialized: Bool { + value.isInitialized + } } public struct RepeatedMessageExtensionField: - ExtensionField { - public typealias BaseType = M - public typealias ValueType = [BaseType] - public var value: ValueType - public var protobufExtension: any AnyMessageExtension - - public static func ==(lhs: RepeatedMessageExtensionField, - rhs: RepeatedMessageExtensionField) -> Bool { - return lhs.value == rhs.value - } - - public init(protobufExtension: any AnyMessageExtension, value: ValueType) { - self.protobufExtension = protobufExtension - self.value = value - } - - public var debugDescription: String { - #if DEBUG - return "[" + value.map{String(reflecting: $0)}.joined(separator: ",") + "]" - #else - return String(reflecting: type(of: self)) - #endif - } - - public func hash(into hasher: inout Hasher) { - for e in value { - e.hash(into: &hasher) - } - } - - public func isEqual(other: any AnyExtensionField) -> Bool { - let o = other as! RepeatedMessageExtensionField - return self == o - } - - public mutating func decodeExtensionField(decoder: inout D) throws { - try decoder.decodeRepeatedMessageField(value: &value) - } - - public init?(protobufExtension: any AnyMessageExtension, decoder: inout D) throws { - var v: ValueType = [] - try decoder.decodeRepeatedMessageField(value: &v) - self.init(protobufExtension: protobufExtension, value: v) - } - - public func traverse(visitor: inout V) throws { - if value.count > 0 { - try visitor.visitRepeatedMessageField( - value: value, fieldNumber: protobufExtension.fieldNumber) - } - } - - public var isInitialized: Bool { - return Internal.areAllInitialized(value) - } + ExtensionField +{ + public typealias BaseType = M + public typealias ValueType = [BaseType] + public var value: ValueType + public var protobufExtension: any AnyMessageExtension + + public static func == ( + lhs: RepeatedMessageExtensionField, + rhs: RepeatedMessageExtensionField + ) -> Bool { + lhs.value == rhs.value + } + + public init(protobufExtension: any AnyMessageExtension, value: ValueType) { + self.protobufExtension = protobufExtension + self.value = value + } + + public var debugDescription: String { + #if DEBUG + return "[" + value.map { String(reflecting: $0) }.joined(separator: ",") + "]" + #else + return String(reflecting: type(of: self)) + #endif + } + + public func hash(into hasher: inout Hasher) { + for e in value { + e.hash(into: &hasher) + } + } + + public func isEqual(other: any AnyExtensionField) -> Bool { + let o = other as! RepeatedMessageExtensionField + return self == o + } + + public mutating func decodeExtensionField(decoder: inout D) throws { + try decoder.decodeRepeatedMessageField(value: &value) + } + + public init?(protobufExtension: any AnyMessageExtension, decoder: inout D) throws { + var v: ValueType = [] + try decoder.decodeRepeatedMessageField(value: &v) + self.init(protobufExtension: protobufExtension, value: v) + } + + public func traverse(visitor: inout V) throws { + if value.count > 0 { + try visitor.visitRepeatedMessageField( + value: value, + fieldNumber: protobufExtension.fieldNumber + ) + } + } + + public var isInitialized: Bool { + Internal.areAllInitialized(value) + } } // @@ -527,119 +552,129 @@ public struct RepeatedMessageExtensionField: // they serialize very differently, so we have separate serialization // handling here... public struct OptionalGroupExtensionField: - ExtensionField { - public typealias BaseType = G - public typealias ValueType = BaseType - public var value: G - public var protobufExtension: any AnyMessageExtension - - public static func ==(lhs: OptionalGroupExtensionField, - rhs: OptionalGroupExtensionField) -> Bool { - return lhs.value == rhs.value - } - - public init(protobufExtension: any AnyMessageExtension, value: ValueType) { - self.protobufExtension = protobufExtension - self.value = value - } - - public var debugDescription: String { - #if DEBUG - return value.debugDescription - #else - return String(reflecting: type(of: self)) - #endif - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(value) - } - - public func isEqual(other: any AnyExtensionField) -> Bool { - let o = other as! OptionalGroupExtensionField - return self == o - } - - public mutating func decodeExtensionField(decoder: inout D) throws { - var v: ValueType? = value - try decoder.decodeSingularGroupField(value: &v) - if let v = v { - value = v - } - } - - public init?(protobufExtension: any AnyMessageExtension, decoder: inout D) throws { - var v: ValueType? - try decoder.decodeSingularGroupField(value: &v) - if let v = v { - self.init(protobufExtension: protobufExtension, value: v) - } else { - return nil - } - } - - public func traverse(visitor: inout V) throws { - try visitor.visitSingularGroupField( - value: value, fieldNumber: protobufExtension.fieldNumber) - } - - public var isInitialized: Bool { - return value.isInitialized - } + ExtensionField +{ + public typealias BaseType = G + public typealias ValueType = BaseType + public var value: G + public var protobufExtension: any AnyMessageExtension + + public static func == ( + lhs: OptionalGroupExtensionField, + rhs: OptionalGroupExtensionField + ) -> Bool { + lhs.value == rhs.value + } + + public init(protobufExtension: any AnyMessageExtension, value: ValueType) { + self.protobufExtension = protobufExtension + self.value = value + } + + public var debugDescription: String { + #if DEBUG + return value.debugDescription + #else + return String(reflecting: type(of: self)) + #endif + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(value) + } + + public func isEqual(other: any AnyExtensionField) -> Bool { + let o = other as! OptionalGroupExtensionField + return self == o + } + + public mutating func decodeExtensionField(decoder: inout D) throws { + var v: ValueType? = value + try decoder.decodeSingularGroupField(value: &v) + if let v = v { + value = v + } + } + + public init?(protobufExtension: any AnyMessageExtension, decoder: inout D) throws { + var v: ValueType? + try decoder.decodeSingularGroupField(value: &v) + if let v = v { + self.init(protobufExtension: protobufExtension, value: v) + } else { + return nil + } + } + + public func traverse(visitor: inout V) throws { + try visitor.visitSingularGroupField( + value: value, + fieldNumber: protobufExtension.fieldNumber + ) + } + + public var isInitialized: Bool { + value.isInitialized + } } public struct RepeatedGroupExtensionField: - ExtensionField { - public typealias BaseType = G - public typealias ValueType = [BaseType] - public var value: ValueType - public var protobufExtension: any AnyMessageExtension - - public static func ==(lhs: RepeatedGroupExtensionField, - rhs: RepeatedGroupExtensionField) -> Bool { - return lhs.value == rhs.value - } - - public init(protobufExtension: any AnyMessageExtension, value: ValueType) { - self.protobufExtension = protobufExtension - self.value = value - } - - public var debugDescription: String { - #if DEBUG - return "[" + value.map{$0.debugDescription}.joined(separator: ",") + "]" - #else - return String(reflecting: type(of: self)) - #endif - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(value) - } - - public func isEqual(other: any AnyExtensionField) -> Bool { - let o = other as! RepeatedGroupExtensionField - return self == o - } - - public mutating func decodeExtensionField(decoder: inout D) throws { - try decoder.decodeRepeatedGroupField(value: &value) - } - - public init?(protobufExtension: any AnyMessageExtension, decoder: inout D) throws { - var v: ValueType = [] - try decoder.decodeRepeatedGroupField(value: &v) - self.init(protobufExtension: protobufExtension, value: v) - } - - public func traverse(visitor: inout V) throws { - if value.count > 0 { - try visitor.visitRepeatedGroupField( - value: value, fieldNumber: protobufExtension.fieldNumber) - } - } - - public var isInitialized: Bool { - return Internal.areAllInitialized(value) - } + ExtensionField +{ + public typealias BaseType = G + public typealias ValueType = [BaseType] + public var value: ValueType + public var protobufExtension: any AnyMessageExtension + + public static func == ( + lhs: RepeatedGroupExtensionField, + rhs: RepeatedGroupExtensionField + ) -> Bool { + lhs.value == rhs.value + } + + public init(protobufExtension: any AnyMessageExtension, value: ValueType) { + self.protobufExtension = protobufExtension + self.value = value + } + + public var debugDescription: String { + #if DEBUG + return "[" + value.map { $0.debugDescription }.joined(separator: ",") + "]" + #else + return String(reflecting: type(of: self)) + #endif + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(value) + } + + public func isEqual(other: any AnyExtensionField) -> Bool { + let o = other as! RepeatedGroupExtensionField + return self == o + } + + public mutating func decodeExtensionField(decoder: inout D) throws { + try decoder.decodeRepeatedGroupField(value: &value) + } + + public init?(protobufExtension: any AnyMessageExtension, decoder: inout D) throws { + var v: ValueType = [] + try decoder.decodeRepeatedGroupField(value: &v) + self.init(protobufExtension: protobufExtension, value: v) + } + + public func traverse(visitor: inout V) throws { + if value.count > 0 { + try visitor.visitRepeatedGroupField( + value: value, + fieldNumber: protobufExtension.fieldNumber + ) + } + } + + public var isInitialized: Bool { + Internal.areAllInitialized(value) + } } diff --git a/Sources/SwiftProtobuf/FieldTag.swift b/Sources/SwiftProtobuf/FieldTag.swift index 0a92cbb8e..da256aed9 100644 --- a/Sources/SwiftProtobuf/FieldTag.swift +++ b/Sources/SwiftProtobuf/FieldTag.swift @@ -21,49 +21,49 @@ /// for validity because they are guaranteed correct at initialization time. internal struct FieldTag: RawRepresentable { - typealias RawValue = UInt32 + typealias RawValue = UInt32 - /// The raw numeric value of the tag, which contains both the field number and - /// wire format. - let rawValue: UInt32 + /// The raw numeric value of the tag, which contains both the field number and + /// wire format. + let rawValue: UInt32 - /// The field number component of the tag. - var fieldNumber: Int { - return Int(rawValue >> 3) - } + /// The field number component of the tag. + var fieldNumber: Int { + Int(rawValue >> 3) + } - /// The wire format component of the tag. - var wireFormat: WireFormat { - // This force-unwrap is safe because there are only two initialization - // paths: one that takes a WireFormat directly (and is guaranteed valid at - // compile-time), or one that takes a raw value but which only lets valid - // wire formats through. - return WireFormat(rawValue: UInt8(rawValue & 7))! - } + /// The wire format component of the tag. + var wireFormat: WireFormat { + // This force-unwrap is safe because there are only two initialization + // paths: one that takes a WireFormat directly (and is guaranteed valid at + // compile-time), or one that takes a raw value but which only lets valid + // wire formats through. + WireFormat(rawValue: UInt8(rawValue & 7))! + } - /// A helper property that returns the number of bytes required to - /// varint-encode this tag. - var encodedSize: Int { - return Varint.encodedSize(of: rawValue) - } + /// A helper property that returns the number of bytes required to + /// varint-encode this tag. + var encodedSize: Int { + Varint.encodedSize(of: rawValue) + } - /// Creates a new tag from its raw numeric representation. - /// - /// Note that if the raw value given here is not a valid tag (for example, it - /// has an invalid wire format), this initializer will fail. - init?(rawValue: UInt32) { - // Verify that the field number and wire format are valid and fail if they - // are not. - guard rawValue & ~0x07 != 0, - let _ = WireFormat(rawValue: UInt8(rawValue % 8)) else { - return nil + /// Creates a new tag from its raw numeric representation. + /// + /// Note that if the raw value given here is not a valid tag (for example, it + /// has an invalid wire format), this initializer will fail. + init?(rawValue: UInt32) { + // Verify that the field number and wire format are valid and fail if they + // are not. + guard rawValue & ~0x07 != 0, + let _ = WireFormat(rawValue: UInt8(rawValue % 8)) + else { + return nil + } + self.rawValue = rawValue } - self.rawValue = rawValue - } - /// Creates a new tag by composing the given field number and wire format. - init(fieldNumber: Int, wireFormat: WireFormat) { - self.rawValue = UInt32(truncatingIfNeeded: fieldNumber) << 3 | - UInt32(wireFormat.rawValue) - } + /// Creates a new tag by composing the given field number and wire format. + init(fieldNumber: Int, wireFormat: WireFormat) { + self.rawValue = UInt32(truncatingIfNeeded: fieldNumber) << 3 | UInt32(wireFormat.rawValue) + } } diff --git a/Sources/SwiftProtobuf/FieldTypes.swift b/Sources/SwiftProtobuf/FieldTypes.swift index b68a38809..a9bbe3cc1 100644 --- a/Sources/SwiftProtobuf/FieldTypes.swift +++ b/Sources/SwiftProtobuf/FieldTypes.swift @@ -61,7 +61,7 @@ public protocol MapKeyType: FieldType { // Default impl for anything `Comparable` extension MapKeyType where BaseType: Comparable { public static func _lessThan(lhs: BaseType, rhs: BaseType) -> Bool { - return lhs < rhs + lhs < rhs } } @@ -82,7 +82,7 @@ public protocol MapValueType: FieldType { /// public struct ProtobufFloat: FieldType, MapValueType { public typealias BaseType = Float - public static var proto3DefaultValue: Float {return 0.0} + public static var proto3DefaultValue: Float { 0.0 } public static func decodeSingular(value: inout BaseType?, from decoder: inout D) throws { try decoder.decodeSingularFloatField(value: &value) } @@ -105,7 +105,7 @@ public struct ProtobufFloat: FieldType, MapValueType { /// public struct ProtobufDouble: FieldType, MapValueType { public typealias BaseType = Double - public static var proto3DefaultValue: Double {return 0.0} + public static var proto3DefaultValue: Double { 0.0 } public static func decodeSingular(value: inout BaseType?, from decoder: inout D) throws { try decoder.decodeSingularDoubleField(value: &value) } @@ -128,7 +128,7 @@ public struct ProtobufDouble: FieldType, MapValueType { /// public struct ProtobufInt32: FieldType, MapKeyType, MapValueType { public typealias BaseType = Int32 - public static var proto3DefaultValue: Int32 {return 0} + public static var proto3DefaultValue: Int32 { 0 } public static func decodeSingular(value: inout BaseType?, from decoder: inout D) throws { try decoder.decodeSingularInt32Field(value: &value) } @@ -152,7 +152,7 @@ public struct ProtobufInt32: FieldType, MapKeyType, MapValueType { public struct ProtobufInt64: FieldType, MapKeyType, MapValueType { public typealias BaseType = Int64 - public static var proto3DefaultValue: Int64 {return 0} + public static var proto3DefaultValue: Int64 { 0 } public static func decodeSingular(value: inout BaseType?, from decoder: inout D) throws { try decoder.decodeSingularInt64Field(value: &value) } @@ -175,7 +175,7 @@ public struct ProtobufInt64: FieldType, MapKeyType, MapValueType { /// public struct ProtobufUInt32: FieldType, MapKeyType, MapValueType { public typealias BaseType = UInt32 - public static var proto3DefaultValue: UInt32 {return 0} + public static var proto3DefaultValue: UInt32 { 0 } public static func decodeSingular(value: inout BaseType?, from decoder: inout D) throws { try decoder.decodeSingularUInt32Field(value: &value) } @@ -199,7 +199,7 @@ public struct ProtobufUInt32: FieldType, MapKeyType, MapValueType { public struct ProtobufUInt64: FieldType, MapKeyType, MapValueType { public typealias BaseType = UInt64 - public static var proto3DefaultValue: UInt64 {return 0} + public static var proto3DefaultValue: UInt64 { 0 } public static func decodeSingular(value: inout BaseType?, from decoder: inout D) throws { try decoder.decodeSingularUInt64Field(value: &value) } @@ -222,7 +222,7 @@ public struct ProtobufUInt64: FieldType, MapKeyType, MapValueType { /// public struct ProtobufSInt32: FieldType, MapKeyType, MapValueType { public typealias BaseType = Int32 - public static var proto3DefaultValue: Int32 {return 0} + public static var proto3DefaultValue: Int32 { 0 } public static func decodeSingular(value: inout BaseType?, from decoder: inout D) throws { try decoder.decodeSingularSInt32Field(value: &value) } @@ -246,7 +246,7 @@ public struct ProtobufSInt32: FieldType, MapKeyType, MapValueType { public struct ProtobufSInt64: FieldType, MapKeyType, MapValueType { public typealias BaseType = Int64 - public static var proto3DefaultValue: Int64 {return 0} + public static var proto3DefaultValue: Int64 { 0 } public static func decodeSingular(value: inout BaseType?, from decoder: inout D) throws { try decoder.decodeSingularSInt64Field(value: &value) } @@ -269,7 +269,7 @@ public struct ProtobufSInt64: FieldType, MapKeyType, MapValueType { /// public struct ProtobufFixed32: FieldType, MapKeyType, MapValueType { public typealias BaseType = UInt32 - public static var proto3DefaultValue: UInt32 {return 0} + public static var proto3DefaultValue: UInt32 { 0 } public static func decodeSingular(value: inout BaseType?, from decoder: inout D) throws { try decoder.decodeSingularFixed32Field(value: &value) } @@ -292,7 +292,7 @@ public struct ProtobufFixed32: FieldType, MapKeyType, MapValueType { /// public struct ProtobufFixed64: FieldType, MapKeyType, MapValueType { public typealias BaseType = UInt64 - public static var proto3DefaultValue: UInt64 {return 0} + public static var proto3DefaultValue: UInt64 { 0 } public static func decodeSingular(value: inout BaseType?, from decoder: inout D) throws { try decoder.decodeSingularFixed64Field(value: &value) } @@ -315,7 +315,7 @@ public struct ProtobufFixed64: FieldType, MapKeyType, MapValueType { /// public struct ProtobufSFixed32: FieldType, MapKeyType, MapValueType { public typealias BaseType = Int32 - public static var proto3DefaultValue: Int32 {return 0} + public static var proto3DefaultValue: Int32 { 0 } public static func decodeSingular(value: inout BaseType?, from decoder: inout D) throws { try decoder.decodeSingularSFixed32Field(value: &value) } @@ -338,7 +338,7 @@ public struct ProtobufSFixed32: FieldType, MapKeyType, MapValueType { /// public struct ProtobufSFixed64: FieldType, MapKeyType, MapValueType { public typealias BaseType = Int64 - public static var proto3DefaultValue: Int64 {return 0} + public static var proto3DefaultValue: Int64 { 0 } public static func decodeSingular(value: inout BaseType?, from decoder: inout D) throws { try decoder.decodeSingularSFixed64Field(value: &value) } @@ -361,7 +361,7 @@ public struct ProtobufSFixed64: FieldType, MapKeyType, MapValueType { /// public struct ProtobufBool: FieldType, MapKeyType, MapValueType { public typealias BaseType = Bool - public static var proto3DefaultValue: Bool {return false} + public static var proto3DefaultValue: Bool { false } public static func decodeSingular(value: inout BaseType?, from decoder: inout D) throws { try decoder.decodeSingularBoolField(value: &value) } @@ -392,7 +392,7 @@ public struct ProtobufBool: FieldType, MapKeyType, MapValueType { /// public struct ProtobufString: FieldType, MapKeyType, MapValueType { public typealias BaseType = String - public static var proto3DefaultValue: String {return String()} + public static var proto3DefaultValue: String { String() } public static func decodeSingular(value: inout BaseType?, from decoder: inout D) throws { try decoder.decodeSingularStringField(value: &value) } @@ -415,7 +415,7 @@ public struct ProtobufString: FieldType, MapKeyType, MapValueType { /// public struct ProtobufBytes: FieldType, MapValueType { public typealias BaseType = Data - public static var proto3DefaultValue: Data {return Data()} + public static var proto3DefaultValue: Data { Data() } public static func decodeSingular(value: inout BaseType?, from decoder: inout D) throws { try decoder.decodeSingularBytesField(value: &value) } diff --git a/Sources/SwiftProtobuf/Google_Protobuf_Any+Extensions.swift b/Sources/SwiftProtobuf/Google_Protobuf_Any+Extensions.swift index 3ccd1d28f..6ece63b67 100644 --- a/Sources/SwiftProtobuf/Google_Protobuf_Any+Extensions.swift +++ b/Sources/SwiftProtobuf/Google_Protobuf_Any+Extensions.swift @@ -19,150 +19,153 @@ import Foundation public let defaultAnyTypeURLPrefix: String = "type.googleapis.com" extension Google_Protobuf_Any { - /// Initialize an Any object from the provided message. - /// - /// This corresponds to the `pack` operation in the C++ API. - /// - /// Unlike the C++ implementation, the message is not immediately - /// serialized; it is merely stored until the Any object itself - /// needs to be serialized. This design avoids unnecessary - /// decoding/recoding when writing JSON format. - /// - /// - Parameters: - /// - partial: If `false` (the default), this method will check - /// ``Message/isInitialized-6abgi`` before encoding to verify that all required - /// fields are present. If any are missing, this method throws - /// ``BinaryEncodingError/missingRequiredFields``. - /// - typePrefix: The prefix to be used when building the `type_url`. - /// Defaults to "type.googleapis.com". - /// - Throws: ``BinaryEncodingError/missingRequiredFields`` if - /// `partial` is false and `message` wasn't fully initialized. - public init( - message: any Message, - partial: Bool = false, - typePrefix: String = defaultAnyTypeURLPrefix - ) throws { - if !partial && !message.isInitialized { - throw BinaryEncodingError.missingRequiredFields + /// Initialize an Any object from the provided message. + /// + /// This corresponds to the `pack` operation in the C++ API. + /// + /// Unlike the C++ implementation, the message is not immediately + /// serialized; it is merely stored until the Any object itself + /// needs to be serialized. This design avoids unnecessary + /// decoding/recoding when writing JSON format. + /// + /// - Parameters: + /// - partial: If `false` (the default), this method will check + /// ``Message/isInitialized-6abgi`` before encoding to verify that all required + /// fields are present. If any are missing, this method throws + /// ``BinaryEncodingError/missingRequiredFields``. + /// - typePrefix: The prefix to be used when building the `type_url`. + /// Defaults to "type.googleapis.com". + /// - Throws: ``BinaryEncodingError/missingRequiredFields`` if + /// `partial` is false and `message` wasn't fully initialized. + public init( + message: any Message, + partial: Bool = false, + typePrefix: String = defaultAnyTypeURLPrefix + ) throws { + if !partial && !message.isInitialized { + throw BinaryEncodingError.missingRequiredFields + } + self.init() + typeURL = buildTypeURL(forMessage: message, typePrefix: typePrefix) + _storage.state = .message(message) + } + + /// Creates a new `Google_Protobuf_Any` by decoding the given string + /// containing a serialized message in Protocol Buffer text format. + /// + /// - Parameters: + /// - textFormatString: The text format string to decode. + /// - extensions: An `ExtensionMap` used to look up and decode any + /// extensions in this message or messages nested within this message's + /// fields. + /// - Throws: an instance of `TextFormatDecodingError` on failure. + @_disfavoredOverload + public init( + textFormatString: String, + extensions: (any ExtensionMap)? = nil + ) throws { + // TODO: Remove this api and default the options instead when we do a major release. + try self.init( + textFormatString: textFormatString, + options: TextFormatDecodingOptions(), + extensions: extensions + ) } - self.init() - typeURL = buildTypeURL(forMessage:message, typePrefix: typePrefix) - _storage.state = .message(message) - } - - /// Creates a new `Google_Protobuf_Any` by decoding the given string - /// containing a serialized message in Protocol Buffer text format. - /// - /// - Parameters: - /// - textFormatString: The text format string to decode. - /// - extensions: An `ExtensionMap` used to look up and decode any - /// extensions in this message or messages nested within this message's - /// fields. - /// - Throws: an instance of `TextFormatDecodingError` on failure. - @_disfavoredOverload - public init( - textFormatString: String, - extensions: (any ExtensionMap)? = nil - ) throws { - // TODO: Remove this api and default the options instead when we do a major release. - try self.init(textFormatString: textFormatString, - options: TextFormatDecodingOptions(), - extensions: extensions) - } - /// Creates a new `Google_Protobuf_Any` by decoding the given string - /// containing a serialized message in Protocol Buffer text format. - /// - /// - Parameters: - /// - textFormatString: The text format string to decode. - /// - options: The ``TextFormatDecodingOptions`` to use. - /// - extensions: An ``ExtensionMap`` used to look up and decode any - /// extensions in this message or messages nested within this message's - /// fields. - /// - Throws: ``TextFormatDecodingError`` on failure. - public init( - textFormatString: String, - options: TextFormatDecodingOptions = TextFormatDecodingOptions(), - extensions: (any ExtensionMap)? = nil - ) throws { - self.init() - if !textFormatString.isEmpty { - if let data = textFormatString.data(using: String.Encoding.utf8) { - try data.withUnsafeBytes { (body: UnsafeRawBufferPointer) in - if let baseAddress = body.baseAddress, body.count > 0 { - var textDecoder = try TextFormatDecoder( - messageType: Google_Protobuf_Any.self, - utf8Pointer: baseAddress, - count: body.count, - options: options, - extensions: extensions) - try decodeTextFormat(decoder: &textDecoder) - if !textDecoder.complete { - throw TextFormatDecodingError.trailingGarbage + /// Creates a new `Google_Protobuf_Any` by decoding the given string + /// containing a serialized message in Protocol Buffer text format. + /// + /// - Parameters: + /// - textFormatString: The text format string to decode. + /// - options: The ``TextFormatDecodingOptions`` to use. + /// - extensions: An ``ExtensionMap`` used to look up and decode any + /// extensions in this message or messages nested within this message's + /// fields. + /// - Throws: ``TextFormatDecodingError`` on failure. + public init( + textFormatString: String, + options: TextFormatDecodingOptions = TextFormatDecodingOptions(), + extensions: (any ExtensionMap)? = nil + ) throws { + self.init() + if !textFormatString.isEmpty { + if let data = textFormatString.data(using: String.Encoding.utf8) { + try data.withUnsafeBytes { (body: UnsafeRawBufferPointer) in + if let baseAddress = body.baseAddress, body.count > 0 { + var textDecoder = try TextFormatDecoder( + messageType: Google_Protobuf_Any.self, + utf8Pointer: baseAddress, + count: body.count, + options: options, + extensions: extensions + ) + try decodeTextFormat(decoder: &textDecoder) + if !textDecoder.complete { + throw TextFormatDecodingError.trailingGarbage + } + } + } } - } } - } } - } - /// Returns true if this `Google_Protobuf_Any` message contains the given - /// message type. - /// - /// The check is performed by looking at the passed `Message.Type` and the - /// `typeURL` of this message. - /// - /// - Parameter type: The concrete message type. - /// - Returns: True if the receiver contains the given message type. - public func isA(_ type: M.Type) -> Bool { - return _storage.isA(type) - } + /// Returns true if this `Google_Protobuf_Any` message contains the given + /// message type. + /// + /// The check is performed by looking at the passed `Message.Type` and the + /// `typeURL` of this message. + /// + /// - Parameter type: The concrete message type. + /// - Returns: True if the receiver contains the given message type. + public func isA(_ type: M.Type) -> Bool { + _storage.isA(type) + } - public func hash(into hasher: inout Hasher) { - _storage.hash(into: &hasher) - } + public func hash(into hasher: inout Hasher) { + _storage.hash(into: &hasher) + } } extension Google_Protobuf_Any { - internal func textTraverse(visitor: inout TextFormatEncodingVisitor) { - _storage.textTraverse(visitor: &visitor) - try! unknownFields.traverse(visitor: &visitor) - } + internal func textTraverse(visitor: inout TextFormatEncodingVisitor) { + _storage.textTraverse(visitor: &visitor) + try! unknownFields.traverse(visitor: &visitor) + } } extension Google_Protobuf_Any { - // Custom text format decoding support for Any objects. - // (Note: This is not a part of any protocol; it's invoked - // directly from TextFormatDecoder whenever it sees an attempt - // to decode an Any object) - internal mutating func decodeTextFormat( - decoder: inout TextFormatDecoder - ) throws { - // First, check if this uses the "verbose" Any encoding. - // If it does, and we have the type available, we can - // eagerly decode the contained Message object. - if let url = try decoder.scanner.nextOptionalAnyURL() { - try _uniqueStorage().decodeTextFormat(typeURL: url, decoder: &decoder) - } else { - // This is not using the specialized encoding, so we can use the - // standard path to decode the binary value. - // First, clear the fields so we don't waste time re-serializing - // the previous contents as this instances get replaced with a - // new value (can happen when a field name/number is repeated in - // the TextFormat input). - self.typeURL = "" - self.value = Data() - try decodeMessage(decoder: &decoder) + // Custom text format decoding support for Any objects. + // (Note: This is not a part of any protocol; it's invoked + // directly from TextFormatDecoder whenever it sees an attempt + // to decode an Any object) + internal mutating func decodeTextFormat( + decoder: inout TextFormatDecoder + ) throws { + // First, check if this uses the "verbose" Any encoding. + // If it does, and we have the type available, we can + // eagerly decode the contained Message object. + if let url = try decoder.scanner.nextOptionalAnyURL() { + try _uniqueStorage().decodeTextFormat(typeURL: url, decoder: &decoder) + } else { + // This is not using the specialized encoding, so we can use the + // standard path to decode the binary value. + // First, clear the fields so we don't waste time re-serializing + // the previous contents as this instances get replaced with a + // new value (can happen when a field name/number is repeated in + // the TextFormat input). + self.typeURL = "" + self.value = Data() + try decodeMessage(decoder: &decoder) + } } - } } extension Google_Protobuf_Any: _CustomJSONCodable { - internal func encodedJSONString(options: JSONEncodingOptions) throws -> String { - return try _storage.encodedJSONString(options: options) - } + internal func encodedJSONString(options: JSONEncodingOptions) throws -> String { + try _storage.encodedJSONString(options: options) + } - internal mutating func decodeJSON(from decoder: inout JSONDecoder) throws { - try _uniqueStorage().decodeJSON(from: &decoder) - } + internal mutating func decodeJSON(from decoder: inout JSONDecoder) throws { + try _uniqueStorage().decodeJSON(from: &decoder) + } } diff --git a/Sources/SwiftProtobuf/Google_Protobuf_Any+Registry.swift b/Sources/SwiftProtobuf/Google_Protobuf_Any+Registry.swift index 32eb1e84e..ade82cd0c 100644 --- a/Sources/SwiftProtobuf/Google_Protobuf_Any+Registry.swift +++ b/Sources/SwiftProtobuf/Google_Protobuf_Any+Registry.swift @@ -14,12 +14,15 @@ // ----------------------------------------------------------------------------- import Foundation + #if !os(WASI) #if canImport(Dispatch) import Dispatch -fileprivate let knownTypesQueue = - DispatchQueue(label: "org.swift.protobuf.typeRegistry", - attributes: .concurrent) +private let knownTypesQueue = + DispatchQueue( + label: "org.swift.protobuf.typeRegistry", + attributes: .concurrent + ) #endif #endif @@ -27,65 +30,65 @@ fileprivate let knownTypesQueue = // the general registry support? internal func buildTypeURL(forMessage message: any Message, typePrefix: String) -> String { - var url = typePrefix - let needsSlash = typePrefix.isEmpty || typePrefix.last != "/" - if needsSlash { - url += "/" - } - return url + typeName(fromMessage: message) + var url = typePrefix + let needsSlash = typePrefix.isEmpty || typePrefix.last != "/" + if needsSlash { + url += "/" + } + return url + typeName(fromMessage: message) } internal func typeName(fromMessage message: any Message) -> String { - let messageType = type(of: message) - return messageType.protoMessageName + let messageType = type(of: message) + return messageType.protoMessageName } internal func typeName(fromURL s: String) -> String { - var typeStart = s.startIndex - var i = typeStart - while i < s.endIndex { - let c = s[i] - i = s.index(after: i) - if c == "/" { - typeStart = i + var typeStart = s.startIndex + var i = typeStart + while i < s.endIndex { + let c = s[i] + i = s.index(after: i) + if c == "/" { + typeStart = i + } } - } - return String(s[typeStart.. { - var wrappedValue: Wrapped - init(_ wrappedValue: Wrapped) { - self.wrappedValue = wrappedValue - } +private final class UnsafeMutableTransferBox { + var wrappedValue: Wrapped + init(_ wrappedValue: Wrapped) { + self.wrappedValue = wrappedValue + } } extension UnsafeMutableTransferBox: @unchecked Sendable {} // All access to this should be done on `knownTypesQueue`. -fileprivate let knownTypes: UnsafeMutableTransferBox<[String:any Message.Type]> = .init([ - // Seeded with the Well Known Types. - "google.protobuf.Any": Google_Protobuf_Any.self, - "google.protobuf.BoolValue": Google_Protobuf_BoolValue.self, - "google.protobuf.BytesValue": Google_Protobuf_BytesValue.self, - "google.protobuf.DoubleValue": Google_Protobuf_DoubleValue.self, - "google.protobuf.Duration": Google_Protobuf_Duration.self, - "google.protobuf.Empty": Google_Protobuf_Empty.self, - "google.protobuf.FieldMask": Google_Protobuf_FieldMask.self, - "google.protobuf.FloatValue": Google_Protobuf_FloatValue.self, - "google.protobuf.Int32Value": Google_Protobuf_Int32Value.self, - "google.protobuf.Int64Value": Google_Protobuf_Int64Value.self, - "google.protobuf.ListValue": Google_Protobuf_ListValue.self, - "google.protobuf.StringValue": Google_Protobuf_StringValue.self, - "google.protobuf.Struct": Google_Protobuf_Struct.self, - "google.protobuf.Timestamp": Google_Protobuf_Timestamp.self, - "google.protobuf.UInt32Value": Google_Protobuf_UInt32Value.self, - "google.protobuf.UInt64Value": Google_Protobuf_UInt64Value.self, - "google.protobuf.Value": Google_Protobuf_Value.self, +private let knownTypes: UnsafeMutableTransferBox<[String: any Message.Type]> = .init([ + // Seeded with the Well Known Types. + "google.protobuf.Any": Google_Protobuf_Any.self, + "google.protobuf.BoolValue": Google_Protobuf_BoolValue.self, + "google.protobuf.BytesValue": Google_Protobuf_BytesValue.self, + "google.protobuf.DoubleValue": Google_Protobuf_DoubleValue.self, + "google.protobuf.Duration": Google_Protobuf_Duration.self, + "google.protobuf.Empty": Google_Protobuf_Empty.self, + "google.protobuf.FieldMask": Google_Protobuf_FieldMask.self, + "google.protobuf.FloatValue": Google_Protobuf_FloatValue.self, + "google.protobuf.Int32Value": Google_Protobuf_Int32Value.self, + "google.protobuf.Int64Value": Google_Protobuf_Int64Value.self, + "google.protobuf.ListValue": Google_Protobuf_ListValue.self, + "google.protobuf.StringValue": Google_Protobuf_StringValue.self, + "google.protobuf.Struct": Google_Protobuf_Struct.self, + "google.protobuf.Timestamp": Google_Protobuf_Timestamp.self, + "google.protobuf.UInt32Value": Google_Protobuf_UInt32Value.self, + "google.protobuf.UInt64Value": Google_Protobuf_UInt64Value.self, + "google.protobuf.Value": Google_Protobuf_Value.self, ]) extension Google_Protobuf_Any { @@ -137,8 +140,8 @@ extension Google_Protobuf_Any { /// Returns the Message.Type expected for the given type URL. public static func messageType(forTypeURL url: String) -> (any Message.Type)? { - let messageTypeName = typeName(fromURL: url) - return messageType(forMessageName: messageTypeName) + let messageTypeName = typeName(fromURL: url) + return messageType(forMessageName: messageTypeName) } /// Returns the Message.Type expected for the given proto message name. @@ -152,12 +155,12 @@ extension Google_Protobuf_Any { } -fileprivate enum DispatchFlags { +private enum DispatchFlags { case barrier case none } -fileprivate func execute(flags: DispatchFlags, _ closure: () -> Void) { +private func execute(flags: DispatchFlags, _ closure: () -> Void) { #if !os(WASI) switch flags { case .barrier: diff --git a/Sources/SwiftProtobuf/Google_Protobuf_Duration+Extensions.swift b/Sources/SwiftProtobuf/Google_Protobuf_Duration+Extensions.swift index b110d02db..9b5b3cf52 100644 --- a/Sources/SwiftProtobuf/Google_Protobuf_Duration+Extensions.swift +++ b/Sources/SwiftProtobuf/Google_Protobuf_Duration+Extensions.swift @@ -17,214 +17,224 @@ import Foundation private let minDurationSeconds: Int64 = -maxDurationSeconds -private let maxDurationSeconds: Int64 = 315576000000 +private let maxDurationSeconds: Int64 = 315_576_000_000 private func parseDuration(text: String) throws -> (Int64, Int32) { - var digits = [Character]() - var digitCount = 0 - var total = 0 - var chars = text.makeIterator() - var seconds: Int64? - var nanos: Int32 = 0 - var isNegative = false - while let c = chars.next() { - switch c { - case "-": - // Only accept '-' as very first character - if total > 0 { - throw JSONDecodingError.malformedDuration - } - digits.append(c) - isNegative = true - case "0", "1", "2", "3", "4", "5", "6", "7", "8", "9": - digits.append(c) - digitCount += 1 - case ".": - if let _ = seconds { - throw JSONDecodingError.malformedDuration - } - let digitString = String(digits) - if let s = Int64(digitString), - s >= minDurationSeconds && s <= maxDurationSeconds { - seconds = s - } else { - throw JSONDecodingError.malformedDuration - } - digits.removeAll() - digitCount = 0 - case "s": - if let _ = seconds { - // Seconds already set, digits holds nanos - while (digitCount < 9) { - digits.append(Character("0")) - digitCount += 1 + var digits = [Character]() + var digitCount = 0 + var total = 0 + var chars = text.makeIterator() + var seconds: Int64? + var nanos: Int32 = 0 + var isNegative = false + while let c = chars.next() { + switch c { + case "-": + // Only accept '-' as very first character + if total > 0 { + throw JSONDecodingError.malformedDuration + } + digits.append(c) + isNegative = true + case "0", "1", "2", "3", "4", "5", "6", "7", "8", "9": + digits.append(c) + digitCount += 1 + case ".": + if let _ = seconds { + throw JSONDecodingError.malformedDuration + } + let digitString = String(digits) + if let s = Int64(digitString), + s >= minDurationSeconds && s <= maxDurationSeconds + { + seconds = s + } else { + throw JSONDecodingError.malformedDuration + } + digits.removeAll() + digitCount = 0 + case "s": + if let _ = seconds { + // Seconds already set, digits holds nanos + while digitCount < 9 { + digits.append(Character("0")) + digitCount += 1 + } + while digitCount > 9 { + digits.removeLast() + digitCount -= 1 + } + let digitString = String(digits) + if let rawNanos = Int32(digitString) { + if isNegative { + nanos = -rawNanos + } else { + nanos = rawNanos + } + } else { + throw JSONDecodingError.malformedDuration + } + } else { + // No fraction, we just have an integral number of seconds + let digitString = String(digits) + if let s = Int64(digitString), + s >= minDurationSeconds && s <= maxDurationSeconds + { + seconds = s + } else { + throw JSONDecodingError.malformedDuration + } + } + // Fail if there are characters after 's' + if chars.next() != nil { + throw JSONDecodingError.malformedDuration + } + return (seconds!, nanos) + default: + throw JSONDecodingError.malformedDuration } - while digitCount > 9 { - digits.removeLast() - digitCount -= 1 - } - let digitString = String(digits) - if let rawNanos = Int32(digitString) { - if isNegative { - nanos = -rawNanos - } else { - nanos = rawNanos - } - } else { - throw JSONDecodingError.malformedDuration - } - } else { - // No fraction, we just have an integral number of seconds - let digitString = String(digits) - if let s = Int64(digitString), - s >= minDurationSeconds && s <= maxDurationSeconds { - seconds = s - } else { - throw JSONDecodingError.malformedDuration - } - } - // Fail if there are characters after 's' - if chars.next() != nil { - throw JSONDecodingError.malformedDuration - } - return (seconds!, nanos) - default: - throw JSONDecodingError.malformedDuration + total += 1 } - total += 1 - } - throw JSONDecodingError.malformedDuration + throw JSONDecodingError.malformedDuration } private func formatDuration(seconds: Int64, nanos: Int32) -> String? { - let (seconds, nanos) = normalizeForDuration(seconds: seconds, nanos: nanos) - guard seconds >= minDurationSeconds && seconds <= maxDurationSeconds else { - return nil - } - let nanosString = nanosToString(nanos: nanos) // Includes leading '.' if needed - if seconds == 0 && nanos < 0 { - return "-0\(nanosString)s" - } - return "\(seconds)\(nanosString)s" + let (seconds, nanos) = normalizeForDuration(seconds: seconds, nanos: nanos) + guard seconds >= minDurationSeconds && seconds <= maxDurationSeconds else { + return nil + } + let nanosString = nanosToString(nanos: nanos) // Includes leading '.' if needed + if seconds == 0 && nanos < 0 { + return "-0\(nanosString)s" + } + return "\(seconds)\(nanosString)s" } extension Google_Protobuf_Duration { - /// Creates a new `Google_Protobuf_Duration` equal to the given number of - /// seconds and nanoseconds. - /// - /// - Parameter seconds: The number of seconds. - /// - Parameter nanos: The number of nanoseconds. - public init(seconds: Int64 = 0, nanos: Int32 = 0) { - self.init() - self.seconds = seconds - self.nanos = nanos - } + /// Creates a new `Google_Protobuf_Duration` equal to the given number of + /// seconds and nanoseconds. + /// + /// - Parameter seconds: The number of seconds. + /// - Parameter nanos: The number of nanoseconds. + public init(seconds: Int64 = 0, nanos: Int32 = 0) { + self.init() + self.seconds = seconds + self.nanos = nanos + } } extension Google_Protobuf_Duration: _CustomJSONCodable { - mutating func decodeJSON(from decoder: inout JSONDecoder) throws { - let s = try decoder.scanner.nextQuotedString() - (seconds, nanos) = try parseDuration(text: s) - } - func encodedJSONString(options: JSONEncodingOptions) throws -> String { - if let formatted = formatDuration(seconds: seconds, nanos: nanos) { - return "\"\(formatted)\"" - } else { - throw JSONEncodingError.durationRange + mutating func decodeJSON(from decoder: inout JSONDecoder) throws { + let s = try decoder.scanner.nextQuotedString() + (seconds, nanos) = try parseDuration(text: s) + } + func encodedJSONString(options: JSONEncodingOptions) throws -> String { + if let formatted = formatDuration(seconds: seconds, nanos: nanos) { + return "\"\(formatted)\"" + } else { + throw JSONEncodingError.durationRange + } } - } } extension Google_Protobuf_Duration: ExpressibleByFloatLiteral { - public typealias FloatLiteralType = Double - - /// Creates a new `Google_Protobuf_Duration` from a floating point literal - /// that is interpreted as a duration in seconds, rounded to the nearest - /// nanosecond. - public init(floatLiteral value: Double) { - let sd = trunc(value) - let nd = round((value - sd) * TimeInterval(nanosPerSecond)) - let (s, n) = normalizeForDuration(seconds: Int64(sd), nanos: Int32(nd)) - self.init(seconds: s, nanos: n) - } + public typealias FloatLiteralType = Double + + /// Creates a new `Google_Protobuf_Duration` from a floating point literal + /// that is interpreted as a duration in seconds, rounded to the nearest + /// nanosecond. + public init(floatLiteral value: Double) { + let sd = trunc(value) + let nd = round((value - sd) * TimeInterval(nanosPerSecond)) + let (s, n) = normalizeForDuration(seconds: Int64(sd), nanos: Int32(nd)) + self.init(seconds: s, nanos: n) + } } extension Google_Protobuf_Duration { - /// Creates a new `Google_Protobuf_Duration` that is equal to the given - /// `TimeInterval` (measured in seconds), rounded to the nearest nanosecond. - /// - /// - Parameter timeInterval: The `TimeInterval`. - public init(timeInterval: TimeInterval) { - let sd = trunc(timeInterval) - let nd = round((timeInterval - sd) * TimeInterval(nanosPerSecond)) - let (s, n) = normalizeForDuration(seconds: Int64(sd), nanos: Int32(nd)) - self.init(seconds: s, nanos: n) - } - - /// The `TimeInterval` (measured in seconds) equal to this duration. - public var timeInterval: TimeInterval { - return TimeInterval(self.seconds) + TimeInterval(self.nanos) / TimeInterval(nanosPerSecond) - } + /// Creates a new `Google_Protobuf_Duration` that is equal to the given + /// `TimeInterval` (measured in seconds), rounded to the nearest nanosecond. + /// + /// - Parameter timeInterval: The `TimeInterval`. + public init(timeInterval: TimeInterval) { + let sd = trunc(timeInterval) + let nd = round((timeInterval - sd) * TimeInterval(nanosPerSecond)) + let (s, n) = normalizeForDuration(seconds: Int64(sd), nanos: Int32(nd)) + self.init(seconds: s, nanos: n) + } + + /// The `TimeInterval` (measured in seconds) equal to this duration. + public var timeInterval: TimeInterval { + TimeInterval(self.seconds) + TimeInterval(self.nanos) / TimeInterval(nanosPerSecond) + } } private func normalizeForDuration( - seconds: Int64, - nanos: Int32 + seconds: Int64, + nanos: Int32 ) -> (seconds: Int64, nanos: Int32) { - var s = seconds - var n = nanos - - // If the magnitude of n exceeds a second then - // we need to factor it into s instead. - if n >= nanosPerSecond || n <= -nanosPerSecond { - s += Int64(n / nanosPerSecond) - n = n % nanosPerSecond - } - - // The Duration spec says that when s != 0, s and - // n must have the same sign. - if s > 0 && n < 0 { - n += nanosPerSecond - s -= 1 - } else if s < 0 && n > 0 { - n -= nanosPerSecond - s += 1 - } - - return (seconds: s, nanos: n) + var s = seconds + var n = nanos + + // If the magnitude of n exceeds a second then + // we need to factor it into s instead. + if n >= nanosPerSecond || n <= -nanosPerSecond { + s += Int64(n / nanosPerSecond) + n = n % nanosPerSecond + } + + // The Duration spec says that when s != 0, s and + // n must have the same sign. + if s > 0 && n < 0 { + n += nanosPerSecond + s -= 1 + } else if s < 0 && n > 0 { + n -= nanosPerSecond + s += 1 + } + + return (seconds: s, nanos: n) } public prefix func - ( - operand: Google_Protobuf_Duration + operand: Google_Protobuf_Duration ) -> Google_Protobuf_Duration { - let (s, n) = normalizeForDuration(seconds: -operand.seconds, - nanos: -operand.nanos) - return Google_Protobuf_Duration(seconds: s, nanos: n) + let (s, n) = normalizeForDuration( + seconds: -operand.seconds, + nanos: -operand.nanos + ) + return Google_Protobuf_Duration(seconds: s, nanos: n) } public func + ( - lhs: Google_Protobuf_Duration, - rhs: Google_Protobuf_Duration + lhs: Google_Protobuf_Duration, + rhs: Google_Protobuf_Duration ) -> Google_Protobuf_Duration { - let (s, n) = normalizeForDuration(seconds: lhs.seconds + rhs.seconds, - nanos: lhs.nanos + rhs.nanos) - return Google_Protobuf_Duration(seconds: s, nanos: n) + let (s, n) = normalizeForDuration( + seconds: lhs.seconds + rhs.seconds, + nanos: lhs.nanos + rhs.nanos + ) + return Google_Protobuf_Duration(seconds: s, nanos: n) } public func - ( - lhs: Google_Protobuf_Duration, - rhs: Google_Protobuf_Duration + lhs: Google_Protobuf_Duration, + rhs: Google_Protobuf_Duration ) -> Google_Protobuf_Duration { - let (s, n) = normalizeForDuration(seconds: lhs.seconds - rhs.seconds, - nanos: lhs.nanos - rhs.nanos) - return Google_Protobuf_Duration(seconds: s, nanos: n) + let (s, n) = normalizeForDuration( + seconds: lhs.seconds - rhs.seconds, + nanos: lhs.nanos - rhs.nanos + ) + return Google_Protobuf_Duration(seconds: s, nanos: n) } public func - ( - lhs: Google_Protobuf_Timestamp, - rhs: Google_Protobuf_Timestamp + lhs: Google_Protobuf_Timestamp, + rhs: Google_Protobuf_Timestamp ) -> Google_Protobuf_Duration { - let (s, n) = normalizeForDuration(seconds: lhs.seconds - rhs.seconds, - nanos: lhs.nanos - rhs.nanos) - return Google_Protobuf_Duration(seconds: s, nanos: n) + let (s, n) = normalizeForDuration( + seconds: lhs.seconds - rhs.seconds, + nanos: lhs.nanos - rhs.nanos + ) + return Google_Protobuf_Duration(seconds: s, nanos: n) } diff --git a/Sources/SwiftProtobuf/Google_Protobuf_FieldMask+Extensions.swift b/Sources/SwiftProtobuf/Google_Protobuf_FieldMask+Extensions.swift index 87cbdd65f..0fb9121f1 100644 --- a/Sources/SwiftProtobuf/Google_Protobuf_FieldMask+Extensions.swift +++ b/Sources/SwiftProtobuf/Google_Protobuf_FieldMask+Extensions.swift @@ -13,356 +13,355 @@ /// // ----------------------------------------------------------------------------- - // True if the string only contains printable (non-control) // ASCII characters. Note: This follows the ASCII standard; // space is not a "printable" character. private func isPrintableASCII(_ s: String) -> Bool { - for u in s.utf8 { - if u <= 0x20 || u >= 0x7f { - return false + for u in s.utf8 { + if u <= 0x20 || u >= 0x7f { + return false + } } - } - return true + return true } private func ProtoToJSON(name: String) -> String? { - guard isPrintableASCII(name) else { return nil } - var jsonPath = String() - var chars = name.makeIterator() - while let c = chars.next() { - switch c { - case "_": - if let toupper = chars.next() { - switch toupper { - case "a"..."z": - jsonPath.append(String(toupper).uppercased()) + guard isPrintableASCII(name) else { return nil } + var jsonPath = String() + var chars = name.makeIterator() + while let c = chars.next() { + switch c { + case "_": + if let toupper = chars.next() { + switch toupper { + case "a"..."z": + jsonPath.append(String(toupper).uppercased()) + default: + return nil + } + } else { + return nil + } + case "A"..."Z": + return nil + case "a"..."z", "0"..."9", ".", "(", ")": + jsonPath.append(c) default: - return nil + // TODO: Change this to `return nil` + // once we know everything legal is handled + // above. + jsonPath.append(c) } - } else { - return nil - } - case "A"..."Z": - return nil - case "a"..."z","0"..."9",".","(",")": - jsonPath.append(c) - default: - // TODO: Change this to `return nil` - // once we know everything legal is handled - // above. - jsonPath.append(c) } - } - return jsonPath + return jsonPath } private func JSONToProto(name: String) -> String? { - guard isPrintableASCII(name) else { return nil } - var path = String() - for c in name { - switch c { - case "_": - return nil - case "A"..."Z": - path.append(Character("_")) - path.append(String(c).lowercased()) - case "a"..."z","0"..."9",".","(",")": - path.append(c) - default: - // TODO: Change to `return nil` once - // we know everything legal is being - // handled above - path.append(c) + guard isPrintableASCII(name) else { return nil } + var path = String() + for c in name { + switch c { + case "_": + return nil + case "A"..."Z": + path.append(Character("_")) + path.append(String(c).lowercased()) + case "a"..."z", "0"..."9", ".", "(", ")": + path.append(c) + default: + // TODO: Change to `return nil` once + // we know everything legal is being + // handled above + path.append(c) + } } - } - return path + return path } private func parseJSONFieldNames(names: String) -> [String]? { - // An empty field mask is the empty string (no paths). - guard !names.isEmpty else { return [] } - var fieldNameCount = 0 - var fieldName = String() - var split = [String]() - for c in names { - switch c { - case ",": - if fieldNameCount == 0 { + // An empty field mask is the empty string (no paths). + guard !names.isEmpty else { return [] } + var fieldNameCount = 0 + var fieldName = String() + var split = [String]() + for c in names { + switch c { + case ",": + if fieldNameCount == 0 { + return nil + } + if let pbName = JSONToProto(name: fieldName) { + split.append(pbName) + } else { + return nil + } + fieldName = String() + fieldNameCount = 0 + default: + fieldName.append(c) + fieldNameCount += 1 + } + } + if fieldNameCount == 0 { // Last field name can't be empty return nil - } - if let pbName = JSONToProto(name: fieldName) { + } + if let pbName = JSONToProto(name: fieldName) { split.append(pbName) - } else { + } else { return nil - } - fieldName = String() - fieldNameCount = 0 - default: - fieldName.append(c) - fieldNameCount += 1 } - } - if fieldNameCount == 0 { // Last field name can't be empty - return nil - } - if let pbName = JSONToProto(name: fieldName) { - split.append(pbName) - } else { - return nil - } - return split + return split } extension Google_Protobuf_FieldMask { - /// Creates a new `Google_Protobuf_FieldMask` from the given array of paths. - /// - /// The paths should match the names used in the .proto file, which may be - /// different than the corresponding Swift property names. - /// - /// - Parameter protoPaths: The paths from which to create the field mask, - /// defined using the .proto names for the fields. - public init(protoPaths: [String]) { - self.init() - paths = protoPaths - } + /// Creates a new `Google_Protobuf_FieldMask` from the given array of paths. + /// + /// The paths should match the names used in the .proto file, which may be + /// different than the corresponding Swift property names. + /// + /// - Parameter protoPaths: The paths from which to create the field mask, + /// defined using the .proto names for the fields. + public init(protoPaths: [String]) { + self.init() + paths = protoPaths + } - /// Creates a new `Google_Protobuf_FieldMask` from the given paths. - /// - /// The paths should match the names used in the .proto file, which may be - /// different than the corresponding Swift property names. - /// - /// - Parameter protoPaths: The paths from which to create the field mask, - /// defined using the .proto names for the fields. - public init(protoPaths: String...) { - self.init(protoPaths: protoPaths) - } + /// Creates a new `Google_Protobuf_FieldMask` from the given paths. + /// + /// The paths should match the names used in the .proto file, which may be + /// different than the corresponding Swift property names. + /// + /// - Parameter protoPaths: The paths from which to create the field mask, + /// defined using the .proto names for the fields. + public init(protoPaths: String...) { + self.init(protoPaths: protoPaths) + } - /// Creates a new `Google_Protobuf_FieldMask` from the given paths. - /// - /// The paths should match the JSON names of the fields, which may be - /// different than the corresponding Swift property names. - /// - /// - Parameter jsonPaths: The paths from which to create the field mask, - /// defined using the JSON names for the fields. - public init?(jsonPaths: String...) { - // TODO: This should fail if any of the conversions from JSON fails - self.init(protoPaths: jsonPaths.compactMap(JSONToProto)) - } + /// Creates a new `Google_Protobuf_FieldMask` from the given paths. + /// + /// The paths should match the JSON names of the fields, which may be + /// different than the corresponding Swift property names. + /// + /// - Parameter jsonPaths: The paths from which to create the field mask, + /// defined using the JSON names for the fields. + public init?(jsonPaths: String...) { + // TODO: This should fail if any of the conversions from JSON fails + self.init(protoPaths: jsonPaths.compactMap(JSONToProto)) + } - // It would be nice if to have an initializer that accepted Swift property - // names, but translating between swift and protobuf/json property - // names is not entirely deterministic. + // It would be nice if to have an initializer that accepted Swift property + // names, but translating between swift and protobuf/json property + // names is not entirely deterministic. } extension Google_Protobuf_FieldMask: _CustomJSONCodable { - mutating func decodeJSON(from decoder: inout JSONDecoder) throws { - let s = try decoder.scanner.nextQuotedString() - if let names = parseJSONFieldNames(names: s) { - paths = names - } else { - throw JSONDecodingError.malformedFieldMask + mutating func decodeJSON(from decoder: inout JSONDecoder) throws { + let s = try decoder.scanner.nextQuotedString() + if let names = parseJSONFieldNames(names: s) { + paths = names + } else { + throw JSONDecodingError.malformedFieldMask + } } - } - func encodedJSONString(options: JSONEncodingOptions) throws -> String { - // Note: Proto requires alphanumeric field names, so there - // cannot be a ',' or '"' character to mess up this formatting. - var jsonPaths = [String]() - for p in paths { - if let jsonPath = ProtoToJSON(name: p) { - jsonPaths.append(jsonPath) - } else { - throw JSONEncodingError.fieldMaskConversion - } + func encodedJSONString(options: JSONEncodingOptions) throws -> String { + // Note: Proto requires alphanumeric field names, so there + // cannot be a ',' or '"' character to mess up this formatting. + var jsonPaths = [String]() + for p in paths { + if let jsonPath = ProtoToJSON(name: p) { + jsonPaths.append(jsonPath) + } else { + throw JSONEncodingError.fieldMaskConversion + } + } + return "\"" + jsonPaths.joined(separator: ",") + "\"" } - return "\"" + jsonPaths.joined(separator: ",") + "\"" - } } extension Google_Protobuf_FieldMask { - /// Initiates a field mask with all fields of the message type. - /// - /// - Parameter messageType: Message type to get all paths from. - public init( - allFieldsOf messageType: M.Type - ) { - self = .with { mask in - mask.paths = M.allProtoNames + /// Initiates a field mask with all fields of the message type. + /// + /// - Parameter messageType: Message type to get all paths from. + public init( + allFieldsOf messageType: M.Type + ) { + self = .with { mask in + mask.paths = M.allProtoNames + } } - } - /// Initiates a field mask from some particular field numbers of a message - /// - /// - Parameters: - /// - messageType: Message type to get all paths from. - /// - fieldNumbers: Field numbers of paths to be included. - /// - Returns: Field mask that include paths of corresponding field numbers. - /// - Throws: `FieldMaskError.invalidFieldNumber` if the field number - /// is not on the message - public init( - fieldNumbers: [Int], - of messageType: M.Type - ) throws { - var paths: [String] = [] - for number in fieldNumbers { - guard let name = M.protoName(for: number) else { - throw FieldMaskError.invalidFieldNumber - } - paths.append(name) - } - self = .with { mask in - mask.paths = paths + /// Initiates a field mask from some particular field numbers of a message + /// + /// - Parameters: + /// - messageType: Message type to get all paths from. + /// - fieldNumbers: Field numbers of paths to be included. + /// - Returns: Field mask that include paths of corresponding field numbers. + /// - Throws: `FieldMaskError.invalidFieldNumber` if the field number + /// is not on the message + public init( + fieldNumbers: [Int], + of messageType: M.Type + ) throws { + var paths: [String] = [] + for number in fieldNumbers { + guard let name = M.protoName(for: number) else { + throw FieldMaskError.invalidFieldNumber + } + paths.append(name) + } + self = .with { mask in + mask.paths = paths + } } - } } extension Google_Protobuf_FieldMask { - /// Adds a path to FieldMask after checking whether the given path is valid. - /// This method check-fails if the path is not a valid path for Message type. - /// - /// - Parameters: - /// - path: Path to be added to FieldMask. - /// - messageType: Message type to check validity. - public mutating func addPath( - _ path: String, - of messageType: M.Type - ) throws { - guard M.isPathValid(path) else { - throw FieldMaskError.invalidPath + /// Adds a path to FieldMask after checking whether the given path is valid. + /// This method check-fails if the path is not a valid path for Message type. + /// + /// - Parameters: + /// - path: Path to be added to FieldMask. + /// - messageType: Message type to check validity. + public mutating func addPath( + _ path: String, + of messageType: M.Type + ) throws { + guard M.isPathValid(path) else { + throw FieldMaskError.invalidPath + } + paths.append(path) } - paths.append(path) - } - /// Converts a FieldMask to the canonical form. It will: - /// 1. Remove paths that are covered by another path. For example, - /// "foo.bar" is covered by "foo" and will be removed if "foo" - /// is also in the FieldMask. - /// 2. Sort all paths in alphabetical order. - public var canonical: Google_Protobuf_FieldMask { - var mask = Google_Protobuf_FieldMask() - let sortedPaths = self.paths.sorted() - for path in sortedPaths { - if let lastPath = mask.paths.last { - if path != lastPath, !path.hasPrefix("\(lastPath).") { - mask.paths.append(path) + /// Converts a FieldMask to the canonical form. It will: + /// 1. Remove paths that are covered by another path. For example, + /// "foo.bar" is covered by "foo" and will be removed if "foo" + /// is also in the FieldMask. + /// 2. Sort all paths in alphabetical order. + public var canonical: Google_Protobuf_FieldMask { + var mask = Google_Protobuf_FieldMask() + let sortedPaths = self.paths.sorted() + for path in sortedPaths { + if let lastPath = mask.paths.last { + if path != lastPath, !path.hasPrefix("\(lastPath).") { + mask.paths.append(path) + } + } else { + mask.paths.append(path) + } } - } else { - mask.paths.append(path) - } + return mask } - return mask - } - /// Creates an union of two FieldMasks. - /// - /// - Parameter mask: FieldMask to union with. - /// - Returns: FieldMask with union of two path sets. - public func union( - _ mask: Google_Protobuf_FieldMask - ) -> Google_Protobuf_FieldMask { - var buffer: Set = .init() - var paths: [String] = [] - let allPaths = self.paths + mask.paths - for path in allPaths where !buffer.contains(path) { - buffer.insert(path) - paths.append(path) - } - return .with { mask in - mask.paths = paths + /// Creates an union of two FieldMasks. + /// + /// - Parameter mask: FieldMask to union with. + /// - Returns: FieldMask with union of two path sets. + public func union( + _ mask: Google_Protobuf_FieldMask + ) -> Google_Protobuf_FieldMask { + var buffer: Set = .init() + var paths: [String] = [] + let allPaths = self.paths + mask.paths + for path in allPaths where !buffer.contains(path) { + buffer.insert(path) + paths.append(path) + } + return .with { mask in + mask.paths = paths + } } - } - /// Creates an intersection of two FieldMasks. - /// - /// - Parameter mask: FieldMask to intersect with. - /// - Returns: FieldMask with intersection of two path sets. - public func intersect( - _ mask: Google_Protobuf_FieldMask - ) -> Google_Protobuf_FieldMask { - let set = Set(mask.paths) - var paths: [String] = [] - var buffer = Set() - for path in self.paths where set.contains(path) && !buffer.contains(path) { - buffer.insert(path) - paths.append(path) - } - return .with { mask in - mask.paths = paths + /// Creates an intersection of two FieldMasks. + /// + /// - Parameter mask: FieldMask to intersect with. + /// - Returns: FieldMask with intersection of two path sets. + public func intersect( + _ mask: Google_Protobuf_FieldMask + ) -> Google_Protobuf_FieldMask { + let set = Set(mask.paths) + var paths: [String] = [] + var buffer = Set() + for path in self.paths where set.contains(path) && !buffer.contains(path) { + buffer.insert(path) + paths.append(path) + } + return .with { mask in + mask.paths = paths + } } - } - /// Creates a FieldMasks with paths of the original FieldMask - /// that does not included in mask. - /// - /// - Parameter mask: FieldMask with paths should be substracted. - /// - Returns: FieldMask with all paths does not included in mask. - public func subtract( - _ mask: Google_Protobuf_FieldMask - ) -> Google_Protobuf_FieldMask { - let set = Set(mask.paths) - var paths: [String] = [] - var buffer = Set() - for path in self.paths where !set.contains(path) && !buffer.contains(path) { - buffer.insert(path) - paths.append(path) - } - return .with { mask in - mask.paths = paths + /// Creates a FieldMasks with paths of the original FieldMask + /// that does not included in mask. + /// + /// - Parameter mask: FieldMask with paths should be substracted. + /// - Returns: FieldMask with all paths does not included in mask. + public func subtract( + _ mask: Google_Protobuf_FieldMask + ) -> Google_Protobuf_FieldMask { + let set = Set(mask.paths) + var paths: [String] = [] + var buffer = Set() + for path in self.paths where !set.contains(path) && !buffer.contains(path) { + buffer.insert(path) + paths.append(path) + } + return .with { mask in + mask.paths = paths + } } - } - /// Returns true if path is covered by the given FieldMask. Note that path - /// "foo.bar" covers all paths like "foo.bar.baz", "foo.bar.quz.x", etc. - /// Also note that parent paths are not covered by explicit child path, i.e. - /// "foo.bar" does NOT cover "foo", even if "bar" is the only child. - /// - /// - Parameter path: Path to be checked. - /// - Returns: Boolean determines is path covered. - public func contains(_ path: String) -> Bool { - for fieldMaskPath in paths { - if path.hasPrefix("\(fieldMaskPath).") || fieldMaskPath == path { - return true - } + /// Returns true if path is covered by the given FieldMask. Note that path + /// "foo.bar" covers all paths like "foo.bar.baz", "foo.bar.quz.x", etc. + /// Also note that parent paths are not covered by explicit child path, i.e. + /// "foo.bar" does NOT cover "foo", even if "bar" is the only child. + /// + /// - Parameter path: Path to be checked. + /// - Returns: Boolean determines is path covered. + public func contains(_ path: String) -> Bool { + for fieldMaskPath in paths { + if path.hasPrefix("\(fieldMaskPath).") || fieldMaskPath == path { + return true + } + } + return false } - return false - } } extension Google_Protobuf_FieldMask { - /// Checks whether the given FieldMask is valid for type M. - /// - /// - Parameter messageType: Message type to paths check with. - /// - Returns: Boolean determines FieldMask is valid. - public func isValid( - for messageType: M.Type - ) -> Bool { - var message = M() - return paths.allSatisfy { path in - message.isPathValid(path) + /// Checks whether the given FieldMask is valid for type M. + /// + /// - Parameter messageType: Message type to paths check with. + /// - Returns: Boolean determines FieldMask is valid. + public func isValid( + for messageType: M.Type + ) -> Bool { + var message = M() + return paths.allSatisfy { path in + message.isPathValid(path) + } } - } } /// Describes errors could happen during FieldMask utilities. public enum FieldMaskError: Error { - /// Describes a path is invalid for a Message type. - case invalidPath + /// Describes a path is invalid for a Message type. + case invalidPath - /// Describes a fieldNumber is invalid for a Message type. - case invalidFieldNumber + /// Describes a fieldNumber is invalid for a Message type. + case invalidFieldNumber } -private extension Message where Self: _ProtoNameProviding { - static func protoName(for number: Int) -> String? { - Self._protobuf_nameMap.names(for: number)?.proto.description - } +extension Message where Self: _ProtoNameProviding { + fileprivate static func protoName(for number: Int) -> String? { + Self._protobuf_nameMap.names(for: number)?.proto.description + } - static var allProtoNames: [String] { - Self._protobuf_nameMap.names.map(\.description) - } + fileprivate static var allProtoNames: [String] { + Self._protobuf_nameMap.names.map(\.description) + } } diff --git a/Sources/SwiftProtobuf/Google_Protobuf_ListValue+Extensions.swift b/Sources/SwiftProtobuf/Google_Protobuf_ListValue+Extensions.swift index a26e0d730..559b99dd8 100644 --- a/Sources/SwiftProtobuf/Google_Protobuf_ListValue+Extensions.swift +++ b/Sources/SwiftProtobuf/Google_Protobuf_ListValue+Extensions.swift @@ -14,72 +14,72 @@ // ----------------------------------------------------------------------------- extension Google_Protobuf_ListValue: ExpressibleByArrayLiteral { - // TODO: Give this a direct array interface by proxying the interesting - // bits down to values - public typealias Element = Google_Protobuf_Value + // TODO: Give this a direct array interface by proxying the interesting + // bits down to values + public typealias Element = Google_Protobuf_Value - /// Creates a new `Google_Protobuf_ListValue` from an array literal containing - /// `Google_Protobuf_Value` elements. - public init(arrayLiteral elements: Element...) { - self.init(values: elements) - } + /// Creates a new `Google_Protobuf_ListValue` from an array literal containing + /// `Google_Protobuf_Value` elements. + public init(arrayLiteral elements: Element...) { + self.init(values: elements) + } } extension Google_Protobuf_ListValue: _CustomJSONCodable { - internal func encodedJSONString(options: JSONEncodingOptions) throws -> String { - var jsonEncoder = JSONEncoder() - jsonEncoder.append(text: "[") - var separator: StaticString = "" - for v in values { - jsonEncoder.append(staticText: separator) - try v.serializeJSONValue(to: &jsonEncoder, options: options) - separator = "," + internal func encodedJSONString(options: JSONEncodingOptions) throws -> String { + var jsonEncoder = JSONEncoder() + jsonEncoder.append(text: "[") + var separator: StaticString = "" + for v in values { + jsonEncoder.append(staticText: separator) + try v.serializeJSONValue(to: &jsonEncoder, options: options) + separator = "," + } + jsonEncoder.append(text: "]") + return jsonEncoder.stringResult } - jsonEncoder.append(text: "]") - return jsonEncoder.stringResult - } - internal mutating func decodeJSON(from decoder: inout JSONDecoder) throws { - if decoder.scanner.skipOptionalNull() { - return - } - try decoder.scanner.skipRequiredArrayStart() - // Since we override the JSON decoding, we can't rely - // on the default recursion depth tracking. - try decoder.scanner.incrementRecursionDepth() - if decoder.scanner.skipOptionalArrayEnd() { - decoder.scanner.decrementRecursionDepth() - return - } - while true { - var v = Google_Protobuf_Value() - try v.decodeJSON(from: &decoder) - values.append(v) - if decoder.scanner.skipOptionalArrayEnd() { - decoder.scanner.decrementRecursionDepth() - return - } - try decoder.scanner.skipRequiredComma() + internal mutating func decodeJSON(from decoder: inout JSONDecoder) throws { + if decoder.scanner.skipOptionalNull() { + return + } + try decoder.scanner.skipRequiredArrayStart() + // Since we override the JSON decoding, we can't rely + // on the default recursion depth tracking. + try decoder.scanner.incrementRecursionDepth() + if decoder.scanner.skipOptionalArrayEnd() { + decoder.scanner.decrementRecursionDepth() + return + } + while true { + var v = Google_Protobuf_Value() + try v.decodeJSON(from: &decoder) + values.append(v) + if decoder.scanner.skipOptionalArrayEnd() { + decoder.scanner.decrementRecursionDepth() + return + } + try decoder.scanner.skipRequiredComma() + } } - } } extension Google_Protobuf_ListValue { - /// Creates a new `Google_Protobuf_ListValue` from the given array of - /// `Google_Protobuf_Value` elements. - /// - /// - Parameter values: The list of `Google_Protobuf_Value` messages from - /// which to create the `Google_Protobuf_ListValue`. - public init(values: [Google_Protobuf_Value]) { - self.init() - self.values = values - } + /// Creates a new `Google_Protobuf_ListValue` from the given array of + /// `Google_Protobuf_Value` elements. + /// + /// - Parameter values: The list of `Google_Protobuf_Value` messages from + /// which to create the `Google_Protobuf_ListValue`. + public init(values: [Google_Protobuf_Value]) { + self.init() + self.values = values + } - /// Accesses the `Google_Protobuf_Value` at the specified position. - /// - /// - Parameter index: The position of the element to access. - public subscript(index: Int) -> Google_Protobuf_Value { - get {return values[index]} - set(newValue) {values[index] = newValue} - } + /// Accesses the `Google_Protobuf_Value` at the specified position. + /// + /// - Parameter index: The position of the element to access. + public subscript(index: Int) -> Google_Protobuf_Value { + get { values[index] } + set(newValue) { values[index] = newValue } + } } diff --git a/Sources/SwiftProtobuf/Google_Protobuf_NullValue+Extensions.swift b/Sources/SwiftProtobuf/Google_Protobuf_NullValue+Extensions.swift index 3e88bfd36..9d80b13a1 100644 --- a/Sources/SwiftProtobuf/Google_Protobuf_NullValue+Extensions.swift +++ b/Sources/SwiftProtobuf/Google_Protobuf_NullValue+Extensions.swift @@ -14,15 +14,15 @@ // ----------------------------------------------------------------------------- extension Google_Protobuf_NullValue: _CustomJSONCodable { - internal func encodedJSONString(options: JSONEncodingOptions) throws -> String { - return "null" - } - internal mutating func decodeJSON(from decoder: inout JSONDecoder) throws { - if decoder.scanner.skipOptionalNull() { - return + internal func encodedJSONString(options: JSONEncodingOptions) throws -> String { + "null" + } + internal mutating func decodeJSON(from decoder: inout JSONDecoder) throws { + if decoder.scanner.skipOptionalNull() { + return + } + } + static func decodedFromJSONNull() -> Google_Protobuf_NullValue? { + .nullValue } - } - static func decodedFromJSONNull() -> Google_Protobuf_NullValue? { - return .nullValue - } } diff --git a/Sources/SwiftProtobuf/Google_Protobuf_Struct+Extensions.swift b/Sources/SwiftProtobuf/Google_Protobuf_Struct+Extensions.swift index 681f82142..1c334393b 100644 --- a/Sources/SwiftProtobuf/Google_Protobuf_Struct+Extensions.swift +++ b/Sources/SwiftProtobuf/Google_Protobuf_Struct+Extensions.swift @@ -14,72 +14,72 @@ // ----------------------------------------------------------------------------- extension Google_Protobuf_Struct: ExpressibleByDictionaryLiteral { - public typealias Key = String - public typealias Value = Google_Protobuf_Value + public typealias Key = String + public typealias Value = Google_Protobuf_Value - /// Creates a new `Google_Protobuf_Struct` from a dictionary of string keys to - /// values of type `Google_Protobuf_Value`. - public init(dictionaryLiteral: (String, Google_Protobuf_Value)...) { - self.init() - for (k,v) in dictionaryLiteral { - fields[k] = v + /// Creates a new `Google_Protobuf_Struct` from a dictionary of string keys to + /// values of type `Google_Protobuf_Value`. + public init(dictionaryLiteral: (String, Google_Protobuf_Value)...) { + self.init() + for (k, v) in dictionaryLiteral { + fields[k] = v + } } - } } extension Google_Protobuf_Struct: _CustomJSONCodable { - internal func encodedJSONString(options: JSONEncodingOptions) throws -> String { - var jsonEncoder = JSONEncoder() - jsonEncoder.startObject() - var mapVisitor = JSONMapEncodingVisitor(encoder: jsonEncoder, options: options) - for (k,v) in fields { - try mapVisitor.visitSingularStringField(value: k, fieldNumber: 1) - try mapVisitor.visitSingularMessageField(value: v, fieldNumber: 2) + internal func encodedJSONString(options: JSONEncodingOptions) throws -> String { + var jsonEncoder = JSONEncoder() + jsonEncoder.startObject() + var mapVisitor = JSONMapEncodingVisitor(encoder: jsonEncoder, options: options) + for (k, v) in fields { + try mapVisitor.visitSingularStringField(value: k, fieldNumber: 1) + try mapVisitor.visitSingularMessageField(value: v, fieldNumber: 2) + } + mapVisitor.encoder.endObject() + return mapVisitor.encoder.stringResult } - mapVisitor.encoder.endObject() - return mapVisitor.encoder.stringResult - } - internal mutating func decodeJSON(from decoder: inout JSONDecoder) throws { - try decoder.scanner.skipRequiredObjectStart() - if decoder.scanner.skipOptionalObjectEnd() { - return + internal mutating func decodeJSON(from decoder: inout JSONDecoder) throws { + try decoder.scanner.skipRequiredObjectStart() + if decoder.scanner.skipOptionalObjectEnd() { + return + } + while true { + let key = try decoder.scanner.nextQuotedString() + try decoder.scanner.skipRequiredColon() + var value = Google_Protobuf_Value() + try value.decodeJSON(from: &decoder) + fields[key] = value + if decoder.scanner.skipOptionalObjectEnd() { + return + } + try decoder.scanner.skipRequiredComma() + } } - while true { - let key = try decoder.scanner.nextQuotedString() - try decoder.scanner.skipRequiredColon() - var value = Google_Protobuf_Value() - try value.decodeJSON(from: &decoder) - fields[key] = value - if decoder.scanner.skipOptionalObjectEnd() { - return - } - try decoder.scanner.skipRequiredComma() - } - } } extension Google_Protobuf_Struct { - /// Creates a new `Google_Protobuf_Struct` from a dictionary of string keys to - /// values of type `Google_Protobuf_Value`. - /// - /// - Parameter fields: The dictionary from field names to - /// `Google_Protobuf_Value` messages that should be used to create the - /// `Struct`. - public init(fields: [String: Google_Protobuf_Value]) { - self.init() - self.fields = fields - } + /// Creates a new `Google_Protobuf_Struct` from a dictionary of string keys to + /// values of type `Google_Protobuf_Value`. + /// + /// - Parameter fields: The dictionary from field names to + /// `Google_Protobuf_Value` messages that should be used to create the + /// `Struct`. + public init(fields: [String: Google_Protobuf_Value]) { + self.init() + self.fields = fields + } - /// Accesses the `Google_Protobuf_Value` with the given key for reading and - /// writing. - /// - /// This key-based subscript returns the `Value` for the given key if the key - /// is found in the `Struct`, or nil if the key is not found. If you assign - /// nil as the `Value` for the given key, the `Struct` removes that key and - /// its associated `Value`. - public subscript(key: String) -> Google_Protobuf_Value? { - get {return fields[key]} - set(newValue) {fields[key] = newValue} - } + /// Accesses the `Google_Protobuf_Value` with the given key for reading and + /// writing. + /// + /// This key-based subscript returns the `Value` for the given key if the key + /// is found in the `Struct`, or nil if the key is not found. If you assign + /// nil as the `Value` for the given key, the `Struct` removes that key and + /// its associated `Value`. + public subscript(key: String) -> Google_Protobuf_Value? { + get { fields[key] } + set(newValue) { fields[key] = newValue } + } } diff --git a/Sources/SwiftProtobuf/Google_Protobuf_Timestamp+Extensions.swift b/Sources/SwiftProtobuf/Google_Protobuf_Timestamp+Extensions.swift index cad7355f2..86af8d5a7 100644 --- a/Sources/SwiftProtobuf/Google_Protobuf_Timestamp+Extensions.swift +++ b/Sources/SwiftProtobuf/Google_Protobuf_Timestamp+Extensions.swift @@ -15,317 +15,326 @@ import Foundation -private let minTimestampSeconds: Int64 = -62135596800 // 0001-01-01T00:00:00Z -private let maxTimestampSeconds: Int64 = 253402300799 // 9999-12-31T23:59:59Z +private let minTimestampSeconds: Int64 = -62_135_596_800 // 0001-01-01T00:00:00Z +private let maxTimestampSeconds: Int64 = 253_402_300_799 // 9999-12-31T23:59:59Z // TODO: Add convenience methods to interoperate with standard // date/time classes: an initializer that accepts Unix timestamp as // Int or Double, an easy way to convert to/from Foundation's // NSDateTime (on Apple platforms only?), others? - // Parse an RFC3339 timestamp into a pair of seconds-since-1970 and nanos. private func parseTimestamp(s: String) throws -> (Int64, Int32) { - // Convert to an array of integer character values - let value = s.utf8.map{Int($0)} - if value.count < 20 { - throw JSONDecodingError.malformedTimestamp - } - // Since the format is fixed-layout, we can just decode - // directly as follows. - let zero = Int(48) - let nine = Int(57) - let dash = Int(45) - let colon = Int(58) - let plus = Int(43) - let letterT = Int(84) - let letterZ = Int(90) - let period = Int(46) - - func fromAscii2(_ digit0: Int, _ digit1: Int) throws -> Int { - if digit0 < zero || digit0 > nine || digit1 < zero || digit1 > nine { - throw JSONDecodingError.malformedTimestamp + // Convert to an array of integer character values + let value = s.utf8.map { Int($0) } + if value.count < 20 { + throw JSONDecodingError.malformedTimestamp + } + // Since the format is fixed-layout, we can just decode + // directly as follows. + let zero = Int(48) + let nine = Int(57) + let dash = Int(45) + let colon = Int(58) + let plus = Int(43) + let letterT = Int(84) + let letterZ = Int(90) + let period = Int(46) + + func fromAscii2(_ digit0: Int, _ digit1: Int) throws -> Int { + if digit0 < zero || digit0 > nine || digit1 < zero || digit1 > nine { + throw JSONDecodingError.malformedTimestamp + } + return digit0 * 10 + digit1 - 528 + } + + func fromAscii4( + _ digit0: Int, + _ digit1: Int, + _ digit2: Int, + _ digit3: Int + ) throws -> Int { + if digit0 < zero || digit0 > nine + || digit1 < zero || digit1 > nine + || digit2 < zero || digit2 > nine + || digit3 < zero || digit3 > nine + { + throw JSONDecodingError.malformedTimestamp + } + return digit0 * 1000 + digit1 * 100 + digit2 * 10 + digit3 - 53328 + } + + // Year: 4 digits followed by '-' + let year = try fromAscii4(value[0], value[1], value[2], value[3]) + if value[4] != dash || year < Int(1) || year > Int(9999) { + throw JSONDecodingError.malformedTimestamp } - return digit0 * 10 + digit1 - 528 - } - - func fromAscii4( - _ digit0: Int, - _ digit1: Int, - _ digit2: Int, - _ digit3: Int - ) throws -> Int { - if (digit0 < zero || digit0 > nine - || digit1 < zero || digit1 > nine - || digit2 < zero || digit2 > nine - || digit3 < zero || digit3 > nine) { - throw JSONDecodingError.malformedTimestamp + + // Month: 2 digits followed by '-' + let month = try fromAscii2(value[5], value[6]) + if value[7] != dash || month < Int(1) || month > Int(12) { + throw JSONDecodingError.malformedTimestamp } - return digit0 * 1000 + digit1 * 100 + digit2 * 10 + digit3 - 53328 - } - - // Year: 4 digits followed by '-' - let year = try fromAscii4(value[0], value[1], value[2], value[3]) - if value[4] != dash || year < Int(1) || year > Int(9999) { - throw JSONDecodingError.malformedTimestamp - } - - // Month: 2 digits followed by '-' - let month = try fromAscii2(value[5], value[6]) - if value[7] != dash || month < Int(1) || month > Int(12) { - throw JSONDecodingError.malformedTimestamp - } - - // Day: 2 digits followed by 'T' - let mday = try fromAscii2(value[8], value[9]) - if value[10] != letterT || mday < Int(1) || mday > Int(31) { - throw JSONDecodingError.malformedTimestamp - } - - // Hour: 2 digits followed by ':' - let hour = try fromAscii2(value[11], value[12]) - if value[13] != colon || hour > Int(23) { - throw JSONDecodingError.malformedTimestamp - } - - // Minute: 2 digits followed by ':' - let minute = try fromAscii2(value[14], value[15]) - if value[16] != colon || minute > Int(59) { - throw JSONDecodingError.malformedTimestamp - } - - // Second: 2 digits (following char is checked below) - let second = try fromAscii2(value[17], value[18]) - if second > Int(61) { - throw JSONDecodingError.malformedTimestamp - } - - // timegm() is almost entirely useless. It's nonexistent on - // some platforms, broken on others. Everything else I've tried - // is even worse. Hence the code below. - // (If you have a better way to do this, try it and see if it - // passes the test suite on both Linux and OS X.) - - // Day of year - let mdayStart: [Int] = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334] - var yday = Int64(mdayStart[month - 1]) - let isleap = (year % 400 == 0) || ((year % 100 != 0) && (year % 4 == 0)) - if isleap && (month > 2) { - yday += 1 - } - yday += Int64(mday - 1) - - // Days since start of epoch (including leap days) - var daysSinceEpoch = yday - daysSinceEpoch += Int64(365 * year) - Int64(719527) - daysSinceEpoch += Int64((year - 1) / 4) - daysSinceEpoch -= Int64((year - 1) / 100) - daysSinceEpoch += Int64((year - 1) / 400) - - // Second within day - var daySec = Int64(hour) - daySec *= 60 - daySec += Int64(minute) - daySec *= 60 - daySec += Int64(second) - - // Seconds since start of epoch - let t = daysSinceEpoch * Int64(86400) + daySec - - // After seconds, comes various optional bits - var pos = 19 - - var nanos: Int32 = 0 - if value[pos] == period { // "." begins fractional seconds - pos += 1 - var digitValue = 100000000 - while pos < value.count && value[pos] >= zero && value[pos] <= nine { - nanos += Int32(digitValue * (value[pos] - zero)) - digitValue /= 10 - pos += 1 + + // Day: 2 digits followed by 'T' + let mday = try fromAscii2(value[8], value[9]) + if value[10] != letterT || mday < Int(1) || mday > Int(31) { + throw JSONDecodingError.malformedTimestamp } - } - - var seconds: Int64 = 0 - // "Z" or "+" or "-" starts Timezone offset - if pos >= value.count { - throw JSONDecodingError.malformedTimestamp - } else if value[pos] == plus || value[pos] == dash { - if pos + 6 > value.count { - throw JSONDecodingError.malformedTimestamp + + // Hour: 2 digits followed by ':' + let hour = try fromAscii2(value[11], value[12]) + if value[13] != colon || hour > Int(23) { + throw JSONDecodingError.malformedTimestamp } - let hourOffset = try fromAscii2(value[pos + 1], value[pos + 2]) - let minuteOffset = try fromAscii2(value[pos + 4], value[pos + 5]) - if hourOffset > Int(13) || minuteOffset > Int(59) || value[pos + 3] != colon { - throw JSONDecodingError.malformedTimestamp + + // Minute: 2 digits followed by ':' + let minute = try fromAscii2(value[14], value[15]) + if value[16] != colon || minute > Int(59) { + throw JSONDecodingError.malformedTimestamp } - var adjusted: Int64 = t - if value[pos] == plus { - adjusted -= Int64(hourOffset) * Int64(3600) - adjusted -= Int64(minuteOffset) * Int64(60) + + // Second: 2 digits (following char is checked below) + let second = try fromAscii2(value[17], value[18]) + if second > Int(61) { + throw JSONDecodingError.malformedTimestamp + } + + // timegm() is almost entirely useless. It's nonexistent on + // some platforms, broken on others. Everything else I've tried + // is even worse. Hence the code below. + // (If you have a better way to do this, try it and see if it + // passes the test suite on both Linux and OS X.) + + // Day of year + let mdayStart: [Int] = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334] + var yday = Int64(mdayStart[month - 1]) + let isleap = (year % 400 == 0) || ((year % 100 != 0) && (year % 4 == 0)) + if isleap && (month > 2) { + yday += 1 + } + yday += Int64(mday - 1) + + // Days since start of epoch (including leap days) + var daysSinceEpoch = yday + daysSinceEpoch += Int64(365 * year) - Int64(719527) + daysSinceEpoch += Int64((year - 1) / 4) + daysSinceEpoch -= Int64((year - 1) / 100) + daysSinceEpoch += Int64((year - 1) / 400) + + // Second within day + var daySec = Int64(hour) + daySec *= 60 + daySec += Int64(minute) + daySec *= 60 + daySec += Int64(second) + + // Seconds since start of epoch + let t = daysSinceEpoch * Int64(86400) + daySec + + // After seconds, comes various optional bits + var pos = 19 + + var nanos: Int32 = 0 + if value[pos] == period { // "." begins fractional seconds + pos += 1 + var digitValue = 100_000_000 + while pos < value.count && value[pos] >= zero && value[pos] <= nine { + nanos += Int32(digitValue * (value[pos] - zero)) + digitValue /= 10 + pos += 1 + } + } + + var seconds: Int64 = 0 + // "Z" or "+" or "-" starts Timezone offset + if pos >= value.count { + throw JSONDecodingError.malformedTimestamp + } else if value[pos] == plus || value[pos] == dash { + if pos + 6 > value.count { + throw JSONDecodingError.malformedTimestamp + } + let hourOffset = try fromAscii2(value[pos + 1], value[pos + 2]) + let minuteOffset = try fromAscii2(value[pos + 4], value[pos + 5]) + if hourOffset > Int(13) || minuteOffset > Int(59) || value[pos + 3] != colon { + throw JSONDecodingError.malformedTimestamp + } + var adjusted: Int64 = t + if value[pos] == plus { + adjusted -= Int64(hourOffset) * Int64(3600) + adjusted -= Int64(minuteOffset) * Int64(60) + } else { + adjusted += Int64(hourOffset) * Int64(3600) + adjusted += Int64(minuteOffset) * Int64(60) + } + seconds = adjusted + pos += 6 + } else if value[pos] == letterZ { // "Z" indicator for UTC + seconds = t + pos += 1 } else { - adjusted += Int64(hourOffset) * Int64(3600) - adjusted += Int64(minuteOffset) * Int64(60) + throw JSONDecodingError.malformedTimestamp + } + if pos != value.count { + throw JSONDecodingError.malformedTimestamp } - seconds = adjusted - pos += 6 - } else if value[pos] == letterZ { // "Z" indicator for UTC - seconds = t - pos += 1 - } else { - throw JSONDecodingError.malformedTimestamp - } - if pos != value.count { - throw JSONDecodingError.malformedTimestamp - } - guard seconds >= minTimestampSeconds && seconds <= maxTimestampSeconds else { - throw JSONDecodingError.malformedTimestamp - } - return (seconds, nanos) + guard seconds >= minTimestampSeconds && seconds <= maxTimestampSeconds else { + throw JSONDecodingError.malformedTimestamp + } + return (seconds, nanos) } private func formatTimestamp(seconds: Int64, nanos: Int32) -> String? { - let (seconds, nanos) = normalizeForTimestamp(seconds: seconds, nanos: nanos) - guard seconds >= minTimestampSeconds && seconds <= maxTimestampSeconds else { - return nil - } + let (seconds, nanos) = normalizeForTimestamp(seconds: seconds, nanos: nanos) + guard seconds >= minTimestampSeconds && seconds <= maxTimestampSeconds else { + return nil + } - let (hh, mm, ss) = timeOfDayFromSecondsSince1970(seconds: seconds) - let (YY, MM, DD) = gregorianDateFromSecondsSince1970(seconds: seconds) + let (hh, mm, ss) = timeOfDayFromSecondsSince1970(seconds: seconds) + let (YY, MM, DD) = gregorianDateFromSecondsSince1970(seconds: seconds) - let dateString = "\(fourDigit(YY))-\(twoDigit(MM))-\(twoDigit(DD))" - let timeString = "\(twoDigit(hh)):\(twoDigit(mm)):\(twoDigit(ss))" - let nanosString = nanosToString(nanos: nanos) // Includes leading '.' if needed + let dateString = "\(fourDigit(YY))-\(twoDigit(MM))-\(twoDigit(DD))" + let timeString = "\(twoDigit(hh)):\(twoDigit(mm)):\(twoDigit(ss))" + let nanosString = nanosToString(nanos: nanos) // Includes leading '.' if needed - return "\(dateString)T\(timeString)\(nanosString)Z" + return "\(dateString)T\(timeString)\(nanosString)Z" } extension Google_Protobuf_Timestamp { - /// Creates a new `Google_Protobuf_Timestamp` equal to the given number of - /// seconds and nanoseconds. - /// - /// - Parameter seconds: The number of seconds. - /// - Parameter nanos: The number of nanoseconds. - public init(seconds: Int64 = 0, nanos: Int32 = 0) { - self.init() - self.seconds = seconds - self.nanos = nanos - } + /// Creates a new `Google_Protobuf_Timestamp` equal to the given number of + /// seconds and nanoseconds. + /// + /// - Parameter seconds: The number of seconds. + /// - Parameter nanos: The number of nanoseconds. + public init(seconds: Int64 = 0, nanos: Int32 = 0) { + self.init() + self.seconds = seconds + self.nanos = nanos + } } extension Google_Protobuf_Timestamp: _CustomJSONCodable { - mutating func decodeJSON(from decoder: inout JSONDecoder) throws { - let s = try decoder.scanner.nextQuotedString() - (seconds, nanos) = try parseTimestamp(s: s) - } - - func encodedJSONString(options: JSONEncodingOptions) throws -> String { - if let formatted = formatTimestamp(seconds: seconds, nanos: nanos) { - return "\"\(formatted)\"" - } else { - throw JSONEncodingError.timestampRange + mutating func decodeJSON(from decoder: inout JSONDecoder) throws { + let s = try decoder.scanner.nextQuotedString() + (seconds, nanos) = try parseTimestamp(s: s) + } + + func encodedJSONString(options: JSONEncodingOptions) throws -> String { + if let formatted = formatTimestamp(seconds: seconds, nanos: nanos) { + return "\"\(formatted)\"" + } else { + throw JSONEncodingError.timestampRange + } } - } } extension Google_Protobuf_Timestamp { - /// Creates a new `Google_Protobuf_Timestamp` initialized relative to 00:00:00 - /// UTC on 1 January 1970 by a given number of seconds. - /// - /// - Parameter timeIntervalSince1970: The `TimeInterval`, interpreted as - /// seconds relative to 00:00:00 UTC on 1 January 1970. - public init(timeIntervalSince1970: TimeInterval) { - let sd = floor(timeIntervalSince1970) - let nd = round((timeIntervalSince1970 - sd) * TimeInterval(nanosPerSecond)) - let (s, n) = normalizeForTimestamp(seconds: Int64(sd), nanos: Int32(nd)) - self.init(seconds: s, nanos: n) - } - - /// Creates a new `Google_Protobuf_Timestamp` initialized relative to 00:00:00 - /// UTC on 1 January 2001 by a given number of seconds. - /// - /// - Parameter timeIntervalSinceReferenceDate: The `TimeInterval`, - /// interpreted as seconds relative to 00:00:00 UTC on 1 January 2001. - public init(timeIntervalSinceReferenceDate: TimeInterval) { - let sd = floor(timeIntervalSinceReferenceDate) - let nd = round( - (timeIntervalSinceReferenceDate - sd) * TimeInterval(nanosPerSecond)) - // The addition of timeIntervalBetween1970And... is deliberately delayed - // until the input is separated into an integer part and a fraction - // part, so that we don't unnecessarily lose precision. - let (s, n) = normalizeForTimestamp( - seconds: Int64(sd) + Int64(Date.timeIntervalBetween1970AndReferenceDate), - nanos: Int32(nd)) - self.init(seconds: s, nanos: n) - } - - /// Creates a new `Google_Protobuf_Timestamp` initialized to the same time as - /// the given `Date`. - /// - /// - Parameter date: The `Date` with which to initialize the timestamp. - public init(date: Date) { - // Note: Internally, Date uses the "reference date," not the 1970 date. - // We use it when interacting with Dates so that Date doesn't perform - // any double arithmetic on our behalf, which might cost us precision. - self.init( - timeIntervalSinceReferenceDate: date.timeIntervalSinceReferenceDate) - } - - /// The interval between the timestamp and 00:00:00 UTC on 1 January 1970. - public var timeIntervalSince1970: TimeInterval { - return TimeInterval(self.seconds) + - TimeInterval(self.nanos) / TimeInterval(nanosPerSecond) - } - - /// The interval between the timestamp and 00:00:00 UTC on 1 January 2001. - public var timeIntervalSinceReferenceDate: TimeInterval { - return TimeInterval( - self.seconds - Int64(Date.timeIntervalBetween1970AndReferenceDate)) + - TimeInterval(self.nanos) / TimeInterval(nanosPerSecond) - } - - /// A `Date` initialized to the same time as the timestamp. - public var date: Date { - return Date( - timeIntervalSinceReferenceDate: self.timeIntervalSinceReferenceDate) - } + /// Creates a new `Google_Protobuf_Timestamp` initialized relative to 00:00:00 + /// UTC on 1 January 1970 by a given number of seconds. + /// + /// - Parameter timeIntervalSince1970: The `TimeInterval`, interpreted as + /// seconds relative to 00:00:00 UTC on 1 January 1970. + public init(timeIntervalSince1970: TimeInterval) { + let sd = floor(timeIntervalSince1970) + let nd = round((timeIntervalSince1970 - sd) * TimeInterval(nanosPerSecond)) + let (s, n) = normalizeForTimestamp(seconds: Int64(sd), nanos: Int32(nd)) + self.init(seconds: s, nanos: n) + } + + /// Creates a new `Google_Protobuf_Timestamp` initialized relative to 00:00:00 + /// UTC on 1 January 2001 by a given number of seconds. + /// + /// - Parameter timeIntervalSinceReferenceDate: The `TimeInterval`, + /// interpreted as seconds relative to 00:00:00 UTC on 1 January 2001. + public init(timeIntervalSinceReferenceDate: TimeInterval) { + let sd = floor(timeIntervalSinceReferenceDate) + let nd = round( + (timeIntervalSinceReferenceDate - sd) * TimeInterval(nanosPerSecond) + ) + // The addition of timeIntervalBetween1970And... is deliberately delayed + // until the input is separated into an integer part and a fraction + // part, so that we don't unnecessarily lose precision. + let (s, n) = normalizeForTimestamp( + seconds: Int64(sd) + Int64(Date.timeIntervalBetween1970AndReferenceDate), + nanos: Int32(nd) + ) + self.init(seconds: s, nanos: n) + } + + /// Creates a new `Google_Protobuf_Timestamp` initialized to the same time as + /// the given `Date`. + /// + /// - Parameter date: The `Date` with which to initialize the timestamp. + public init(date: Date) { + // Note: Internally, Date uses the "reference date," not the 1970 date. + // We use it when interacting with Dates so that Date doesn't perform + // any double arithmetic on our behalf, which might cost us precision. + self.init( + timeIntervalSinceReferenceDate: date.timeIntervalSinceReferenceDate + ) + } + + /// The interval between the timestamp and 00:00:00 UTC on 1 January 1970. + public var timeIntervalSince1970: TimeInterval { + TimeInterval(self.seconds) + TimeInterval(self.nanos) / TimeInterval(nanosPerSecond) + } + + /// The interval between the timestamp and 00:00:00 UTC on 1 January 2001. + public var timeIntervalSinceReferenceDate: TimeInterval { + TimeInterval( + self.seconds - Int64(Date.timeIntervalBetween1970AndReferenceDate) + ) + TimeInterval(self.nanos) / TimeInterval(nanosPerSecond) + } + + /// A `Date` initialized to the same time as the timestamp. + public var date: Date { + Date( + timeIntervalSinceReferenceDate: self.timeIntervalSinceReferenceDate + ) + } } private func normalizeForTimestamp( - seconds: Int64, - nanos: Int32 + seconds: Int64, + nanos: Int32 ) -> (seconds: Int64, nanos: Int32) { - // The Timestamp spec says that nanos must be in the range [0, 999999999), - // as in actual modular arithmetic. + // The Timestamp spec says that nanos must be in the range [0, 999999999), + // as in actual modular arithmetic. - let s = seconds + Int64(div(nanos, nanosPerSecond)) - let n = mod(nanos, nanosPerSecond) - return (seconds: s, nanos: n) + let s = seconds + Int64(div(nanos, nanosPerSecond)) + let n = mod(nanos, nanosPerSecond) + return (seconds: s, nanos: n) } public func + ( - lhs: Google_Protobuf_Timestamp, - rhs: Google_Protobuf_Duration + lhs: Google_Protobuf_Timestamp, + rhs: Google_Protobuf_Duration ) -> Google_Protobuf_Timestamp { - let (s, n) = normalizeForTimestamp(seconds: lhs.seconds + rhs.seconds, - nanos: lhs.nanos + rhs.nanos) - return Google_Protobuf_Timestamp(seconds: s, nanos: n) + let (s, n) = normalizeForTimestamp( + seconds: lhs.seconds + rhs.seconds, + nanos: lhs.nanos + rhs.nanos + ) + return Google_Protobuf_Timestamp(seconds: s, nanos: n) } public func + ( - lhs: Google_Protobuf_Duration, - rhs: Google_Protobuf_Timestamp + lhs: Google_Protobuf_Duration, + rhs: Google_Protobuf_Timestamp ) -> Google_Protobuf_Timestamp { - let (s, n) = normalizeForTimestamp(seconds: lhs.seconds + rhs.seconds, - nanos: lhs.nanos + rhs.nanos) - return Google_Protobuf_Timestamp(seconds: s, nanos: n) + let (s, n) = normalizeForTimestamp( + seconds: lhs.seconds + rhs.seconds, + nanos: lhs.nanos + rhs.nanos + ) + return Google_Protobuf_Timestamp(seconds: s, nanos: n) } public func - ( - lhs: Google_Protobuf_Timestamp, - rhs: Google_Protobuf_Duration + lhs: Google_Protobuf_Timestamp, + rhs: Google_Protobuf_Duration ) -> Google_Protobuf_Timestamp { - let (s, n) = normalizeForTimestamp(seconds: lhs.seconds - rhs.seconds, - nanos: lhs.nanos - rhs.nanos) - return Google_Protobuf_Timestamp(seconds: s, nanos: n) + let (s, n) = normalizeForTimestamp( + seconds: lhs.seconds - rhs.seconds, + nanos: lhs.nanos - rhs.nanos + ) + return Google_Protobuf_Timestamp(seconds: s, nanos: n) } diff --git a/Sources/SwiftProtobuf/Google_Protobuf_Value+Extensions.swift b/Sources/SwiftProtobuf/Google_Protobuf_Value+Extensions.swift index 0bc90f8e8..65b5b74f7 100644 --- a/Sources/SwiftProtobuf/Google_Protobuf_Value+Extensions.swift +++ b/Sources/SwiftProtobuf/Google_Protobuf_Value+Extensions.swift @@ -14,154 +14,154 @@ // ----------------------------------------------------------------------------- extension Google_Protobuf_Value: ExpressibleByIntegerLiteral { - public typealias IntegerLiteralType = Int64 + public typealias IntegerLiteralType = Int64 - /// Creates a new `Google_Protobuf_Value` from an integer literal. - public init(integerLiteral value: Int64) { - self.init(kind: .numberValue(Double(value))) - } + /// Creates a new `Google_Protobuf_Value` from an integer literal. + public init(integerLiteral value: Int64) { + self.init(kind: .numberValue(Double(value))) + } } extension Google_Protobuf_Value: ExpressibleByFloatLiteral { - public typealias FloatLiteralType = Double + public typealias FloatLiteralType = Double - /// Creates a new `Google_Protobuf_Value` from a floating point literal. - public init(floatLiteral value: Double) { - self.init(kind: .numberValue(value)) - } + /// Creates a new `Google_Protobuf_Value` from a floating point literal. + public init(floatLiteral value: Double) { + self.init(kind: .numberValue(value)) + } } extension Google_Protobuf_Value: ExpressibleByBooleanLiteral { - public typealias BooleanLiteralType = Bool + public typealias BooleanLiteralType = Bool - /// Creates a new `Google_Protobuf_Value` from a boolean literal. - public init(booleanLiteral value: Bool) { - self.init(kind: .boolValue(value)) - } + /// Creates a new `Google_Protobuf_Value` from a boolean literal. + public init(booleanLiteral value: Bool) { + self.init(kind: .boolValue(value)) + } } extension Google_Protobuf_Value: ExpressibleByStringLiteral { - public typealias StringLiteralType = String - public typealias ExtendedGraphemeClusterLiteralType = String - public typealias UnicodeScalarLiteralType = String - - /// Creates a new `Google_Protobuf_Value` from a string literal. - public init(stringLiteral value: String) { - self.init(kind: .stringValue(value)) - } - - /// Creates a new `Google_Protobuf_Value` from a Unicode scalar literal. - public init(unicodeScalarLiteral value: String) { - self.init(kind: .stringValue(value)) - } - - /// Creates a new `Google_Protobuf_Value` from a character literal. - public init(extendedGraphemeClusterLiteral value: String) { - self.init(kind: .stringValue(value)) - } + public typealias StringLiteralType = String + public typealias ExtendedGraphemeClusterLiteralType = String + public typealias UnicodeScalarLiteralType = String + + /// Creates a new `Google_Protobuf_Value` from a string literal. + public init(stringLiteral value: String) { + self.init(kind: .stringValue(value)) + } + + /// Creates a new `Google_Protobuf_Value` from a Unicode scalar literal. + public init(unicodeScalarLiteral value: String) { + self.init(kind: .stringValue(value)) + } + + /// Creates a new `Google_Protobuf_Value` from a character literal. + public init(extendedGraphemeClusterLiteral value: String) { + self.init(kind: .stringValue(value)) + } } extension Google_Protobuf_Value: ExpressibleByNilLiteral { - /// Creates a new `Google_Protobuf_Value` from the nil literal. - public init(nilLiteral: ()) { - self.init(kind: .nullValue(.nullValue)) - } + /// Creates a new `Google_Protobuf_Value` from the nil literal. + public init(nilLiteral: ()) { + self.init(kind: .nullValue(.nullValue)) + } } extension Google_Protobuf_Value: _CustomJSONCodable { - internal func encodedJSONString(options: JSONEncodingOptions) throws -> String { - var jsonEncoder = JSONEncoder() - try serializeJSONValue(to: &jsonEncoder, options: options) - return jsonEncoder.stringResult - } - - internal mutating func decodeJSON(from decoder: inout JSONDecoder) throws { - let c = try decoder.scanner.peekOneCharacter() - switch c { - case "n": - if !decoder.scanner.skipOptionalNull() { - throw JSONDecodingError.failure - } - kind = .nullValue(.nullValue) - case "[": - var l = Google_Protobuf_ListValue() - try l.decodeJSON(from: &decoder) - kind = .listValue(l) - case "{": - var s = Google_Protobuf_Struct() - try s.decodeJSON(from: &decoder) - kind = .structValue(s) - case "t", "f": - let b = try decoder.scanner.nextBool() - kind = .boolValue(b) - case "\"": - let s = try decoder.scanner.nextQuotedString() - kind = .stringValue(s) - default: - let d = try decoder.scanner.nextDouble() - kind = .numberValue(d) - } - } - - internal static func decodedFromJSONNull() -> Google_Protobuf_Value? { - return Google_Protobuf_Value(kind: .nullValue(.nullValue)) - } + internal func encodedJSONString(options: JSONEncodingOptions) throws -> String { + var jsonEncoder = JSONEncoder() + try serializeJSONValue(to: &jsonEncoder, options: options) + return jsonEncoder.stringResult + } + + internal mutating func decodeJSON(from decoder: inout JSONDecoder) throws { + let c = try decoder.scanner.peekOneCharacter() + switch c { + case "n": + if !decoder.scanner.skipOptionalNull() { + throw JSONDecodingError.failure + } + kind = .nullValue(.nullValue) + case "[": + var l = Google_Protobuf_ListValue() + try l.decodeJSON(from: &decoder) + kind = .listValue(l) + case "{": + var s = Google_Protobuf_Struct() + try s.decodeJSON(from: &decoder) + kind = .structValue(s) + case "t", "f": + let b = try decoder.scanner.nextBool() + kind = .boolValue(b) + case "\"": + let s = try decoder.scanner.nextQuotedString() + kind = .stringValue(s) + default: + let d = try decoder.scanner.nextDouble() + kind = .numberValue(d) + } + } + + internal static func decodedFromJSONNull() -> Google_Protobuf_Value? { + Google_Protobuf_Value(kind: .nullValue(.nullValue)) + } } extension Google_Protobuf_Value { - /// Creates a new `Google_Protobuf_Value` with the given kind. - fileprivate init(kind: OneOf_Kind) { - self.init() - self.kind = kind - } - - /// Creates a new `Google_Protobuf_Value` whose `kind` is `numberValue` with - /// the given floating-point value. - public init(numberValue: Double) { - self.init(kind: .numberValue(numberValue)) - } - - /// Creates a new `Google_Protobuf_Value` whose `kind` is `stringValue` with - /// the given string value. - public init(stringValue: String) { - self.init(kind: .stringValue(stringValue)) - } - - /// Creates a new `Google_Protobuf_Value` whose `kind` is `boolValue` with the - /// given boolean value. - public init(boolValue: Bool) { - self.init(kind: .boolValue(boolValue)) - } - - /// Creates a new `Google_Protobuf_Value` whose `kind` is `structValue` with - /// the given `Google_Protobuf_Struct` value. - public init(structValue: Google_Protobuf_Struct) { - self.init(kind: .structValue(structValue)) - } - - /// Creates a new `Google_Protobuf_Value` whose `kind` is `listValue` with the - /// given `Google_Struct_ListValue` value. - public init(listValue: Google_Protobuf_ListValue) { - self.init(kind: .listValue(listValue)) - } - - /// Writes out the JSON representation of the value to the given encoder. - internal func serializeJSONValue( - to encoder: inout JSONEncoder, - options: JSONEncodingOptions - ) throws { - switch kind { - case .nullValue?: encoder.putNullValue() - case .numberValue(let v)?: - guard v.isFinite else { - throw JSONEncodingError.valueNumberNotFinite - } - encoder.putDoubleValue(value: v) - case .stringValue(let v)?: encoder.putStringValue(value: v) - case .boolValue(let v)?: encoder.putNonQuotedBoolValue(value: v) - case .structValue(let v)?: encoder.append(text: try v.jsonString(options: options)) - case .listValue(let v)?: encoder.append(text: try v.jsonString(options: options)) - case nil: throw JSONEncodingError.missingValue - } - } + /// Creates a new `Google_Protobuf_Value` with the given kind. + fileprivate init(kind: OneOf_Kind) { + self.init() + self.kind = kind + } + + /// Creates a new `Google_Protobuf_Value` whose `kind` is `numberValue` with + /// the given floating-point value. + public init(numberValue: Double) { + self.init(kind: .numberValue(numberValue)) + } + + /// Creates a new `Google_Protobuf_Value` whose `kind` is `stringValue` with + /// the given string value. + public init(stringValue: String) { + self.init(kind: .stringValue(stringValue)) + } + + /// Creates a new `Google_Protobuf_Value` whose `kind` is `boolValue` with the + /// given boolean value. + public init(boolValue: Bool) { + self.init(kind: .boolValue(boolValue)) + } + + /// Creates a new `Google_Protobuf_Value` whose `kind` is `structValue` with + /// the given `Google_Protobuf_Struct` value. + public init(structValue: Google_Protobuf_Struct) { + self.init(kind: .structValue(structValue)) + } + + /// Creates a new `Google_Protobuf_Value` whose `kind` is `listValue` with the + /// given `Google_Struct_ListValue` value. + public init(listValue: Google_Protobuf_ListValue) { + self.init(kind: .listValue(listValue)) + } + + /// Writes out the JSON representation of the value to the given encoder. + internal func serializeJSONValue( + to encoder: inout JSONEncoder, + options: JSONEncodingOptions + ) throws { + switch kind { + case .nullValue?: encoder.putNullValue() + case .numberValue(let v)?: + guard v.isFinite else { + throw JSONEncodingError.valueNumberNotFinite + } + encoder.putDoubleValue(value: v) + case .stringValue(let v)?: encoder.putStringValue(value: v) + case .boolValue(let v)?: encoder.putNonQuotedBoolValue(value: v) + case .structValue(let v)?: encoder.append(text: try v.jsonString(options: options)) + case .listValue(let v)?: encoder.append(text: try v.jsonString(options: options)) + case nil: throw JSONEncodingError.missingValue + } + } } diff --git a/Sources/SwiftProtobuf/Google_Protobuf_Wrappers+Extensions.swift b/Sources/SwiftProtobuf/Google_Protobuf_Wrappers+Extensions.swift index 5a8af0c1e..f3c3ba1f7 100644 --- a/Sources/SwiftProtobuf/Google_Protobuf_Wrappers+Extensions.swift +++ b/Sources/SwiftProtobuf/Google_Protobuf_Wrappers+Extensions.swift @@ -19,237 +19,245 @@ import Foundation /// wrapper types extended below. protocol ProtobufWrapper { - /// The wrapped protobuf type (for example, `ProtobufDouble`). - associatedtype WrappedType: FieldType + /// The wrapped protobuf type (for example, `ProtobufDouble`). + associatedtype WrappedType: FieldType - /// Exposes the generated property to the extensions here. - var value: WrappedType.BaseType { get set } + /// Exposes the generated property to the extensions here. + var value: WrappedType.BaseType { get set } - /// Exposes the parameterless initializer to the extensions here. - init() + /// Exposes the parameterless initializer to the extensions here. + init() - /// Creates a new instance of the wrapper with the given value. - init(_ value: WrappedType.BaseType) + /// Creates a new instance of the wrapper with the given value. + init(_ value: WrappedType.BaseType) } extension ProtobufWrapper { - mutating func decodeJSON(from decoder: inout JSONDecoder) throws { - var v: WrappedType.BaseType? - try WrappedType.decodeSingular(value: &v, from: &decoder) - value = v ?? WrappedType.proto3DefaultValue - } + mutating func decodeJSON(from decoder: inout JSONDecoder) throws { + var v: WrappedType.BaseType? + try WrappedType.decodeSingular(value: &v, from: &decoder) + value = v ?? WrappedType.proto3DefaultValue + } } extension Google_Protobuf_DoubleValue: - ProtobufWrapper, ExpressibleByFloatLiteral, _CustomJSONCodable { - - public typealias WrappedType = ProtobufDouble - public typealias FloatLiteralType = WrappedType.BaseType - - public init(_ value: WrappedType.BaseType) { - self.init() - self.value = value - } - - public init(floatLiteral: FloatLiteralType) { - self.init(floatLiteral) - } - - func encodedJSONString(options: JSONEncodingOptions) throws -> String { - if value.isFinite { - // Swift 4.2 and later guarantees that this is accurate - // enough to parse back to the exact value on the other end. - return value.description - } else { - // Protobuf-specific handling of NaN and infinities - var encoder = JSONEncoder() - encoder.putDoubleValue(value: value) - return encoder.stringResult - } - } + ProtobufWrapper, ExpressibleByFloatLiteral, _CustomJSONCodable +{ + + public typealias WrappedType = ProtobufDouble + public typealias FloatLiteralType = WrappedType.BaseType + + public init(_ value: WrappedType.BaseType) { + self.init() + self.value = value + } + + public init(floatLiteral: FloatLiteralType) { + self.init(floatLiteral) + } + + func encodedJSONString(options: JSONEncodingOptions) throws -> String { + if value.isFinite { + // Swift 4.2 and later guarantees that this is accurate + // enough to parse back to the exact value on the other end. + return value.description + } else { + // Protobuf-specific handling of NaN and infinities + var encoder = JSONEncoder() + encoder.putDoubleValue(value: value) + return encoder.stringResult + } + } } extension Google_Protobuf_FloatValue: - ProtobufWrapper, ExpressibleByFloatLiteral, _CustomJSONCodable { - - public typealias WrappedType = ProtobufFloat - public typealias FloatLiteralType = Float - - public init(_ value: WrappedType.BaseType) { - self.init() - self.value = value - } - - public init(floatLiteral: FloatLiteralType) { - self.init(floatLiteral) - } - - func encodedJSONString(options: JSONEncodingOptions) throws -> String { - if value.isFinite { - // Swift 4.2 and later guarantees that this is accurate - // enough to parse back to the exact value on the other end. - return value.description - } else { - // Protobuf-specific handling of NaN and infinities - var encoder = JSONEncoder() - encoder.putFloatValue(value: value) - return encoder.stringResult - } - } + ProtobufWrapper, ExpressibleByFloatLiteral, _CustomJSONCodable +{ + + public typealias WrappedType = ProtobufFloat + public typealias FloatLiteralType = Float + + public init(_ value: WrappedType.BaseType) { + self.init() + self.value = value + } + + public init(floatLiteral: FloatLiteralType) { + self.init(floatLiteral) + } + + func encodedJSONString(options: JSONEncodingOptions) throws -> String { + if value.isFinite { + // Swift 4.2 and later guarantees that this is accurate + // enough to parse back to the exact value on the other end. + return value.description + } else { + // Protobuf-specific handling of NaN and infinities + var encoder = JSONEncoder() + encoder.putFloatValue(value: value) + return encoder.stringResult + } + } } extension Google_Protobuf_Int64Value: - ProtobufWrapper, ExpressibleByIntegerLiteral, _CustomJSONCodable { + ProtobufWrapper, ExpressibleByIntegerLiteral, _CustomJSONCodable +{ - public typealias WrappedType = ProtobufInt64 - public typealias IntegerLiteralType = WrappedType.BaseType + public typealias WrappedType = ProtobufInt64 + public typealias IntegerLiteralType = WrappedType.BaseType - public init(_ value: WrappedType.BaseType) { - self.init() - self.value = value - } + public init(_ value: WrappedType.BaseType) { + self.init() + self.value = value + } - public init(integerLiteral: IntegerLiteralType) { - self.init(integerLiteral) - } + public init(integerLiteral: IntegerLiteralType) { + self.init(integerLiteral) + } - func encodedJSONString(options: JSONEncodingOptions) throws -> String { - var encoded = value.description - if !options.alwaysPrintInt64sAsNumbers { - encoded = "\"" + encoded + "\"" + func encodedJSONString(options: JSONEncodingOptions) throws -> String { + var encoded = value.description + if !options.alwaysPrintInt64sAsNumbers { + encoded = "\"" + encoded + "\"" + } + return encoded } - return encoded - } } extension Google_Protobuf_UInt64Value: - ProtobufWrapper, ExpressibleByIntegerLiteral, _CustomJSONCodable { + ProtobufWrapper, ExpressibleByIntegerLiteral, _CustomJSONCodable +{ - public typealias WrappedType = ProtobufUInt64 - public typealias IntegerLiteralType = WrappedType.BaseType + public typealias WrappedType = ProtobufUInt64 + public typealias IntegerLiteralType = WrappedType.BaseType - public init(_ value: WrappedType.BaseType) { - self.init() - self.value = value - } + public init(_ value: WrappedType.BaseType) { + self.init() + self.value = value + } - public init(integerLiteral: IntegerLiteralType) { - self.init(integerLiteral) - } + public init(integerLiteral: IntegerLiteralType) { + self.init(integerLiteral) + } - func encodedJSONString(options: JSONEncodingOptions) throws -> String { - var encoded = String(value) - if !options.alwaysPrintInt64sAsNumbers { - encoded = "\"" + encoded + "\"" + func encodedJSONString(options: JSONEncodingOptions) throws -> String { + var encoded = String(value) + if !options.alwaysPrintInt64sAsNumbers { + encoded = "\"" + encoded + "\"" + } + return encoded } - return encoded - } } extension Google_Protobuf_Int32Value: - ProtobufWrapper, ExpressibleByIntegerLiteral, _CustomJSONCodable { + ProtobufWrapper, ExpressibleByIntegerLiteral, _CustomJSONCodable +{ - public typealias WrappedType = ProtobufInt32 - public typealias IntegerLiteralType = WrappedType.BaseType + public typealias WrappedType = ProtobufInt32 + public typealias IntegerLiteralType = WrappedType.BaseType - public init(_ value: WrappedType.BaseType) { - self.init() - self.value = value - } + public init(_ value: WrappedType.BaseType) { + self.init() + self.value = value + } - public init(integerLiteral: IntegerLiteralType) { - self.init(integerLiteral) - } + public init(integerLiteral: IntegerLiteralType) { + self.init(integerLiteral) + } - func encodedJSONString(options: JSONEncodingOptions) throws -> String { - return String(value) - } + func encodedJSONString(options: JSONEncodingOptions) throws -> String { + String(value) + } } extension Google_Protobuf_UInt32Value: - ProtobufWrapper, ExpressibleByIntegerLiteral, _CustomJSONCodable { + ProtobufWrapper, ExpressibleByIntegerLiteral, _CustomJSONCodable +{ - public typealias WrappedType = ProtobufUInt32 - public typealias IntegerLiteralType = WrappedType.BaseType + public typealias WrappedType = ProtobufUInt32 + public typealias IntegerLiteralType = WrappedType.BaseType - public init(_ value: WrappedType.BaseType) { - self.init() - self.value = value - } + public init(_ value: WrappedType.BaseType) { + self.init() + self.value = value + } - public init(integerLiteral: IntegerLiteralType) { - self.init(integerLiteral) - } + public init(integerLiteral: IntegerLiteralType) { + self.init(integerLiteral) + } - func encodedJSONString(options: JSONEncodingOptions) throws -> String { - return String(value) - } + func encodedJSONString(options: JSONEncodingOptions) throws -> String { + String(value) + } } extension Google_Protobuf_BoolValue: - ProtobufWrapper, ExpressibleByBooleanLiteral, _CustomJSONCodable { + ProtobufWrapper, ExpressibleByBooleanLiteral, _CustomJSONCodable +{ - public typealias WrappedType = ProtobufBool - public typealias BooleanLiteralType = Bool + public typealias WrappedType = ProtobufBool + public typealias BooleanLiteralType = Bool - public init(_ value: WrappedType.BaseType) { - self.init() - self.value = value - } + public init(_ value: WrappedType.BaseType) { + self.init() + self.value = value + } - public init(booleanLiteral: Bool) { - self.init(booleanLiteral) - } + public init(booleanLiteral: Bool) { + self.init(booleanLiteral) + } - func encodedJSONString(options: JSONEncodingOptions) throws -> String { - return value ? "true" : "false" - } + func encodedJSONString(options: JSONEncodingOptions) throws -> String { + value ? "true" : "false" + } } extension Google_Protobuf_StringValue: - ProtobufWrapper, ExpressibleByStringLiteral, _CustomJSONCodable { - - public typealias WrappedType = ProtobufString - public typealias StringLiteralType = String - public typealias ExtendedGraphemeClusterLiteralType = String - public typealias UnicodeScalarLiteralType = String - - public init(_ value: WrappedType.BaseType) { - self.init() - self.value = value - } - - public init(stringLiteral: String) { - self.init(stringLiteral) - } - - public init(extendedGraphemeClusterLiteral: String) { - self.init(extendedGraphemeClusterLiteral) - } - - public init(unicodeScalarLiteral: String) { - self.init(unicodeScalarLiteral) - } - - func encodedJSONString(options: JSONEncodingOptions) throws -> String { - var encoder = JSONEncoder() - encoder.putStringValue(value: value) - return encoder.stringResult - } + ProtobufWrapper, ExpressibleByStringLiteral, _CustomJSONCodable +{ + + public typealias WrappedType = ProtobufString + public typealias StringLiteralType = String + public typealias ExtendedGraphemeClusterLiteralType = String + public typealias UnicodeScalarLiteralType = String + + public init(_ value: WrappedType.BaseType) { + self.init() + self.value = value + } + + public init(stringLiteral: String) { + self.init(stringLiteral) + } + + public init(extendedGraphemeClusterLiteral: String) { + self.init(extendedGraphemeClusterLiteral) + } + + public init(unicodeScalarLiteral: String) { + self.init(unicodeScalarLiteral) + } + + func encodedJSONString(options: JSONEncodingOptions) throws -> String { + var encoder = JSONEncoder() + encoder.putStringValue(value: value) + return encoder.stringResult + } } extension Google_Protobuf_BytesValue: ProtobufWrapper, _CustomJSONCodable { - public typealias WrappedType = ProtobufBytes + public typealias WrappedType = ProtobufBytes - public init(_ value: WrappedType.BaseType) { - self.init() - self.value = value - } + public init(_ value: WrappedType.BaseType) { + self.init() + self.value = value + } - func encodedJSONString(options: JSONEncodingOptions) throws -> String { - var encoder = JSONEncoder() - encoder.putBytesValue(value: value) - return encoder.stringResult - } + func encodedJSONString(options: JSONEncodingOptions) throws -> String { + var encoder = JSONEncoder() + encoder.putBytesValue(value: value) + return encoder.stringResult + } } diff --git a/Sources/SwiftProtobuf/HashVisitor.swift b/Sources/SwiftProtobuf/HashVisitor.swift index 67ccea36b..47f2a0acd 100644 --- a/Sources/SwiftProtobuf/HashVisitor.swift +++ b/Sources/SwiftProtobuf/HashVisitor.swift @@ -15,8 +15,8 @@ import Foundation -private let i_2166136261 = Int(bitPattern: 2166136261) -private let i_16777619 = Int(16777619) +private let i_2166136261 = Int(bitPattern: 2_166_136_261) +private let i_16777619 = Int(16_777_619) /// Computes the hash of a message by visiting its fields recursively. /// @@ -27,193 +27,195 @@ private let i_16777619 = Int(16777619) /// message fields they want to include. internal struct HashVisitor: Visitor { - internal private(set) var hasher: Hasher - - init(_ hasher: Hasher) { - self.hasher = hasher - } - - mutating func visitUnknown(bytes: Data) throws { - hasher.combine(bytes) - } - - mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws { - hasher.combine(fieldNumber) - hasher.combine(value) - } - - mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws { - hasher.combine(fieldNumber) - hasher.combine(value) - } - - mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws { - hasher.combine(fieldNumber) - hasher.combine(value) - } - - mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws { - hasher.combine(fieldNumber) - hasher.combine(value) - } - - mutating func visitSingularStringField(value: String, fieldNumber: Int) throws { - hasher.combine(fieldNumber) - hasher.combine(value) - } - - mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws { - hasher.combine(fieldNumber) - hasher.combine(value) - } - - mutating func visitSingularEnumField(value: E, - fieldNumber: Int) { - hasher.combine(fieldNumber) - hasher.combine(value) - } - - mutating func visitSingularMessageField(value: M, fieldNumber: Int) { - hasher.combine(fieldNumber) - value.hash(into: &hasher) - } - - mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws { - assert(!value.isEmpty) - hasher.combine(fieldNumber) - hasher.combine(value) - } - - mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws { - assert(!value.isEmpty) - hasher.combine(fieldNumber) - hasher.combine(value) - } - - mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws { - assert(!value.isEmpty) - hasher.combine(fieldNumber) - hasher.combine(value) - } - - mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws { - assert(!value.isEmpty) - hasher.combine(fieldNumber) - hasher.combine(value) - } - - mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws { - assert(!value.isEmpty) - hasher.combine(fieldNumber) - hasher.combine(value) - } - - mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws { - assert(!value.isEmpty) - hasher.combine(fieldNumber) - hasher.combine(value) - } - - mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws { - assert(!value.isEmpty) - hasher.combine(fieldNumber) - hasher.combine(value) - } - - mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws { - assert(!value.isEmpty) - hasher.combine(fieldNumber) - hasher.combine(value) - } - - mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws { - assert(!value.isEmpty) - hasher.combine(fieldNumber) - hasher.combine(value) - } - - mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws { - assert(!value.isEmpty) - hasher.combine(fieldNumber) - hasher.combine(value) - } - - mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws { - assert(!value.isEmpty) - hasher.combine(fieldNumber) - hasher.combine(value) - } - - mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws { - assert(!value.isEmpty) - hasher.combine(fieldNumber) - hasher.combine(value) - } - - mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws { - assert(!value.isEmpty) - hasher.combine(fieldNumber) - hasher.combine(value) - } - - mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws { - assert(!value.isEmpty) - hasher.combine(fieldNumber) - hasher.combine(value) - } - - mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws { - assert(!value.isEmpty) - hasher.combine(fieldNumber) - hasher.combine(value) - } - - mutating func visitRepeatedEnumField(value: [E], fieldNumber: Int) throws { - assert(!value.isEmpty) - hasher.combine(fieldNumber) - hasher.combine(value) - } - - mutating func visitRepeatedMessageField(value: [M], fieldNumber: Int) throws { - assert(!value.isEmpty) - hasher.combine(fieldNumber) - for v in value { - v.hash(into: &hasher) - } - } - - mutating func visitRepeatedGroupField(value: [G], fieldNumber: Int) throws { - assert(!value.isEmpty) - hasher.combine(fieldNumber) - for v in value { - v.hash(into: &hasher) - } - } - - mutating func visitMapField( - fieldType: _ProtobufMap.Type, - value: _ProtobufMap.BaseType, - fieldNumber: Int - ) throws { - hasher.combine(fieldNumber) - hasher.combine(value) - } - - mutating func visitMapField( - fieldType: _ProtobufEnumMap.Type, - value: _ProtobufEnumMap.BaseType, - fieldNumber: Int - ) throws where ValueType.RawValue == Int { - hasher.combine(fieldNumber) - hasher.combine(value) - } - - mutating func visitMapField( - fieldType: _ProtobufMessageMap.Type, - value: _ProtobufMessageMap.BaseType, - fieldNumber: Int - ) throws { - hasher.combine(fieldNumber) - hasher.combine(value) - } + internal private(set) var hasher: Hasher + + init(_ hasher: Hasher) { + self.hasher = hasher + } + + mutating func visitUnknown(bytes: Data) throws { + hasher.combine(bytes) + } + + mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws { + hasher.combine(fieldNumber) + hasher.combine(value) + } + + mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws { + hasher.combine(fieldNumber) + hasher.combine(value) + } + + mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws { + hasher.combine(fieldNumber) + hasher.combine(value) + } + + mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws { + hasher.combine(fieldNumber) + hasher.combine(value) + } + + mutating func visitSingularStringField(value: String, fieldNumber: Int) throws { + hasher.combine(fieldNumber) + hasher.combine(value) + } + + mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws { + hasher.combine(fieldNumber) + hasher.combine(value) + } + + mutating func visitSingularEnumField( + value: E, + fieldNumber: Int + ) { + hasher.combine(fieldNumber) + hasher.combine(value) + } + + mutating func visitSingularMessageField(value: M, fieldNumber: Int) { + hasher.combine(fieldNumber) + value.hash(into: &hasher) + } + + mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws { + assert(!value.isEmpty) + hasher.combine(fieldNumber) + hasher.combine(value) + } + + mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws { + assert(!value.isEmpty) + hasher.combine(fieldNumber) + hasher.combine(value) + } + + mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws { + assert(!value.isEmpty) + hasher.combine(fieldNumber) + hasher.combine(value) + } + + mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws { + assert(!value.isEmpty) + hasher.combine(fieldNumber) + hasher.combine(value) + } + + mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws { + assert(!value.isEmpty) + hasher.combine(fieldNumber) + hasher.combine(value) + } + + mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws { + assert(!value.isEmpty) + hasher.combine(fieldNumber) + hasher.combine(value) + } + + mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws { + assert(!value.isEmpty) + hasher.combine(fieldNumber) + hasher.combine(value) + } + + mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws { + assert(!value.isEmpty) + hasher.combine(fieldNumber) + hasher.combine(value) + } + + mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws { + assert(!value.isEmpty) + hasher.combine(fieldNumber) + hasher.combine(value) + } + + mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws { + assert(!value.isEmpty) + hasher.combine(fieldNumber) + hasher.combine(value) + } + + mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws { + assert(!value.isEmpty) + hasher.combine(fieldNumber) + hasher.combine(value) + } + + mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws { + assert(!value.isEmpty) + hasher.combine(fieldNumber) + hasher.combine(value) + } + + mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws { + assert(!value.isEmpty) + hasher.combine(fieldNumber) + hasher.combine(value) + } + + mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws { + assert(!value.isEmpty) + hasher.combine(fieldNumber) + hasher.combine(value) + } + + mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws { + assert(!value.isEmpty) + hasher.combine(fieldNumber) + hasher.combine(value) + } + + mutating func visitRepeatedEnumField(value: [E], fieldNumber: Int) throws { + assert(!value.isEmpty) + hasher.combine(fieldNumber) + hasher.combine(value) + } + + mutating func visitRepeatedMessageField(value: [M], fieldNumber: Int) throws { + assert(!value.isEmpty) + hasher.combine(fieldNumber) + for v in value { + v.hash(into: &hasher) + } + } + + mutating func visitRepeatedGroupField(value: [G], fieldNumber: Int) throws { + assert(!value.isEmpty) + hasher.combine(fieldNumber) + for v in value { + v.hash(into: &hasher) + } + } + + mutating func visitMapField( + fieldType: _ProtobufMap.Type, + value: _ProtobufMap.BaseType, + fieldNumber: Int + ) throws { + hasher.combine(fieldNumber) + hasher.combine(value) + } + + mutating func visitMapField( + fieldType: _ProtobufEnumMap.Type, + value: _ProtobufEnumMap.BaseType, + fieldNumber: Int + ) throws where ValueType.RawValue == Int { + hasher.combine(fieldNumber) + hasher.combine(value) + } + + mutating func visitMapField( + fieldType: _ProtobufMessageMap.Type, + value: _ProtobufMessageMap.BaseType, + fieldNumber: Int + ) throws { + hasher.combine(fieldNumber) + hasher.combine(value) + } } diff --git a/Sources/SwiftProtobuf/Internal.swift b/Sources/SwiftProtobuf/Internal.swift index 9f175984d..dececa054 100644 --- a/Sources/SwiftProtobuf/Internal.swift +++ b/Sources/SwiftProtobuf/Internal.swift @@ -19,35 +19,40 @@ import Foundation /// Functions that are public only because they are used by generated message implementations. /// - Important: NOT INTENDED TO BE CALLED BY CLIENTS. public enum Internal { - - /// A singleton instance of an empty data that is used by the generated code - /// for default values. This is a performance enhancement to work around the - /// fact that the `Data` type in Swift involves a new heap allocation every - /// time an empty instance is initialized, instead of sharing a common empty - /// backing storage. - /// - Note: This isn't really used any longer - it's only here to support code generated by 1.10.2 and earlier. - @available(*, deprecated, message: "Internal.emptyData isn't used any longer in newer versions of the generator. Generate code with a version later than 1.10.2 to get performance improvements. See https://github.com/apple/swift-protobuf/pull/1028 for more information.") - public static let emptyData = Data() - /// Helper to loop over a list of Messages to see if they are all - /// initialized (see Message.isInitialized for what that means). - public static func areAllInitialized(_ listOfMessages: [any Message]) -> Bool { - for msg in listOfMessages { - if !msg.isInitialized { - return false - } + /// A singleton instance of an empty data that is used by the generated code + /// for default values. This is a performance enhancement to work around the + /// fact that the `Data` type in Swift involves a new heap allocation every + /// time an empty instance is initialized, instead of sharing a common empty + /// backing storage. + /// - Note: This isn't really used any longer - it's only here to support code generated by 1.10.2 and earlier. + @available( + *, + deprecated, + message: + "Internal.emptyData isn't used any longer in newer versions of the generator. Generate code with a version later than 1.10.2 to get performance improvements. See https://github.com/apple/swift-protobuf/pull/1028 for more information." + ) + public static let emptyData = Data() + + /// Helper to loop over a list of Messages to see if they are all + /// initialized (see Message.isInitialized for what that means). + public static func areAllInitialized(_ listOfMessages: [any Message]) -> Bool { + for msg in listOfMessages { + if !msg.isInitialized { + return false + } + } + return true } - return true - } - /// Helper to loop over dictionary with values that are Messages to see if - /// they are all initialized (see Message.isInitialized for what that means). - public static func areAllInitialized(_ mapToMessages: [K: any Message]) -> Bool { - for (_, msg) in mapToMessages { - if !msg.isInitialized { - return false - } + /// Helper to loop over dictionary with values that are Messages to see if + /// they are all initialized (see Message.isInitialized for what that means). + public static func areAllInitialized(_ mapToMessages: [K: any Message]) -> Bool { + for (_, msg) in mapToMessages { + if !msg.isInitialized { + return false + } + } + return true } - return true - } } diff --git a/Sources/SwiftProtobuf/JSONDecoder.swift b/Sources/SwiftProtobuf/JSONDecoder.swift index 3b22cc31e..d8fd4e0a8 100644 --- a/Sources/SwiftProtobuf/JSONDecoder.swift +++ b/Sources/SwiftProtobuf/JSONDecoder.swift @@ -15,733 +15,742 @@ import Foundation internal struct JSONDecoder: Decoder { - internal var scanner: JSONScanner - internal var messageType: any Message.Type - private var fieldCount = 0 - private var isMapKey = false - private var fieldNameMap: _NameMap? - - internal var options: JSONDecodingOptions { - return scanner.options - } - - mutating func handleConflictingOneOf() throws { - throw JSONDecodingError.conflictingOneOf - } - - internal init(source: UnsafeRawBufferPointer, options: JSONDecodingOptions, - messageType: any Message.Type, extensions: (any ExtensionMap)?) { - let scanner = JSONScanner(source: source, - options: options, - extensions: extensions) - self.init(scanner: scanner, messageType: messageType) - } - - private init(scanner: JSONScanner, messageType: any Message.Type) { - self.scanner = scanner - self.messageType = messageType - } - - mutating func nextFieldNumber() throws -> Int? { - if scanner.skipOptionalObjectEnd() { - return nil - } - if fieldCount > 0 { - try scanner.skipRequiredComma() - } - let fieldNumber = try scanner.nextFieldNumber(names: fieldNameMap!, - messageType: messageType) - if let fieldNumber = fieldNumber { - fieldCount += 1 - return fieldNumber - } - return nil - } - - mutating func decodeSingularFloatField(value: inout Float) throws { - if scanner.skipOptionalNull() { - value = 0 - return - } - value = try scanner.nextFloat() - } - - mutating func decodeSingularFloatField(value: inout Float?) throws { - if scanner.skipOptionalNull() { - value = nil - return - } - value = try scanner.nextFloat() - } - - mutating func decodeRepeatedFloatField(value: inout [Float]) throws { - if scanner.skipOptionalNull() { - return - } - try scanner.skipRequiredArrayStart() - if scanner.skipOptionalArrayEnd() { - return - } - while true { - let n = try scanner.nextFloat() - value.append(n) - if scanner.skipOptionalArrayEnd() { - return - } - try scanner.skipRequiredComma() - } - } - - mutating func decodeSingularDoubleField(value: inout Double) throws { - if scanner.skipOptionalNull() { - value = 0 - return - } - value = try scanner.nextDouble() - } - - mutating func decodeSingularDoubleField(value: inout Double?) throws { - if scanner.skipOptionalNull() { - value = nil - return - } - value = try scanner.nextDouble() - } - - mutating func decodeRepeatedDoubleField(value: inout [Double]) throws { - if scanner.skipOptionalNull() { - return - } - try scanner.skipRequiredArrayStart() - if scanner.skipOptionalArrayEnd() { - return - } - while true { - let n = try scanner.nextDouble() - value.append(n) - if scanner.skipOptionalArrayEnd() { - return - } - try scanner.skipRequiredComma() - } - } - - mutating func decodeSingularInt32Field(value: inout Int32) throws { - if scanner.skipOptionalNull() { - value = 0 - return - } - let n = try scanner.nextSInt() - if n > Int64(Int32.max) || n < Int64(Int32.min) { - throw JSONDecodingError.numberRange - } - value = Int32(truncatingIfNeeded: n) - } - - mutating func decodeSingularInt32Field(value: inout Int32?) throws { - if scanner.skipOptionalNull() { - value = nil - return - } - let n = try scanner.nextSInt() - if n > Int64(Int32.max) || n < Int64(Int32.min) { - throw JSONDecodingError.numberRange - } - value = Int32(truncatingIfNeeded: n) - } - - mutating func decodeRepeatedInt32Field(value: inout [Int32]) throws { - if scanner.skipOptionalNull() { - return - } - try scanner.skipRequiredArrayStart() - if scanner.skipOptionalArrayEnd() { - return - } - while true { - let n = try scanner.nextSInt() - if n > Int64(Int32.max) || n < Int64(Int32.min) { - throw JSONDecodingError.numberRange - } - value.append(Int32(truncatingIfNeeded: n)) - if scanner.skipOptionalArrayEnd() { - return - } - try scanner.skipRequiredComma() - } - } - - mutating func decodeSingularInt64Field(value: inout Int64) throws { - if scanner.skipOptionalNull() { - value = 0 - return - } - value = try scanner.nextSInt() - } - - mutating func decodeSingularInt64Field(value: inout Int64?) throws { - if scanner.skipOptionalNull() { - value = nil - return - } - value = try scanner.nextSInt() - } - - mutating func decodeRepeatedInt64Field(value: inout [Int64]) throws { - if scanner.skipOptionalNull() { - return - } - try scanner.skipRequiredArrayStart() - if scanner.skipOptionalArrayEnd() { - return - } - while true { - let n = try scanner.nextSInt() - value.append(n) - if scanner.skipOptionalArrayEnd() { - return - } - try scanner.skipRequiredComma() - } - } - - mutating func decodeSingularUInt32Field(value: inout UInt32) throws { - if scanner.skipOptionalNull() { - value = 0 - return - } - let n = try scanner.nextUInt() - if n > UInt64(UInt32.max) { - throw JSONDecodingError.numberRange - } - value = UInt32(truncatingIfNeeded: n) - } - - mutating func decodeSingularUInt32Field(value: inout UInt32?) throws { - if scanner.skipOptionalNull() { - value = nil - return - } - let n = try scanner.nextUInt() - if n > UInt64(UInt32.max) { - throw JSONDecodingError.numberRange - } - value = UInt32(truncatingIfNeeded: n) - } - - mutating func decodeRepeatedUInt32Field(value: inout [UInt32]) throws { - if scanner.skipOptionalNull() { - return - } - try scanner.skipRequiredArrayStart() - if scanner.skipOptionalArrayEnd() { - return - } - while true { - let n = try scanner.nextUInt() - if n > UInt64(UInt32.max) { - throw JSONDecodingError.numberRange - } - value.append(UInt32(truncatingIfNeeded: n)) - if scanner.skipOptionalArrayEnd() { - return - } - try scanner.skipRequiredComma() - } - } - - mutating func decodeSingularUInt64Field(value: inout UInt64) throws { - if scanner.skipOptionalNull() { - value = 0 - return - } - value = try scanner.nextUInt() - } - - mutating func decodeSingularUInt64Field(value: inout UInt64?) throws { - if scanner.skipOptionalNull() { - value = nil - return - } - value = try scanner.nextUInt() - } - - mutating func decodeRepeatedUInt64Field(value: inout [UInt64]) throws { - if scanner.skipOptionalNull() { - return - } - try scanner.skipRequiredArrayStart() - if scanner.skipOptionalArrayEnd() { - return - } - while true { - let n = try scanner.nextUInt() - value.append(n) - if scanner.skipOptionalArrayEnd() { - return - } - try scanner.skipRequiredComma() - } - } - - mutating func decodeSingularSInt32Field(value: inout Int32) throws { - try decodeSingularInt32Field(value: &value) - } - - mutating func decodeSingularSInt32Field(value: inout Int32?) throws { - try decodeSingularInt32Field(value: &value) - } - - mutating func decodeRepeatedSInt32Field(value: inout [Int32]) throws { - try decodeRepeatedInt32Field(value: &value) - } - - mutating func decodeSingularSInt64Field(value: inout Int64) throws { - try decodeSingularInt64Field(value: &value) - } - - mutating func decodeSingularSInt64Field(value: inout Int64?) throws { - try decodeSingularInt64Field(value: &value) - } - - mutating func decodeRepeatedSInt64Field(value: inout [Int64]) throws { - try decodeRepeatedInt64Field(value: &value) - } - - mutating func decodeSingularFixed32Field(value: inout UInt32) throws { - try decodeSingularUInt32Field(value: &value) - } - - mutating func decodeSingularFixed32Field(value: inout UInt32?) throws { - try decodeSingularUInt32Field(value: &value) - } - - mutating func decodeRepeatedFixed32Field(value: inout [UInt32]) throws { - try decodeRepeatedUInt32Field(value: &value) - } - - mutating func decodeSingularFixed64Field(value: inout UInt64) throws { - try decodeSingularUInt64Field(value: &value) - } - - mutating func decodeSingularFixed64Field(value: inout UInt64?) throws { - try decodeSingularUInt64Field(value: &value) - } - - mutating func decodeRepeatedFixed64Field(value: inout [UInt64]) throws { - try decodeRepeatedUInt64Field(value: &value) - } - - mutating func decodeSingularSFixed32Field(value: inout Int32) throws { - try decodeSingularInt32Field(value: &value) - } - - mutating func decodeSingularSFixed32Field(value: inout Int32?) throws { - try decodeSingularInt32Field(value: &value) - } - - mutating func decodeRepeatedSFixed32Field(value: inout [Int32]) throws { - try decodeRepeatedInt32Field(value: &value) - } - - mutating func decodeSingularSFixed64Field(value: inout Int64) throws { - try decodeSingularInt64Field(value: &value) - } + internal var scanner: JSONScanner + internal var messageType: any Message.Type + private var fieldCount = 0 + private var isMapKey = false + private var fieldNameMap: _NameMap? - mutating func decodeSingularSFixed64Field(value: inout Int64?) throws { - try decodeSingularInt64Field(value: &value) - } - - mutating func decodeRepeatedSFixed64Field(value: inout [Int64]) throws { - try decodeRepeatedInt64Field(value: &value) - } - - mutating func decodeSingularBoolField(value: inout Bool) throws { - if scanner.skipOptionalNull() { - value = false - return - } - if isMapKey { - value = try scanner.nextQuotedBool() - } else { - value = try scanner.nextBool() - } - } - - mutating func decodeSingularBoolField(value: inout Bool?) throws { - if scanner.skipOptionalNull() { - value = nil - return - } - if isMapKey { - value = try scanner.nextQuotedBool() - } else { - value = try scanner.nextBool() - } - } - - mutating func decodeRepeatedBoolField(value: inout [Bool]) throws { - if scanner.skipOptionalNull() { - return - } - try scanner.skipRequiredArrayStart() - if scanner.skipOptionalArrayEnd() { - return - } - while true { - let n = try scanner.nextBool() - value.append(n) - if scanner.skipOptionalArrayEnd() { - return - } - try scanner.skipRequiredComma() - } - } - - mutating func decodeSingularStringField(value: inout String) throws { - if scanner.skipOptionalNull() { - value = String() - return - } - value = try scanner.nextQuotedString() - } - - mutating func decodeSingularStringField(value: inout String?) throws { - if scanner.skipOptionalNull() { - value = nil - return - } - value = try scanner.nextQuotedString() - } - - mutating func decodeRepeatedStringField(value: inout [String]) throws { - if scanner.skipOptionalNull() { - return - } - try scanner.skipRequiredArrayStart() - if scanner.skipOptionalArrayEnd() { - return - } - while true { - let n = try scanner.nextQuotedString() - value.append(n) - if scanner.skipOptionalArrayEnd() { - return - } - try scanner.skipRequiredComma() - } - } - - mutating func decodeSingularBytesField(value: inout Data) throws { - if scanner.skipOptionalNull() { - value = Data() - return - } - value = try scanner.nextBytesValue() - } - - mutating func decodeSingularBytesField(value: inout Data?) throws { - if scanner.skipOptionalNull() { - value = nil - return - } - value = try scanner.nextBytesValue() - } - - mutating func decodeRepeatedBytesField(value: inout [Data]) throws { - if scanner.skipOptionalNull() { - return - } - try scanner.skipRequiredArrayStart() - if scanner.skipOptionalArrayEnd() { - return - } - while true { - let n = try scanner.nextBytesValue() - value.append(n) - if scanner.skipOptionalArrayEnd() { - return - } - try scanner.skipRequiredComma() - } - } - - mutating func decodeSingularEnumField(value: inout E?) throws - where E.RawValue == Int { - if scanner.skipOptionalNull() { - if let customDecodable = E.self as? any _CustomJSONCodable.Type { - value = try customDecodable.decodedFromJSONNull() as? E - return - } - value = nil - return - } - // Only change the value if a value was read. - if let e: E = try scanner.nextEnumValue() { - value = e - } - } - - mutating func decodeSingularEnumField(value: inout E) throws - where E.RawValue == Int { - if scanner.skipOptionalNull() { - if let customDecodable = E.self as? any _CustomJSONCodable.Type { - value = try customDecodable.decodedFromJSONNull() as! E - return - } - value = E() - return - } - if let e: E = try scanner.nextEnumValue() { - value = e - } - - } - - mutating func decodeRepeatedEnumField(value: inout [E]) throws - where E.RawValue == Int { - if scanner.skipOptionalNull() { - return - } - try scanner.skipRequiredArrayStart() - if scanner.skipOptionalArrayEnd() { - return - } - let maybeCustomDecodable = E.self as? any _CustomJSONCodable.Type - while true { - if scanner.skipOptionalNull() { - if let customDecodable = maybeCustomDecodable { - let e = try customDecodable.decodedFromJSONNull() as! E - value.append(e) + internal var options: JSONDecodingOptions { + scanner.options + } + + mutating func handleConflictingOneOf() throws { + throw JSONDecodingError.conflictingOneOf + } + + internal init( + source: UnsafeRawBufferPointer, + options: JSONDecodingOptions, + messageType: any Message.Type, + extensions: (any ExtensionMap)? + ) { + let scanner = JSONScanner( + source: source, + options: options, + extensions: extensions + ) + self.init(scanner: scanner, messageType: messageType) + } + + private init(scanner: JSONScanner, messageType: any Message.Type) { + self.scanner = scanner + self.messageType = messageType + } + + mutating func nextFieldNumber() throws -> Int? { + if scanner.skipOptionalObjectEnd() { + return nil + } + if fieldCount > 0 { + try scanner.skipRequiredComma() + } + let fieldNumber = try scanner.nextFieldNumber( + names: fieldNameMap!, + messageType: messageType + ) + if let fieldNumber = fieldNumber { + fieldCount += 1 + return fieldNumber + } + return nil + } + + mutating func decodeSingularFloatField(value: inout Float) throws { + if scanner.skipOptionalNull() { + value = 0 + return + } + value = try scanner.nextFloat() + } + + mutating func decodeSingularFloatField(value: inout Float?) throws { + if scanner.skipOptionalNull() { + value = nil + return + } + value = try scanner.nextFloat() + } + + mutating func decodeRepeatedFloatField(value: inout [Float]) throws { + if scanner.skipOptionalNull() { + return + } + try scanner.skipRequiredArrayStart() + if scanner.skipOptionalArrayEnd() { + return + } + while true { + let n = try scanner.nextFloat() + value.append(n) + if scanner.skipOptionalArrayEnd() { + return + } + try scanner.skipRequiredComma() + } + } + + mutating func decodeSingularDoubleField(value: inout Double) throws { + if scanner.skipOptionalNull() { + value = 0 + return + } + value = try scanner.nextDouble() + } + + mutating func decodeSingularDoubleField(value: inout Double?) throws { + if scanner.skipOptionalNull() { + value = nil + return + } + value = try scanner.nextDouble() + } + + mutating func decodeRepeatedDoubleField(value: inout [Double]) throws { + if scanner.skipOptionalNull() { + return + } + try scanner.skipRequiredArrayStart() + if scanner.skipOptionalArrayEnd() { + return + } + while true { + let n = try scanner.nextDouble() + value.append(n) + if scanner.skipOptionalArrayEnd() { + return + } + try scanner.skipRequiredComma() + } + } + + mutating func decodeSingularInt32Field(value: inout Int32) throws { + if scanner.skipOptionalNull() { + value = 0 + return + } + let n = try scanner.nextSInt() + if n > Int64(Int32.max) || n < Int64(Int32.min) { + throw JSONDecodingError.numberRange + } + value = Int32(truncatingIfNeeded: n) + } + + mutating func decodeSingularInt32Field(value: inout Int32?) throws { + if scanner.skipOptionalNull() { + value = nil + return + } + let n = try scanner.nextSInt() + if n > Int64(Int32.max) || n < Int64(Int32.min) { + throw JSONDecodingError.numberRange + } + value = Int32(truncatingIfNeeded: n) + } + + mutating func decodeRepeatedInt32Field(value: inout [Int32]) throws { + if scanner.skipOptionalNull() { + return + } + try scanner.skipRequiredArrayStart() + if scanner.skipOptionalArrayEnd() { + return + } + while true { + let n = try scanner.nextSInt() + if n > Int64(Int32.max) || n < Int64(Int32.min) { + throw JSONDecodingError.numberRange + } + value.append(Int32(truncatingIfNeeded: n)) + if scanner.skipOptionalArrayEnd() { + return + } + try scanner.skipRequiredComma() + } + } + + mutating func decodeSingularInt64Field(value: inout Int64) throws { + if scanner.skipOptionalNull() { + value = 0 + return + } + value = try scanner.nextSInt() + } + + mutating func decodeSingularInt64Field(value: inout Int64?) throws { + if scanner.skipOptionalNull() { + value = nil + return + } + value = try scanner.nextSInt() + } + + mutating func decodeRepeatedInt64Field(value: inout [Int64]) throws { + if scanner.skipOptionalNull() { + return + } + try scanner.skipRequiredArrayStart() + if scanner.skipOptionalArrayEnd() { + return + } + while true { + let n = try scanner.nextSInt() + value.append(n) + if scanner.skipOptionalArrayEnd() { + return + } + try scanner.skipRequiredComma() + } + } + + mutating func decodeSingularUInt32Field(value: inout UInt32) throws { + if scanner.skipOptionalNull() { + value = 0 + return + } + let n = try scanner.nextUInt() + if n > UInt64(UInt32.max) { + throw JSONDecodingError.numberRange + } + value = UInt32(truncatingIfNeeded: n) + } + + mutating func decodeSingularUInt32Field(value: inout UInt32?) throws { + if scanner.skipOptionalNull() { + value = nil + return + } + let n = try scanner.nextUInt() + if n > UInt64(UInt32.max) { + throw JSONDecodingError.numberRange + } + value = UInt32(truncatingIfNeeded: n) + } + + mutating func decodeRepeatedUInt32Field(value: inout [UInt32]) throws { + if scanner.skipOptionalNull() { + return + } + try scanner.skipRequiredArrayStart() + if scanner.skipOptionalArrayEnd() { + return + } + while true { + let n = try scanner.nextUInt() + if n > UInt64(UInt32.max) { + throw JSONDecodingError.numberRange + } + value.append(UInt32(truncatingIfNeeded: n)) + if scanner.skipOptionalArrayEnd() { + return + } + try scanner.skipRequiredComma() + } + } + + mutating func decodeSingularUInt64Field(value: inout UInt64) throws { + if scanner.skipOptionalNull() { + value = 0 + return + } + value = try scanner.nextUInt() + } + + mutating func decodeSingularUInt64Field(value: inout UInt64?) throws { + if scanner.skipOptionalNull() { + value = nil + return + } + value = try scanner.nextUInt() + } + + mutating func decodeRepeatedUInt64Field(value: inout [UInt64]) throws { + if scanner.skipOptionalNull() { + return + } + try scanner.skipRequiredArrayStart() + if scanner.skipOptionalArrayEnd() { + return + } + while true { + let n = try scanner.nextUInt() + value.append(n) + if scanner.skipOptionalArrayEnd() { + return + } + try scanner.skipRequiredComma() + } + } + + mutating func decodeSingularSInt32Field(value: inout Int32) throws { + try decodeSingularInt32Field(value: &value) + } + + mutating func decodeSingularSInt32Field(value: inout Int32?) throws { + try decodeSingularInt32Field(value: &value) + } + + mutating func decodeRepeatedSInt32Field(value: inout [Int32]) throws { + try decodeRepeatedInt32Field(value: &value) + } + + mutating func decodeSingularSInt64Field(value: inout Int64) throws { + try decodeSingularInt64Field(value: &value) + } + + mutating func decodeSingularSInt64Field(value: inout Int64?) throws { + try decodeSingularInt64Field(value: &value) + } + + mutating func decodeRepeatedSInt64Field(value: inout [Int64]) throws { + try decodeRepeatedInt64Field(value: &value) + } + + mutating func decodeSingularFixed32Field(value: inout UInt32) throws { + try decodeSingularUInt32Field(value: &value) + } + + mutating func decodeSingularFixed32Field(value: inout UInt32?) throws { + try decodeSingularUInt32Field(value: &value) + } + + mutating func decodeRepeatedFixed32Field(value: inout [UInt32]) throws { + try decodeRepeatedUInt32Field(value: &value) + } + + mutating func decodeSingularFixed64Field(value: inout UInt64) throws { + try decodeSingularUInt64Field(value: &value) + } + + mutating func decodeSingularFixed64Field(value: inout UInt64?) throws { + try decodeSingularUInt64Field(value: &value) + } + + mutating func decodeRepeatedFixed64Field(value: inout [UInt64]) throws { + try decodeRepeatedUInt64Field(value: &value) + } + + mutating func decodeSingularSFixed32Field(value: inout Int32) throws { + try decodeSingularInt32Field(value: &value) + } + + mutating func decodeSingularSFixed32Field(value: inout Int32?) throws { + try decodeSingularInt32Field(value: &value) + } + + mutating func decodeRepeatedSFixed32Field(value: inout [Int32]) throws { + try decodeRepeatedInt32Field(value: &value) + } + + mutating func decodeSingularSFixed64Field(value: inout Int64) throws { + try decodeSingularInt64Field(value: &value) + } + + mutating func decodeSingularSFixed64Field(value: inout Int64?) throws { + try decodeSingularInt64Field(value: &value) + } + + mutating func decodeRepeatedSFixed64Field(value: inout [Int64]) throws { + try decodeRepeatedInt64Field(value: &value) + } + + mutating func decodeSingularBoolField(value: inout Bool) throws { + if scanner.skipOptionalNull() { + value = false + return + } + if isMapKey { + value = try scanner.nextQuotedBool() } else { - throw JSONDecodingError.illegalNull + value = try scanner.nextBool() + } + } + + mutating func decodeSingularBoolField(value: inout Bool?) throws { + if scanner.skipOptionalNull() { + value = nil + return + } + if isMapKey { + value = try scanner.nextQuotedBool() + } else { + value = try scanner.nextBool() + } + } + + mutating func decodeRepeatedBoolField(value: inout [Bool]) throws { + if scanner.skipOptionalNull() { + return + } + try scanner.skipRequiredArrayStart() + if scanner.skipOptionalArrayEnd() { + return + } + while true { + let n = try scanner.nextBool() + value.append(n) + if scanner.skipOptionalArrayEnd() { + return + } + try scanner.skipRequiredComma() + } + } + + mutating func decodeSingularStringField(value: inout String) throws { + if scanner.skipOptionalNull() { + value = String() + return + } + value = try scanner.nextQuotedString() + } + + mutating func decodeSingularStringField(value: inout String?) throws { + if scanner.skipOptionalNull() { + value = nil + return + } + value = try scanner.nextQuotedString() + } + + mutating func decodeRepeatedStringField(value: inout [String]) throws { + if scanner.skipOptionalNull() { + return + } + try scanner.skipRequiredArrayStart() + if scanner.skipOptionalArrayEnd() { + return + } + while true { + let n = try scanner.nextQuotedString() + value.append(n) + if scanner.skipOptionalArrayEnd() { + return + } + try scanner.skipRequiredComma() + } + } + + mutating func decodeSingularBytesField(value: inout Data) throws { + if scanner.skipOptionalNull() { + value = Data() + return + } + value = try scanner.nextBytesValue() + } + + mutating func decodeSingularBytesField(value: inout Data?) throws { + if scanner.skipOptionalNull() { + value = nil + return + } + value = try scanner.nextBytesValue() + } + + mutating func decodeRepeatedBytesField(value: inout [Data]) throws { + if scanner.skipOptionalNull() { + return + } + try scanner.skipRequiredArrayStart() + if scanner.skipOptionalArrayEnd() { + return + } + while true { + let n = try scanner.nextBytesValue() + value.append(n) + if scanner.skipOptionalArrayEnd() { + return + } + try scanner.skipRequiredComma() + } + } + + mutating func decodeSingularEnumField(value: inout E?) throws + where E.RawValue == Int { + if scanner.skipOptionalNull() { + if let customDecodable = E.self as? any _CustomJSONCodable.Type { + value = try customDecodable.decodedFromJSONNull() as? E + return + } + value = nil + return + } + // Only change the value if a value was read. + if let e: E = try scanner.nextEnumValue() { + value = e + } + } + + mutating func decodeSingularEnumField(value: inout E) throws + where E.RawValue == Int { + if scanner.skipOptionalNull() { + if let customDecodable = E.self as? any _CustomJSONCodable.Type { + value = try customDecodable.decodedFromJSONNull() as! E + return + } + value = E() + return } - } else { if let e: E = try scanner.nextEnumValue() { - value.append(e) - } - } - if scanner.skipOptionalArrayEnd() { - return - } - try scanner.skipRequiredComma() - } - } - - internal mutating func decodeFullObject(message: inout M) throws { - guard let nameProviding = (M.self as? any _ProtoNameProviding.Type) else { - throw JSONDecodingError.missingFieldNames - } - fieldNameMap = nameProviding._protobuf_nameMap - if let m = message as? (any _CustomJSONCodable) { - var customCodable = m - try customCodable.decodeJSON(from: &self) - message = customCodable as! M - } else { - try scanner.skipRequiredObjectStart() - if scanner.skipOptionalObjectEnd() { - return - } - try message.decodeMessage(decoder: &self) - } - } - - mutating func decodeSingularMessageField(value: inout M?) throws { - if scanner.skipOptionalNull() { - if M.self is any _CustomJSONCodable.Type { - value = - try (M.self as! any _CustomJSONCodable.Type).decodedFromJSONNull() as? M - return - } - // All other message field types treat 'null' as an unset - value = nil - return - } - if value == nil { - value = M() - } - var subDecoder = JSONDecoder(scanner: scanner, messageType: M.self) - try subDecoder.decodeFullObject(message: &value!) - assert(scanner.recursionBudget == subDecoder.scanner.recursionBudget) - scanner = subDecoder.scanner - } - - mutating func decodeRepeatedMessageField( - value: inout [M] - ) throws { - if scanner.skipOptionalNull() { - return - } - try scanner.skipRequiredArrayStart() - if scanner.skipOptionalArrayEnd() { - return - } - while true { - if scanner.skipOptionalNull() { - var appended = false - if M.self is any _CustomJSONCodable.Type { - if let message = try (M.self as! any _CustomJSONCodable.Type) - .decodedFromJSONNull() as? M { - value.append(message) - appended = true - } - } - if !appended { - throw JSONDecodingError.illegalNull - } - } else { - var message = M() + value = e + } + + } + + mutating func decodeRepeatedEnumField(value: inout [E]) throws + where E.RawValue == Int { + if scanner.skipOptionalNull() { + return + } + try scanner.skipRequiredArrayStart() + if scanner.skipOptionalArrayEnd() { + return + } + let maybeCustomDecodable = E.self as? any _CustomJSONCodable.Type + while true { + if scanner.skipOptionalNull() { + if let customDecodable = maybeCustomDecodable { + let e = try customDecodable.decodedFromJSONNull() as! E + value.append(e) + } else { + throw JSONDecodingError.illegalNull + } + } else { + if let e: E = try scanner.nextEnumValue() { + value.append(e) + } + } + if scanner.skipOptionalArrayEnd() { + return + } + try scanner.skipRequiredComma() + } + } + + internal mutating func decodeFullObject(message: inout M) throws { + guard let nameProviding = (M.self as? any _ProtoNameProviding.Type) else { + throw JSONDecodingError.missingFieldNames + } + fieldNameMap = nameProviding._protobuf_nameMap + if let m = message as? (any _CustomJSONCodable) { + var customCodable = m + try customCodable.decodeJSON(from: &self) + message = customCodable as! M + } else { + try scanner.skipRequiredObjectStart() + if scanner.skipOptionalObjectEnd() { + return + } + try message.decodeMessage(decoder: &self) + } + } + + mutating func decodeSingularMessageField(value: inout M?) throws { + if scanner.skipOptionalNull() { + if M.self is any _CustomJSONCodable.Type { + value = + try (M.self as! any _CustomJSONCodable.Type).decodedFromJSONNull() as? M + return + } + // All other message field types treat 'null' as an unset + value = nil + return + } + if value == nil { + value = M() + } var subDecoder = JSONDecoder(scanner: scanner, messageType: M.self) - try subDecoder.decodeFullObject(message: &message) - value.append(message) + try subDecoder.decodeFullObject(message: &value!) assert(scanner.recursionBudget == subDecoder.scanner.recursionBudget) scanner = subDecoder.scanner - } - if scanner.skipOptionalArrayEnd() { - return - } - try scanner.skipRequiredComma() - } - } - - mutating func decodeSingularGroupField(value: inout G?) throws { - try decodeSingularMessageField(value: &value) - } - - mutating func decodeRepeatedGroupField(value: inout [G]) throws { - try decodeRepeatedMessageField(value: &value) - } - - mutating func decodeMapField( - fieldType: _ProtobufMap.Type, - value: inout _ProtobufMap.BaseType - ) throws { - if scanner.skipOptionalNull() { - return - } - try scanner.skipRequiredObjectStart() - if scanner.skipOptionalObjectEnd() { - return - } - while true { - // Next character must be double quote, because - // map keys must always be quoted strings. - let c = try scanner.peekOneCharacter() - if c != "\"" { - throw JSONDecodingError.unquotedMapKey - } - isMapKey = true - var keyField: KeyType.BaseType? - try KeyType.decodeSingular(value: &keyField, from: &self) - isMapKey = false - try scanner.skipRequiredColon() - var valueField: ValueType.BaseType? - try ValueType.decodeSingular(value: &valueField, from: &self) - if let keyField = keyField, let valueField = valueField { - value[keyField] = valueField - } else { - throw JSONDecodingError.malformedMap - } - if scanner.skipOptionalObjectEnd() { - return - } - try scanner.skipRequiredComma() - } - } - - mutating func decodeMapField( - fieldType: _ProtobufEnumMap.Type, - value: inout _ProtobufEnumMap.BaseType - ) throws where ValueType.RawValue == Int { - if scanner.skipOptionalNull() { - return - } - try scanner.skipRequiredObjectStart() - if scanner.skipOptionalObjectEnd() { - return - } - while true { - // Next character must be double quote, because - // map keys must always be quoted strings. - let c = try scanner.peekOneCharacter() - if c != "\"" { - throw JSONDecodingError.unquotedMapKey - } - isMapKey = true - var keyFieldOpt: KeyType.BaseType? - try KeyType.decodeSingular(value: &keyFieldOpt, from: &self) - guard let keyField = keyFieldOpt else { - throw JSONDecodingError.malformedMap - } - isMapKey = false - try scanner.skipRequiredColon() - var valueField: ValueType? - try decodeSingularEnumField(value: &valueField) - if let valueField = valueField { - value[keyField] = valueField - } else { - // Nothing, the only way ``decodeSingularEnumField(value:)`` leaves - // it as nil is if ignoreUnknownFields option is enabled which also - // means to ignore unknown enum values. - } - if scanner.skipOptionalObjectEnd() { - return - } - try scanner.skipRequiredComma() - } - } - - mutating func decodeMapField( - fieldType: _ProtobufMessageMap.Type, - value: inout _ProtobufMessageMap.BaseType - ) throws { - if scanner.skipOptionalNull() { - return - } - try scanner.skipRequiredObjectStart() - if scanner.skipOptionalObjectEnd() { - return - } - while true { - // Next character must be double quote, because - // map keys must always be quoted strings. - let c = try scanner.peekOneCharacter() - if c != "\"" { - throw JSONDecodingError.unquotedMapKey - } - isMapKey = true - var keyField: KeyType.BaseType? - try KeyType.decodeSingular(value: &keyField, from: &self) - isMapKey = false - try scanner.skipRequiredColon() - var valueField: ValueType? - try decodeSingularMessageField(value: &valueField) - if let keyField = keyField, let valueField = valueField { - value[keyField] = valueField - } else { - throw JSONDecodingError.malformedMap - } - if scanner.skipOptionalObjectEnd() { - return - } - try scanner.skipRequiredComma() - } - } - - mutating func decodeExtensionField( - values: inout ExtensionFieldValueSet, - messageType: any Message.Type, - fieldNumber: Int - ) throws { - // Force-unwrap: we can only get here if the extension exists. - let ext = scanner.extensions[messageType, fieldNumber]! - - try values.modify(index: fieldNumber) { fieldValue in - if fieldValue != nil { - try fieldValue!.decodeExtensionField(decoder: &self) - } else { - fieldValue = try ext._protobuf_newField(decoder: &self) - } - } - } + } + + mutating func decodeRepeatedMessageField( + value: inout [M] + ) throws { + if scanner.skipOptionalNull() { + return + } + try scanner.skipRequiredArrayStart() + if scanner.skipOptionalArrayEnd() { + return + } + while true { + if scanner.skipOptionalNull() { + var appended = false + if M.self is any _CustomJSONCodable.Type { + if let message = try (M.self as! any _CustomJSONCodable.Type) + .decodedFromJSONNull() as? M + { + value.append(message) + appended = true + } + } + if !appended { + throw JSONDecodingError.illegalNull + } + } else { + var message = M() + var subDecoder = JSONDecoder(scanner: scanner, messageType: M.self) + try subDecoder.decodeFullObject(message: &message) + value.append(message) + assert(scanner.recursionBudget == subDecoder.scanner.recursionBudget) + scanner = subDecoder.scanner + } + if scanner.skipOptionalArrayEnd() { + return + } + try scanner.skipRequiredComma() + } + } + + mutating func decodeSingularGroupField(value: inout G?) throws { + try decodeSingularMessageField(value: &value) + } + + mutating func decodeRepeatedGroupField(value: inout [G]) throws { + try decodeRepeatedMessageField(value: &value) + } + + mutating func decodeMapField( + fieldType: _ProtobufMap.Type, + value: inout _ProtobufMap.BaseType + ) throws { + if scanner.skipOptionalNull() { + return + } + try scanner.skipRequiredObjectStart() + if scanner.skipOptionalObjectEnd() { + return + } + while true { + // Next character must be double quote, because + // map keys must always be quoted strings. + let c = try scanner.peekOneCharacter() + if c != "\"" { + throw JSONDecodingError.unquotedMapKey + } + isMapKey = true + var keyField: KeyType.BaseType? + try KeyType.decodeSingular(value: &keyField, from: &self) + isMapKey = false + try scanner.skipRequiredColon() + var valueField: ValueType.BaseType? + try ValueType.decodeSingular(value: &valueField, from: &self) + if let keyField = keyField, let valueField = valueField { + value[keyField] = valueField + } else { + throw JSONDecodingError.malformedMap + } + if scanner.skipOptionalObjectEnd() { + return + } + try scanner.skipRequiredComma() + } + } + + mutating func decodeMapField( + fieldType: _ProtobufEnumMap.Type, + value: inout _ProtobufEnumMap.BaseType + ) throws where ValueType.RawValue == Int { + if scanner.skipOptionalNull() { + return + } + try scanner.skipRequiredObjectStart() + if scanner.skipOptionalObjectEnd() { + return + } + while true { + // Next character must be double quote, because + // map keys must always be quoted strings. + let c = try scanner.peekOneCharacter() + if c != "\"" { + throw JSONDecodingError.unquotedMapKey + } + isMapKey = true + var keyFieldOpt: KeyType.BaseType? + try KeyType.decodeSingular(value: &keyFieldOpt, from: &self) + guard let keyField = keyFieldOpt else { + throw JSONDecodingError.malformedMap + } + isMapKey = false + try scanner.skipRequiredColon() + var valueField: ValueType? + try decodeSingularEnumField(value: &valueField) + if let valueField = valueField { + value[keyField] = valueField + } else { + // Nothing, the only way ``decodeSingularEnumField(value:)`` leaves + // it as nil is if ignoreUnknownFields option is enabled which also + // means to ignore unknown enum values. + } + if scanner.skipOptionalObjectEnd() { + return + } + try scanner.skipRequiredComma() + } + } + + mutating func decodeMapField( + fieldType: _ProtobufMessageMap.Type, + value: inout _ProtobufMessageMap.BaseType + ) throws { + if scanner.skipOptionalNull() { + return + } + try scanner.skipRequiredObjectStart() + if scanner.skipOptionalObjectEnd() { + return + } + while true { + // Next character must be double quote, because + // map keys must always be quoted strings. + let c = try scanner.peekOneCharacter() + if c != "\"" { + throw JSONDecodingError.unquotedMapKey + } + isMapKey = true + var keyField: KeyType.BaseType? + try KeyType.decodeSingular(value: &keyField, from: &self) + isMapKey = false + try scanner.skipRequiredColon() + var valueField: ValueType? + try decodeSingularMessageField(value: &valueField) + if let keyField = keyField, let valueField = valueField { + value[keyField] = valueField + } else { + throw JSONDecodingError.malformedMap + } + if scanner.skipOptionalObjectEnd() { + return + } + try scanner.skipRequiredComma() + } + } + + mutating func decodeExtensionField( + values: inout ExtensionFieldValueSet, + messageType: any Message.Type, + fieldNumber: Int + ) throws { + // Force-unwrap: we can only get here if the extension exists. + let ext = scanner.extensions[messageType, fieldNumber]! + + try values.modify(index: fieldNumber) { fieldValue in + if fieldValue != nil { + try fieldValue!.decodeExtensionField(decoder: &self) + } else { + fieldValue = try ext._protobuf_newField(decoder: &self) + } + } + } } diff --git a/Sources/SwiftProtobuf/JSONDecodingOptions.swift b/Sources/SwiftProtobuf/JSONDecodingOptions.swift index 3ab2edb3a..16cf6a41b 100644 --- a/Sources/SwiftProtobuf/JSONDecodingOptions.swift +++ b/Sources/SwiftProtobuf/JSONDecodingOptions.swift @@ -14,18 +14,18 @@ /// Options for JSONDecoding. public struct JSONDecodingOptions: Sendable { - /// The maximum nesting of message with messages. The default is 100. - /// - /// To prevent corrupt or malicious messages from causing stack overflows, - /// this controls how deep messages can be nested within other messages - /// while parsing. - public var messageDepthLimit: Int = 100 + /// The maximum nesting of message with messages. The default is 100. + /// + /// To prevent corrupt or malicious messages from causing stack overflows, + /// this controls how deep messages can be nested within other messages + /// while parsing. + public var messageDepthLimit: Int = 100 - /// If unknown fields in the JSON should be ignored. If they aren't - /// ignored, an error will be raised if one is encountered. This also - /// causes unknown enum values (especially string values) to be silently - /// ignored. - public var ignoreUnknownFields: Bool = false + /// If unknown fields in the JSON should be ignored. If they aren't + /// ignored, an error will be raised if one is encountered. This also + /// causes unknown enum values (especially string values) to be silently + /// ignored. + public var ignoreUnknownFields: Bool = false - public init() {} + public init() {} } diff --git a/Sources/SwiftProtobuf/JSONEncoder.swift b/Sources/SwiftProtobuf/JSONEncoder.swift index 6520ed0b6..b19d270d9 100644 --- a/Sources/SwiftProtobuf/JSONEncoder.swift +++ b/Sources/SwiftProtobuf/JSONEncoder.swift @@ -69,15 +69,15 @@ internal struct JSONEncoder { internal init() {} - internal var dataResult: [UInt8] { return data } + internal var dataResult: [UInt8] { data } internal var stringResult: String { get { - return String(bytes: data, encoding: String.Encoding.utf8)! + String(bytes: data, encoding: String.Encoding.utf8)! } } - internal var bytesResult: [UInt8] { return data } + internal var bytesResult: [UInt8] { data } /// Append a `StaticString` to the JSON text. Because /// `StaticString` is already UTF8 internally, this is faster @@ -328,7 +328,7 @@ internal struct JSONEncoder { case 13: append(staticText: "\\r") case 34: append(staticText: "\\\"") case 92: append(staticText: "\\\\") - case 0...31, 127...159: // Hex form for C0 control chars + case 0...31, 127...159: // Hex form for C0 control chars append(staticText: "\\u00") data.append(hexDigits[Int(c.value / 16)]) data.append(hexDigits[Int(c.value & 15)]) @@ -356,46 +356,45 @@ internal struct JSONEncoder { data.append(asciiDoubleQuote) if value.count > 0 { value.withUnsafeBytes { (body: UnsafeRawBufferPointer) in - if let p = body.baseAddress, body.count > 0 { - var t: Int = 0 - var bytesInGroup: Int = 0 - for i in 0.. 0 { + var t: Int = 0 + var bytesInGroup: Int = 0 + for i in 0..> 18) & 63]) + data.append(base64Digits[(t >> 12) & 63]) + data.append(base64Digits[(t >> 6) & 63]) + data.append(base64Digits[t & 63]) + t = 0 + bytesInGroup = 0 + } + t = (t << 8) + Int(p[i]) + bytesInGroup += 1 + } + switch bytesInGroup { + case 3: data.append(base64Digits[(t >> 18) & 63]) data.append(base64Digits[(t >> 12) & 63]) data.append(base64Digits[(t >> 6) & 63]) data.append(base64Digits[t & 63]) - t = 0 - bytesInGroup = 0 + case 2: + t <<= 8 + data.append(base64Digits[(t >> 18) & 63]) + data.append(base64Digits[(t >> 12) & 63]) + data.append(base64Digits[(t >> 6) & 63]) + data.append(asciiEquals) + case 1: + t <<= 16 + data.append(base64Digits[(t >> 18) & 63]) + data.append(base64Digits[(t >> 12) & 63]) + data.append(asciiEquals) + data.append(asciiEquals) + default: + break } - t = (t << 8) + Int(p[i]) - bytesInGroup += 1 } - switch bytesInGroup { - case 3: - data.append(base64Digits[(t >> 18) & 63]) - data.append(base64Digits[(t >> 12) & 63]) - data.append(base64Digits[(t >> 6) & 63]) - data.append(base64Digits[t & 63]) - case 2: - t <<= 8 - data.append(base64Digits[(t >> 18) & 63]) - data.append(base64Digits[(t >> 12) & 63]) - data.append(base64Digits[(t >> 6) & 63]) - data.append(asciiEquals) - case 1: - t <<= 16 - data.append(base64Digits[(t >> 18) & 63]) - data.append(base64Digits[(t >> 12) & 63]) - data.append(asciiEquals) - data.append(asciiEquals) - default: - break - } - } } } data.append(asciiDoubleQuote) } } - diff --git a/Sources/SwiftProtobuf/JSONEncodingOptions.swift b/Sources/SwiftProtobuf/JSONEncodingOptions.swift index d80025426..ebc9671e2 100644 --- a/Sources/SwiftProtobuf/JSONEncodingOptions.swift +++ b/Sources/SwiftProtobuf/JSONEncodingOptions.swift @@ -15,31 +15,31 @@ /// Options for JSONEncoding. public struct JSONEncodingOptions: Sendable { - /// Always prints int64s values as numbers. - /// By default, they are printed as strings as per proto3 JSON mapping rules. - /// NB: When used as Map keys, int64s will be printed as strings as expected. - public var alwaysPrintInt64sAsNumbers: Bool = false + /// Always prints int64s values as numbers. + /// By default, they are printed as strings as per proto3 JSON mapping rules. + /// NB: When used as Map keys, int64s will be printed as strings as expected. + public var alwaysPrintInt64sAsNumbers: Bool = false - /// Always print enums as ints. By default they are printed as strings. - public var alwaysPrintEnumsAsInts: Bool = false + /// Always print enums as ints. By default they are printed as strings. + public var alwaysPrintEnumsAsInts: Bool = false - /// Whether to preserve proto field names. - /// By default they are converted to JSON(lowerCamelCase) names. - public var preserveProtoFieldNames: Bool = false + /// Whether to preserve proto field names. + /// By default they are converted to JSON(lowerCamelCase) names. + public var preserveProtoFieldNames: Bool = false - /// Whether to use deterministic ordering when serializing. - /// - /// Note that the deterministic serialization is NOT canonical across languages. - /// It is NOT guaranteed to remain stable over time. It is unstable across - /// different builds with schema changes due to unknown fields. Users who need - /// canonical serialization (e.g., persistent storage in a canonical form, - /// fingerprinting, etc.) should define their own canonicalization specification - /// and implement their own serializer rather than relying on this API. - /// - /// If deterministic serialization is requested, map entries will be sorted - /// by keys in lexicographical order. This is an implementation detail - /// and subject to change. - public var useDeterministicOrdering: Bool = false + /// Whether to use deterministic ordering when serializing. + /// + /// Note that the deterministic serialization is NOT canonical across languages. + /// It is NOT guaranteed to remain stable over time. It is unstable across + /// different builds with schema changes due to unknown fields. Users who need + /// canonical serialization (e.g., persistent storage in a canonical form, + /// fingerprinting, etc.) should define their own canonicalization specification + /// and implement their own serializer rather than relying on this API. + /// + /// If deterministic serialization is requested, map entries will be sorted + /// by keys in lexicographical order. This is an implementation detail + /// and subject to change. + public var useDeterministicOrdering: Bool = false - public init() {} + public init() {} } diff --git a/Sources/SwiftProtobuf/JSONEncodingVisitor.swift b/Sources/SwiftProtobuf/JSONEncodingVisitor.swift index cd84cf725..6ffb13afc 100644 --- a/Sources/SwiftProtobuf/JSONEncodingVisitor.swift +++ b/Sources/SwiftProtobuf/JSONEncodingVisitor.swift @@ -17,411 +17,423 @@ import Foundation /// Visitor that serializes a message into JSON format. internal struct JSONEncodingVisitor: Visitor { - private var encoder = JSONEncoder() - private var nameMap: _NameMap - private var extensions: ExtensionFieldValueSet? - private let options: JSONEncodingOptions - - /// The JSON text produced by the visitor, as raw UTF8 bytes. - var dataResult: [UInt8] { - return encoder.dataResult - } - - /// The JSON text produced by the visitor, as a String. - internal var stringResult: String { - return encoder.stringResult - } - - /// Creates a new visitor for serializing a message of the given type to JSON - /// format. - init(type: any Message.Type, options: JSONEncodingOptions) throws { - if let nameProviding = type as? any _ProtoNameProviding.Type { - self.nameMap = nameProviding._protobuf_nameMap - } else { - throw JSONEncodingError.missingFieldNames - } - self.options = options - } - - mutating func startArray() { - encoder.startArray() - } - - mutating func endArray() { - encoder.endArray() - } - - mutating func startObject(message: any Message) { - self.extensions = (message as? (any ExtensibleMessage))?._protobuf_extensionFieldValues - encoder.startObject() - } - - mutating func startArrayObject(message: any Message) { - self.extensions = (message as? (any ExtensibleMessage))?._protobuf_extensionFieldValues - encoder.startArrayObject() - } - - mutating func endObject() { - encoder.endObject() - } - - mutating func encodeField(name: String, stringValue value: String) { - encoder.startField(name: name) - encoder.putStringValue(value: value) - } - - mutating func encodeField(name: String, jsonText text: String) { - encoder.startField(name: name) - encoder.append(text: text) - } - - mutating func visitUnknown(bytes: Data) throws { - // JSON encoding has no provision for carrying proto2 unknown fields. - } - - mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws { - try startField(for: fieldNumber) - encoder.putFloatValue(value: value) - } - - mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws { - try startField(for: fieldNumber) - encoder.putDoubleValue(value: value) - } - - mutating func visitSingularInt32Field(value: Int32, fieldNumber: Int) throws { - try startField(for: fieldNumber) - encoder.putNonQuotedInt32(value: value) - } - - mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws { - try startField(for: fieldNumber) - options.alwaysPrintInt64sAsNumbers - ? encoder.putNonQuotedInt64(value: value) - : encoder.putQuotedInt64(value: value) - } - - mutating func visitSingularUInt32Field(value: UInt32, fieldNumber: Int) throws { - try startField(for: fieldNumber) - encoder.putNonQuotedUInt32(value: value) - } - - mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws { - try startField(for: fieldNumber) - options.alwaysPrintInt64sAsNumbers - ? encoder.putNonQuotedUInt64(value: value) - : encoder.putQuotedUInt64(value: value) - } - - mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws { - try startField(for: fieldNumber) - encoder.putNonQuotedUInt32(value: value) - } - - mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws { - try startField(for: fieldNumber) - encoder.putNonQuotedInt32(value: value) - } - - mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws { - try startField(for: fieldNumber) - encoder.putNonQuotedBoolValue(value: value) - } - - mutating func visitSingularStringField(value: String, fieldNumber: Int) throws { - try startField(for: fieldNumber) - encoder.putStringValue(value: value) - } - - mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws { - try startField(for: fieldNumber) - encoder.putBytesValue(value: value) - } - - private mutating func _visitRepeated( - value: [T], - fieldNumber: Int, - encode: (inout JSONEncoder, T) throws -> () - ) throws { - assert(!value.isEmpty) - try startField(for: fieldNumber) - var comma = false - encoder.startArray() - for v in value { - if comma { - encoder.comma() - } - comma = true - try encode(&encoder, v) - } - encoder.endArray() - } - - mutating func visitSingularEnumField(value: E, fieldNumber: Int) throws { - try startField(for: fieldNumber) - if let e = value as? (any _CustomJSONCodable) { - let json = try e.encodedJSONString(options: options) - encoder.append(text: json) - } else if !options.alwaysPrintEnumsAsInts, let n = value.name { - encoder.appendQuoted(name: n) - } else { - encoder.putEnumInt(value: value.rawValue) - } - } - - mutating func visitSingularMessageField(value: M, fieldNumber: Int) throws { - try startField(for: fieldNumber) - if let m = value as? (any _CustomJSONCodable) { - let json = try m.encodedJSONString(options: options) - encoder.append(text: json) - } else if let newNameMap = (M.self as? any _ProtoNameProviding.Type)?._protobuf_nameMap { - // Preserve outer object's name and extension maps; restore them before returning - let oldNameMap = self.nameMap - let oldExtensions = self.extensions - // Install inner object's name and extension maps - self.nameMap = newNameMap - startObject(message: value) - try value.traverse(visitor: &self) - endObject() - self.nameMap = oldNameMap - self.extensions = oldExtensions - } else { - throw JSONEncodingError.missingFieldNames - } - } - - mutating func visitSingularGroupField(value: G, fieldNumber: Int) throws { - try visitSingularMessageField(value: value, fieldNumber: fieldNumber) - } - - mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws { - try _visitRepeated(value: value, fieldNumber: fieldNumber) { - (encoder: inout JSONEncoder, v: Float) in - encoder.putFloatValue(value: v) - } - } - - mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws { - try _visitRepeated(value: value, fieldNumber: fieldNumber) { - (encoder: inout JSONEncoder, v: Double) in - encoder.putDoubleValue(value: v) - } - } - - mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws { - try _visitRepeated(value: value, fieldNumber: fieldNumber) { - (encoder: inout JSONEncoder, v: Int32) in - encoder.putNonQuotedInt32(value: v) - } - } - - mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws { - if options.alwaysPrintInt64sAsNumbers { - try _visitRepeated(value: value, fieldNumber: fieldNumber) { - (encoder: inout JSONEncoder, v: Int64) in - encoder.putNonQuotedInt64(value: v) - } - } else { - try _visitRepeated(value: value, fieldNumber: fieldNumber) { - (encoder: inout JSONEncoder, v: Int64) in - encoder.putQuotedInt64(value: v) - } - } - } - - mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws { - try _visitRepeated(value: value, fieldNumber: fieldNumber) { - (encoder: inout JSONEncoder, v: UInt32) in - encoder.putNonQuotedUInt32(value: v) - } - } - - mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws { - if options.alwaysPrintInt64sAsNumbers { - try _visitRepeated(value: value, fieldNumber: fieldNumber) { - (encoder: inout JSONEncoder, v: UInt64) in - encoder.putNonQuotedUInt64(value: v) - } - } else { - try _visitRepeated(value: value, fieldNumber: fieldNumber) { - (encoder: inout JSONEncoder, v: UInt64) in - encoder.putQuotedUInt64(value: v) - } - } - } - - mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws { - try visitRepeatedInt32Field(value: value, fieldNumber: fieldNumber) - } - - mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws { - try visitRepeatedInt64Field(value: value, fieldNumber: fieldNumber) - } - - mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws { - try visitRepeatedUInt32Field(value: value, fieldNumber: fieldNumber) - } - - mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws { - try visitRepeatedUInt64Field(value: value, fieldNumber: fieldNumber) - } - - mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws { - try visitRepeatedInt32Field(value: value, fieldNumber: fieldNumber) - } - - mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws { - try visitRepeatedInt64Field(value: value, fieldNumber: fieldNumber) - } - - mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws { - try _visitRepeated(value: value, fieldNumber: fieldNumber) { - (encoder: inout JSONEncoder, v: Bool) in - encoder.putNonQuotedBoolValue(value: v) - } - } - - mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws { - try _visitRepeated(value: value, fieldNumber: fieldNumber) { - (encoder: inout JSONEncoder, v: String) in - encoder.putStringValue(value: v) - } - } - - mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws { - try _visitRepeated(value: value, fieldNumber: fieldNumber) { - (encoder: inout JSONEncoder, v: Data) in - encoder.putBytesValue(value: v) - } - } - - mutating func visitRepeatedEnumField(value: [E], fieldNumber: Int) throws { - if let _ = E.self as? any _CustomJSONCodable.Type { - let options = self.options - try _visitRepeated(value: value, fieldNumber: fieldNumber) { - (encoder: inout JSONEncoder, v: E) throws in - let e = v as! (any _CustomJSONCodable) - let json = try e.encodedJSONString(options: options) - encoder.append(text: json) - } - } else { - let alwaysPrintEnumsAsInts = options.alwaysPrintEnumsAsInts - try _visitRepeated(value: value, fieldNumber: fieldNumber) { - (encoder: inout JSONEncoder, v: E) throws in - if !alwaysPrintEnumsAsInts, let n = v.name { - encoder.appendQuoted(name: n) + private var encoder = JSONEncoder() + private var nameMap: _NameMap + private var extensions: ExtensionFieldValueSet? + private let options: JSONEncodingOptions + + /// The JSON text produced by the visitor, as raw UTF8 bytes. + var dataResult: [UInt8] { + encoder.dataResult + } + + /// The JSON text produced by the visitor, as a String. + internal var stringResult: String { + encoder.stringResult + } + + /// Creates a new visitor for serializing a message of the given type to JSON + /// format. + init(type: any Message.Type, options: JSONEncodingOptions) throws { + if let nameProviding = type as? any _ProtoNameProviding.Type { + self.nameMap = nameProviding._protobuf_nameMap } else { - encoder.putEnumInt(value: v.rawValue) - } - } - } - } - - mutating func visitRepeatedMessageField(value: [M], fieldNumber: Int) throws { - assert(!value.isEmpty) - try startField(for: fieldNumber) - var comma = false - encoder.startArray() - if let _ = M.self as? any _CustomJSONCodable.Type { - for v in value { - if comma { - encoder.comma() + throw JSONEncodingError.missingFieldNames } - comma = true - let json = try v.jsonString(options: options) - encoder.append(text: json) - } - } else if let newNameMap = (M.self as? any _ProtoNameProviding.Type)?._protobuf_nameMap { - // Preserve name and extension maps for outer object - let oldNameMap = self.nameMap - let oldExtensions = self.extensions - self.nameMap = newNameMap - for v in value { - startArrayObject(message: v) - try v.traverse(visitor: &self) + self.options = options + } + + mutating func startArray() { + encoder.startArray() + } + + mutating func endArray() { + encoder.endArray() + } + + mutating func startObject(message: any Message) { + self.extensions = (message as? (any ExtensibleMessage))?._protobuf_extensionFieldValues + encoder.startObject() + } + + mutating func startArrayObject(message: any Message) { + self.extensions = (message as? (any ExtensibleMessage))?._protobuf_extensionFieldValues + encoder.startArrayObject() + } + + mutating func endObject() { encoder.endObject() - } - // Restore outer object's name and extension maps before returning - self.nameMap = oldNameMap - self.extensions = oldExtensions - } else { - throw JSONEncodingError.missingFieldNames - } - encoder.endArray() - } - - mutating func visitRepeatedGroupField(value: [G], fieldNumber: Int) throws { - try visitRepeatedMessageField(value: value, fieldNumber: fieldNumber) - } - - // Packed fields are handled the same as non-packed fields, so JSON just - // relies on the default implementations in Visitor.swift - - mutating func visitMapField(fieldType: _ProtobufMap.Type, value: _ProtobufMap.BaseType, fieldNumber: Int) throws { - try iterateAndEncode(map: value, fieldNumber: fieldNumber, isOrderedBefore: KeyType._lessThan) { - (visitor: inout JSONMapEncodingVisitor, key, value) throws -> () in - try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor) - try ValueType.visitSingular(value: value, fieldNumber: 2, with: &visitor) - } - } - - mutating func visitMapField(fieldType: _ProtobufEnumMap.Type, value: _ProtobufEnumMap.BaseType, fieldNumber: Int) throws where ValueType.RawValue == Int { - try iterateAndEncode(map: value, fieldNumber: fieldNumber, isOrderedBefore: KeyType._lessThan) { - (visitor: inout JSONMapEncodingVisitor, key, value) throws -> () in - try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor) - try visitor.visitSingularEnumField(value: value, fieldNumber: 2) - } - } - - mutating func visitMapField(fieldType: _ProtobufMessageMap.Type, value: _ProtobufMessageMap.BaseType, fieldNumber: Int) throws { - try iterateAndEncode(map: value, fieldNumber: fieldNumber, isOrderedBefore: KeyType._lessThan) { - (visitor: inout JSONMapEncodingVisitor, key, value) throws -> () in - try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor) - try visitor.visitSingularMessageField(value: value, fieldNumber: 2) - } - } - - /// Helper to encapsulate the common structure of iterating over a map - /// and encoding the keys and values. - private mutating func iterateAndEncode( - map: Dictionary, - fieldNumber: Int, - isOrderedBefore: (K, K) -> Bool, - encode: (inout JSONMapEncodingVisitor, K, V) throws -> () - ) throws { - try startField(for: fieldNumber) - encoder.append(text: "{") - var mapVisitor = JSONMapEncodingVisitor(encoder: JSONEncoder(), options: options) - if options.useDeterministicOrdering { - for (k,v) in map.sorted(by: { isOrderedBefore( $0.0, $1.0) }) { - try encode(&mapVisitor, k, v) - } - } else { - for (k,v) in map { - try encode(&mapVisitor, k, v) - } - } - encoder.append(utf8Bytes: mapVisitor.bytesResult) - encoder.append(text: "}") - } - - /// Helper function that throws an error if the field number could not be - /// resolved. - private mutating func startField(for number: Int) throws { - let name: _NameMap.Name? - - if options.preserveProtoFieldNames { - name = nameMap.names(for: number)?.proto - } else { - name = nameMap.names(for: number)?.json - } - - if let name = name { + } + + mutating func encodeField(name: String, stringValue value: String) { encoder.startField(name: name) - } else if let name = extensions?[number]?.protobufExtension.fieldName { - encoder.startExtensionField(name: name) - } else { - throw JSONEncodingError.missingFieldNames + encoder.putStringValue(value: value) + } + + mutating func encodeField(name: String, jsonText text: String) { + encoder.startField(name: name) + encoder.append(text: text) + } + + mutating func visitUnknown(bytes: Data) throws { + // JSON encoding has no provision for carrying proto2 unknown fields. + } + + mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws { + try startField(for: fieldNumber) + encoder.putFloatValue(value: value) + } + + mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws { + try startField(for: fieldNumber) + encoder.putDoubleValue(value: value) + } + + mutating func visitSingularInt32Field(value: Int32, fieldNumber: Int) throws { + try startField(for: fieldNumber) + encoder.putNonQuotedInt32(value: value) + } + + mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws { + try startField(for: fieldNumber) + options.alwaysPrintInt64sAsNumbers + ? encoder.putNonQuotedInt64(value: value) + : encoder.putQuotedInt64(value: value) + } + + mutating func visitSingularUInt32Field(value: UInt32, fieldNumber: Int) throws { + try startField(for: fieldNumber) + encoder.putNonQuotedUInt32(value: value) + } + + mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws { + try startField(for: fieldNumber) + options.alwaysPrintInt64sAsNumbers + ? encoder.putNonQuotedUInt64(value: value) + : encoder.putQuotedUInt64(value: value) + } + + mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws { + try startField(for: fieldNumber) + encoder.putNonQuotedUInt32(value: value) + } + + mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws { + try startField(for: fieldNumber) + encoder.putNonQuotedInt32(value: value) + } + + mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws { + try startField(for: fieldNumber) + encoder.putNonQuotedBoolValue(value: value) + } + + mutating func visitSingularStringField(value: String, fieldNumber: Int) throws { + try startField(for: fieldNumber) + encoder.putStringValue(value: value) + } + + mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws { + try startField(for: fieldNumber) + encoder.putBytesValue(value: value) + } + + private mutating func _visitRepeated( + value: [T], + fieldNumber: Int, + encode: (inout JSONEncoder, T) throws -> Void + ) throws { + assert(!value.isEmpty) + try startField(for: fieldNumber) + var comma = false + encoder.startArray() + for v in value { + if comma { + encoder.comma() + } + comma = true + try encode(&encoder, v) + } + encoder.endArray() + } + + mutating func visitSingularEnumField(value: E, fieldNumber: Int) throws { + try startField(for: fieldNumber) + if let e = value as? (any _CustomJSONCodable) { + let json = try e.encodedJSONString(options: options) + encoder.append(text: json) + } else if !options.alwaysPrintEnumsAsInts, let n = value.name { + encoder.appendQuoted(name: n) + } else { + encoder.putEnumInt(value: value.rawValue) + } + } + + mutating func visitSingularMessageField(value: M, fieldNumber: Int) throws { + try startField(for: fieldNumber) + if let m = value as? (any _CustomJSONCodable) { + let json = try m.encodedJSONString(options: options) + encoder.append(text: json) + } else if let newNameMap = (M.self as? any _ProtoNameProviding.Type)?._protobuf_nameMap { + // Preserve outer object's name and extension maps; restore them before returning + let oldNameMap = self.nameMap + let oldExtensions = self.extensions + // Install inner object's name and extension maps + self.nameMap = newNameMap + startObject(message: value) + try value.traverse(visitor: &self) + endObject() + self.nameMap = oldNameMap + self.extensions = oldExtensions + } else { + throw JSONEncodingError.missingFieldNames + } + } + + mutating func visitSingularGroupField(value: G, fieldNumber: Int) throws { + try visitSingularMessageField(value: value, fieldNumber: fieldNumber) + } + + mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws { + try _visitRepeated(value: value, fieldNumber: fieldNumber) { + (encoder: inout JSONEncoder, v: Float) in + encoder.putFloatValue(value: v) + } + } + + mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws { + try _visitRepeated(value: value, fieldNumber: fieldNumber) { + (encoder: inout JSONEncoder, v: Double) in + encoder.putDoubleValue(value: v) + } + } + + mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws { + try _visitRepeated(value: value, fieldNumber: fieldNumber) { + (encoder: inout JSONEncoder, v: Int32) in + encoder.putNonQuotedInt32(value: v) + } + } + + mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws { + if options.alwaysPrintInt64sAsNumbers { + try _visitRepeated(value: value, fieldNumber: fieldNumber) { + (encoder: inout JSONEncoder, v: Int64) in + encoder.putNonQuotedInt64(value: v) + } + } else { + try _visitRepeated(value: value, fieldNumber: fieldNumber) { + (encoder: inout JSONEncoder, v: Int64) in + encoder.putQuotedInt64(value: v) + } + } + } + + mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws { + try _visitRepeated(value: value, fieldNumber: fieldNumber) { + (encoder: inout JSONEncoder, v: UInt32) in + encoder.putNonQuotedUInt32(value: v) + } + } + + mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws { + if options.alwaysPrintInt64sAsNumbers { + try _visitRepeated(value: value, fieldNumber: fieldNumber) { + (encoder: inout JSONEncoder, v: UInt64) in + encoder.putNonQuotedUInt64(value: v) + } + } else { + try _visitRepeated(value: value, fieldNumber: fieldNumber) { + (encoder: inout JSONEncoder, v: UInt64) in + encoder.putQuotedUInt64(value: v) + } + } + } + + mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws { + try visitRepeatedInt32Field(value: value, fieldNumber: fieldNumber) + } + + mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws { + try visitRepeatedInt64Field(value: value, fieldNumber: fieldNumber) + } + + mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws { + try visitRepeatedUInt32Field(value: value, fieldNumber: fieldNumber) + } + + mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws { + try visitRepeatedUInt64Field(value: value, fieldNumber: fieldNumber) + } + + mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws { + try visitRepeatedInt32Field(value: value, fieldNumber: fieldNumber) + } + + mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws { + try visitRepeatedInt64Field(value: value, fieldNumber: fieldNumber) + } + + mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws { + try _visitRepeated(value: value, fieldNumber: fieldNumber) { + (encoder: inout JSONEncoder, v: Bool) in + encoder.putNonQuotedBoolValue(value: v) + } + } + + mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws { + try _visitRepeated(value: value, fieldNumber: fieldNumber) { + (encoder: inout JSONEncoder, v: String) in + encoder.putStringValue(value: v) + } + } + + mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws { + try _visitRepeated(value: value, fieldNumber: fieldNumber) { + (encoder: inout JSONEncoder, v: Data) in + encoder.putBytesValue(value: v) + } + } + + mutating func visitRepeatedEnumField(value: [E], fieldNumber: Int) throws { + if let _ = E.self as? any _CustomJSONCodable.Type { + let options = self.options + try _visitRepeated(value: value, fieldNumber: fieldNumber) { + (encoder: inout JSONEncoder, v: E) throws in + let e = v as! (any _CustomJSONCodable) + let json = try e.encodedJSONString(options: options) + encoder.append(text: json) + } + } else { + let alwaysPrintEnumsAsInts = options.alwaysPrintEnumsAsInts + try _visitRepeated(value: value, fieldNumber: fieldNumber) { + (encoder: inout JSONEncoder, v: E) throws in + if !alwaysPrintEnumsAsInts, let n = v.name { + encoder.appendQuoted(name: n) + } else { + encoder.putEnumInt(value: v.rawValue) + } + } + } + } + + mutating func visitRepeatedMessageField(value: [M], fieldNumber: Int) throws { + assert(!value.isEmpty) + try startField(for: fieldNumber) + var comma = false + encoder.startArray() + if let _ = M.self as? any _CustomJSONCodable.Type { + for v in value { + if comma { + encoder.comma() + } + comma = true + let json = try v.jsonString(options: options) + encoder.append(text: json) + } + } else if let newNameMap = (M.self as? any _ProtoNameProviding.Type)?._protobuf_nameMap { + // Preserve name and extension maps for outer object + let oldNameMap = self.nameMap + let oldExtensions = self.extensions + self.nameMap = newNameMap + for v in value { + startArrayObject(message: v) + try v.traverse(visitor: &self) + encoder.endObject() + } + // Restore outer object's name and extension maps before returning + self.nameMap = oldNameMap + self.extensions = oldExtensions + } else { + throw JSONEncodingError.missingFieldNames + } + encoder.endArray() + } + + mutating func visitRepeatedGroupField(value: [G], fieldNumber: Int) throws { + try visitRepeatedMessageField(value: value, fieldNumber: fieldNumber) + } + + // Packed fields are handled the same as non-packed fields, so JSON just + // relies on the default implementations in Visitor.swift + + mutating func visitMapField( + fieldType: _ProtobufMap.Type, + value: _ProtobufMap.BaseType, + fieldNumber: Int + ) throws { + try iterateAndEncode(map: value, fieldNumber: fieldNumber, isOrderedBefore: KeyType._lessThan) { + (visitor: inout JSONMapEncodingVisitor, key, value) throws -> Void in + try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor) + try ValueType.visitSingular(value: value, fieldNumber: 2, with: &visitor) + } + } + + mutating func visitMapField( + fieldType: _ProtobufEnumMap.Type, + value: _ProtobufEnumMap.BaseType, + fieldNumber: Int + ) throws where ValueType.RawValue == Int { + try iterateAndEncode(map: value, fieldNumber: fieldNumber, isOrderedBefore: KeyType._lessThan) { + (visitor: inout JSONMapEncodingVisitor, key, value) throws -> Void in + try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor) + try visitor.visitSingularEnumField(value: value, fieldNumber: 2) + } + } + + mutating func visitMapField( + fieldType: _ProtobufMessageMap.Type, + value: _ProtobufMessageMap.BaseType, + fieldNumber: Int + ) throws { + try iterateAndEncode(map: value, fieldNumber: fieldNumber, isOrderedBefore: KeyType._lessThan) { + (visitor: inout JSONMapEncodingVisitor, key, value) throws -> Void in + try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor) + try visitor.visitSingularMessageField(value: value, fieldNumber: 2) + } + } + + /// Helper to encapsulate the common structure of iterating over a map + /// and encoding the keys and values. + private mutating func iterateAndEncode( + map: [K: V], + fieldNumber: Int, + isOrderedBefore: (K, K) -> Bool, + encode: (inout JSONMapEncodingVisitor, K, V) throws -> Void + ) throws { + try startField(for: fieldNumber) + encoder.append(text: "{") + var mapVisitor = JSONMapEncodingVisitor(encoder: JSONEncoder(), options: options) + if options.useDeterministicOrdering { + for (k, v) in map.sorted(by: { isOrderedBefore($0.0, $1.0) }) { + try encode(&mapVisitor, k, v) + } + } else { + for (k, v) in map { + try encode(&mapVisitor, k, v) + } + } + encoder.append(utf8Bytes: mapVisitor.bytesResult) + encoder.append(text: "}") + } + + /// Helper function that throws an error if the field number could not be + /// resolved. + private mutating func startField(for number: Int) throws { + let name: _NameMap.Name? + + if options.preserveProtoFieldNames { + name = nameMap.names(for: number)?.proto + } else { + name = nameMap.names(for: number)?.json + } + + if let name = name { + encoder.startField(name: name) + } else if let name = extensions?[number]?.protobufExtension.fieldName { + encoder.startExtensionField(name: name) + } else { + throw JSONEncodingError.missingFieldNames + } } - } } diff --git a/Sources/SwiftProtobuf/JSONMapEncodingVisitor.swift b/Sources/SwiftProtobuf/JSONMapEncodingVisitor.swift index 592ed4f60..f09656f77 100644 --- a/Sources/SwiftProtobuf/JSONMapEncodingVisitor.swift +++ b/Sources/SwiftProtobuf/JSONMapEncodingVisitor.swift @@ -21,165 +21,165 @@ import Foundation /// as `fieldNumber:1`, values should be identified as `fieldNumber:2` /// internal struct JSONMapEncodingVisitor: SelectiveVisitor { - private var separator: StaticString? - internal var encoder: JSONEncoder - private let options: JSONEncodingOptions - - init(encoder: JSONEncoder, options: JSONEncodingOptions) { - self.encoder = encoder - self.options = options - } - - internal var bytesResult: [UInt8] { - get { - return encoder.bytesResult - } - } - - private mutating func startKey() { - if let s = separator { - encoder.append(staticText: s) - } else { - separator = "," - } - } - - private mutating func startValue() { - encoder.append(staticText: ":") - } - - mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws { - // Doubles/Floats can never be map keys, only values - assert(fieldNumber == 2) - startValue() - encoder.putFloatValue(value: value) - } - - mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws { - // Doubles/Floats can never be map keys, only values - assert(fieldNumber == 2) - startValue() - encoder.putDoubleValue(value: value) - } - - mutating func visitSingularInt32Field(value: Int32, fieldNumber: Int) throws { - if fieldNumber == 1 { - startKey() - encoder.putQuotedInt32(value: value) - } else { - startValue() - encoder.putNonQuotedInt32(value: value) - } - } - - mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws { - if fieldNumber == 1 { - startKey() - encoder.putQuotedInt64(value: value) - } else { - startValue() - options.alwaysPrintInt64sAsNumbers - ? encoder.putNonQuotedInt64(value: value) - : encoder.putQuotedInt64(value: value) - } - } - - mutating func visitSingularUInt32Field(value: UInt32, fieldNumber: Int) throws { - if fieldNumber == 1 { - startKey() - encoder.putQuotedUInt32(value: value) - } else { - startValue() - encoder.putNonQuotedUInt32(value: value) - } - } - - mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws { - if fieldNumber == 1 { - startKey() - encoder.putQuotedUInt64(value: value) - } else { - startValue() - options.alwaysPrintInt64sAsNumbers - ? encoder.putNonQuotedUInt64(value: value) - : encoder.putQuotedUInt64(value: value) - } - } - - mutating func visitSingularSInt32Field(value: Int32, fieldNumber: Int) throws { - try visitSingularInt32Field(value: value, fieldNumber: fieldNumber) - } - - mutating func visitSingularSInt64Field(value: Int64, fieldNumber: Int) throws { - try visitSingularInt64Field(value: value, fieldNumber: fieldNumber) - } - - mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws { - try visitSingularUInt32Field(value: value, fieldNumber: fieldNumber) - } - - mutating func visitSingularFixed64Field(value: UInt64, fieldNumber: Int) throws { - try visitSingularUInt64Field(value: value, fieldNumber: fieldNumber) - } - - mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws { - try visitSingularInt32Field(value: value, fieldNumber: fieldNumber) - } - - mutating func visitSingularSFixed64Field(value: Int64, fieldNumber: Int) throws { - try visitSingularInt64Field(value: value, fieldNumber: fieldNumber) - } - - mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws { - if fieldNumber == 1 { - startKey() - encoder.putQuotedBoolValue(value: value) - } else { - startValue() - encoder.putNonQuotedBoolValue(value: value) - } - } - - mutating func visitSingularStringField(value: String, fieldNumber: Int) throws { - if fieldNumber == 1 { - startKey() - } else { - startValue() - } - encoder.putStringValue(value: value) - } - - mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws { - // Bytes can only be map values, never keys - assert(fieldNumber == 2) - startValue() - encoder.putBytesValue(value: value) - } - - mutating func visitSingularEnumField(value: E, fieldNumber: Int) throws { - // Enums can only be map values, never keys - assert(fieldNumber == 2) - startValue() - if !options.alwaysPrintEnumsAsInts, let n = value.name { - encoder.putStringValue(value: String(describing: n)) - } else { - encoder.putEnumInt(value: value.rawValue) - } - } - - mutating func visitSingularMessageField(value: M, fieldNumber: Int) throws { - // Messages can only be map values, never keys - assert(fieldNumber == 2) - startValue() - let json = try value.jsonString(options: options) - encoder.append(text: json) - } - - // SelectiveVisitor will block: - // - single Groups - // - everything repeated - // - everything packed - // - all maps - // - unknown fields - // - extensions + private var separator: StaticString? + internal var encoder: JSONEncoder + private let options: JSONEncodingOptions + + init(encoder: JSONEncoder, options: JSONEncodingOptions) { + self.encoder = encoder + self.options = options + } + + internal var bytesResult: [UInt8] { + get { + encoder.bytesResult + } + } + + private mutating func startKey() { + if let s = separator { + encoder.append(staticText: s) + } else { + separator = "," + } + } + + private mutating func startValue() { + encoder.append(staticText: ":") + } + + mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws { + // Doubles/Floats can never be map keys, only values + assert(fieldNumber == 2) + startValue() + encoder.putFloatValue(value: value) + } + + mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws { + // Doubles/Floats can never be map keys, only values + assert(fieldNumber == 2) + startValue() + encoder.putDoubleValue(value: value) + } + + mutating func visitSingularInt32Field(value: Int32, fieldNumber: Int) throws { + if fieldNumber == 1 { + startKey() + encoder.putQuotedInt32(value: value) + } else { + startValue() + encoder.putNonQuotedInt32(value: value) + } + } + + mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws { + if fieldNumber == 1 { + startKey() + encoder.putQuotedInt64(value: value) + } else { + startValue() + options.alwaysPrintInt64sAsNumbers + ? encoder.putNonQuotedInt64(value: value) + : encoder.putQuotedInt64(value: value) + } + } + + mutating func visitSingularUInt32Field(value: UInt32, fieldNumber: Int) throws { + if fieldNumber == 1 { + startKey() + encoder.putQuotedUInt32(value: value) + } else { + startValue() + encoder.putNonQuotedUInt32(value: value) + } + } + + mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws { + if fieldNumber == 1 { + startKey() + encoder.putQuotedUInt64(value: value) + } else { + startValue() + options.alwaysPrintInt64sAsNumbers + ? encoder.putNonQuotedUInt64(value: value) + : encoder.putQuotedUInt64(value: value) + } + } + + mutating func visitSingularSInt32Field(value: Int32, fieldNumber: Int) throws { + try visitSingularInt32Field(value: value, fieldNumber: fieldNumber) + } + + mutating func visitSingularSInt64Field(value: Int64, fieldNumber: Int) throws { + try visitSingularInt64Field(value: value, fieldNumber: fieldNumber) + } + + mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws { + try visitSingularUInt32Field(value: value, fieldNumber: fieldNumber) + } + + mutating func visitSingularFixed64Field(value: UInt64, fieldNumber: Int) throws { + try visitSingularUInt64Field(value: value, fieldNumber: fieldNumber) + } + + mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws { + try visitSingularInt32Field(value: value, fieldNumber: fieldNumber) + } + + mutating func visitSingularSFixed64Field(value: Int64, fieldNumber: Int) throws { + try visitSingularInt64Field(value: value, fieldNumber: fieldNumber) + } + + mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws { + if fieldNumber == 1 { + startKey() + encoder.putQuotedBoolValue(value: value) + } else { + startValue() + encoder.putNonQuotedBoolValue(value: value) + } + } + + mutating func visitSingularStringField(value: String, fieldNumber: Int) throws { + if fieldNumber == 1 { + startKey() + } else { + startValue() + } + encoder.putStringValue(value: value) + } + + mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws { + // Bytes can only be map values, never keys + assert(fieldNumber == 2) + startValue() + encoder.putBytesValue(value: value) + } + + mutating func visitSingularEnumField(value: E, fieldNumber: Int) throws { + // Enums can only be map values, never keys + assert(fieldNumber == 2) + startValue() + if !options.alwaysPrintEnumsAsInts, let n = value.name { + encoder.putStringValue(value: String(describing: n)) + } else { + encoder.putEnumInt(value: value.rawValue) + } + } + + mutating func visitSingularMessageField(value: M, fieldNumber: Int) throws { + // Messages can only be map values, never keys + assert(fieldNumber == 2) + startValue() + let json = try value.jsonString(options: options) + encoder.append(text: json) + } + + // SelectiveVisitor will block: + // - single Groups + // - everything repeated + // - everything packed + // - all maps + // - unknown fields + // - extensions } diff --git a/Sources/SwiftProtobuf/JSONScanner.swift b/Sources/SwiftProtobuf/JSONScanner.swift index 6b3eb4110..f4cafbaeb 100644 --- a/Sources/SwiftProtobuf/JSONScanner.swift +++ b/Sources/SwiftProtobuf/JSONScanner.swift @@ -64,20 +64,20 @@ private let asciiLowerZ = UInt8(ascii: "z") private let asciiUpperZ = UInt8(ascii: "Z") private func fromHexDigit(_ c: UnicodeScalar) -> UInt32? { - let n = c.value - if n >= 48 && n <= 57 { - return UInt32(n - 48) - } - switch n { - case 65, 97: return 10 - case 66, 98: return 11 - case 67, 99: return 12 - case 68, 100: return 13 - case 69, 101: return 14 - case 70, 102: return 15 - default: - return nil - } + let n = c.value + if n >= 48 && n <= 57 { + return UInt32(n - 48) + } + switch n { + case 65, 97: return 10 + case 66, 98: return 11 + case 67, 99: return 12 + case 68, 100: return 13 + case 69, 101: return 14 + case 70, 102: return 15 + default: + return nil + } } // Decode both the RFC 4648 section 4 Base 64 encoding and the RFC @@ -86,22 +86,22 @@ private func fromHexDigit(_ c: UnicodeScalar) -> UInt32? { // Note that both "-" and "+" decode to 62 and "/" and "_" both // decode as 63. let base64Values: [Int] = [ -/* 0x00 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -/* 0x10 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -/* 0x20 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63, -/* 0x30 */ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -/* 0x40 */ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -/* 0x50 */ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, -/* 0x60 */ -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, -/* 0x70 */ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, -/* 0x80 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -/* 0x90 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -/* 0xa0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -/* 0xb0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -/* 0xc0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -/* 0xd0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -/* 0xe0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -/* 0xf0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* 0x00 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* 0x10 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* 0x20 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63, + /* 0x30 */ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, + /* 0x40 */ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + /* 0x50 */ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, + /* 0x60 */ -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + /* 0x70 */ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, + /* 0x80 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* 0x90 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* 0xa0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* 0xb0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* 0xc0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* 0xd0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* 0xe0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + /* 0xf0 */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, ] /// Returns a `Data` value containing bytes equivalent to the given @@ -116,9 +116,9 @@ let base64Values: [Int] = [ /// Base 64 encoding and the "URL and Filename Safe Alphabet" variant. /// private func parseBytes( - source: UnsafeRawBufferPointer, - index: inout UnsafeRawBufferPointer.Index, - end: UnsafeRawBufferPointer.Index + source: UnsafeRawBufferPointer, + index: inout UnsafeRawBufferPointer.Index, + end: UnsafeRawBufferPointer.Index ) throws -> Data { let c = source[index] if c != asciiDoubleQuote { @@ -191,83 +191,83 @@ private func parseBytes( index = digitsStart try value.withUnsafeMutableBytes { (body: UnsafeMutableRawBufferPointer) in - if var p = body.baseAddress, body.count > 0 { - var n = 0 - var chars = 0 // # chars in current group - var padding = 0 // # padding '=' chars - digits: while true { - let digit = source[index] - var k = base64Values[Int(digit)] - if k < 0 { - switch digit { - case asciiDoubleQuote: - break digits - case asciiBackslash: - source.formIndex(after: &index) - let escaped = source[index] - switch escaped { - case asciiForwardSlash: - k = base64Values[Int(escaped)] - default: - // Note: Invalid backslash escapes were caught - // above; we should never get here. - throw JSONDecodingError.malformedString - } - case asciiSpace: - source.formIndex(after: &index) - continue digits - case asciiEqualSign: // Count padding - while true { - switch source[index] { - case asciiDoubleQuote: - break digits - case asciiSpace: - break - case 61: - padding += 1 - default: // Only '=' and whitespace permitted + if var p = body.baseAddress, body.count > 0 { + var n = 0 + var chars = 0 // # chars in current group + var padding = 0 // # padding '=' chars + digits: while true { + let digit = source[index] + var k = base64Values[Int(digit)] + if k < 0 { + switch digit { + case asciiDoubleQuote: + break digits + case asciiBackslash: + source.formIndex(after: &index) + let escaped = source[index] + switch escaped { + case asciiForwardSlash: + k = base64Values[Int(escaped)] + default: + // Note: Invalid backslash escapes were caught + // above; we should never get here. throw JSONDecodingError.malformedString } + case asciiSpace: source.formIndex(after: &index) + continue digits + case asciiEqualSign: // Count padding + while true { + switch source[index] { + case asciiDoubleQuote: + break digits + case asciiSpace: + break + case 61: + padding += 1 + default: // Only '=' and whitespace permitted + throw JSONDecodingError.malformedString + } + source.formIndex(after: &index) + } + default: + throw JSONDecodingError.malformedString } - default: - throw JSONDecodingError.malformedString } + n <<= 6 + n |= k + chars += 1 + if chars == 4 { + p[0] = UInt8(truncatingIfNeeded: n >> 16) + p[1] = UInt8(truncatingIfNeeded: n >> 8) + p[2] = UInt8(truncatingIfNeeded: n) + p += 3 + chars = 0 + n = 0 + } + source.formIndex(after: &index) } - n <<= 6 - n |= k - chars += 1 - if chars == 4 { - p[0] = UInt8(truncatingIfNeeded: n >> 16) - p[1] = UInt8(truncatingIfNeeded: n >> 8) - p[2] = UInt8(truncatingIfNeeded: n) - p += 3 - chars = 0 - n = 0 - } - source.formIndex(after: &index) - } - switch chars { - case 3: - p[0] = UInt8(truncatingIfNeeded: n >> 10) - p[1] = UInt8(truncatingIfNeeded: n >> 2) - if padding == 1 || padding == 0 { - return - } - case 2: - p[0] = UInt8(truncatingIfNeeded: n >> 4) - if padding == 2 || padding == 0 { - return - } - case 0: - if padding == 0 { - return + switch chars { + case 3: + p[0] = UInt8(truncatingIfNeeded: n >> 10) + p[1] = UInt8(truncatingIfNeeded: n >> 2) + if padding == 1 || padding == 0 { + return + } + case 2: + p[0] = UInt8(truncatingIfNeeded: n >> 4) + if padding == 2 || padding == 0 { + return + } + case 0: + if padding == 0 { + return + } + default: + break } - default: - break + throw JSONDecodingError.malformedString } - throw JSONDecodingError.malformedString - } } source.formIndex(after: &index) return value @@ -276,93 +276,95 @@ private func parseBytes( // JSON encoding allows a variety of \-escapes, including // escaping UTF-16 code points (which may be surrogate pairs). private func decodeString(_ s: String) -> String? { - var out = String.UnicodeScalarView() - var chars = s.unicodeScalars.makeIterator() - while let c = chars.next() { - switch c.value { - case UInt32(asciiBackslash): // backslash - if let escaped = chars.next() { - switch escaped.value { - case UInt32(asciiLowerU): // "\u" - // Exactly 4 hex digits: - if let digit1 = chars.next(), - let d1 = fromHexDigit(digit1), - let digit2 = chars.next(), - let d2 = fromHexDigit(digit2), - let digit3 = chars.next(), - let d3 = fromHexDigit(digit3), - let digit4 = chars.next(), - let d4 = fromHexDigit(digit4) { - let codePoint = ((d1 * 16 + d2) * 16 + d3) * 16 + d4 - if let scalar = UnicodeScalar(codePoint) { - out.append(scalar) - } else if codePoint < 0xD800 || codePoint >= 0xE000 { - // Not a valid Unicode scalar. - return nil - } else if codePoint >= 0xDC00 { - // Low surrogate without a preceding high surrogate. - return nil - } else { - // We have a high surrogate (in the range 0xD800..<0xDC00), so - // verify that it is followed by a low surrogate. - guard chars.next() == "\\", chars.next() == "u" else { - // High surrogate was not followed by a Unicode escape sequence. - return nil - } - if let digit1 = chars.next(), - let d1 = fromHexDigit(digit1), - let digit2 = chars.next(), - let d2 = fromHexDigit(digit2), - let digit3 = chars.next(), - let d3 = fromHexDigit(digit3), - let digit4 = chars.next(), - let d4 = fromHexDigit(digit4) { - let follower = ((d1 * 16 + d2) * 16 + d3) * 16 + d4 - guard 0xDC00 <= follower && follower < 0xE000 else { - // High surrogate was not followed by a low surrogate. - return nil - } - let high = codePoint - 0xD800 - let low = follower - 0xDC00 - let composed = 0x10000 | high << 10 | low - guard let composedScalar = UnicodeScalar(composed) else { - // Composed value is not a valid Unicode scalar. - return nil + var out = String.UnicodeScalarView() + var chars = s.unicodeScalars.makeIterator() + while let c = chars.next() { + switch c.value { + case UInt32(asciiBackslash): // backslash + if let escaped = chars.next() { + switch escaped.value { + case UInt32(asciiLowerU): // "\u" + // Exactly 4 hex digits: + if let digit1 = chars.next(), + let d1 = fromHexDigit(digit1), + let digit2 = chars.next(), + let d2 = fromHexDigit(digit2), + let digit3 = chars.next(), + let d3 = fromHexDigit(digit3), + let digit4 = chars.next(), + let d4 = fromHexDigit(digit4) + { + let codePoint = ((d1 * 16 + d2) * 16 + d3) * 16 + d4 + if let scalar = UnicodeScalar(codePoint) { + out.append(scalar) + } else if codePoint < 0xD800 || codePoint >= 0xE000 { + // Not a valid Unicode scalar. + return nil + } else if codePoint >= 0xDC00 { + // Low surrogate without a preceding high surrogate. + return nil + } else { + // We have a high surrogate (in the range 0xD800..<0xDC00), so + // verify that it is followed by a low surrogate. + guard chars.next() == "\\", chars.next() == "u" else { + // High surrogate was not followed by a Unicode escape sequence. + return nil + } + if let digit1 = chars.next(), + let d1 = fromHexDigit(digit1), + let digit2 = chars.next(), + let d2 = fromHexDigit(digit2), + let digit3 = chars.next(), + let d3 = fromHexDigit(digit3), + let digit4 = chars.next(), + let d4 = fromHexDigit(digit4) + { + let follower = ((d1 * 16 + d2) * 16 + d3) * 16 + d4 + guard 0xDC00 <= follower && follower < 0xE000 else { + // High surrogate was not followed by a low surrogate. + return nil + } + let high = codePoint - 0xD800 + let low = follower - 0xDC00 + let composed = 0x10000 | high << 10 | low + guard let composedScalar = UnicodeScalar(composed) else { + // Composed value is not a valid Unicode scalar. + return nil + } + out.append(composedScalar) + } else { + // Malformed \u escape for low surrogate + return nil + } + } + } else { + // Malformed \u escape + return nil + } + case UInt32(asciiLowerB): // \b + out.append("\u{08}") + case UInt32(asciiLowerF): // \f + out.append("\u{0c}") + case UInt32(asciiLowerN): // \n + out.append("\u{0a}") + case UInt32(asciiLowerR): // \r + out.append("\u{0d}") + case UInt32(asciiLowerT): // \t + out.append("\u{09}") + case UInt32(asciiDoubleQuote), UInt32(asciiBackslash), + UInt32(asciiForwardSlash): // " \ / + out.append(escaped) + default: + return nil // Unrecognized escape } - out.append(composedScalar) - } else { - // Malformed \u escape for low surrogate - return nil - } + } else { + return nil // Input ends with backslash } - } else { - // Malformed \u escape - return nil - } - case UInt32(asciiLowerB): // \b - out.append("\u{08}") - case UInt32(asciiLowerF): // \f - out.append("\u{0c}") - case UInt32(asciiLowerN): // \n - out.append("\u{0a}") - case UInt32(asciiLowerR): // \r - out.append("\u{0d}") - case UInt32(asciiLowerT): // \t - out.append("\u{09}") - case UInt32(asciiDoubleQuote), UInt32(asciiBackslash), - UInt32(asciiForwardSlash): // " \ / - out.append(escaped) default: - return nil // Unrecognized escape + out.append(c) } - } else { - return nil // Input ends with backslash - } - default: - out.append(c) } - } - return String(out) + return String(out) } /// @@ -370,1174 +372,1203 @@ private func decodeString(_ s: String) -> String? { /// /// For performance, it works directly against UTF-8 bytes in memory. internal struct JSONScanner { - private let source: UnsafeRawBufferPointer - private var index: UnsafeRawBufferPointer.Index - private var numberParser = DoubleParser() - internal let options: JSONDecodingOptions - internal let extensions: any ExtensionMap - internal var recursionBudget: Int + private let source: UnsafeRawBufferPointer + private var index: UnsafeRawBufferPointer.Index + private var numberParser = DoubleParser() + internal let options: JSONDecodingOptions + internal let extensions: any ExtensionMap + internal var recursionBudget: Int - /// True if the scanner has read all of the data from the source, with the - /// exception of any trailing whitespace (which is consumed by reading this - /// property). - internal var complete: Bool { - mutating get { - skipWhitespace() - return !hasMoreContent + /// True if the scanner has read all of the data from the source, with the + /// exception of any trailing whitespace (which is consumed by reading this + /// property). + internal var complete: Bool { + mutating get { + skipWhitespace() + return !hasMoreContent + } } - } - - /// True if the scanner has not yet reached the end of the source. - private var hasMoreContent: Bool { - return index != source.endIndex - } - /// The byte (UTF-8 code unit) at the scanner's current position. - private var currentByte: UInt8 { - return source[index] - } + /// True if the scanner has not yet reached the end of the source. + private var hasMoreContent: Bool { + index != source.endIndex + } - internal init( - source: UnsafeRawBufferPointer, - options: JSONDecodingOptions, - extensions: (any ExtensionMap)? - ) { - self.source = source - self.index = source.startIndex - self.recursionBudget = options.messageDepthLimit - self.options = options - self.extensions = extensions ?? SimpleExtensionMap() - } + /// The byte (UTF-8 code unit) at the scanner's current position. + private var currentByte: UInt8 { + source[index] + } - internal mutating func incrementRecursionDepth() throws { - recursionBudget -= 1 - if recursionBudget < 0 { - throw JSONDecodingError.messageDepthLimit + internal init( + source: UnsafeRawBufferPointer, + options: JSONDecodingOptions, + extensions: (any ExtensionMap)? + ) { + self.source = source + self.index = source.startIndex + self.recursionBudget = options.messageDepthLimit + self.options = options + self.extensions = extensions ?? SimpleExtensionMap() } - } - internal mutating func decrementRecursionDepth() { - recursionBudget += 1 - // This should never happen, if it does, something is probably corrupting memory, and - // simply throwing doesn't make much sense. - if recursionBudget > options.messageDepthLimit { - fatalError("Somehow JSONDecoding unwound more objects than it started") + internal mutating func incrementRecursionDepth() throws { + recursionBudget -= 1 + if recursionBudget < 0 { + throw JSONDecodingError.messageDepthLimit + } } - } - /// Advances the scanner to the next position in the source. - private mutating func advance() { - source.formIndex(after: &index) - } + internal mutating func decrementRecursionDepth() { + recursionBudget += 1 + // This should never happen, if it does, something is probably corrupting memory, and + // simply throwing doesn't make much sense. + if recursionBudget > options.messageDepthLimit { + fatalError("Somehow JSONDecoding unwound more objects than it started") + } + } - /// Skip whitespace. - private mutating func skipWhitespace() { - while hasMoreContent { - let u = currentByte - switch u { - case asciiSpace, asciiTab, asciiNewLine, asciiCarriageReturn: - advance() - default: - return - } + /// Advances the scanner to the next position in the source. + private mutating func advance() { + source.formIndex(after: &index) } - } - /// Returns (but does not consume) the next non-whitespace - /// character. This is used by google.protobuf.Value, for - /// example, for custom JSON parsing. - internal mutating func peekOneCharacter() throws -> Character { - skipWhitespace() - guard hasMoreContent else { - throw JSONDecodingError.truncated + /// Skip whitespace. + private mutating func skipWhitespace() { + while hasMoreContent { + let u = currentByte + switch u { + case asciiSpace, asciiTab, asciiNewLine, asciiCarriageReturn: + advance() + default: + return + } + } } - return Character(UnicodeScalar(UInt32(currentByte))!) - } - // Parse the leading UInt64 from the provided utf8 bytes. - // - // This is called in three different situations: - // - // * Unquoted number. - // - // * Simple quoted number. If a number is quoted but has no - // backslashes, the caller can use this directly on the UTF8 by - // just verifying the quote marks. This code returns `nil` if it - // sees a backslash, in which case the caller will need to handle ... - // - // * Complex quoted number. In this case, the caller must parse the - // quoted value as a string, then convert the string to utf8 and - // use this to parse the result. This is slow but fortunately - // rare. - // - // In the common case where the number is written in integer form, - // this code does a simple straight conversion. If the number is in - // floating-point format, this uses a slower and less accurate - // approach: it identifies a substring comprising a float, and then - // uses Double() and UInt64() to convert that string to an unsigned - // integer. In particular, it cannot preserve full 64-bit integer - // values when they are written in floating-point format. - // - // If it encounters a "\" backslash character, it returns a nil. This - // is used by callers that are parsing quoted numbers. See nextSInt() - // and nextUInt() below. - private func parseBareUInt64( - source: UnsafeRawBufferPointer, - index: inout UnsafeRawBufferPointer.Index, - end: UnsafeRawBufferPointer.Index - ) throws -> UInt64? { - if index == end { - throw JSONDecodingError.truncated + /// Returns (but does not consume) the next non-whitespace + /// character. This is used by google.protobuf.Value, for + /// example, for custom JSON parsing. + internal mutating func peekOneCharacter() throws -> Character { + skipWhitespace() + guard hasMoreContent else { + throw JSONDecodingError.truncated + } + return Character(UnicodeScalar(UInt32(currentByte))!) } - let start = index - let c = source[index] - switch c { - case asciiZero: // 0 - source.formIndex(after: &index) - if index != end { - let after = source[index] - switch after { - case asciiZero...asciiNine: // 0...9 - // leading '0' forbidden unless it is the only digit - throw JSONDecodingError.leadingZero - case asciiPeriod, asciiLowerE, asciiUpperE: // . e - // Slow path: JSON numbers can be written in floating-point notation - index = start - if let d = try parseBareDouble(source: source, - index: &index, - end: end) { - if let u = UInt64(exactly: d) { - return u + + // Parse the leading UInt64 from the provided utf8 bytes. + // + // This is called in three different situations: + // + // * Unquoted number. + // + // * Simple quoted number. If a number is quoted but has no + // backslashes, the caller can use this directly on the UTF8 by + // just verifying the quote marks. This code returns `nil` if it + // sees a backslash, in which case the caller will need to handle ... + // + // * Complex quoted number. In this case, the caller must parse the + // quoted value as a string, then convert the string to utf8 and + // use this to parse the result. This is slow but fortunately + // rare. + // + // In the common case where the number is written in integer form, + // this code does a simple straight conversion. If the number is in + // floating-point format, this uses a slower and less accurate + // approach: it identifies a substring comprising a float, and then + // uses Double() and UInt64() to convert that string to an unsigned + // integer. In particular, it cannot preserve full 64-bit integer + // values when they are written in floating-point format. + // + // If it encounters a "\" backslash character, it returns a nil. This + // is used by callers that are parsing quoted numbers. See nextSInt() + // and nextUInt() below. + private func parseBareUInt64( + source: UnsafeRawBufferPointer, + index: inout UnsafeRawBufferPointer.Index, + end: UnsafeRawBufferPointer.Index + ) throws -> UInt64? { + if index == end { + throw JSONDecodingError.truncated + } + let start = index + let c = source[index] + switch c { + case asciiZero: // 0 + source.formIndex(after: &index) + if index != end { + let after = source[index] + switch after { + case asciiZero...asciiNine: // 0...9 + // leading '0' forbidden unless it is the only digit + throw JSONDecodingError.leadingZero + case asciiPeriod, asciiLowerE, asciiUpperE: // . e + // Slow path: JSON numbers can be written in floating-point notation + index = start + if let d = try parseBareDouble( + source: source, + index: &index, + end: end + ) { + if let u = UInt64(exactly: d) { + return u + } + } + throw JSONDecodingError.malformedNumber + case asciiBackslash: + return nil + default: + return 0 + } } - } - throw JSONDecodingError.malformedNumber - case asciiBackslash: - return nil - default: - return 0 - } - } - return 0 - case asciiOne...asciiNine: // 1...9 - var n = 0 as UInt64 - while index != end { - let digit = source[index] - switch digit { - case asciiZero...asciiNine: // 0...9 - let val = UInt64(digit - asciiZero) - if n > UInt64.max / 10 || n * 10 > UInt64.max - val { - throw JSONDecodingError.numberRange - } - source.formIndex(after: &index) - n = n * 10 + val - case asciiPeriod, asciiLowerE, asciiUpperE: // . e - // Slow path: JSON allows floating-point notation for integers - index = start - if let d = try parseBareDouble(source: source, - index: &index, - end: end) { - if let u = UInt64(exactly: d) { - return u + return 0 + case asciiOne...asciiNine: // 1...9 + var n = 0 as UInt64 + while index != end { + let digit = source[index] + switch digit { + case asciiZero...asciiNine: // 0...9 + let val = UInt64(digit - asciiZero) + if n > UInt64.max / 10 || n * 10 > UInt64.max - val { + throw JSONDecodingError.numberRange + } + source.formIndex(after: &index) + n = n * 10 + val + case asciiPeriod, asciiLowerE, asciiUpperE: // . e + // Slow path: JSON allows floating-point notation for integers + index = start + if let d = try parseBareDouble( + source: source, + index: &index, + end: end + ) { + if let u = UInt64(exactly: d) { + return u + } + } + throw JSONDecodingError.malformedNumber + case asciiBackslash: + return nil + default: + return n + } } - } - throw JSONDecodingError.malformedNumber + return n case asciiBackslash: - return nil + return nil default: - return n + throw JSONDecodingError.malformedNumber } - } - return n - case asciiBackslash: - return nil - default: - throw JSONDecodingError.malformedNumber } - } - // Parse the leading Int64 from the provided utf8. - // - // This uses parseBareUInt64() to do the heavy lifting; - // we just check for a leading minus and negate the result - // as necessary. - // - // As with parseBareUInt64(), if it encounters a "\" backslash - // character, it returns a nil. This allows callers to use this to - // do a "fast-path" decode of simple quoted numbers by parsing the - // UTF8 directly, only falling back to a full String decode when - // absolutely necessary. - private func parseBareSInt64( - source: UnsafeRawBufferPointer, - index: inout UnsafeRawBufferPointer.Index, - end: UnsafeRawBufferPointer.Index - ) throws -> Int64? { - if index == end { - throw JSONDecodingError.truncated - } - let c = source[index] - if c == asciiMinus { // - - source.formIndex(after: &index) - if index == end { - throw JSONDecodingError.truncated - } - // character after '-' must be digit - let digit = source[index] - if digit < asciiZero || digit > asciiNine { - throw JSONDecodingError.malformedNumber - } - if let n = try parseBareUInt64(source: source, index: &index, end: end) { - let limit: UInt64 = 0x8000000000000000 // -Int64.min - if n >= limit { - if n > limit { - // Too large negative number - throw JSONDecodingError.numberRange - } else { - return Int64.min // Special case for Int64.min - } - } - return -Int64(bitPattern: n) - } else { - return nil - } - } else if let n = try parseBareUInt64(source: source, index: &index, end: end) { - if n > UInt64(bitPattern: Int64.max) { - throw JSONDecodingError.numberRange - } - return Int64(bitPattern: n) - } else { - return nil + // Parse the leading Int64 from the provided utf8. + // + // This uses parseBareUInt64() to do the heavy lifting; + // we just check for a leading minus and negate the result + // as necessary. + // + // As with parseBareUInt64(), if it encounters a "\" backslash + // character, it returns a nil. This allows callers to use this to + // do a "fast-path" decode of simple quoted numbers by parsing the + // UTF8 directly, only falling back to a full String decode when + // absolutely necessary. + private func parseBareSInt64( + source: UnsafeRawBufferPointer, + index: inout UnsafeRawBufferPointer.Index, + end: UnsafeRawBufferPointer.Index + ) throws -> Int64? { + if index == end { + throw JSONDecodingError.truncated + } + let c = source[index] + if c == asciiMinus { // - + source.formIndex(after: &index) + if index == end { + throw JSONDecodingError.truncated + } + // character after '-' must be digit + let digit = source[index] + if digit < asciiZero || digit > asciiNine { + throw JSONDecodingError.malformedNumber + } + if let n = try parseBareUInt64(source: source, index: &index, end: end) { + let limit: UInt64 = 0x8000_0000_0000_0000 // -Int64.min + if n >= limit { + if n > limit { + // Too large negative number + throw JSONDecodingError.numberRange + } else { + return Int64.min // Special case for Int64.min + } + } + return -Int64(bitPattern: n) + } else { + return nil + } + } else if let n = try parseBareUInt64(source: source, index: &index, end: end) { + if n > UInt64(bitPattern: Int64.max) { + throw JSONDecodingError.numberRange + } + return Int64(bitPattern: n) + } else { + return nil + } } - } - // Identify a floating-point token in the upcoming UTF8 bytes. - // - // This implements the full grammar defined by the JSON RFC 7159. - // Note that Swift's string-to-number conversions are much more - // lenient, so this is necessary if we want to accurately reject - // malformed JSON numbers. - // - // This is used by nextDouble() and nextFloat() to parse double and - // floating-point values, including values that happen to be in quotes. - // It's also used by the slow path in parseBareSInt64() and parseBareUInt64() - // above to handle integer values that are written in float-point notation. - private func parseBareDouble( - source: UnsafeRawBufferPointer, - index: inout UnsafeRawBufferPointer.Index, - end: UnsafeRawBufferPointer.Index - ) throws -> Double? { - // RFC 7159 defines the grammar for JSON numbers as: - // number = [ minus ] int [ frac ] [ exp ] - if index == end { - throw JSONDecodingError.truncated - } - let start = index - var c = source[index] - if c == asciiBackslash { - return nil - } + // Identify a floating-point token in the upcoming UTF8 bytes. + // + // This implements the full grammar defined by the JSON RFC 7159. + // Note that Swift's string-to-number conversions are much more + // lenient, so this is necessary if we want to accurately reject + // malformed JSON numbers. + // + // This is used by nextDouble() and nextFloat() to parse double and + // floating-point values, including values that happen to be in quotes. + // It's also used by the slow path in parseBareSInt64() and parseBareUInt64() + // above to handle integer values that are written in float-point notation. + private func parseBareDouble( + source: UnsafeRawBufferPointer, + index: inout UnsafeRawBufferPointer.Index, + end: UnsafeRawBufferPointer.Index + ) throws -> Double? { + // RFC 7159 defines the grammar for JSON numbers as: + // number = [ minus ] int [ frac ] [ exp ] + if index == end { + throw JSONDecodingError.truncated + } + let start = index + var c = source[index] + if c == asciiBackslash { + return nil + } - // Optional leading minus sign - if c == asciiMinus { // - - source.formIndex(after: &index) - if index == end { - index = start - throw JSONDecodingError.truncated - } - c = source[index] - if c == asciiBackslash { - return nil - } - } else if c == asciiUpperN { // Maybe NaN? - // Return nil, let the caller deal with it. - return nil - } + // Optional leading minus sign + if c == asciiMinus { // - + source.formIndex(after: &index) + if index == end { + index = start + throw JSONDecodingError.truncated + } + c = source[index] + if c == asciiBackslash { + return nil + } + } else if c == asciiUpperN { // Maybe NaN? + // Return nil, let the caller deal with it. + return nil + } - if c == asciiUpperI { // Maybe Infinity, Inf, -Infinity, or -Inf ? - // Return nil, let the caller deal with it. - return nil - } + if c == asciiUpperI { // Maybe Infinity, Inf, -Infinity, or -Inf ? + // Return nil, let the caller deal with it. + return nil + } - // Integer part can be zero or a series of digits not starting with zero - // int = zero / (digit1-9 *DIGIT) - switch c { - case asciiZero: - // First digit can be zero only if not followed by a digit - source.formIndex(after: &index) - if index == end { - return 0.0 - } - c = source[index] - if c == asciiBackslash { - return nil - } - if c >= asciiZero && c <= asciiNine { - throw JSONDecodingError.leadingZero - } - case asciiOne...asciiNine: - while c >= asciiZero && c <= asciiNine { - source.formIndex(after: &index) - if index == end { - if let d = numberParser.utf8ToDouble(bytes: source, start: start, end: index) { + // Integer part can be zero or a series of digits not starting with zero + // int = zero / (digit1-9 *DIGIT) + switch c { + case asciiZero: + // First digit can be zero only if not followed by a digit + source.formIndex(after: &index) + if index == end { + return 0.0 + } + c = source[index] + if c == asciiBackslash { + return nil + } + if c >= asciiZero && c <= asciiNine { + throw JSONDecodingError.leadingZero + } + case asciiOne...asciiNine: + while c >= asciiZero && c <= asciiNine { + source.formIndex(after: &index) + if index == end { + if let d = numberParser.utf8ToDouble(bytes: source, start: start, end: index) { + return d + } else { + throw JSONDecodingError.invalidUTF8 + } + } + c = source[index] + if c == asciiBackslash { + return nil + } + } + default: + // Integer part cannot be empty + throw JSONDecodingError.malformedNumber + } + + // frac = decimal-point 1*DIGIT + if c == asciiPeriod { + source.formIndex(after: &index) + if index == end { + // decimal point must have a following digit + throw JSONDecodingError.truncated + } + c = source[index] + switch c { + case asciiZero...asciiNine: // 0...9 + while c >= asciiZero && c <= asciiNine { + source.formIndex(after: &index) + if index == end { + if let d = numberParser.utf8ToDouble(bytes: source, start: start, end: index) { + return d + } else { + throw JSONDecodingError.invalidUTF8 + } + } + c = source[index] + if c == asciiBackslash { + return nil + } + } + case asciiBackslash: + return nil + default: + // decimal point must be followed by at least one digit + throw JSONDecodingError.malformedNumber + } + } + + // exp = e [ minus / plus ] 1*DIGIT + if c == asciiLowerE || c == asciiUpperE { + source.formIndex(after: &index) + if index == end { + // "e" must be followed by +,-, or digit + throw JSONDecodingError.truncated + } + c = source[index] + if c == asciiBackslash { + return nil + } + if c == asciiPlus || c == asciiMinus { // + - + source.formIndex(after: &index) + if index == end { + // must be at least one digit in exponent + throw JSONDecodingError.truncated + } + c = source[index] + if c == asciiBackslash { + return nil + } + } + switch c { + case asciiZero...asciiNine: + while c >= asciiZero && c <= asciiNine { + source.formIndex(after: &index) + if index == end { + if let d = numberParser.utf8ToDouble(bytes: source, start: start, end: index) { + return d + } else { + throw JSONDecodingError.invalidUTF8 + } + } + c = source[index] + if c == asciiBackslash { + return nil + } + } + default: + // must be at least one digit in exponent + throw JSONDecodingError.malformedNumber + } + } + if let d = numberParser.utf8ToDouble(bytes: source, start: start, end: index) { return d - } else { + } else { throw JSONDecodingError.invalidUTF8 - } } - c = source[index] - if c == asciiBackslash { - return nil + } + + /// Returns a fully-parsed string with all backslash escapes + /// correctly processed, or nil if next token is not a string. + /// + /// Assumes the leading quote has been verified (but not consumed) + private mutating func parseOptionalQuotedString() -> String? { + // Caller has already asserted that currentByte == quote here + var sawBackslash = false + advance() + let start = index + while hasMoreContent { + switch currentByte { + case asciiDoubleQuote: // " + let s = utf8ToString(bytes: source, start: start, end: index) + advance() + if let t = s { + if sawBackslash { + return decodeString(t) + } else { + return t + } + } else { + return nil // Invalid UTF8 + } + case asciiBackslash: // \ + advance() + guard hasMoreContent else { + return nil // Unterminated escape + } + sawBackslash = true + default: + break + } + advance() } - } - default: - // Integer part cannot be empty - throw JSONDecodingError.malformedNumber + return nil // Unterminated quoted string } - // frac = decimal-point 1*DIGIT - if c == asciiPeriod { - source.formIndex(after: &index) - if index == end { - // decimal point must have a following digit - throw JSONDecodingError.truncated - } - c = source[index] - switch c { - case asciiZero...asciiNine: // 0...9 - while c >= asciiZero && c <= asciiNine { - source.formIndex(after: &index) - if index == end { - if let d = numberParser.utf8ToDouble(bytes: source, start: start, end: index) { - return d + /// Parse an unsigned integer, whether or not its quoted. + /// This also handles cases such as quoted numbers that have + /// backslash escapes in them. + /// + /// This supports the full range of UInt64 (whether quoted or not) + /// unless the number is written in floating-point format. In that + /// case, we decode it with only Double precision. + internal mutating func nextUInt() throws -> UInt64 { + skipWhitespace() + guard hasMoreContent else { + throw JSONDecodingError.truncated + } + let c = currentByte + if c == asciiDoubleQuote { + let start = index + advance() + if let u = try parseBareUInt64( + source: source, + index: &index, + end: source.endIndex + ) { + guard hasMoreContent else { + throw JSONDecodingError.truncated + } + if currentByte != asciiDoubleQuote { + throw JSONDecodingError.malformedNumber + } + advance() + return u } else { - throw JSONDecodingError.invalidUTF8 + // Couldn't parse because it had a "\" in the string, + // so parse out the quoted string and then reparse + // the result to get a UInt. + index = start + let s = try nextQuotedString() + let raw = s.data(using: String.Encoding.utf8)! + let n = try raw.withUnsafeBytes { + (body: UnsafeRawBufferPointer) -> UInt64? in + if body.count > 0 { + var index = body.startIndex + let end = body.endIndex + if let u = try parseBareUInt64( + source: body, + index: &index, + end: end + ) { + if index == end { + return u + } + } + } + return nil + } + if let n = n { + return n + } } - } - c = source[index] - if c == asciiBackslash { - return nil - } + } else if let u = try parseBareUInt64( + source: source, + index: &index, + end: source.endIndex + ) { + return u } - case asciiBackslash: - return nil - default: - // decimal point must be followed by at least one digit throw JSONDecodingError.malformedNumber - } } - // exp = e [ minus / plus ] 1*DIGIT - if c == asciiLowerE || c == asciiUpperE { - source.formIndex(after: &index) - if index == end { - // "e" must be followed by +,-, or digit - throw JSONDecodingError.truncated - } - c = source[index] - if c == asciiBackslash { - return nil - } - if c == asciiPlus || c == asciiMinus { // + - - source.formIndex(after: &index) - if index == end { - // must be at least one digit in exponent - throw JSONDecodingError.truncated + /// Parse a signed integer, quoted or not, including handling + /// backslash escapes for quoted values. + /// + /// This supports the full range of Int64 (whether quoted or not) + /// unless the number is written in floating-point format. In that + /// case, we decode it with only Double precision. + internal mutating func nextSInt() throws -> Int64 { + skipWhitespace() + guard hasMoreContent else { + throw JSONDecodingError.truncated } - c = source[index] - if c == asciiBackslash { - return nil - } - } - switch c { - case asciiZero...asciiNine: - while c >= asciiZero && c <= asciiNine { - source.formIndex(after: &index) - if index == end { - if let d = numberParser.utf8ToDouble(bytes: source, start: start, end: index) { - return d + let c = currentByte + if c == asciiDoubleQuote { + let start = index + advance() + if let s = try parseBareSInt64( + source: source, + index: &index, + end: source.endIndex + ) { + guard hasMoreContent else { + throw JSONDecodingError.truncated + } + if currentByte != asciiDoubleQuote { + throw JSONDecodingError.malformedNumber + } + advance() + return s } else { - throw JSONDecodingError.invalidUTF8 + // Couldn't parse because it had a "\" in the string, + // so parse out the quoted string and then reparse + // the result as an SInt. + index = start + let s = try nextQuotedString() + let raw = s.data(using: String.Encoding.utf8)! + let n = try raw.withUnsafeBytes { + (body: UnsafeRawBufferPointer) -> Int64? in + if body.count > 0 { + var index = body.startIndex + let end = body.endIndex + if let s = try parseBareSInt64( + source: body, + index: &index, + end: end + ) { + if index == end { + return s + } + } + } + return nil + } + if let n = n { + return n + } } - } - c = source[index] - if c == asciiBackslash { - return nil - } + } else if let s = try parseBareSInt64( + source: source, + index: &index, + end: source.endIndex + ) { + return s } - default: - // must be at least one digit in exponent throw JSONDecodingError.malformedNumber - } } - if let d = numberParser.utf8ToDouble(bytes: source, start: start, end: index) { - return d - } else { - throw JSONDecodingError.invalidUTF8 + + /// Parse the next Float value, regardless of whether it + /// is quoted, including handling backslash escapes for + /// quoted strings. + internal mutating func nextFloat() throws -> Float { + skipWhitespace() + guard hasMoreContent else { + throw JSONDecodingError.truncated + } + let c = currentByte + if c == asciiDoubleQuote { // " + let start = index + advance() + if let d = try parseBareDouble( + source: source, + index: &index, + end: source.endIndex + ) { + guard hasMoreContent else { + throw JSONDecodingError.truncated + } + if currentByte != asciiDoubleQuote { + throw JSONDecodingError.malformedNumber + } + advance() + return Float(d) + } else { + // Slow Path: parseBareDouble returned nil: It might be + // a valid float, but had something that + // parseBareDouble cannot directly handle. So we reset, + // try a full string parse, then examine the result: + index = start + let s = try nextQuotedString() + switch s { + case "NaN": return Float.nan + case "Inf": return Float.infinity + case "-Inf": return -Float.infinity + case "Infinity": return Float.infinity + case "-Infinity": return -Float.infinity + default: + let raw = s.data(using: String.Encoding.utf8)! + let n = try raw.withUnsafeBytes { + (body: UnsafeRawBufferPointer) -> Float? in + if body.count > 0 { + var index = body.startIndex + let end = body.endIndex + if let d = try parseBareDouble( + source: body, + index: &index, + end: end + ) { + let f = Float(d) + if index == end && f.isFinite { + return f + } + } + } + return nil + } + if let n = n { + return n + } + } + } + } else { + if let d = try parseBareDouble( + source: source, + index: &index, + end: source.endIndex + ) { + let f = Float(d) + if f.isFinite { + return f + } + } + } + throw JSONDecodingError.malformedNumber } - } - /// Returns a fully-parsed string with all backslash escapes - /// correctly processed, or nil if next token is not a string. - /// - /// Assumes the leading quote has been verified (but not consumed) - private mutating func parseOptionalQuotedString() -> String? { - // Caller has already asserted that currentByte == quote here - var sawBackslash = false - advance() - let start = index - while hasMoreContent { - switch currentByte { - case asciiDoubleQuote: // " - let s = utf8ToString(bytes: source, start: start, end: index) - advance() - if let t = s { - if sawBackslash { - return decodeString(t) - } else { - return t - } + /// Parse the next Double value, regardless of whether it + /// is quoted, including handling backslash escapes for + /// quoted strings. + internal mutating func nextDouble() throws -> Double { + skipWhitespace() + guard hasMoreContent else { + throw JSONDecodingError.truncated + } + let c = currentByte + if c == asciiDoubleQuote { // " + let start = index + advance() + if let d = try parseBareDouble( + source: source, + index: &index, + end: source.endIndex + ) { + guard hasMoreContent else { + throw JSONDecodingError.truncated + } + if currentByte != asciiDoubleQuote { + throw JSONDecodingError.malformedNumber + } + advance() + return d + } else { + // Slow Path: parseBareDouble returned nil: It might be + // a valid float, but had something that + // parseBareDouble cannot directly handle. So we reset, + // try a full string parse, then examine the result: + index = start + let s = try nextQuotedString() + switch s { + case "NaN": return Double.nan + case "Inf": return Double.infinity + case "-Inf": return -Double.infinity + case "Infinity": return Double.infinity + case "-Infinity": return -Double.infinity + default: + let raw = s.data(using: String.Encoding.utf8)! + let n = try raw.withUnsafeBytes { + (body: UnsafeRawBufferPointer) -> Double? in + if body.count > 0 { + var index = body.startIndex + let end = body.endIndex + if let d = try parseBareDouble( + source: body, + index: &index, + end: end + ) { + if index == end { + return d + } + } + } + return nil + } + if let n = n { + return n + } + } + } } else { - return nil // Invalid UTF8 + if let d = try parseBareDouble( + source: source, + index: &index, + end: source.endIndex + ) { + return d + } } - case asciiBackslash: // \ - advance() + throw JSONDecodingError.malformedNumber + } + + /// Return the contents of the following quoted string, + /// or throw an error if the next token is not a string. + internal mutating func nextQuotedString() throws -> String { + skipWhitespace() guard hasMoreContent else { - return nil // Unterminated escape + throw JSONDecodingError.truncated + } + let c = currentByte + if c != asciiDoubleQuote { + throw JSONDecodingError.malformedString + } + if let s = parseOptionalQuotedString() { + return s + } else { + throw JSONDecodingError.malformedString } - sawBackslash = true - default: - break - } - advance() } - return nil // Unterminated quoted string - } - /// Parse an unsigned integer, whether or not its quoted. - /// This also handles cases such as quoted numbers that have - /// backslash escapes in them. - /// - /// This supports the full range of UInt64 (whether quoted or not) - /// unless the number is written in floating-point format. In that - /// case, we decode it with only Double precision. - internal mutating func nextUInt() throws -> UInt64 { - skipWhitespace() - guard hasMoreContent else { - throw JSONDecodingError.truncated + /// Return the contents of the following quoted string, + /// or nil if the next token is not a string. + /// This will only throw an error if the next token starts + /// out as a string but is malformed in some way. + internal mutating func nextOptionalQuotedString() throws -> String? { + skipWhitespace() + guard hasMoreContent else { + return nil + } + let c = currentByte + if c != asciiDoubleQuote { + return nil + } + return try nextQuotedString() } - let c = currentByte - if c == asciiDoubleQuote { - let start = index - advance() - if let u = try parseBareUInt64(source: source, - index: &index, - end: source.endIndex) { + + /// Return a Data with the decoded contents of the + /// following base-64 string. + /// + /// Notes on Google's implementation: + /// * Google's C++ implementation accepts arbitrary whitespace + /// mixed in with the base-64 characters + /// * Google's C++ implementation ignores missing '=' characters + /// but if present, there must be the exact correct number of them. + /// * Google's C++ implementation accepts both "regular" and + /// "web-safe" base-64 variants (it seems to prefer the + /// web-safe version as defined in RFC 4648 + internal mutating func nextBytesValue() throws -> Data { + skipWhitespace() guard hasMoreContent else { - throw JSONDecodingError.truncated + throw JSONDecodingError.truncated } - if currentByte != asciiDoubleQuote { - throw JSONDecodingError.malformedNumber + return try parseBytes(source: source, index: &index, end: source.endIndex) + } + + /// Private function to help parse keywords. + private mutating func skipOptionalKeyword(bytes: [UInt8]) -> Bool { + let start = index + for b in bytes { + guard hasMoreContent else { + index = start + return false + } + let c = currentByte + if c != b { + index = start + return false + } + advance() } - advance() - return u - } else { - // Couldn't parse because it had a "\" in the string, - // so parse out the quoted string and then reparse - // the result to get a UInt. - index = start - let s = try nextQuotedString() - let raw = s.data(using: String.Encoding.utf8)! - let n = try raw.withUnsafeBytes { - (body: UnsafeRawBufferPointer) -> UInt64? in - if body.count > 0 { - var index = body.startIndex - let end = body.endIndex - if let u = try parseBareUInt64(source: body, - index: &index, - end: end) { - if index == end { - return u - } + if hasMoreContent { + let c = currentByte + if (c >= asciiUpperA && c <= asciiUpperZ) || (c >= asciiLowerA && c <= asciiLowerZ) { + index = start + return false } - } - return nil } - if let n = n { - return n + return true + } + + /// If the next token is the identifier "null", consume it and return true. + internal mutating func skipOptionalNull() -> Bool { + skipWhitespace() + if hasMoreContent && currentByte == asciiLowerN { + return skipOptionalKeyword(bytes: [ + asciiLowerN, asciiLowerU, asciiLowerL, asciiLowerL, + ]) } - } - } else if let u = try parseBareUInt64(source: source, - index: &index, - end: source.endIndex) { - return u + return false } - throw JSONDecodingError.malformedNumber - } - /// Parse a signed integer, quoted or not, including handling - /// backslash escapes for quoted values. - /// - /// This supports the full range of Int64 (whether quoted or not) - /// unless the number is written in floating-point format. In that - /// case, we decode it with only Double precision. - internal mutating func nextSInt() throws -> Int64 { - skipWhitespace() - guard hasMoreContent else { - throw JSONDecodingError.truncated + /// Return the following Bool "true" or "false", including + /// full processing of quoted boolean values. (Used in map + /// keys, for instance.) + internal mutating func nextBool() throws -> Bool { + skipWhitespace() + guard hasMoreContent else { + throw JSONDecodingError.truncated + } + let c = currentByte + switch c { + case asciiLowerF: // f + if skipOptionalKeyword(bytes: [ + asciiLowerF, asciiLowerA, asciiLowerL, asciiLowerS, asciiLowerE, + ]) { + return false + } + case asciiLowerT: // t + if skipOptionalKeyword(bytes: [ + asciiLowerT, asciiLowerR, asciiLowerU, asciiLowerE, + ]) { + return true + } + default: + break + } + throw JSONDecodingError.malformedBool } - let c = currentByte - if c == asciiDoubleQuote { - let start = index - advance() - if let s = try parseBareSInt64(source: source, - index: &index, - end: source.endIndex) { + + /// Return the following Bool "true" or "false", including + /// full processing of quoted boolean values. (Used in map + /// keys, for instance.) + internal mutating func nextQuotedBool() throws -> Bool { + skipWhitespace() guard hasMoreContent else { - throw JSONDecodingError.truncated + throw JSONDecodingError.truncated } if currentByte != asciiDoubleQuote { - throw JSONDecodingError.malformedNumber + throw JSONDecodingError.unquotedMapKey } - advance() - return s - } else { - // Couldn't parse because it had a "\" in the string, - // so parse out the quoted string and then reparse - // the result as an SInt. - index = start - let s = try nextQuotedString() - let raw = s.data(using: String.Encoding.utf8)! - let n = try raw.withUnsafeBytes { - (body: UnsafeRawBufferPointer) -> Int64? in - if body.count > 0 { - var index = body.startIndex - let end = body.endIndex - if let s = try parseBareSInt64(source: body, - index: &index, - end: end) { - if index == end { - return s - } + if let s = parseOptionalQuotedString() { + switch s { + case "false": return false + case "true": return true + default: break } - } - return nil - } - if let n = n { - return n } - } - } else if let s = try parseBareSInt64(source: source, - index: &index, - end: source.endIndex) { - return s + throw JSONDecodingError.malformedBool } - throw JSONDecodingError.malformedNumber - } - /// Parse the next Float value, regardless of whether it - /// is quoted, including handling backslash escapes for - /// quoted strings. - internal mutating func nextFloat() throws -> Float { - skipWhitespace() - guard hasMoreContent else { - throw JSONDecodingError.truncated - } - let c = currentByte - if c == asciiDoubleQuote { // " - let start = index - advance() - if let d = try parseBareDouble(source: source, - index: &index, - end: source.endIndex) { + /// Returns pointer/count spanning the UTF8 bytes of the next regular + /// key or nil if the key contains a backslash (and therefore requires + /// the full string-parsing logic to properly parse). + private mutating func nextOptionalKey() throws -> UnsafeRawBufferPointer? { + skipWhitespace() + let stringStart = index guard hasMoreContent else { - throw JSONDecodingError.truncated + throw JSONDecodingError.truncated } if currentByte != asciiDoubleQuote { - throw JSONDecodingError.malformedNumber + return nil } advance() - return Float(d) - } else { - // Slow Path: parseBareDouble returned nil: It might be - // a valid float, but had something that - // parseBareDouble cannot directly handle. So we reset, - // try a full string parse, then examine the result: - index = start - let s = try nextQuotedString() - switch s { - case "NaN": return Float.nan - case "Inf": return Float.infinity - case "-Inf": return -Float.infinity - case "Infinity": return Float.infinity - case "-Infinity": return -Float.infinity - default: - let raw = s.data(using: String.Encoding.utf8)! - let n = try raw.withUnsafeBytes { - (body: UnsafeRawBufferPointer) -> Float? in - if body.count > 0 { - var index = body.startIndex - let end = body.endIndex - if let d = try parseBareDouble(source: body, - index: &index, - end: end) { - let f = Float(d) - if index == end && f.isFinite { - return f - } - } + let nameStart = index + while hasMoreContent && currentByte != asciiDoubleQuote { + if currentByte == asciiBackslash { + index = stringStart // Reset to open quote + return nil } - return nil - } - if let n = n { - return n - } - } - } - } else { - if let d = try parseBareDouble(source: source, - index: &index, - end: source.endIndex) { - let f = Float(d) - if f.isFinite { - return f - } - } + advance() + } + guard hasMoreContent else { + throw JSONDecodingError.truncated + } + let buff = UnsafeRawBufferPointer( + start: source.baseAddress! + nameStart, + count: index - nameStart + ) + advance() + return buff } - throw JSONDecodingError.malformedNumber - } - /// Parse the next Double value, regardless of whether it - /// is quoted, including handling backslash escapes for - /// quoted strings. - internal mutating func nextDouble() throws -> Double { - skipWhitespace() - guard hasMoreContent else { - throw JSONDecodingError.truncated + /// Parse a field name, look it up in the provided field name map, + /// and return the corresponding field number. + /// + /// Throws if field name cannot be parsed. + /// If it encounters an unknown field name, it throws + /// unless `options.ignoreUnknownFields` is set, in which case + /// it silently skips it. + internal mutating func nextFieldNumber( + names: _NameMap, + messageType: any Message.Type + ) throws -> Int? { + while true { + var fieldName: String + if let key = try nextOptionalKey() { + // Fast path: We parsed it as UTF8 bytes... + try skipRequiredCharacter(asciiColon) // : + if let fieldNumber = names.number(forJSONName: key) { + return fieldNumber + } + if let s = utf8ToString(bytes: key.baseAddress!, count: key.count) { + fieldName = s + } else { + throw JSONDecodingError.invalidUTF8 + } + } else { + // Slow path: We parsed a String; lookups from String are slower. + fieldName = try nextQuotedString() + try skipRequiredCharacter(asciiColon) // : + if let fieldNumber = names.number(forJSONName: fieldName) { + return fieldNumber + } + } + if let first = fieldName.utf8.first, first == UInt8(ascii: "["), + let last = fieldName.utf8.last, last == UInt8(ascii: "]") + { + fieldName.removeFirst() + fieldName.removeLast() + if let fieldNumber = extensions.fieldNumberForProto(messageType: messageType, protoFieldName: fieldName) + { + return fieldNumber + } + } + if !options.ignoreUnknownFields { + throw JSONDecodingError.unknownField(fieldName) + } + // Unknown field, skip it and try to parse the next field name + try skipValue() + if skipOptionalObjectEnd() { + return nil + } + try skipRequiredComma() + } } - let c = currentByte - if c == asciiDoubleQuote { // " - let start = index - advance() - if let d = try parseBareDouble(source: source, - index: &index, - end: source.endIndex) { - guard hasMoreContent else { - throw JSONDecodingError.truncated + + /// Parse the next token as a string or numeric enum value. Throws + /// unrecognizedEnumValue if the string/number can't initialize the + /// enum. Will throw other errors if the JSON is malformed. + internal mutating func nextEnumValue() throws -> E? { + func throwOrIgnore() throws -> E? { + if options.ignoreUnknownFields { + return nil + } else { + throw JSONDecodingError.unrecognizedEnumValue + } } - if currentByte != asciiDoubleQuote { - throw JSONDecodingError.malformedNumber + skipWhitespace() + guard hasMoreContent else { + throw JSONDecodingError.truncated } - advance() - return d - } else { - // Slow Path: parseBareDouble returned nil: It might be - // a valid float, but had something that - // parseBareDouble cannot directly handle. So we reset, - // try a full string parse, then examine the result: - index = start - let s = try nextQuotedString() - switch s { - case "NaN": return Double.nan - case "Inf": return Double.infinity - case "-Inf": return -Double.infinity - case "Infinity": return Double.infinity - case "-Infinity": return -Double.infinity - default: - let raw = s.data(using: String.Encoding.utf8)! - let n = try raw.withUnsafeBytes { - (body: UnsafeRawBufferPointer) -> Double? in - if body.count > 0 { - var index = body.startIndex - let end = body.endIndex - if let d = try parseBareDouble(source: body, - index: &index, - end: end) { - if index == end { - return d + if currentByte == asciiDoubleQuote { + if let name = try nextOptionalKey() { + if let e = E(rawUTF8: name) { + return e + } else { + return try throwOrIgnore() } - } } - return nil - } - if let n = n { - return n - } - } - } - } else { - if let d = try parseBareDouble(source: source, - index: &index, - end: source.endIndex) { - return d - } + let name = try nextQuotedString() + if let e = E(name: name) { + return e + } else { + return try throwOrIgnore() + } + } else { + let n = try nextSInt() + if let i = Int(exactly: n) { + if let e = E(rawValue: i) { + return e + } else { + return try throwOrIgnore() + } + } else { + throw JSONDecodingError.numberRange + } + } } - throw JSONDecodingError.malformedNumber - } - /// Return the contents of the following quoted string, - /// or throw an error if the next token is not a string. - internal mutating func nextQuotedString() throws -> String { - skipWhitespace() - guard hasMoreContent else { - throw JSONDecodingError.truncated - } - let c = currentByte - if c != asciiDoubleQuote { - throw JSONDecodingError.malformedString - } - if let s = parseOptionalQuotedString() { - return s - } else { - throw JSONDecodingError.malformedString + /// Helper for skipping a single-character token. + private mutating func skipRequiredCharacter(_ required: UInt8) throws { + skipWhitespace() + guard hasMoreContent else { + throw JSONDecodingError.truncated + } + let next = currentByte + if next == required { + advance() + return + } + throw JSONDecodingError.failure } - } - /// Return the contents of the following quoted string, - /// or nil if the next token is not a string. - /// This will only throw an error if the next token starts - /// out as a string but is malformed in some way. - internal mutating func nextOptionalQuotedString() throws -> String? { - skipWhitespace() - guard hasMoreContent else { - return nil + /// Skip "{", throw if that's not the next character. + internal mutating func skipRequiredObjectStart() throws { + try skipRequiredCharacter(asciiOpenCurlyBracket) // { + try incrementRecursionDepth() } - let c = currentByte - if c != asciiDoubleQuote { - return nil - } - return try nextQuotedString() - } - /// Return a Data with the decoded contents of the - /// following base-64 string. - /// - /// Notes on Google's implementation: - /// * Google's C++ implementation accepts arbitrary whitespace - /// mixed in with the base-64 characters - /// * Google's C++ implementation ignores missing '=' characters - /// but if present, there must be the exact correct number of them. - /// * Google's C++ implementation accepts both "regular" and - /// "web-safe" base-64 variants (it seems to prefer the - /// web-safe version as defined in RFC 4648 - internal mutating func nextBytesValue() throws -> Data { - skipWhitespace() - guard hasMoreContent else { - throw JSONDecodingError.truncated + /// Skip ",", throw if that's not the next character. + internal mutating func skipRequiredComma() throws { + try skipRequiredCharacter(asciiComma) } - return try parseBytes(source: source, index: &index, end: source.endIndex) - } - /// Private function to help parse keywords. - private mutating func skipOptionalKeyword(bytes: [UInt8]) -> Bool { - let start = index - for b in bytes { - guard hasMoreContent else { - index = start - return false - } - let c = currentByte - if c != b { - index = start - return false - } - advance() - } - if hasMoreContent { - let c = currentByte - if (c >= asciiUpperA && c <= asciiUpperZ) || - (c >= asciiLowerA && c <= asciiLowerZ) { - index = start - return false - } + /// Skip ":", throw if that's not the next character. + internal mutating func skipRequiredColon() throws { + try skipRequiredCharacter(asciiColon) } - return true - } - /// If the next token is the identifier "null", consume it and return true. - internal mutating func skipOptionalNull() -> Bool { - skipWhitespace() - if hasMoreContent && currentByte == asciiLowerN { - return skipOptionalKeyword(bytes: [ - asciiLowerN, asciiLowerU, asciiLowerL, asciiLowerL - ]) + /// Skip "[", throw if that's not the next character. + internal mutating func skipRequiredArrayStart() throws { + try skipRequiredCharacter(asciiOpenSquareBracket) // [ } - return false - } - /// Return the following Bool "true" or "false", including - /// full processing of quoted boolean values. (Used in map - /// keys, for instance.) - internal mutating func nextBool() throws -> Bool { - skipWhitespace() - guard hasMoreContent else { - throw JSONDecodingError.truncated - } - let c = currentByte - switch c { - case asciiLowerF: // f - if skipOptionalKeyword(bytes: [ - asciiLowerF, asciiLowerA, asciiLowerL, asciiLowerS, asciiLowerE - ]) { + /// Helper for skipping optional single-character tokens. + private mutating func skipOptionalCharacter(_ c: UInt8) -> Bool { + skipWhitespace() + if hasMoreContent && currentByte == c { + advance() + return true + } return false - } - case asciiLowerT: // t - if skipOptionalKeyword(bytes: [ - asciiLowerT, asciiLowerR, asciiLowerU, asciiLowerE - ]) { - return true - } - default: - break } - throw JSONDecodingError.malformedBool - } - /// Return the following Bool "true" or "false", including - /// full processing of quoted boolean values. (Used in map - /// keys, for instance.) - internal mutating func nextQuotedBool() throws -> Bool { - skipWhitespace() - guard hasMoreContent else { - throw JSONDecodingError.truncated + /// If the next non-whitespace character is "[", skip it + /// and return true. Otherwise, return false. + internal mutating func skipOptionalArrayStart() -> Bool { + skipOptionalCharacter(asciiOpenSquareBracket) } - if currentByte != asciiDoubleQuote { - throw JSONDecodingError.unquotedMapKey - } - if let s = parseOptionalQuotedString() { - switch s { - case "false": return false - case "true": return true - default: break - } - } - throw JSONDecodingError.malformedBool - } - /// Returns pointer/count spanning the UTF8 bytes of the next regular - /// key or nil if the key contains a backslash (and therefore requires - /// the full string-parsing logic to properly parse). - private mutating func nextOptionalKey() throws -> UnsafeRawBufferPointer? { - skipWhitespace() - let stringStart = index - guard hasMoreContent else { - throw JSONDecodingError.truncated - } - if currentByte != asciiDoubleQuote { - return nil - } - advance() - let nameStart = index - while hasMoreContent && currentByte != asciiDoubleQuote { - if currentByte == asciiBackslash { - index = stringStart // Reset to open quote - return nil - } - advance() - } - guard hasMoreContent else { - throw JSONDecodingError.truncated + /// If the next non-whitespace character is "]", skip it + /// and return true. Otherwise, return false. + internal mutating func skipOptionalArrayEnd() -> Bool { + skipOptionalCharacter(asciiCloseSquareBracket) // ]// ] } - let buff = UnsafeRawBufferPointer( - start: source.baseAddress! + nameStart, - count: index - nameStart) - advance() - return buff - } - /// Parse a field name, look it up in the provided field name map, - /// and return the corresponding field number. - /// - /// Throws if field name cannot be parsed. - /// If it encounters an unknown field name, it throws - /// unless `options.ignoreUnknownFields` is set, in which case - /// it silently skips it. - internal mutating func nextFieldNumber( - names: _NameMap, - messageType: any Message.Type - ) throws -> Int? { - while true { - var fieldName: String - if let key = try nextOptionalKey() { - // Fast path: We parsed it as UTF8 bytes... - try skipRequiredCharacter(asciiColon) // : - if let fieldNumber = names.number(forJSONName: key) { - return fieldNumber - } - if let s = utf8ToString(bytes: key.baseAddress!, count: key.count) { - fieldName = s - } else { - throw JSONDecodingError.invalidUTF8 - } - } else { - // Slow path: We parsed a String; lookups from String are slower. - fieldName = try nextQuotedString() - try skipRequiredCharacter(asciiColon) // : - if let fieldNumber = names.number(forJSONName: fieldName) { - return fieldNumber - } - } - if let first = fieldName.utf8.first, first == UInt8(ascii: "["), - let last = fieldName.utf8.last, last == UInt8(ascii: "]") - { - fieldName.removeFirst() - fieldName.removeLast() - if let fieldNumber = extensions.fieldNumberForProto(messageType: messageType, protoFieldName: fieldName) { - return fieldNumber - } - } - if !options.ignoreUnknownFields { - throw JSONDecodingError.unknownField(fieldName) - } - // Unknown field, skip it and try to parse the next field name - try skipValue() - if skipOptionalObjectEnd() { - return nil - } - try skipRequiredComma() + /// If the next non-whitespace character is "}", skip it + /// and return true. Otherwise, return false. + internal mutating func skipOptionalObjectEnd() -> Bool { + let result = skipOptionalCharacter(asciiCloseCurlyBracket) // } + if result { + decrementRecursionDepth() + } + return result } - } - /// Parse the next token as a string or numeric enum value. Throws - /// unrecognizedEnumValue if the string/number can't initialize the - /// enum. Will throw other errors if the JSON is malformed. - internal mutating func nextEnumValue() throws -> E? { - func throwOrIgnore() throws -> E? { - if options.ignoreUnknownFields { - return nil - } else { - throw JSONDecodingError.unrecognizedEnumValue - } - } - skipWhitespace() - guard hasMoreContent else { - throw JSONDecodingError.truncated - } - if currentByte == asciiDoubleQuote { - if let name = try nextOptionalKey() { - if let e = E(rawUTF8: name) { - return e + /// Return the next complete JSON structure as a string. + /// For example, this might return "true", or "123.456", + /// or "{\"foo\": 7, \"bar\": [8, 9]}" + /// + /// Used by Any to get the upcoming JSON value as a string. + /// Note: The value might be an object or array. + internal mutating func skip() throws -> String { + skipWhitespace() + let start = index + try skipValue() + if let s = utf8ToString(bytes: source, start: start, end: index) { + return s } else { - return try throwOrIgnore() - } - } - let name = try nextQuotedString() - if let e = E(name: name) { - return e - } else { - return try throwOrIgnore() - } - } else { - let n = try nextSInt() - if let i = Int(exactly: n) { - if let e = E(rawValue: i) { - return e - } else { - return try throwOrIgnore() + throw JSONDecodingError.invalidUTF8 } - } else { - throw JSONDecodingError.numberRange - } - } - } - - /// Helper for skipping a single-character token. - private mutating func skipRequiredCharacter(_ required: UInt8) throws { - skipWhitespace() - guard hasMoreContent else { - throw JSONDecodingError.truncated - } - let next = currentByte - if next == required { - advance() - return - } - throw JSONDecodingError.failure - } - - /// Skip "{", throw if that's not the next character. - internal mutating func skipRequiredObjectStart() throws { - try skipRequiredCharacter(asciiOpenCurlyBracket) // { - try incrementRecursionDepth() - } - - /// Skip ",", throw if that's not the next character. - internal mutating func skipRequiredComma() throws { - try skipRequiredCharacter(asciiComma) - } - - /// Skip ":", throw if that's not the next character. - internal mutating func skipRequiredColon() throws { - try skipRequiredCharacter(asciiColon) - } - - /// Skip "[", throw if that's not the next character. - internal mutating func skipRequiredArrayStart() throws { - try skipRequiredCharacter(asciiOpenSquareBracket) // [ - } - - /// Helper for skipping optional single-character tokens. - private mutating func skipOptionalCharacter(_ c: UInt8) -> Bool { - skipWhitespace() - if hasMoreContent && currentByte == c { - advance() - return true - } - return false - } - - /// If the next non-whitespace character is "[", skip it - /// and return true. Otherwise, return false. - internal mutating func skipOptionalArrayStart() -> Bool { - return skipOptionalCharacter(asciiOpenSquareBracket) - } - - /// If the next non-whitespace character is "]", skip it - /// and return true. Otherwise, return false. - internal mutating func skipOptionalArrayEnd() -> Bool { - return skipOptionalCharacter(asciiCloseSquareBracket) // ] - } - - /// If the next non-whitespace character is "}", skip it - /// and return true. Otherwise, return false. - internal mutating func skipOptionalObjectEnd() -> Bool { - let result = skipOptionalCharacter(asciiCloseCurlyBracket) // } - if result { - decrementRecursionDepth() - } - return result - } - - /// Return the next complete JSON structure as a string. - /// For example, this might return "true", or "123.456", - /// or "{\"foo\": 7, \"bar\": [8, 9]}" - /// - /// Used by Any to get the upcoming JSON value as a string. - /// Note: The value might be an object or array. - internal mutating func skip() throws -> String { - skipWhitespace() - let start = index - try skipValue() - if let s = utf8ToString(bytes: source, start: start, end: index) { - return s - } else { - throw JSONDecodingError.invalidUTF8 } - } - /// Advance index past the next value. This is used - /// by skip() and by unknown field handling. - /// Note: This handles objects {...} recursively but arrays [...] non-recursively - /// This avoids us requiring excessive stack space for deeply nested - /// arrays (which are not included in the recursion budget check). - private mutating func skipValue() throws { - skipWhitespace() - var totalArrayDepth = 0 - while true { - var arrayDepth = 0 - while skipOptionalArrayStart() { - arrayDepth += 1 - } - guard hasMoreContent else { - throw JSONDecodingError.truncated - } - switch currentByte { - case asciiDoubleQuote: // " begins a string - try skipString() - case asciiOpenCurlyBracket: // { begins an object - try skipObject() - case asciiCloseSquareBracket: // ] ends an empty array - if arrayDepth == 0 { - throw JSONDecodingError.failure - } - // We also close out [[]] or [[[]]] here - while arrayDepth > 0 && skipOptionalArrayEnd() { - arrayDepth -= 1 + /// Advance index past the next value. This is used + /// by skip() and by unknown field handling. + /// Note: This handles objects {...} recursively but arrays [...] non-recursively + /// This avoids us requiring excessive stack space for deeply nested + /// arrays (which are not included in the recursion budget check). + private mutating func skipValue() throws { + skipWhitespace() + var totalArrayDepth = 0 + while true { + var arrayDepth = 0 + while skipOptionalArrayStart() { + arrayDepth += 1 } - case asciiLowerN: // n must be null - if !skipOptionalKeyword(bytes: [ - asciiLowerN, asciiLowerU, asciiLowerL, asciiLowerL - ]) { + guard hasMoreContent else { throw JSONDecodingError.truncated } - case asciiLowerF: // f must be false - if !skipOptionalKeyword(bytes: [ - asciiLowerF, asciiLowerA, asciiLowerL, asciiLowerS, asciiLowerE - ]) { - throw JSONDecodingError.truncated + switch currentByte { + case asciiDoubleQuote: // " begins a string + try skipString() + case asciiOpenCurlyBracket: // { begins an object + try skipObject() + case asciiCloseSquareBracket: // ] ends an empty array + if arrayDepth == 0 { + throw JSONDecodingError.failure + } + // We also close out [[]] or [[[]]] here + while arrayDepth > 0 && skipOptionalArrayEnd() { + arrayDepth -= 1 + } + case asciiLowerN: // n must be null + if !skipOptionalKeyword(bytes: [ + asciiLowerN, asciiLowerU, asciiLowerL, asciiLowerL, + ]) { + throw JSONDecodingError.truncated + } + case asciiLowerF: // f must be false + if !skipOptionalKeyword(bytes: [ + asciiLowerF, asciiLowerA, asciiLowerL, asciiLowerS, asciiLowerE, + ]) { + throw JSONDecodingError.truncated + } + case asciiLowerT: // t must be true + if !skipOptionalKeyword(bytes: [ + asciiLowerT, asciiLowerR, asciiLowerU, asciiLowerE, + ]) { + throw JSONDecodingError.truncated + } + default: // everything else is a number token + _ = try nextDouble() } - case asciiLowerT: // t must be true - if !skipOptionalKeyword(bytes: [ - asciiLowerT, asciiLowerR, asciiLowerU, asciiLowerE - ]) { - throw JSONDecodingError.truncated + totalArrayDepth += arrayDepth + while totalArrayDepth > 0 && skipOptionalArrayEnd() { + totalArrayDepth -= 1 + } + if totalArrayDepth > 0 { + try skipRequiredComma() + } else { + return } - default: // everything else is a number token - _ = try nextDouble() } - totalArrayDepth += arrayDepth - while totalArrayDepth > 0 && skipOptionalArrayEnd() { - totalArrayDepth -= 1 + } + + /// Advance the index past the next complete {...} construct. + private mutating func skipObject() throws { + try skipRequiredObjectStart() + if skipOptionalObjectEnd() { + return } - if totalArrayDepth > 0 { + while true { + skipWhitespace() + try skipString() + try skipRequiredColon() + try skipValue() + if skipOptionalObjectEnd() { + return + } try skipRequiredComma() - } else { - return } } - } - - /// Advance the index past the next complete {...} construct. - private mutating func skipObject() throws { - try skipRequiredObjectStart() - if skipOptionalObjectEnd() { - return - } - while true { - skipWhitespace() - try skipString() - try skipRequiredColon() - try skipValue() - if skipOptionalObjectEnd() { - return - } - try skipRequiredComma() - } - } - /// Advance the index past the next complete quoted string. - /// - // Caveat: This does not fully validate; it will accept - // strings that have malformed \ escapes. - // - // It would be nice to do better, but I don't think it's critical, - // since there are many reasons that strings (and other tokens for - // that matter) may be skippable but not parsable. For example: - // Old clients that don't know new field types will skip fields - // they don't know; newer clients may reject the same input due to - // schema mismatches or other issues. - private mutating func skipString() throws { - guard hasMoreContent else { - throw JSONDecodingError.truncated - } - if currentByte != asciiDoubleQuote { - throw JSONDecodingError.malformedString - } - advance() - while hasMoreContent { - let c = currentByte - switch c { - case asciiDoubleQuote: - advance() - return - case asciiBackslash: - advance() + /// Advance the index past the next complete quoted string. + /// + // Caveat: This does not fully validate; it will accept + // strings that have malformed \ escapes. + // + // It would be nice to do better, but I don't think it's critical, + // since there are many reasons that strings (and other tokens for + // that matter) may be skippable but not parsable. For example: + // Old clients that don't know new field types will skip fields + // they don't know; newer clients may reject the same input due to + // schema mismatches or other issues. + private mutating func skipString() throws { guard hasMoreContent else { - throw JSONDecodingError.truncated + throw JSONDecodingError.truncated + } + if currentByte != asciiDoubleQuote { + throw JSONDecodingError.malformedString } advance() - default: - advance() - } + while hasMoreContent { + let c = currentByte + switch c { + case asciiDoubleQuote: + advance() + return + case asciiBackslash: + advance() + guard hasMoreContent else { + throw JSONDecodingError.truncated + } + advance() + default: + advance() + } + } + throw JSONDecodingError.truncated } - throw JSONDecodingError.truncated - } } diff --git a/Sources/SwiftProtobuf/MathUtils.swift b/Sources/SwiftProtobuf/MathUtils.swift index 2e8550b9c..cb8da925b 100644 --- a/Sources/SwiftProtobuf/MathUtils.swift +++ b/Sources/SwiftProtobuf/MathUtils.swift @@ -21,7 +21,7 @@ import Foundation /// - a: The dividend. Can be positive, 0 or negative. /// - b: The divisor. This must be positive, and is an error if 0 or negative. /// - Returns: The unique value r such that 0 <= r < b and b * q + r = a for some q. -internal func mod(_ a: T, _ b: T) -> T { +internal func mod(_ a: T, _ b: T) -> T { assert(b > 0) let r = a % b return r >= 0 ? r : r + b @@ -34,7 +34,7 @@ internal func mod(_ a: T, _ b: T) -> T { /// - a: The dividend. Can be positive, 0 or negative. /// - b: The divisor. This must be positive, and is an error if 0 or negative. /// - Returns: The unique value q such that for some 0 <= r < b, b * q + r = a. -internal func div(_ a: T, _ b: T) -> T { +internal func div(_ a: T, _ b: T) -> T { assert(b > 0) return a >= 0 ? a / b : (a + 1) / b - 1 } diff --git a/Sources/SwiftProtobuf/Message+AnyAdditions.swift b/Sources/SwiftProtobuf/Message+AnyAdditions.swift index 3db9bbe5f..be0946620 100644 --- a/Sources/SwiftProtobuf/Message+AnyAdditions.swift +++ b/Sources/SwiftProtobuf/Message+AnyAdditions.swift @@ -13,33 +13,33 @@ // ----------------------------------------------------------------------------- extension Message { - /// Initialize this message from the provided `google.protobuf.Any` - /// well-known type. - /// - /// This corresponds to the `unpack` method in the Google C++ API. - /// - /// If the Any object was decoded from Protobuf Binary or JSON - /// format, then the enclosed field data was stored and is not - /// fully decoded until you unpack the Any object into a message. - /// As such, this method will typically need to perform a full - /// deserialization of the enclosed data and can fail for any - /// reason that deserialization can fail. - /// - /// See `Google_Protobuf_Any.unpackTo()` for more discussion. - /// - /// - Parameter unpackingAny: the message to decode. - /// - Parameter extensions: An `ExtensionMap` used to look up and decode any - /// extensions in this message or messages nested within this message's - /// fields. - /// - Parameter options: The BinaryDecodingOptions to use. - /// - Throws: an instance of ``AnyUnpackError``, ``JSONDecodingError``, or - /// ``BinaryDecodingError`` on failure. - public init( - unpackingAny: Google_Protobuf_Any, - extensions: (any ExtensionMap)? = nil, - options: BinaryDecodingOptions = BinaryDecodingOptions() - ) throws { - self.init() - try unpackingAny._storage.unpackTo(target: &self, extensions: extensions, options: options) - } + /// Initialize this message from the provided `google.protobuf.Any` + /// well-known type. + /// + /// This corresponds to the `unpack` method in the Google C++ API. + /// + /// If the Any object was decoded from Protobuf Binary or JSON + /// format, then the enclosed field data was stored and is not + /// fully decoded until you unpack the Any object into a message. + /// As such, this method will typically need to perform a full + /// deserialization of the enclosed data and can fail for any + /// reason that deserialization can fail. + /// + /// See `Google_Protobuf_Any.unpackTo()` for more discussion. + /// + /// - Parameter unpackingAny: the message to decode. + /// - Parameter extensions: An `ExtensionMap` used to look up and decode any + /// extensions in this message or messages nested within this message's + /// fields. + /// - Parameter options: The BinaryDecodingOptions to use. + /// - Throws: an instance of ``AnyUnpackError``, ``JSONDecodingError``, or + /// ``BinaryDecodingError`` on failure. + public init( + unpackingAny: Google_Protobuf_Any, + extensions: (any ExtensionMap)? = nil, + options: BinaryDecodingOptions = BinaryDecodingOptions() + ) throws { + self.init() + try unpackingAny._storage.unpackTo(target: &self, extensions: extensions, options: options) + } } diff --git a/Sources/SwiftProtobuf/Message+BinaryAdditions.swift b/Sources/SwiftProtobuf/Message+BinaryAdditions.swift index 1f8c88de6..ee35f76da 100644 --- a/Sources/SwiftProtobuf/Message+BinaryAdditions.swift +++ b/Sources/SwiftProtobuf/Message+BinaryAdditions.swift @@ -16,141 +16,143 @@ import Foundation /// Binary encoding and decoding methods for messages. extension Message { - /// Returns a ``SwiftProtobufContiguousBytes`` instance containing the Protocol Buffer binary - /// format serialization of the message. - /// - /// - Parameters: - /// - partial: If `false` (the default), this method will check - /// `Message.isInitialized` before encoding to verify that all required - /// fields are present. If any are missing, this method throws. - /// ``BinaryEncodingError/missingRequiredFields``. - /// - options: The ``BinaryEncodingOptions`` to use. - /// - Returns: A ``SwiftProtobufContiguousBytes`` instance containing the binary serialization - /// of the message. - /// - /// - Throws: ``SwiftProtobufError`` or ``BinaryEncodingError`` if encoding fails. - public func serializedBytes( - partial: Bool = false, - options: BinaryEncodingOptions = BinaryEncodingOptions() - ) throws -> Bytes { - if !partial && !isInitialized { - throw BinaryEncodingError.missingRequiredFields - } + /// Returns a ``SwiftProtobufContiguousBytes`` instance containing the Protocol Buffer binary + /// format serialization of the message. + /// + /// - Parameters: + /// - partial: If `false` (the default), this method will check + /// `Message.isInitialized` before encoding to verify that all required + /// fields are present. If any are missing, this method throws. + /// ``BinaryEncodingError/missingRequiredFields``. + /// - options: The ``BinaryEncodingOptions`` to use. + /// - Returns: A ``SwiftProtobufContiguousBytes`` instance containing the binary serialization + /// of the message. + /// + /// - Throws: ``SwiftProtobufError`` or ``BinaryEncodingError`` if encoding fails. + public func serializedBytes( + partial: Bool = false, + options: BinaryEncodingOptions = BinaryEncodingOptions() + ) throws -> Bytes { + if !partial && !isInitialized { + throw BinaryEncodingError.missingRequiredFields + } - // Note that this assumes `options` will not change the required size. - let requiredSize = try serializedDataSize() + // Note that this assumes `options` will not change the required size. + let requiredSize = try serializedDataSize() - // Messages have a 2GB limit in encoded size, the upstread C++ code - // (message_lite, etc.) does this enforcement also. - // https://protobuf.dev/programming-guides/encoding/#cheat-sheet - // - // Testing here enables the limit without adding extra conditionals to all - // the places that encode message fields (or strings/bytes fields), keeping - // the overhead of the check to a minimum. - guard requiredSize < 0x7fffffff else { - // Adding a new error is a breaking change. - throw BinaryEncodingError.missingRequiredFields - } + // Messages have a 2GB limit in encoded size, the upstread C++ code + // (message_lite, etc.) does this enforcement also. + // https://protobuf.dev/programming-guides/encoding/#cheat-sheet + // + // Testing here enables the limit without adding extra conditionals to all + // the places that encode message fields (or strings/bytes fields), keeping + // the overhead of the check to a minimum. + guard requiredSize < 0x7fff_ffff else { + // Adding a new error is a breaking change. + throw BinaryEncodingError.missingRequiredFields + } - var data = Bytes(repeating: 0, count: requiredSize) - try data.withUnsafeMutableBytes { (body: UnsafeMutableRawBufferPointer) in - var visitor = BinaryEncodingVisitor(forWritingInto: body, options: options) - try traverse(visitor: &visitor) - // Currently not exposing this from the api because it really would be - // an internal error in the library and should never happen. - assert(visitor.encoder.remainder.count == 0) + var data = Bytes(repeating: 0, count: requiredSize) + try data.withUnsafeMutableBytes { (body: UnsafeMutableRawBufferPointer) in + var visitor = BinaryEncodingVisitor(forWritingInto: body, options: options) + try traverse(visitor: &visitor) + // Currently not exposing this from the api because it really would be + // an internal error in the library and should never happen. + assert(visitor.encoder.remainder.count == 0) + } + return data } - return data - } - - /// Returns the size in bytes required to encode the message in binary format. - /// This is used by `serializedData()` to precalculate the size of the buffer - /// so that encoding can proceed without bounds checks or reallocation. - internal func serializedDataSize() throws -> Int { - // Note: since this api is internal, it doesn't currently worry about - // needing a partial argument to handle required fields. If this become - // public, it will need that added. - var visitor = BinaryEncodingSizeVisitor() - try traverse(visitor: &visitor) - return visitor.serializedSize - } - /// Creates a new message by decoding the given `SwiftProtobufContiguousBytes` value - /// containing a serialized message in Protocol Buffer binary format. - /// - /// - Parameters: - /// - serializedBytes: The binary-encoded message data to decode. - /// - extensions: An ``ExtensionMap`` used to look up and decode any - /// extensions in this message or messages nested within this message's - /// fields. - /// - partial: If `false` (the default), this method will check - /// ``Message/isInitialized-6abgi`` after decoding to verify that all required - /// fields are present. If any are missing, this method throws - /// ``BinaryDecodingError/missingRequiredFields``. - /// - options: The ``BinaryDecodingOptions`` to use. - /// - Throws: ``BinaryDecodingError`` if decoding fails. - @inlinable - public init( - serializedBytes bytes: Bytes, - extensions: (any ExtensionMap)? = nil, - partial: Bool = false, - options: BinaryDecodingOptions = BinaryDecodingOptions() - ) throws { - self.init() - try merge(serializedBytes: bytes, extensions: extensions, partial: partial, options: options) - } + /// Returns the size in bytes required to encode the message in binary format. + /// This is used by `serializedData()` to precalculate the size of the buffer + /// so that encoding can proceed without bounds checks or reallocation. + internal func serializedDataSize() throws -> Int { + // Note: since this api is internal, it doesn't currently worry about + // needing a partial argument to handle required fields. If this become + // public, it will need that added. + var visitor = BinaryEncodingSizeVisitor() + try traverse(visitor: &visitor) + return visitor.serializedSize + } - /// Updates the message by decoding the given `SwiftProtobufContiguousBytes` value - /// containing a serialized message in Protocol Buffer binary format into the - /// receiver. - /// - /// - Note: If this method throws an error, the message may still have been - /// partially mutated by the binary data that was decoded before the error - /// occurred. - /// - /// - Parameters: - /// - serializedBytes: The binary-encoded message data to decode. - /// - extensions: An ``ExtensionMap`` used to look up and decode any - /// extensions in this message or messages nested within this message's - /// fields. - /// - partial: If `false` (the default), this method will check - /// ``Message/isInitialized-6abgi`` after decoding to verify that all required - /// fields are present. If any are missing, this method throws - /// ``BinaryDecodingError/missingRequiredFields``. - /// - options: The ``BinaryDecodingOptions`` to use. - /// - Throws: ``BinaryDecodingError`` if decoding fails. - @inlinable - public mutating func merge( - serializedBytes bytes: Bytes, - extensions: (any ExtensionMap)? = nil, - partial: Bool = false, - options: BinaryDecodingOptions = BinaryDecodingOptions() - ) throws { - try bytes.withUnsafeBytes { (body: UnsafeRawBufferPointer) in - try _merge(rawBuffer: body, extensions: extensions, partial: partial, options: options) + /// Creates a new message by decoding the given `SwiftProtobufContiguousBytes` value + /// containing a serialized message in Protocol Buffer binary format. + /// + /// - Parameters: + /// - serializedBytes: The binary-encoded message data to decode. + /// - extensions: An ``ExtensionMap`` used to look up and decode any + /// extensions in this message or messages nested within this message's + /// fields. + /// - partial: If `false` (the default), this method will check + /// ``Message/isInitialized-6abgi`` after decoding to verify that all required + /// fields are present. If any are missing, this method throws + /// ``BinaryDecodingError/missingRequiredFields``. + /// - options: The ``BinaryDecodingOptions`` to use. + /// - Throws: ``BinaryDecodingError`` if decoding fails. + @inlinable + public init( + serializedBytes bytes: Bytes, + extensions: (any ExtensionMap)? = nil, + partial: Bool = false, + options: BinaryDecodingOptions = BinaryDecodingOptions() + ) throws { + self.init() + try merge(serializedBytes: bytes, extensions: extensions, partial: partial, options: options) } - } - // Helper for `merge()`s to keep the Decoder internal to SwiftProtobuf while - // allowing the generic over `SwiftProtobufContiguousBytes` to get better codegen from the - // compiler by being `@inlinable`. For some discussion on this see - // https://github.com/apple/swift-protobuf/pull/914#issuecomment-555458153 - @usableFromInline - internal mutating func _merge( - rawBuffer body: UnsafeRawBufferPointer, - extensions: (any ExtensionMap)?, - partial: Bool, - options: BinaryDecodingOptions - ) throws { - if let baseAddress = body.baseAddress, body.count > 0 { - var decoder = BinaryDecoder(forReadingFrom: baseAddress, - count: body.count, - options: options, - extensions: extensions) - try decoder.decodeFullMessage(message: &self) + /// Updates the message by decoding the given `SwiftProtobufContiguousBytes` value + /// containing a serialized message in Protocol Buffer binary format into the + /// receiver. + /// + /// - Note: If this method throws an error, the message may still have been + /// partially mutated by the binary data that was decoded before the error + /// occurred. + /// + /// - Parameters: + /// - serializedBytes: The binary-encoded message data to decode. + /// - extensions: An ``ExtensionMap`` used to look up and decode any + /// extensions in this message or messages nested within this message's + /// fields. + /// - partial: If `false` (the default), this method will check + /// ``Message/isInitialized-6abgi`` after decoding to verify that all required + /// fields are present. If any are missing, this method throws + /// ``BinaryDecodingError/missingRequiredFields``. + /// - options: The ``BinaryDecodingOptions`` to use. + /// - Throws: ``BinaryDecodingError`` if decoding fails. + @inlinable + public mutating func merge( + serializedBytes bytes: Bytes, + extensions: (any ExtensionMap)? = nil, + partial: Bool = false, + options: BinaryDecodingOptions = BinaryDecodingOptions() + ) throws { + try bytes.withUnsafeBytes { (body: UnsafeRawBufferPointer) in + try _merge(rawBuffer: body, extensions: extensions, partial: partial, options: options) + } } - if !partial && !isInitialized { - throw BinaryDecodingError.missingRequiredFields + + // Helper for `merge()`s to keep the Decoder internal to SwiftProtobuf while + // allowing the generic over `SwiftProtobufContiguousBytes` to get better codegen from the + // compiler by being `@inlinable`. For some discussion on this see + // https://github.com/apple/swift-protobuf/pull/914#issuecomment-555458153 + @usableFromInline + internal mutating func _merge( + rawBuffer body: UnsafeRawBufferPointer, + extensions: (any ExtensionMap)?, + partial: Bool, + options: BinaryDecodingOptions + ) throws { + if let baseAddress = body.baseAddress, body.count > 0 { + var decoder = BinaryDecoder( + forReadingFrom: baseAddress, + count: body.count, + options: options, + extensions: extensions + ) + try decoder.decodeFullMessage(message: &self) + } + if !partial && !isInitialized { + throw BinaryDecodingError.missingRequiredFields + } } - } } diff --git a/Sources/SwiftProtobuf/Message+BinaryAdditions_Data.swift b/Sources/SwiftProtobuf/Message+BinaryAdditions_Data.swift index 149b1260f..0af9eaa2b 100644 --- a/Sources/SwiftProtobuf/Message+BinaryAdditions_Data.swift +++ b/Sources/SwiftProtobuf/Message+BinaryAdditions_Data.swift @@ -16,210 +16,220 @@ import Foundation /// Binary encoding and decoding methods for messages. extension Message { - /// Creates a new message by decoding the given `Data` value - /// containing a serialized message in Protocol Buffer binary format. - /// - /// - Parameters: - /// - serializedData: The binary-encoded message `Data` to decode. - /// - extensions: An ``ExtensionMap`` used to look up and decode any - /// extensions in this message or messages nested within this message's - /// fields. - /// - partial: If `false` (the default), this method will check - /// ``Message/isInitialized-6abgi`` after decoding to verify that all required - /// fields are present. If any are missing, this method throws - /// ``BinaryDecodingError/missingRequiredFields``. - /// - options: The ``BinaryDecodingOptions`` to use. - /// - Throws: ``BinaryDecodingError`` if decoding fails. - @inlinable - @available(*, deprecated, renamed: "init(serializedBytes:extensions:partial:options:)") - public init( - serializedData data: Data, - extensions: (any ExtensionMap)? = nil, - partial: Bool = false, - options: BinaryDecodingOptions = BinaryDecodingOptions() - ) throws { - self.init() - try merge(serializedBytes: data, extensions: extensions, partial: partial, options: options) - } + /// Creates a new message by decoding the given `Data` value + /// containing a serialized message in Protocol Buffer binary format. + /// + /// - Parameters: + /// - serializedData: The binary-encoded message `Data` to decode. + /// - extensions: An ``ExtensionMap`` used to look up and decode any + /// extensions in this message or messages nested within this message's + /// fields. + /// - partial: If `false` (the default), this method will check + /// ``Message/isInitialized-6abgi`` after decoding to verify that all required + /// fields are present. If any are missing, this method throws + /// ``BinaryDecodingError/missingRequiredFields``. + /// - options: The ``BinaryDecodingOptions`` to use. + /// - Throws: ``BinaryDecodingError`` if decoding fails. + @inlinable + @available(*, deprecated, renamed: "init(serializedBytes:extensions:partial:options:)") + public init( + serializedData data: Data, + extensions: (any ExtensionMap)? = nil, + partial: Bool = false, + options: BinaryDecodingOptions = BinaryDecodingOptions() + ) throws { + self.init() + try merge(serializedBytes: data, extensions: extensions, partial: partial, options: options) + } - /// Creates a new message by decoding the given `Foundation/ContiguousBytes` value - /// containing a serialized message in Protocol Buffer binary format. - /// - /// - Parameters: - /// - contiguousBytes: The binary-encoded message data to decode. - /// - extensions: An ``ExtensionMap`` used to look up and decode any - /// extensions in this message or messages nested within this message's - /// fields. - /// - partial: If `false` (the default), this method will check - /// ``Message/isInitialized-6abgi`` after decoding to verify that all required - /// fields are present. If any are missing, this method throws - /// ``SwiftProtobufError/BinaryDecoding/missingRequiredFields``. - /// - options: The ``BinaryDecodingOptions`` to use. - /// - Throws: ``SwiftProtobufError`` if decoding fails. - @inlinable - @_disfavoredOverload - @available(*, deprecated, renamed: "init(serializedBytes:extensions:partial:options:)") - public init( - contiguousBytes bytes: Bytes, - extensions: (any ExtensionMap)? = nil, - partial: Bool = false, - options: BinaryDecodingOptions = BinaryDecodingOptions() - ) throws { - self.init() - try merge(serializedBytes: bytes, extensions: extensions, partial: partial, options: options) - } + /// Creates a new message by decoding the given `Foundation/ContiguousBytes` value + /// containing a serialized message in Protocol Buffer binary format. + /// + /// - Parameters: + /// - contiguousBytes: The binary-encoded message data to decode. + /// - extensions: An ``ExtensionMap`` used to look up and decode any + /// extensions in this message or messages nested within this message's + /// fields. + /// - partial: If `false` (the default), this method will check + /// ``Message/isInitialized-6abgi`` after decoding to verify that all required + /// fields are present. If any are missing, this method throws + /// ``SwiftProtobufError/BinaryDecoding/missingRequiredFields``. + /// - options: The ``BinaryDecodingOptions`` to use. + /// - Throws: ``SwiftProtobufError`` if decoding fails. + @inlinable + @_disfavoredOverload + @available(*, deprecated, renamed: "init(serializedBytes:extensions:partial:options:)") + public init( + contiguousBytes bytes: Bytes, + extensions: (any ExtensionMap)? = nil, + partial: Bool = false, + options: BinaryDecodingOptions = BinaryDecodingOptions() + ) throws { + self.init() + try merge(serializedBytes: bytes, extensions: extensions, partial: partial, options: options) + } - /// Creates a new message by decoding the given `Foundation/ContiguousBytes` value - /// containing a serialized message in Protocol Buffer binary format. - /// - /// - Parameters: - /// - serializedBytes: The binary-encoded message data to decode. - /// - extensions: An ``ExtensionMap`` used to look up and decode any - /// extensions in this message or messages nested within this message's - /// fields. - /// - partial: If `false` (the default), this method will check - /// ``Message/isInitialized-6abgi`` after decoding to verify that all required - /// fields are present. If any are missing, this method throws - /// ``SwiftProtobufError/BinaryDecoding/missingRequiredFields``. - /// - options: The ``BinaryDecodingOptions`` to use. - /// - Throws: ``SwiftProtobufError`` if decoding fails. - @inlinable - @_disfavoredOverload - @available(*, deprecated, message: "Please conform your Bytes type to `SwiftProtobufContiguousBytes` instead of `Foundation.ContiguousBytes`.") - public init( - serializedBytes bytes: Bytes, - extensions: (any ExtensionMap)? = nil, - partial: Bool = false, - options: BinaryDecodingOptions = BinaryDecodingOptions() - ) throws { - self.init() - try merge(serializedBytes: bytes, extensions: extensions, partial: partial, options: options) - } + /// Creates a new message by decoding the given `Foundation/ContiguousBytes` value + /// containing a serialized message in Protocol Buffer binary format. + /// + /// - Parameters: + /// - serializedBytes: The binary-encoded message data to decode. + /// - extensions: An ``ExtensionMap`` used to look up and decode any + /// extensions in this message or messages nested within this message's + /// fields. + /// - partial: If `false` (the default), this method will check + /// ``Message/isInitialized-6abgi`` after decoding to verify that all required + /// fields are present. If any are missing, this method throws + /// ``SwiftProtobufError/BinaryDecoding/missingRequiredFields``. + /// - options: The ``BinaryDecodingOptions`` to use. + /// - Throws: ``SwiftProtobufError`` if decoding fails. + @inlinable + @_disfavoredOverload + @available( + *, + deprecated, + message: + "Please conform your Bytes type to `SwiftProtobufContiguousBytes` instead of `Foundation.ContiguousBytes`." + ) + public init( + serializedBytes bytes: Bytes, + extensions: (any ExtensionMap)? = nil, + partial: Bool = false, + options: BinaryDecodingOptions = BinaryDecodingOptions() + ) throws { + self.init() + try merge(serializedBytes: bytes, extensions: extensions, partial: partial, options: options) + } - /// Updates the message by decoding the given `Foundation/ContiguousBytes` value - /// containing a serialized message in Protocol Buffer binary format into the - /// receiver. - /// - /// - Note: If this method throws an error, the message may still have been - /// partially mutated by the binary data that was decoded before the error - /// occurred. - /// - /// - Parameters: - /// - contiguousBytes: The binary-encoded message data to decode. - /// - extensions: An ``ExtensionMap`` used to look up and decode any - /// extensions in this message or messages nested within this message's - /// fields. - /// - partial: If `false` (the default), this method will check - /// ``Message/isInitialized-6abgi`` after decoding to verify that all required - /// fields are present. If any are missing, this method throws - /// ``SwiftProtobufError/BinaryDecoding/missingRequiredFields``. - /// - options: The ``BinaryDecodingOptions`` to use. - /// - Throws: ``SwiftProtobufError`` if decoding fails. - @inlinable - @_disfavoredOverload - @available(*, deprecated, renamed: "merge(serializedBytes:extensions:partial:options:)") - public mutating func merge( - contiguousBytes bytes: Bytes, - extensions: (any ExtensionMap)? = nil, - partial: Bool = false, - options: BinaryDecodingOptions = BinaryDecodingOptions() - ) throws { - try bytes.withUnsafeBytes { (body: UnsafeRawBufferPointer) in - try _merge(rawBuffer: body, extensions: extensions, partial: partial, options: options) + /// Updates the message by decoding the given `Foundation/ContiguousBytes` value + /// containing a serialized message in Protocol Buffer binary format into the + /// receiver. + /// + /// - Note: If this method throws an error, the message may still have been + /// partially mutated by the binary data that was decoded before the error + /// occurred. + /// + /// - Parameters: + /// - contiguousBytes: The binary-encoded message data to decode. + /// - extensions: An ``ExtensionMap`` used to look up and decode any + /// extensions in this message or messages nested within this message's + /// fields. + /// - partial: If `false` (the default), this method will check + /// ``Message/isInitialized-6abgi`` after decoding to verify that all required + /// fields are present. If any are missing, this method throws + /// ``SwiftProtobufError/BinaryDecoding/missingRequiredFields``. + /// - options: The ``BinaryDecodingOptions`` to use. + /// - Throws: ``SwiftProtobufError`` if decoding fails. + @inlinable + @_disfavoredOverload + @available(*, deprecated, renamed: "merge(serializedBytes:extensions:partial:options:)") + public mutating func merge( + contiguousBytes bytes: Bytes, + extensions: (any ExtensionMap)? = nil, + partial: Bool = false, + options: BinaryDecodingOptions = BinaryDecodingOptions() + ) throws { + try bytes.withUnsafeBytes { (body: UnsafeRawBufferPointer) in + try _merge(rawBuffer: body, extensions: extensions, partial: partial, options: options) + } } - } - /// Updates the message by decoding the given `Foundation/ContiguousBytes` value - /// containing a serialized message in Protocol Buffer binary format into the - /// receiver. - /// - /// - Note: If this method throws an error, the message may still have been - /// partially mutated by the binary data that was decoded before the error - /// occurred. - /// - /// - Parameters: - /// - serializedBytes: The binary-encoded message data to decode. - /// - extensions: An ``ExtensionMap`` used to look up and decode any - /// extensions in this message or messages nested within this message's - /// fields. - /// - partial: If `false` (the default), this method will check - /// ``Message/isInitialized-6abgi`` after decoding to verify that all required - /// fields are present. If any are missing, this method throws - /// ``SwiftProtobufError/BinaryDecoding/missingRequiredFields``. - /// - options: The ``BinaryDecodingOptions`` to use. - /// - Throws: ``SwiftProtobufError`` if decoding fails. - @inlinable - @_disfavoredOverload - @available(*, deprecated, message: "Please conform your Bytes type to `SwiftProtobufContiguousBytes` instead of `Foundation.ContiguousBytes`.") - public mutating func merge( - serializedBytes bytes: Bytes, - extensions: (any ExtensionMap)? = nil, - partial: Bool = false, - options: BinaryDecodingOptions = BinaryDecodingOptions() - ) throws { - try bytes.withUnsafeBytes { (body: UnsafeRawBufferPointer) in - try _merge(rawBuffer: body, extensions: extensions, partial: partial, options: options) + /// Updates the message by decoding the given `Foundation/ContiguousBytes` value + /// containing a serialized message in Protocol Buffer binary format into the + /// receiver. + /// + /// - Note: If this method throws an error, the message may still have been + /// partially mutated by the binary data that was decoded before the error + /// occurred. + /// + /// - Parameters: + /// - serializedBytes: The binary-encoded message data to decode. + /// - extensions: An ``ExtensionMap`` used to look up and decode any + /// extensions in this message or messages nested within this message's + /// fields. + /// - partial: If `false` (the default), this method will check + /// ``Message/isInitialized-6abgi`` after decoding to verify that all required + /// fields are present. If any are missing, this method throws + /// ``SwiftProtobufError/BinaryDecoding/missingRequiredFields``. + /// - options: The ``BinaryDecodingOptions`` to use. + /// - Throws: ``SwiftProtobufError`` if decoding fails. + @inlinable + @_disfavoredOverload + @available( + *, + deprecated, + message: + "Please conform your Bytes type to `SwiftProtobufContiguousBytes` instead of `Foundation.ContiguousBytes`." + ) + public mutating func merge( + serializedBytes bytes: Bytes, + extensions: (any ExtensionMap)? = nil, + partial: Bool = false, + options: BinaryDecodingOptions = BinaryDecodingOptions() + ) throws { + try bytes.withUnsafeBytes { (body: UnsafeRawBufferPointer) in + try _merge(rawBuffer: body, extensions: extensions, partial: partial, options: options) + } } - } - /// Updates the message by decoding the given `Data` value - /// containing a serialized message in Protocol Buffer binary format into the - /// receiver. - /// - /// - Note: If this method throws an error, the message may still have been - /// partially mutated by the binary data that was decoded before the error - /// occurred. - /// - /// - Parameters: - /// - serializedData: The binary-encoded message data to decode. - /// - extensions: An ``ExtensionMap`` used to look up and decode any - /// extensions in this message or messages nested within this message's - /// fields. - /// - partial: If `false` (the default), this method will check - /// ``Message/isInitialized-6abgi`` after decoding to verify that all required - /// fields are present. If any are missing, this method throws - /// ``BinaryDecodingError/missingRequiredFields``. - /// - options: The ``BinaryDecodingOptions`` to use. - /// - Throws: ``BinaryDecodingError`` if decoding fails. - @inlinable - public mutating func merge( - serializedData data: Data, - extensions: (any ExtensionMap)? = nil, - partial: Bool = false, - options: BinaryDecodingOptions = BinaryDecodingOptions() - ) throws { - try merge(serializedBytes: data, extensions: extensions, partial: partial, options: options) - } + /// Updates the message by decoding the given `Data` value + /// containing a serialized message in Protocol Buffer binary format into the + /// receiver. + /// + /// - Note: If this method throws an error, the message may still have been + /// partially mutated by the binary data that was decoded before the error + /// occurred. + /// + /// - Parameters: + /// - serializedData: The binary-encoded message data to decode. + /// - extensions: An ``ExtensionMap`` used to look up and decode any + /// extensions in this message or messages nested within this message's + /// fields. + /// - partial: If `false` (the default), this method will check + /// ``Message/isInitialized-6abgi`` after decoding to verify that all required + /// fields are present. If any are missing, this method throws + /// ``BinaryDecodingError/missingRequiredFields``. + /// - options: The ``BinaryDecodingOptions`` to use. + /// - Throws: ``BinaryDecodingError`` if decoding fails. + @inlinable + public mutating func merge( + serializedData data: Data, + extensions: (any ExtensionMap)? = nil, + partial: Bool = false, + options: BinaryDecodingOptions = BinaryDecodingOptions() + ) throws { + try merge(serializedBytes: data, extensions: extensions, partial: partial, options: options) + } - /// Returns a `Data` instance containing the Protocol Buffer binary - /// format serialization of the message. - /// - /// - Parameters: - /// - partial: If `false` (the default), this method will check - /// ``Message/isInitialized-6abgi`` before encoding to verify that all required - /// fields are present. If any are missing, this method throws - /// ``BinaryEncodingError/missingRequiredFields``. - /// - Returns: A `Data` instance containing the binary serialization of the message. - /// - Throws: ``BinaryEncodingError`` if encoding fails. - public func serializedData(partial: Bool = false) throws -> Data { - try serializedBytes(partial: partial, options: BinaryEncodingOptions()) - } + /// Returns a `Data` instance containing the Protocol Buffer binary + /// format serialization of the message. + /// + /// - Parameters: + /// - partial: If `false` (the default), this method will check + /// ``Message/isInitialized-6abgi`` before encoding to verify that all required + /// fields are present. If any are missing, this method throws + /// ``BinaryEncodingError/missingRequiredFields``. + /// - Returns: A `Data` instance containing the binary serialization of the message. + /// - Throws: ``BinaryEncodingError`` if encoding fails. + public func serializedData(partial: Bool = false) throws -> Data { + try serializedBytes(partial: partial, options: BinaryEncodingOptions()) + } - /// Returns a `Data` instance containing the Protocol Buffer binary - /// format serialization of the message. - /// - /// - Parameters: - /// - partial: If `false` (the default), this method will check - /// ``Message/isInitialized-6abgi`` before encoding to verify that all required - /// fields are present. If any are missing, this method throws - /// ``BinaryEncodingError/missingRequiredFields``. - /// - options: The ``BinaryEncodingOptions`` to use. - /// - Returns: A `Data` instance containing the binary serialization of the message. - /// - Throws: ``BinaryEncodingError`` if encoding fails. - public func serializedData( - partial: Bool = false, - options: BinaryEncodingOptions = BinaryEncodingOptions() - ) throws -> Data { - try serializedBytes(partial: partial, options: options) - } + /// Returns a `Data` instance containing the Protocol Buffer binary + /// format serialization of the message. + /// + /// - Parameters: + /// - partial: If `false` (the default), this method will check + /// ``Message/isInitialized-6abgi`` before encoding to verify that all required + /// fields are present. If any are missing, this method throws + /// ``BinaryEncodingError/missingRequiredFields``. + /// - options: The ``BinaryEncodingOptions`` to use. + /// - Returns: A `Data` instance containing the binary serialization of the message. + /// - Throws: ``BinaryEncodingError`` if encoding fails. + public func serializedData( + partial: Bool = false, + options: BinaryEncodingOptions = BinaryEncodingOptions() + ) throws -> Data { + try serializedBytes(partial: partial, options: options) + } } diff --git a/Sources/SwiftProtobuf/Message+FieldMask.swift b/Sources/SwiftProtobuf/Message+FieldMask.swift index 2622d7214..3ac8ac92f 100644 --- a/Sources/SwiftProtobuf/Message+FieldMask.swift +++ b/Sources/SwiftProtobuf/Message+FieldMask.swift @@ -16,117 +16,118 @@ import Foundation extension Message { - /// Checks whether the given path is valid for Message type. - /// - /// - Parameter path: Path to be checked - /// - Returns: Boolean determines path is valid. - public static func isPathValid( - _ path: String - ) -> Bool { - var message = Self() - return message.hasPath(path: path) - } + /// Checks whether the given path is valid for Message type. + /// + /// - Parameter path: Path to be checked + /// - Returns: Boolean determines path is valid. + public static func isPathValid( + _ path: String + ) -> Bool { + var message = Self() + return message.hasPath(path: path) + } - internal mutating func hasPath(path: String) -> Bool { - do { - try set(path: path, value: nil, mergeOption: .init()) - return true - } catch let error as PathDecodingError { - return error != .pathNotFound - } catch { - return false + internal mutating func hasPath(path: String) -> Bool { + do { + try set(path: path, value: nil, mergeOption: .init()) + return true + } catch let error as PathDecodingError { + return error != .pathNotFound + } catch { + return false + } } - } - internal mutating func isPathValid( - _ path: String - ) -> Bool { - hasPath(path: path) - } + internal mutating func isPathValid( + _ path: String + ) -> Bool { + hasPath(path: path) + } } extension Google_Protobuf_FieldMask { - /// Defines available options for merging two messages. - public struct MergeOptions { + /// Defines available options for merging two messages. + public struct MergeOptions { - public init() {} + public init() {} - /// The default merging behavior will append entries from the source - /// repeated field to the destination repeated field. If you only want - /// to keep the entries from the source repeated field, set this flag - /// to true. - public var replaceRepeatedFields = false - } + /// The default merging behavior will append entries from the source + /// repeated field to the destination repeated field. If you only want + /// to keep the entries from the source repeated field, set this flag + /// to true. + public var replaceRepeatedFields = false + } } extension Message { - /// Merges fields specified in a FieldMask into another message. - /// - /// - Parameters: - /// - source: Message that should be merged to the original one. - /// - fieldMask: FieldMask specifies which fields should be merged. - public mutating func merge( - from source: Self, - fieldMask: Google_Protobuf_FieldMask, - mergeOption: Google_Protobuf_FieldMask.MergeOptions = .init() - ) throws { - var visitor = PathVisitor() - try source.traverse(visitor: &visitor) - let values = visitor.values - // TODO: setting all values with only one decoding - for path in fieldMask.paths { - try? set( - path: path, - value: values[path], - mergeOption: mergeOption - ) + /// Merges fields specified in a FieldMask into another message. + /// + /// - Parameters: + /// - source: Message that should be merged to the original one. + /// - fieldMask: FieldMask specifies which fields should be merged. + public mutating func merge( + from source: Self, + fieldMask: Google_Protobuf_FieldMask, + mergeOption: Google_Protobuf_FieldMask.MergeOptions = .init() + ) throws { + var visitor = PathVisitor() + try source.traverse(visitor: &visitor) + let values = visitor.values + // TODO: setting all values with only one decoding + for path in fieldMask.paths { + try? set( + path: path, + value: values[path], + mergeOption: mergeOption + ) + } } - } } extension Message where Self: Equatable, Self: _ProtoNameProviding { - // TODO: Re-implement using clear fields instead of copying message + // TODO: Re-implement using clear fields instead of copying message - /// Removes from 'message' any field that is not represented in the given - /// FieldMask. If the FieldMask is empty, does nothing. - /// - /// - Parameter fieldMask: FieldMask specifies which fields should be kept. - /// - Returns: Boolean determines if the message is modified - @discardableResult - public mutating func trim( - keeping fieldMask: Google_Protobuf_FieldMask - ) -> Bool { - if !fieldMask.isValid(for: Self.self) { - return false + /// Removes from 'message' any field that is not represented in the given + /// FieldMask. If the FieldMask is empty, does nothing. + /// + /// - Parameter fieldMask: FieldMask specifies which fields should be kept. + /// - Returns: Boolean determines if the message is modified + @discardableResult + public mutating func trim( + keeping fieldMask: Google_Protobuf_FieldMask + ) -> Bool { + if !fieldMask.isValid(for: Self.self) { + return false + } + if fieldMask.paths.isEmpty { + return false + } + var tmp = Self(removingAllFieldsOf: self) + do { + try tmp.merge(from: self, fieldMask: fieldMask) + let changed = tmp != self + self = tmp + return changed + } catch { + return false + } } - if fieldMask.paths.isEmpty { - return false - } - var tmp = Self(removingAllFieldsOf: self) - do { - try tmp.merge(from: self, fieldMask: fieldMask) - let changed = tmp != self - self = tmp - return changed - } catch { - return false - } - } } -private extension Message { - init(removingAllFieldsOf message: Self) { - let newMessage: Self = .init() - if var newExtensible = newMessage as? any ExtensibleMessage, - let extensible = message as? any ExtensibleMessage { - newExtensible._protobuf_extensionFieldValues = extensible._protobuf_extensionFieldValues - self = newExtensible as? Self ?? newMessage - } else { - self = newMessage +extension Message { + fileprivate init(removingAllFieldsOf message: Self) { + let newMessage: Self = .init() + if var newExtensible = newMessage as? any ExtensibleMessage, + let extensible = message as? any ExtensibleMessage + { + newExtensible._protobuf_extensionFieldValues = extensible._protobuf_extensionFieldValues + self = newExtensible as? Self ?? newMessage + } else { + self = newMessage + } + self.unknownFields = message.unknownFields } - self.unknownFields = message.unknownFields - } } diff --git a/Sources/SwiftProtobuf/Message+JSONAdditions.swift b/Sources/SwiftProtobuf/Message+JSONAdditions.swift index d3afd1e13..3ca6eb8b7 100644 --- a/Sources/SwiftProtobuf/Message+JSONAdditions.swift +++ b/Sources/SwiftProtobuf/Message+JSONAdditions.swift @@ -16,134 +16,138 @@ import Foundation /// JSON encoding and decoding methods for messages. extension Message { - /// Returns a string containing the JSON serialization of the message. - /// - /// Unlike binary encoding, presence of required fields is not enforced when - /// serializing to JSON. - /// - /// - Returns: A string containing the JSON serialization of the message. - /// - Parameters: - /// - options: The JSONEncodingOptions to use. - /// - Throws: ``JSONEncodingError`` if encoding fails. - public func jsonString( - options: JSONEncodingOptions = JSONEncodingOptions() - ) throws -> String { - if let m = self as? (any _CustomJSONCodable) { - return try m.encodedJSONString(options: options) + /// Returns a string containing the JSON serialization of the message. + /// + /// Unlike binary encoding, presence of required fields is not enforced when + /// serializing to JSON. + /// + /// - Returns: A string containing the JSON serialization of the message. + /// - Parameters: + /// - options: The JSONEncodingOptions to use. + /// - Throws: ``JSONEncodingError`` if encoding fails. + public func jsonString( + options: JSONEncodingOptions = JSONEncodingOptions() + ) throws -> String { + if let m = self as? (any _CustomJSONCodable) { + return try m.encodedJSONString(options: options) + } + let data: [UInt8] = try jsonUTF8Bytes(options: options) + return String(bytes: data, encoding: .utf8)! } - let data: [UInt8] = try jsonUTF8Bytes(options: options) - return String(bytes: data, encoding: .utf8)! - } - /// Returns a `SwiftProtobufContiguousBytes` containing the UTF-8 JSON serialization of the message. - /// - /// Unlike binary encoding, presence of required fields is not enforced when - /// serializing to JSON. - /// - /// - Returns: A `SwiftProtobufContiguousBytes` containing the JSON serialization of the message. - /// - Parameters: - /// - options: The JSONEncodingOptions to use. - /// - Throws: ``JSONEncodingError`` if encoding fails. - public func jsonUTF8Bytes( - options: JSONEncodingOptions = JSONEncodingOptions() - ) throws -> Bytes { - if let m = self as? (any _CustomJSONCodable) { - let string = try m.encodedJSONString(options: options) - return Bytes(string.utf8) + /// Returns a `SwiftProtobufContiguousBytes` containing the UTF-8 JSON serialization of the message. + /// + /// Unlike binary encoding, presence of required fields is not enforced when + /// serializing to JSON. + /// + /// - Returns: A `SwiftProtobufContiguousBytes` containing the JSON serialization of the message. + /// - Parameters: + /// - options: The JSONEncodingOptions to use. + /// - Throws: ``JSONEncodingError`` if encoding fails. + public func jsonUTF8Bytes( + options: JSONEncodingOptions = JSONEncodingOptions() + ) throws -> Bytes { + if let m = self as? (any _CustomJSONCodable) { + let string = try m.encodedJSONString(options: options) + return Bytes(string.utf8) + } + var visitor = try JSONEncodingVisitor(type: Self.self, options: options) + visitor.startObject(message: self) + try traverse(visitor: &visitor) + visitor.endObject() + return Bytes(visitor.dataResult) } - var visitor = try JSONEncodingVisitor(type: Self.self, options: options) - visitor.startObject(message: self) - try traverse(visitor: &visitor) - visitor.endObject() - return Bytes(visitor.dataResult) - } - - /// Creates a new message by decoding the given string containing a - /// serialized message in JSON format. - /// - /// - Parameter jsonString: The JSON-formatted string to decode. - /// - Parameter options: The JSONDecodingOptions to use. - /// - Throws: ``JSONDecodingError`` if decoding fails. - public init( - jsonString: String, - options: JSONDecodingOptions = JSONDecodingOptions() - ) throws { - try self.init(jsonString: jsonString, extensions: nil, options: options) - } - /// Creates a new message by decoding the given string containing a - /// serialized message in JSON format. - /// - /// - Parameter jsonString: The JSON-formatted string to decode. - /// - Parameter extensions: An ExtensionMap for looking up extensions by name - /// - Parameter options: The JSONDecodingOptions to use. - /// - Throws: ``JSONDecodingError`` if decoding fails. - public init( - jsonString: String, - extensions: (any ExtensionMap)? = nil, - options: JSONDecodingOptions = JSONDecodingOptions() - ) throws { - if jsonString.isEmpty { - throw JSONDecodingError.truncated + /// Creates a new message by decoding the given string containing a + /// serialized message in JSON format. + /// + /// - Parameter jsonString: The JSON-formatted string to decode. + /// - Parameter options: The JSONDecodingOptions to use. + /// - Throws: ``JSONDecodingError`` if decoding fails. + public init( + jsonString: String, + options: JSONDecodingOptions = JSONDecodingOptions() + ) throws { + try self.init(jsonString: jsonString, extensions: nil, options: options) } - if let data = jsonString.data(using: String.Encoding.utf8) { - try self.init(jsonUTF8Bytes: data, extensions: extensions, options: options) - } else { - throw JSONDecodingError.truncated + + /// Creates a new message by decoding the given string containing a + /// serialized message in JSON format. + /// + /// - Parameter jsonString: The JSON-formatted string to decode. + /// - Parameter extensions: An ExtensionMap for looking up extensions by name + /// - Parameter options: The JSONDecodingOptions to use. + /// - Throws: ``JSONDecodingError`` if decoding fails. + public init( + jsonString: String, + extensions: (any ExtensionMap)? = nil, + options: JSONDecodingOptions = JSONDecodingOptions() + ) throws { + if jsonString.isEmpty { + throw JSONDecodingError.truncated + } + if let data = jsonString.data(using: String.Encoding.utf8) { + try self.init(jsonUTF8Bytes: data, extensions: extensions, options: options) + } else { + throw JSONDecodingError.truncated + } } - } - /// Creates a new message by decoding the given `SwiftProtobufContiguousBytes` - /// containing a serialized message in JSON format, interpreting the data as UTF-8 encoded - /// text. - /// - /// - Parameter jsonUTF8Bytes: The JSON-formatted data to decode, represented - /// as UTF-8 encoded text. - /// - Parameter options: The JSONDecodingOptions to use. - /// - Throws: ``JSONDecodingError`` if decoding fails. - public init( - jsonUTF8Bytes: Bytes, - options: JSONDecodingOptions = JSONDecodingOptions() - ) throws { - try self.init(jsonUTF8Bytes: jsonUTF8Bytes, extensions: nil, options: options) - } + /// Creates a new message by decoding the given `SwiftProtobufContiguousBytes` + /// containing a serialized message in JSON format, interpreting the data as UTF-8 encoded + /// text. + /// + /// - Parameter jsonUTF8Bytes: The JSON-formatted data to decode, represented + /// as UTF-8 encoded text. + /// - Parameter options: The JSONDecodingOptions to use. + /// - Throws: ``JSONDecodingError`` if decoding fails. + public init( + jsonUTF8Bytes: Bytes, + options: JSONDecodingOptions = JSONDecodingOptions() + ) throws { + try self.init(jsonUTF8Bytes: jsonUTF8Bytes, extensions: nil, options: options) + } - /// Creates a new message by decoding the given `SwiftProtobufContiguousBytes` - /// containing a serialized message in JSON format, interpreting the data as UTF-8 encoded - /// text. - /// - /// - Parameter jsonUTF8Bytes: The JSON-formatted data to decode, represented - /// as UTF-8 encoded text. - /// - Parameter extensions: The extension map to use with this decode - /// - Parameter options: The JSONDecodingOptions to use. - /// - Throws: ``JSONDecodingError`` if decoding fails. - public init( - jsonUTF8Bytes: Bytes, - extensions: (any ExtensionMap)? = nil, - options: JSONDecodingOptions = JSONDecodingOptions() - ) throws { - self.init() - try jsonUTF8Bytes.withUnsafeBytes { (body: UnsafeRawBufferPointer) in - // Empty input is valid for binary, but not for JSON. - guard body.count > 0 else { - throw JSONDecodingError.truncated - } - var decoder = JSONDecoder(source: body, options: options, - messageType: Self.self, extensions: extensions) - if decoder.scanner.skipOptionalNull() { - if let customCodable = Self.self as? any _CustomJSONCodable.Type, - let message = try customCodable.decodedFromJSONNull() { - self = message as! Self - } else { - throw JSONDecodingError.illegalNull + /// Creates a new message by decoding the given `SwiftProtobufContiguousBytes` + /// containing a serialized message in JSON format, interpreting the data as UTF-8 encoded + /// text. + /// + /// - Parameter jsonUTF8Bytes: The JSON-formatted data to decode, represented + /// as UTF-8 encoded text. + /// - Parameter extensions: The extension map to use with this decode + /// - Parameter options: The JSONDecodingOptions to use. + /// - Throws: ``JSONDecodingError`` if decoding fails. + public init( + jsonUTF8Bytes: Bytes, + extensions: (any ExtensionMap)? = nil, + options: JSONDecodingOptions = JSONDecodingOptions() + ) throws { + self.init() + try jsonUTF8Bytes.withUnsafeBytes { (body: UnsafeRawBufferPointer) in + // Empty input is valid for binary, but not for JSON. + guard body.count > 0 else { + throw JSONDecodingError.truncated + } + var decoder = JSONDecoder( + source: body, + options: options, + messageType: Self.self, + extensions: extensions + ) + if decoder.scanner.skipOptionalNull() { + if let customCodable = Self.self as? any _CustomJSONCodable.Type, + let message = try customCodable.decodedFromJSONNull() + { + self = message as! Self + } else { + throw JSONDecodingError.illegalNull + } + } else { + try decoder.decodeFullObject(message: &self) + } + if !decoder.scanner.complete { + throw JSONDecodingError.trailingGarbage + } } - } else { - try decoder.decodeFullObject(message: &self) - } - if !decoder.scanner.complete { - throw JSONDecodingError.trailingGarbage - } } - } } - diff --git a/Sources/SwiftProtobuf/Message+JSONAdditions_Data.swift b/Sources/SwiftProtobuf/Message+JSONAdditions_Data.swift index ecc0d2f2f..e2f00ac04 100644 --- a/Sources/SwiftProtobuf/Message+JSONAdditions_Data.swift +++ b/Sources/SwiftProtobuf/Message+JSONAdditions_Data.swift @@ -16,49 +16,48 @@ import Foundation /// JSON encoding and decoding methods for messages. extension Message { - /// Creates a new message by decoding the given `Data` containing a serialized message - /// in JSON format, interpreting the data as UTF-8 encoded text. - /// - /// - Parameter jsonUTF8Data: The JSON-formatted data to decode, represented - /// as UTF-8 encoded text. - /// - Parameter options: The JSONDecodingOptions to use. - /// - Throws: ``JSONDecodingError`` if decoding fails. - public init( - jsonUTF8Data: Data, - options: JSONDecodingOptions = JSONDecodingOptions() - ) throws { - try self.init(jsonUTF8Bytes: jsonUTF8Data, extensions: nil, options: options) - } + /// Creates a new message by decoding the given `Data` containing a serialized message + /// in JSON format, interpreting the data as UTF-8 encoded text. + /// + /// - Parameter jsonUTF8Data: The JSON-formatted data to decode, represented + /// as UTF-8 encoded text. + /// - Parameter options: The JSONDecodingOptions to use. + /// - Throws: ``JSONDecodingError`` if decoding fails. + public init( + jsonUTF8Data: Data, + options: JSONDecodingOptions = JSONDecodingOptions() + ) throws { + try self.init(jsonUTF8Bytes: jsonUTF8Data, extensions: nil, options: options) + } - /// Creates a new message by decoding the given `Data` containing a serialized message - /// in JSON format, interpreting the data as UTF-8 encoded text. - /// - /// - Parameter jsonUTF8Data: The JSON-formatted data to decode, represented - /// as UTF-8 encoded text. - /// - Parameter extensions: The extension map to use with this decode - /// - Parameter options: The JSONDecodingOptions to use. - /// - Throws: ``JSONDecodingError`` if decoding fails. - public init( - jsonUTF8Data: Data, - extensions: (any ExtensionMap)? = nil, - options: JSONDecodingOptions = JSONDecodingOptions() - ) throws { - try self.init(jsonUTF8Bytes: jsonUTF8Data, extensions: extensions, options: options) - } + /// Creates a new message by decoding the given `Data` containing a serialized message + /// in JSON format, interpreting the data as UTF-8 encoded text. + /// + /// - Parameter jsonUTF8Data: The JSON-formatted data to decode, represented + /// as UTF-8 encoded text. + /// - Parameter extensions: The extension map to use with this decode + /// - Parameter options: The JSONDecodingOptions to use. + /// - Throws: ``JSONDecodingError`` if decoding fails. + public init( + jsonUTF8Data: Data, + extensions: (any ExtensionMap)? = nil, + options: JSONDecodingOptions = JSONDecodingOptions() + ) throws { + try self.init(jsonUTF8Bytes: jsonUTF8Data, extensions: extensions, options: options) + } - /// Returns a Data containing the UTF-8 JSON serialization of the message. - /// - /// Unlike binary encoding, presence of required fields is not enforced when - /// serializing to JSON. - /// - /// - Returns: A Data containing the JSON serialization of the message. - /// - Parameters: - /// - options: The JSONEncodingOptions to use. - /// - Throws: ``JSONDecodingError`` if encoding fails. - public func jsonUTF8Data( - options: JSONEncodingOptions = JSONEncodingOptions() - ) throws -> Data { - try jsonUTF8Bytes(options: options) - } + /// Returns a Data containing the UTF-8 JSON serialization of the message. + /// + /// Unlike binary encoding, presence of required fields is not enforced when + /// serializing to JSON. + /// + /// - Returns: A Data containing the JSON serialization of the message. + /// - Parameters: + /// - options: The JSONEncodingOptions to use. + /// - Throws: ``JSONDecodingError`` if encoding fails. + public func jsonUTF8Data( + options: JSONEncodingOptions = JSONEncodingOptions() + ) throws -> Data { + try jsonUTF8Bytes(options: options) + } } - diff --git a/Sources/SwiftProtobuf/Message+JSONArrayAdditions.swift b/Sources/SwiftProtobuf/Message+JSONArrayAdditions.swift index ad243015d..eb6947644 100644 --- a/Sources/SwiftProtobuf/Message+JSONArrayAdditions.swift +++ b/Sources/SwiftProtobuf/Message+JSONArrayAdditions.swift @@ -16,131 +16,139 @@ import Foundation /// JSON encoding and decoding methods for arrays of messages. extension Message { - /// Returns a string containing the JSON serialization of the messages. - /// - /// Unlike binary encoding, presence of required fields is not enforced when - /// serializing to JSON. - /// - /// - Returns: A string containing the JSON serialization of the messages. - /// - Parameters: - /// - collection: The list of messages to encode. - /// - options: The JSONEncodingOptions to use. - /// - Throws: ``JSONEncodingError`` if encoding fails. - public static func jsonString( - from collection: C, - options: JSONEncodingOptions = JSONEncodingOptions() - ) throws -> String where C.Iterator.Element == Self { - let data: [UInt8] = try jsonUTF8Bytes(from: collection, options: options) - return String(bytes: data, encoding: .utf8)! - } + /// Returns a string containing the JSON serialization of the messages. + /// + /// Unlike binary encoding, presence of required fields is not enforced when + /// serializing to JSON. + /// + /// - Returns: A string containing the JSON serialization of the messages. + /// - Parameters: + /// - collection: The list of messages to encode. + /// - options: The JSONEncodingOptions to use. + /// - Throws: ``JSONEncodingError`` if encoding fails. + public static func jsonString( + from collection: C, + options: JSONEncodingOptions = JSONEncodingOptions() + ) throws -> String where C.Iterator.Element == Self { + let data: [UInt8] = try jsonUTF8Bytes(from: collection, options: options) + return String(bytes: data, encoding: .utf8)! + } - /// Returns a `SwiftProtobufContiguousBytes` containing the UTF-8 JSON serialization of the messages. - /// - /// Unlike binary encoding, presence of required fields is not enforced when - /// serializing to JSON. - /// - /// - Returns: A `SwiftProtobufContiguousBytes` containing the JSON serialization of the messages. - /// - Parameters: - /// - collection: The list of messages to encode. - /// - options: The JSONEncodingOptions to use. - /// - Throws: ``JSONEncodingError`` if encoding fails. - public static func jsonUTF8Bytes( - from collection: C, - options: JSONEncodingOptions = JSONEncodingOptions() - ) throws -> Bytes where C.Iterator.Element == Self { - var visitor = try JSONEncodingVisitor(type: Self.self, options: options) - visitor.startArray() - for message in collection { - visitor.startArrayObject(message: message) - try message.traverse(visitor: &visitor) - visitor.endObject() + /// Returns a `SwiftProtobufContiguousBytes` containing the UTF-8 JSON serialization of the messages. + /// + /// Unlike binary encoding, presence of required fields is not enforced when + /// serializing to JSON. + /// + /// - Returns: A `SwiftProtobufContiguousBytes` containing the JSON serialization of the messages. + /// - Parameters: + /// - collection: The list of messages to encode. + /// - options: The JSONEncodingOptions to use. + /// - Throws: ``JSONEncodingError`` if encoding fails. + public static func jsonUTF8Bytes( + from collection: C, + options: JSONEncodingOptions = JSONEncodingOptions() + ) throws -> Bytes where C.Iterator.Element == Self { + var visitor = try JSONEncodingVisitor(type: Self.self, options: options) + visitor.startArray() + for message in collection { + visitor.startArrayObject(message: message) + try message.traverse(visitor: &visitor) + visitor.endObject() + } + visitor.endArray() + return Bytes(visitor.dataResult) } - visitor.endArray() - return Bytes(visitor.dataResult) - } - /// Creates a new array of messages by decoding the given string containing a - /// serialized array of messages in JSON format. - /// - /// - Parameter jsonString: The JSON-formatted string to decode. - /// - Parameter options: The JSONDecodingOptions to use. - /// - Throws: ``JSONDecodingError`` if decoding fails. - public static func array( - fromJSONString jsonString: String, - options: JSONDecodingOptions = JSONDecodingOptions() - ) throws -> [Self] { - return try self.array(fromJSONString: jsonString, - extensions: SimpleExtensionMap(), - options: options) - } + /// Creates a new array of messages by decoding the given string containing a + /// serialized array of messages in JSON format. + /// + /// - Parameter jsonString: The JSON-formatted string to decode. + /// - Parameter options: The JSONDecodingOptions to use. + /// - Throws: ``JSONDecodingError`` if decoding fails. + public static func array( + fromJSONString jsonString: String, + options: JSONDecodingOptions = JSONDecodingOptions() + ) throws -> [Self] { + try self.array( + fromJSONString: jsonString, + extensions: SimpleExtensionMap(), + options: options + ) + } - /// Creates a new array of messages by decoding the given string containing a - /// serialized array of messages in JSON format. - /// - /// - Parameter jsonString: The JSON-formatted string to decode. - /// - Parameter extensions: The extension map to use with this decode - /// - Parameter options: The JSONDecodingOptions to use. - /// - Throws: ``JSONDecodingError`` if decoding fails. - public static func array( - fromJSONString jsonString: String, - extensions: any ExtensionMap = SimpleExtensionMap(), - options: JSONDecodingOptions = JSONDecodingOptions() - ) throws -> [Self] { - if jsonString.isEmpty { - throw JSONDecodingError.truncated + /// Creates a new array of messages by decoding the given string containing a + /// serialized array of messages in JSON format. + /// + /// - Parameter jsonString: The JSON-formatted string to decode. + /// - Parameter extensions: The extension map to use with this decode + /// - Parameter options: The JSONDecodingOptions to use. + /// - Throws: ``JSONDecodingError`` if decoding fails. + public static func array( + fromJSONString jsonString: String, + extensions: any ExtensionMap = SimpleExtensionMap(), + options: JSONDecodingOptions = JSONDecodingOptions() + ) throws -> [Self] { + if jsonString.isEmpty { + throw JSONDecodingError.truncated + } + if let data = jsonString.data(using: String.Encoding.utf8) { + return try array(fromJSONUTF8Bytes: data, extensions: extensions, options: options) + } else { + throw JSONDecodingError.truncated + } } - if let data = jsonString.data(using: String.Encoding.utf8) { - return try array(fromJSONUTF8Bytes: data, extensions: extensions, options: options) - } else { - throw JSONDecodingError.truncated + + /// Creates a new array of messages by decoding the given ``SwiftProtobufContiguousBytes`` + /// containing a serialized array of messages in JSON format, interpreting the data as + /// UTF-8 encoded text. + /// + /// - Parameter jsonUTF8Bytes: The JSON-formatted data to decode, represented + /// as UTF-8 encoded text. + /// - Parameter options: The JSONDecodingOptions to use. + /// - Throws: ``JSONDecodingError`` if decoding fails. + public static func array( + fromJSONUTF8Bytes jsonUTF8Bytes: Bytes, + options: JSONDecodingOptions = JSONDecodingOptions() + ) throws -> [Self] { + try self.array( + fromJSONUTF8Bytes: jsonUTF8Bytes, + extensions: SimpleExtensionMap(), + options: options + ) } - } - /// Creates a new array of messages by decoding the given ``SwiftProtobufContiguousBytes`` - /// containing a serialized array of messages in JSON format, interpreting the data as - /// UTF-8 encoded text. - /// - /// - Parameter jsonUTF8Bytes: The JSON-formatted data to decode, represented - /// as UTF-8 encoded text. - /// - Parameter options: The JSONDecodingOptions to use. - /// - Throws: ``JSONDecodingError`` if decoding fails. - public static func array( - fromJSONUTF8Bytes jsonUTF8Bytes: Bytes, - options: JSONDecodingOptions = JSONDecodingOptions() - ) throws -> [Self] { - return try self.array(fromJSONUTF8Bytes: jsonUTF8Bytes, - extensions: SimpleExtensionMap(), - options: options) - } + /// Creates a new array of messages by decoding the given ``SwiftProtobufContiguousBytes`` + /// containing a serialized array of messages in JSON format, interpreting the data as + /// UTF-8 encoded text. + /// + /// - Parameter jsonUTF8Bytes: The JSON-formatted data to decode, represented + /// as UTF-8 encoded text. + /// - Parameter extensions: The extension map to use with this decode + /// - Parameter options: The JSONDecodingOptions to use. + /// - Throws: ``JSONDecodingError`` if decoding fails. + public static func array( + fromJSONUTF8Bytes jsonUTF8Bytes: Bytes, + extensions: any ExtensionMap = SimpleExtensionMap(), + options: JSONDecodingOptions = JSONDecodingOptions() + ) throws -> [Self] { + try jsonUTF8Bytes.withUnsafeBytes { (body: UnsafeRawBufferPointer) in + var array = [Self]() - /// Creates a new array of messages by decoding the given ``SwiftProtobufContiguousBytes`` - /// containing a serialized array of messages in JSON format, interpreting the data as - /// UTF-8 encoded text. - /// - /// - Parameter jsonUTF8Bytes: The JSON-formatted data to decode, represented - /// as UTF-8 encoded text. - /// - Parameter extensions: The extension map to use with this decode - /// - Parameter options: The JSONDecodingOptions to use. - /// - Throws: ``JSONDecodingError`` if decoding fails. - public static func array( - fromJSONUTF8Bytes jsonUTF8Bytes: Bytes, - extensions: any ExtensionMap = SimpleExtensionMap(), - options: JSONDecodingOptions = JSONDecodingOptions() - ) throws -> [Self] { - return try jsonUTF8Bytes.withUnsafeBytes { (body: UnsafeRawBufferPointer) in - var array = [Self]() + if body.count > 0 { + var decoder = JSONDecoder( + source: body, + options: options, + messageType: Self.self, + extensions: extensions + ) + try decoder.decodeRepeatedMessageField(value: &array) + if !decoder.scanner.complete { + throw JSONDecodingError.trailingGarbage + } + } - if body.count > 0 { - var decoder = JSONDecoder(source: body, options: options, - messageType: Self.self, extensions: extensions) - try decoder.decodeRepeatedMessageField(value: &array) - if !decoder.scanner.complete { - throw JSONDecodingError.trailingGarbage + return array } - } - - return array } - } } diff --git a/Sources/SwiftProtobuf/Message+JSONArrayAdditions_Data.swift b/Sources/SwiftProtobuf/Message+JSONArrayAdditions_Data.swift index be41e50f6..06a7d0b07 100644 --- a/Sources/SwiftProtobuf/Message+JSONArrayAdditions_Data.swift +++ b/Sources/SwiftProtobuf/Message+JSONArrayAdditions_Data.swift @@ -16,56 +16,60 @@ import Foundation /// JSON encoding and decoding methods for arrays of messages. extension Message { - /// Creates a new array of messages by decoding the given `Data` - /// containing a serialized array of messages in JSON format, interpreting the data as - /// UTF-8 encoded text. - /// - /// - Parameter jsonUTF8Data: The JSON-formatted data to decode, represented - /// as UTF-8 encoded text. - /// - Parameter options: The JSONDecodingOptions to use. - /// - Throws: ``JSONDecodingError`` if decoding fails. - public static func array( - fromJSONUTF8Data jsonUTF8Data: Data, - options: JSONDecodingOptions = JSONDecodingOptions() - ) throws -> [Self] { - return try self.array(fromJSONUTF8Bytes: jsonUTF8Data, - extensions: SimpleExtensionMap(), - options: options) - } + /// Creates a new array of messages by decoding the given `Data` + /// containing a serialized array of messages in JSON format, interpreting the data as + /// UTF-8 encoded text. + /// + /// - Parameter jsonUTF8Data: The JSON-formatted data to decode, represented + /// as UTF-8 encoded text. + /// - Parameter options: The JSONDecodingOptions to use. + /// - Throws: ``JSONDecodingError`` if decoding fails. + public static func array( + fromJSONUTF8Data jsonUTF8Data: Data, + options: JSONDecodingOptions = JSONDecodingOptions() + ) throws -> [Self] { + try self.array( + fromJSONUTF8Bytes: jsonUTF8Data, + extensions: SimpleExtensionMap(), + options: options + ) + } - /// Creates a new array of messages by decoding the given `Data` - /// containing a serialized array of messages in JSON format, interpreting the data as - /// UTF-8 encoded text. - /// - /// - Parameter jsonUTF8Data: The JSON-formatted data to decode, represented - /// as UTF-8 encoded text. - /// - Parameter extensions: The extension map to use with this decode - /// - Parameter options: The JSONDecodingOptions to use. - /// - Throws: ``JSONDecodingError`` if decoding fails. - public static func array( - fromJSONUTF8Data jsonUTF8Data: Data, - extensions: any ExtensionMap = SimpleExtensionMap(), - options: JSONDecodingOptions = JSONDecodingOptions() - ) throws -> [Self] { - return try array(fromJSONUTF8Bytes: jsonUTF8Data, - extensions: extensions, - options: options) - } + /// Creates a new array of messages by decoding the given `Data` + /// containing a serialized array of messages in JSON format, interpreting the data as + /// UTF-8 encoded text. + /// + /// - Parameter jsonUTF8Data: The JSON-formatted data to decode, represented + /// as UTF-8 encoded text. + /// - Parameter extensions: The extension map to use with this decode + /// - Parameter options: The JSONDecodingOptions to use. + /// - Throws: ``JSONDecodingError`` if decoding fails. + public static func array( + fromJSONUTF8Data jsonUTF8Data: Data, + extensions: any ExtensionMap = SimpleExtensionMap(), + options: JSONDecodingOptions = JSONDecodingOptions() + ) throws -> [Self] { + try array( + fromJSONUTF8Bytes: jsonUTF8Data, + extensions: extensions, + options: options + ) + } - /// Returns a Data containing the UTF-8 JSON serialization of the messages. - /// - /// Unlike binary encoding, presence of required fields is not enforced when - /// serializing to JSON. - /// - /// - Returns: A Data containing the JSON serialization of the messages. - /// - Parameters: - /// - collection: The list of messages to encode. - /// - options: The JSONEncodingOptions to use. - /// - Throws: ``JSONEncodingError`` if encoding fails. - public static func jsonUTF8Data( - from collection: C, - options: JSONEncodingOptions = JSONEncodingOptions() - ) throws -> Data where C.Iterator.Element == Self { - try jsonUTF8Bytes(from: collection, options: options) - } + /// Returns a Data containing the UTF-8 JSON serialization of the messages. + /// + /// Unlike binary encoding, presence of required fields is not enforced when + /// serializing to JSON. + /// + /// - Returns: A Data containing the JSON serialization of the messages. + /// - Parameters: + /// - collection: The list of messages to encode. + /// - options: The JSONEncodingOptions to use. + /// - Throws: ``JSONEncodingError`` if encoding fails. + public static func jsonUTF8Data( + from collection: C, + options: JSONEncodingOptions = JSONEncodingOptions() + ) throws -> Data where C.Iterator.Element == Self { + try jsonUTF8Bytes(from: collection, options: options) + } } diff --git a/Sources/SwiftProtobuf/Message+TextFormatAdditions.swift b/Sources/SwiftProtobuf/Message+TextFormatAdditions.swift index dc4fee0b2..a31502ef1 100644 --- a/Sources/SwiftProtobuf/Message+TextFormatAdditions.swift +++ b/Sources/SwiftProtobuf/Message+TextFormatAdditions.swift @@ -16,95 +16,99 @@ import Foundation /// Text format encoding and decoding methods for messages. extension Message { - /// Returns a string containing the Protocol Buffer text format serialization - /// of the message. - /// - /// Unlike binary encoding, presence of required fields is not enforced when - /// serializing to text format. - /// - /// - Returns: A string containing the text format serialization of the - /// message. - public func textFormatString() -> String { - // This is implemented as a separate zero-argument function - // to preserve binary compatibility. - return textFormatString(options: TextFormatEncodingOptions()) - } + /// Returns a string containing the Protocol Buffer text format serialization + /// of the message. + /// + /// Unlike binary encoding, presence of required fields is not enforced when + /// serializing to text format. + /// + /// - Returns: A string containing the text format serialization of the + /// message. + public func textFormatString() -> String { + // This is implemented as a separate zero-argument function + // to preserve binary compatibility. + textFormatString(options: TextFormatEncodingOptions()) + } + + /// Returns a string containing the Protocol Buffer text format serialization + /// of the message. + /// + /// Unlike binary encoding, presence of required fields is not enforced when + /// serializing to text format. + /// + /// - Returns: A string containing the text format serialization of the message. + /// - Parameters: + /// - options: The TextFormatEncodingOptions to use. + public func textFormatString( + options: TextFormatEncodingOptions + ) -> String { + var visitor = TextFormatEncodingVisitor(message: self, options: options) + if let any = self as? Google_Protobuf_Any { + any._storage.textTraverse(visitor: &visitor) + } else { + // Although the general traversal/encoding infrastructure supports + // throwing errors (needed for JSON/Binary WKTs support, binary format + // missing required fields); TextEncoding never actually does throw. + try! traverse(visitor: &visitor) + } + return visitor.result + } - /// Returns a string containing the Protocol Buffer text format serialization - /// of the message. - /// - /// Unlike binary encoding, presence of required fields is not enforced when - /// serializing to text format. - /// - /// - Returns: A string containing the text format serialization of the message. - /// - Parameters: - /// - options: The TextFormatEncodingOptions to use. - public func textFormatString( - options: TextFormatEncodingOptions - ) -> String { - var visitor = TextFormatEncodingVisitor(message: self, options: options) - if let any = self as? Google_Protobuf_Any { - any._storage.textTraverse(visitor: &visitor) - } else { - // Although the general traversal/encoding infrastructure supports - // throwing errors (needed for JSON/Binary WKTs support, binary format - // missing required fields); TextEncoding never actually does throw. - try! traverse(visitor: &visitor) + /// Creates a new message by decoding the given string containing a + /// serialized message in Protocol Buffer text format. + /// + /// - Parameters: + /// - textFormatString: The text format string to decode. + /// - extensions: An ``ExtensionMap`` used to look up and decode any + /// extensions in this message or messages nested within this message's + /// fields. + /// - Throws: ``SwiftProtobufError`` on failure. + // TODO: delete this (and keep the one with the extra param instead) when we break API + public init( + textFormatString: String, + extensions: (any ExtensionMap)? = nil + ) throws { + try self.init( + textFormatString: textFormatString, + options: TextFormatDecodingOptions(), + extensions: extensions + ) } - return visitor.result - } - - /// Creates a new message by decoding the given string containing a - /// serialized message in Protocol Buffer text format. - /// - /// - Parameters: - /// - textFormatString: The text format string to decode. - /// - extensions: An ``ExtensionMap`` used to look up and decode any - /// extensions in this message or messages nested within this message's - /// fields. - /// - Throws: ``SwiftProtobufError`` on failure. - // TODO: delete this (and keep the one with the extra param instead) when we break API - public init( - textFormatString: String, - extensions: (any ExtensionMap)? = nil - ) throws { - try self.init(textFormatString: textFormatString, - options: TextFormatDecodingOptions(), - extensions: extensions) - } - /// Creates a new message by decoding the given string containing a - /// serialized message in Protocol Buffer text format. - /// - /// - Parameters: - /// - textFormatString: The text format string to decode. - /// - options: The ``TextFormatDecodingOptions`` to use. - /// - extensions: An ``ExtensionMap`` used to look up and decode any - /// extensions in this message or messages nested within this message's - /// fields. - /// - Throws: ``TextFormatDecodingError`` on failure. - public init( - textFormatString: String, - options: TextFormatDecodingOptions = TextFormatDecodingOptions(), - extensions: (any ExtensionMap)? = nil - ) throws { - self.init() - if !textFormatString.isEmpty { - if let data = textFormatString.data(using: String.Encoding.utf8) { - try data.withUnsafeBytes { (body: UnsafeRawBufferPointer) in - if let baseAddress = body.baseAddress, body.count > 0 { - var decoder = try TextFormatDecoder(messageType: Self.self, - utf8Pointer: baseAddress, - count: body.count, - options: options, - extensions: extensions) - try decodeMessage(decoder: &decoder) - if !decoder.complete { - throw TextFormatDecodingError.trailingGarbage + /// Creates a new message by decoding the given string containing a + /// serialized message in Protocol Buffer text format. + /// + /// - Parameters: + /// - textFormatString: The text format string to decode. + /// - options: The ``TextFormatDecodingOptions`` to use. + /// - extensions: An ``ExtensionMap`` used to look up and decode any + /// extensions in this message or messages nested within this message's + /// fields. + /// - Throws: ``TextFormatDecodingError`` on failure. + public init( + textFormatString: String, + options: TextFormatDecodingOptions = TextFormatDecodingOptions(), + extensions: (any ExtensionMap)? = nil + ) throws { + self.init() + if !textFormatString.isEmpty { + if let data = textFormatString.data(using: String.Encoding.utf8) { + try data.withUnsafeBytes { (body: UnsafeRawBufferPointer) in + if let baseAddress = body.baseAddress, body.count > 0 { + var decoder = try TextFormatDecoder( + messageType: Self.self, + utf8Pointer: baseAddress, + count: body.count, + options: options, + extensions: extensions + ) + try decodeMessage(decoder: &decoder) + if !decoder.complete { + throw TextFormatDecodingError.trailingGarbage + } + } + } } - } } - } } - } } diff --git a/Sources/SwiftProtobuf/Message.swift b/Sources/SwiftProtobuf/Message.swift index 4405dd270..4d59ab93c 100644 --- a/Sources/SwiftProtobuf/Message.swift +++ b/Sources/SwiftProtobuf/Message.swift @@ -33,141 +33,141 @@ /// default implementations of the below methods and properties. @preconcurrency public protocol Message: Sendable, CustomDebugStringConvertible { - /// Creates a new message with all of its fields initialized to their default - /// values. - init() - - // Metadata - // Basic facts about this class and the proto message it was generated from - // Used by various encoders and decoders - - /// The fully-scoped name of the message from the original .proto file, - /// including any relevant package name. - static var protoMessageName: String { get } - - /// True if all required fields (if any) on this message and any nested - /// messages (recursively) have values set; otherwise, false. - var isInitialized: Bool { get } - - /// Some formats include enough information to transport fields that were - /// not known at generation time. When encountered, they are stored here. - var unknownFields: UnknownStorage { get set } - - // - // General serialization/deserialization machinery - // - - /// Decode all of the fields from the given decoder. - /// - /// This is a simple loop that repeatedly gets the next field number - /// from `decoder.nextFieldNumber()` and then uses the number returned - /// and the type information from the original .proto file to decide - /// what type of data should be decoded for that field. The corresponding - /// method on the decoder is then called to get the field value. - /// - /// This is the core method used by the deserialization machinery. It is - /// `public` to enable users to implement their own encoding formats by - /// conforming to `Decoder`; it should not be called otherwise. - /// - /// Note that this is not specific to binary encodng; formats that use - /// textual identifiers translate those to field numbers and also go - /// through this to decode messages. - /// - /// - Parameters: - /// - decoder: a `Decoder`; the `Message` will call the method - /// corresponding to the type of this field. - /// - Throws: an error on failure or type mismatch. The type of error - /// thrown depends on which decoder is used. - mutating func decodeMessage(decoder: inout D) throws - - /// Traverses the fields of the message, calling the appropriate methods - /// of the passed `Visitor` object. - /// - /// This is used internally by: - /// - /// * Protobuf binary serialization - /// * JSON serialization (with some twists to account for specialty JSON) - /// * Protobuf Text serialization - /// * `Hashable` computation - /// - /// Conceptually, serializers create visitor objects that are - /// then passed recursively to every message and field via generated - /// `traverse` methods. The details get a little involved due to - /// the need to allow particular messages to override particular - /// behaviors for specific encodings, but the general idea is quite simple. - func traverse(visitor: inout V) throws - - // Standard utility properties and methods. - // Most of these are simple wrappers on top of the visitor machinery. - // They are implemented in the protocol, not in the generated structs, - // so can be overridden in user code by defining custom extensions to - // the generated struct. - - /// An implementation of hash(into:) to provide conformance with the - /// `Hashable` protocol. - func hash(into hasher: inout Hasher) - - /// Helper to compare `Message`s when not having a specific type to use - /// normal `Equatable`. `Equatable` is provided with specific generated - /// types. - func isEqualTo(message: any Message) -> Bool + /// Creates a new message with all of its fields initialized to their default + /// values. + init() + + // Metadata + // Basic facts about this class and the proto message it was generated from + // Used by various encoders and decoders + + /// The fully-scoped name of the message from the original .proto file, + /// including any relevant package name. + static var protoMessageName: String { get } + + /// True if all required fields (if any) on this message and any nested + /// messages (recursively) have values set; otherwise, false. + var isInitialized: Bool { get } + + /// Some formats include enough information to transport fields that were + /// not known at generation time. When encountered, they are stored here. + var unknownFields: UnknownStorage { get set } + + // + // General serialization/deserialization machinery + // + + /// Decode all of the fields from the given decoder. + /// + /// This is a simple loop that repeatedly gets the next field number + /// from `decoder.nextFieldNumber()` and then uses the number returned + /// and the type information from the original .proto file to decide + /// what type of data should be decoded for that field. The corresponding + /// method on the decoder is then called to get the field value. + /// + /// This is the core method used by the deserialization machinery. It is + /// `public` to enable users to implement their own encoding formats by + /// conforming to `Decoder`; it should not be called otherwise. + /// + /// Note that this is not specific to binary encodng; formats that use + /// textual identifiers translate those to field numbers and also go + /// through this to decode messages. + /// + /// - Parameters: + /// - decoder: a `Decoder`; the `Message` will call the method + /// corresponding to the type of this field. + /// - Throws: an error on failure or type mismatch. The type of error + /// thrown depends on which decoder is used. + mutating func decodeMessage(decoder: inout D) throws + + /// Traverses the fields of the message, calling the appropriate methods + /// of the passed `Visitor` object. + /// + /// This is used internally by: + /// + /// * Protobuf binary serialization + /// * JSON serialization (with some twists to account for specialty JSON) + /// * Protobuf Text serialization + /// * `Hashable` computation + /// + /// Conceptually, serializers create visitor objects that are + /// then passed recursively to every message and field via generated + /// `traverse` methods. The details get a little involved due to + /// the need to allow particular messages to override particular + /// behaviors for specific encodings, but the general idea is quite simple. + func traverse(visitor: inout V) throws + + // Standard utility properties and methods. + // Most of these are simple wrappers on top of the visitor machinery. + // They are implemented in the protocol, not in the generated structs, + // so can be overridden in user code by defining custom extensions to + // the generated struct. + + /// An implementation of hash(into:) to provide conformance with the + /// `Hashable` protocol. + func hash(into hasher: inout Hasher) + + /// Helper to compare `Message`s when not having a specific type to use + /// normal `Equatable`. `Equatable` is provided with specific generated + /// types. + func isEqualTo(message: any Message) -> Bool } extension Message { - /// Generated proto2 messages that contain required fields, nested messages - /// that contain required fields, and/or extensions will provide their own - /// implementation of this property that tests that all required fields are - /// set. Users of the generated code SHOULD NOT override this property. - public var isInitialized: Bool { - // The generated code will include a specialization as needed. - return true - } - - /// A hash based on the message's full contents. - public func hash(into hasher: inout Hasher) { - var visitor = HashVisitor(hasher) - try? traverse(visitor: &visitor) - hasher = visitor.hasher - } - - /// A description generated by recursively visiting all fields in the message, - /// including messages. - public var debugDescription: String { - #if DEBUG - // TODO Ideally there would be something like serializeText() that can - // take a prefix so we could do something like: - // [class name]( - // [text format] - // ) - let className = String(reflecting: type(of: self)) - let header = "\(className):\n" - return header + textFormatString() - #else - return String(reflecting: type(of: self)) - #endif - } - - /// Creates an instance of the message type on which this method is called, - /// executes the given block passing the message in as its sole `inout` - /// argument, and then returns the message. - /// - /// This method acts essentially as a "builder" in that the initialization of - /// the message is captured within the block, allowing the returned value to - /// be set in an immutable variable. For example, - /// - /// let msg = MyMessage.with { $0.myField = "foo" } - /// msg.myOtherField = 5 // error: msg is immutable - /// - /// - Parameter populator: A block or function that populates the new message, - /// which is passed into the block as an `inout` argument. - /// - Returns: The message after execution of the block. - public static func with( - _ populator: (inout Self) throws -> () - ) rethrows -> Self { - var message = Self() - try populator(&message) - return message - } + /// Generated proto2 messages that contain required fields, nested messages + /// that contain required fields, and/or extensions will provide their own + /// implementation of this property that tests that all required fields are + /// set. Users of the generated code SHOULD NOT override this property. + public var isInitialized: Bool { + // The generated code will include a specialization as needed. + true + } + + /// A hash based on the message's full contents. + public func hash(into hasher: inout Hasher) { + var visitor = HashVisitor(hasher) + try? traverse(visitor: &visitor) + hasher = visitor.hasher + } + + /// A description generated by recursively visiting all fields in the message, + /// including messages. + public var debugDescription: String { + #if DEBUG + // TODO Ideally there would be something like serializeText() that can + // take a prefix so we could do something like: + // [class name]( + // [text format] + // ) + let className = String(reflecting: type(of: self)) + let header = "\(className):\n" + return header + textFormatString() + #else + return String(reflecting: type(of: self)) + #endif + } + + /// Creates an instance of the message type on which this method is called, + /// executes the given block passing the message in as its sole `inout` + /// argument, and then returns the message. + /// + /// This method acts essentially as a "builder" in that the initialization of + /// the message is captured within the block, allowing the returned value to + /// be set in an immutable variable. For example, + /// + /// let msg = MyMessage.with { $0.myField = "foo" } + /// msg.myOtherField = 5 // error: msg is immutable + /// + /// - Parameter populator: A block or function that populates the new message, + /// which is passed into the block as an `inout` argument. + /// - Returns: The message after execution of the block. + public static func with( + _ populator: (inout Self) throws -> Void + ) rethrows -> Self { + var message = Self() + try populator(&message) + return message + } } /// Implementation base for all messages; not intended for client use. @@ -181,28 +181,28 @@ extension Message { @preconcurrency public protocol _MessageImplementationBase: Message, Hashable { - // Legacy function; no longer used, but left to maintain source compatibility. - func _protobuf_generated_isEqualTo(other: Self) -> Bool + // Legacy function; no longer used, but left to maintain source compatibility. + func _protobuf_generated_isEqualTo(other: Self) -> Bool } extension _MessageImplementationBase { - public func isEqualTo(message: any Message) -> Bool { - guard let other = message as? Self else { - return false + public func isEqualTo(message: any Message) -> Bool { + guard let other = message as? Self else { + return false + } + return self == other + } + + // Legacy default implementation that is used by old generated code, current + // versions of the plugin/generator provide this directly, but this is here + // just to avoid breaking source compatibility. + public static func == (lhs: Self, rhs: Self) -> Bool { + lhs._protobuf_generated_isEqualTo(other: rhs) + } + + // Legacy function that is generated by old versions of the plugin/generator, + // defaulted to keep things simple without changing the api surface. + public func _protobuf_generated_isEqualTo(other: Self) -> Bool { + self == other } - return self == other - } - - // Legacy default implementation that is used by old generated code, current - // versions of the plugin/generator provide this directly, but this is here - // just to avoid breaking source compatibility. - public static func ==(lhs: Self, rhs: Self) -> Bool { - return lhs._protobuf_generated_isEqualTo(other: rhs) - } - - // Legacy function that is generated by old versions of the plugin/generator, - // defaulted to keep things simple without changing the api surface. - public func _protobuf_generated_isEqualTo(other: Self) -> Bool { - return self == other - } } diff --git a/Sources/SwiftProtobuf/MessageExtension.swift b/Sources/SwiftProtobuf/MessageExtension.swift index 94cbc90b8..9d05d0987 100644 --- a/Sources/SwiftProtobuf/MessageExtension.swift +++ b/Sources/SwiftProtobuf/MessageExtension.swift @@ -37,6 +37,6 @@ public final class MessageExtension(decoder: inout D) throws -> (any AnyExtensionField)? { - return try FieldType(protobufExtension: self, decoder: &decoder) + try FieldType(protobufExtension: self, decoder: &decoder) } } diff --git a/Sources/SwiftProtobuf/NameMap.swift b/Sources/SwiftProtobuf/NameMap.swift index ca08579e0..3e92d9906 100644 --- a/Sources/SwiftProtobuf/NameMap.swift +++ b/Sources/SwiftProtobuf/NameMap.swift @@ -39,32 +39,36 @@ private func toJsonFieldName(_ s: String) -> String { /// Allocate static memory buffers to intern UTF-8 /// string data. Track the buffers and release all of those buffers /// in case we ever get deallocated. -fileprivate class InternPool { - private var interned = [UnsafeRawBufferPointer]() - - func intern(utf8: String.UTF8View) -> UnsafeRawBufferPointer { - let mutable = UnsafeMutableRawBufferPointer.allocate(byteCount: utf8.count, - alignment: MemoryLayout.alignment) - mutable.copyBytes(from: utf8) - let immutable = UnsafeRawBufferPointer(mutable) - interned.append(immutable) - return immutable - } - - func intern(utf8Ptr: UnsafeBufferPointer) -> UnsafeRawBufferPointer { - let mutable = UnsafeMutableRawBufferPointer.allocate(byteCount: utf8Ptr.count, - alignment: MemoryLayout.alignment) - mutable.copyBytes(from: utf8Ptr) - let immutable = UnsafeRawBufferPointer(mutable) - interned.append(immutable) - return immutable - } - - deinit { - for buff in interned { - buff.deallocate() +private class InternPool { + private var interned = [UnsafeRawBufferPointer]() + + func intern(utf8: String.UTF8View) -> UnsafeRawBufferPointer { + let mutable = UnsafeMutableRawBufferPointer.allocate( + byteCount: utf8.count, + alignment: MemoryLayout.alignment + ) + mutable.copyBytes(from: utf8) + let immutable = UnsafeRawBufferPointer(mutable) + interned.append(immutable) + return immutable + } + + func intern(utf8Ptr: UnsafeBufferPointer) -> UnsafeRawBufferPointer { + let mutable = UnsafeMutableRawBufferPointer.allocate( + byteCount: utf8Ptr.count, + alignment: MemoryLayout.alignment + ) + mutable.copyBytes(from: utf8Ptr) + let immutable = UnsafeRawBufferPointer(mutable) + interned.append(immutable) + return immutable + } + + deinit { + for buff in interned { + buff.deallocate() + } } - } } /// An immutable bidirectional mapping between field/enum-case names @@ -74,215 +78,217 @@ fileprivate class InternPool { /// users who do not use text-based serialization formats. public struct _NameMap: ExpressibleByDictionaryLiteral { - /// An immutable interned string container. The `utf8Start` pointer - /// is guaranteed valid for the lifetime of the `NameMap` that you - /// fetched it from. Since `NameMap`s are only instantiated as - /// immutable static values, that should be the lifetime of the - /// program. - /// - /// Internally, this uses `StaticString` (which refers to a fixed - /// block of UTF-8 data) where possible. In cases where the string - /// has to be computed, it caches the UTF-8 bytes in an - /// unmovable and immutable heap area. - internal struct Name: Hashable, CustomStringConvertible { - // This should not be used outside of this file, as it requires - // coordinating the lifecycle with the lifecycle of the pool - // where the raw UTF8 gets interned. - fileprivate init(staticString: StaticString, pool: InternPool) { - self.nameString = .staticString(staticString) - if staticString.hasPointerRepresentation { - self.utf8Buffer = UnsafeRawBufferPointer(start: staticString.utf8Start, - count: staticString.utf8CodeUnitCount) - } else { - self.utf8Buffer = staticString.withUTF8Buffer { pool.intern(utf8Ptr: $0) } + /// An immutable interned string container. The `utf8Start` pointer + /// is guaranteed valid for the lifetime of the `NameMap` that you + /// fetched it from. Since `NameMap`s are only instantiated as + /// immutable static values, that should be the lifetime of the + /// program. + /// + /// Internally, this uses `StaticString` (which refers to a fixed + /// block of UTF-8 data) where possible. In cases where the string + /// has to be computed, it caches the UTF-8 bytes in an + /// unmovable and immutable heap area. + internal struct Name: Hashable, CustomStringConvertible { + // This should not be used outside of this file, as it requires + // coordinating the lifecycle with the lifecycle of the pool + // where the raw UTF8 gets interned. + fileprivate init(staticString: StaticString, pool: InternPool) { + self.nameString = .staticString(staticString) + if staticString.hasPointerRepresentation { + self.utf8Buffer = UnsafeRawBufferPointer( + start: staticString.utf8Start, + count: staticString.utf8CodeUnitCount + ) + } else { + self.utf8Buffer = staticString.withUTF8Buffer { pool.intern(utf8Ptr: $0) } + } } - } - // This should not be used outside of this file, as it requires - // coordinating the lifecycle with the lifecycle of the pool - // where the raw UTF8 gets interned. - fileprivate init(string: String, pool: InternPool) { - let utf8 = string.utf8 - self.utf8Buffer = pool.intern(utf8: utf8) - self.nameString = .string(string) + // This should not be used outside of this file, as it requires + // coordinating the lifecycle with the lifecycle of the pool + // where the raw UTF8 gets interned. + fileprivate init(string: String, pool: InternPool) { + let utf8 = string.utf8 + self.utf8Buffer = pool.intern(utf8: utf8) + self.nameString = .string(string) + } + + // This is for building a transient `Name` object sufficient for lookup purposes. + // It MUST NOT be exposed outside of this file. + fileprivate init(transientUtf8Buffer: UnsafeRawBufferPointer) { + self.nameString = .staticString("") + self.utf8Buffer = transientUtf8Buffer + } + + private(set) var utf8Buffer: UnsafeRawBufferPointer + + private enum NameString { + case string(String) + case staticString(StaticString) + } + private var nameString: NameString + + public var description: String { + switch nameString { + case .string(let s): return s + case .staticString(let s): return s.description + } + } + + public func hash(into hasher: inout Hasher) { + for byte in utf8Buffer { + hasher.combine(byte) + } + } + + public static func == (lhs: Name, rhs: Name) -> Bool { + if lhs.utf8Buffer.count != rhs.utf8Buffer.count { + return false + } + return lhs.utf8Buffer.elementsEqual(rhs.utf8Buffer) + } } - // This is for building a transient `Name` object sufficient for lookup purposes. - // It MUST NOT be exposed outside of this file. - fileprivate init(transientUtf8Buffer: UnsafeRawBufferPointer) { - self.nameString = .staticString("") - self.utf8Buffer = transientUtf8Buffer + /// The JSON and proto names for a particular field, enum case, or extension. + internal struct Names { + private(set) var json: Name? + private(set) var proto: Name } - private(set) var utf8Buffer: UnsafeRawBufferPointer + /// A description of the names for a particular field or enum case. + /// The different forms here let us minimize the amount of string + /// data that we store in the binary. + /// + /// These are only used in the generated code to initialize a NameMap. + public enum NameDescription { + + /// The proto (text format) name and the JSON name are the same string. + case same(proto: StaticString) + + /// The JSON name can be computed from the proto string + case standard(proto: StaticString) - private enum NameString { - case string(String) - case staticString(StaticString) + /// The JSON and text format names are just different. + case unique(proto: StaticString, json: StaticString) + + /// Used for enum cases only to represent a value's primary proto name (the + /// first defined case) and its aliases. The JSON and text format names for + /// enums are always the same. + case aliased(proto: StaticString, aliases: [StaticString]) } - private var nameString: NameString - public var description: String { - switch nameString { - case .string(let s): return s - case .staticString(let s): return s.description - } + private var internPool = InternPool() + + /// The mapping from field/enum-case numbers to names. + private var numberToNameMap: [Int: Names] = [:] + + /// The mapping from proto/text names to field/enum-case numbers. + private var protoToNumberMap: [Name: Int] = [:] + + /// The mapping from JSON names to field/enum-case numbers. + /// Note that this also contains all of the proto/text names, + /// as required by Google's spec for protobuf JSON. + private var jsonToNumberMap: [Name: Int] = [:] + + /// Creates a new empty field/enum-case name/number mapping. + public init() {} + + /// Build the bidirectional maps between numbers and proto/JSON names. + public init(dictionaryLiteral elements: (Int, NameDescription)...) { + for (number, description) in elements { + switch description { + + case .same(proto: let p): + let protoName = Name(staticString: p, pool: internPool) + let names = Names(json: protoName, proto: protoName) + numberToNameMap[number] = names + protoToNumberMap[protoName] = number + jsonToNumberMap[protoName] = number + + case .standard(proto: let p): + let protoName = Name(staticString: p, pool: internPool) + let jsonString = toJsonFieldName(protoName.description) + let jsonName = Name(string: jsonString, pool: internPool) + let names = Names(json: jsonName, proto: protoName) + numberToNameMap[number] = names + protoToNumberMap[protoName] = number + jsonToNumberMap[protoName] = number + jsonToNumberMap[jsonName] = number + + case .unique(proto: let p, json: let j): + let jsonName = Name(staticString: j, pool: internPool) + let protoName = Name(staticString: p, pool: internPool) + let names = Names(json: jsonName, proto: protoName) + numberToNameMap[number] = names + protoToNumberMap[protoName] = number + jsonToNumberMap[protoName] = number + jsonToNumberMap[jsonName] = number + + case .aliased(proto: let p, let aliases): + let protoName = Name(staticString: p, pool: internPool) + let names = Names(json: protoName, proto: protoName) + numberToNameMap[number] = names + protoToNumberMap[protoName] = number + jsonToNumberMap[protoName] = number + for alias in aliases { + let protoName = Name(staticString: alias, pool: internPool) + protoToNumberMap[protoName] = number + jsonToNumberMap[protoName] = number + } + } + } } - public func hash(into hasher: inout Hasher) { - for byte in utf8Buffer { - hasher.combine(byte) - } + /// Returns the name bundle for the field/enum-case with the given number, or + /// `nil` if there is no match. + internal func names(for number: Int) -> Names? { + numberToNameMap[number] } - public static func ==(lhs: Name, rhs: Name) -> Bool { - if lhs.utf8Buffer.count != rhs.utf8Buffer.count { - return false - } - return lhs.utf8Buffer.elementsEqual(rhs.utf8Buffer) + /// Returns the field/enum-case number that has the given JSON name, + /// or `nil` if there is no match. + /// + /// This is used by the Text format parser to look up field or enum + /// names using a direct reference to the un-decoded UTF8 bytes. + internal func number(forProtoName raw: UnsafeRawBufferPointer) -> Int? { + let n = Name(transientUtf8Buffer: raw) + return protoToNumberMap[n] } - } - - /// The JSON and proto names for a particular field, enum case, or extension. - internal struct Names { - private(set) var json: Name? - private(set) var proto: Name - } - - /// A description of the names for a particular field or enum case. - /// The different forms here let us minimize the amount of string - /// data that we store in the binary. - /// - /// These are only used in the generated code to initialize a NameMap. - public enum NameDescription { - - /// The proto (text format) name and the JSON name are the same string. - case same(proto: StaticString) - - /// The JSON name can be computed from the proto string - case standard(proto: StaticString) - - /// The JSON and text format names are just different. - case unique(proto: StaticString, json: StaticString) - - /// Used for enum cases only to represent a value's primary proto name (the - /// first defined case) and its aliases. The JSON and text format names for - /// enums are always the same. - case aliased(proto: StaticString, aliases: [StaticString]) - } - - private var internPool = InternPool() - - /// The mapping from field/enum-case numbers to names. - private var numberToNameMap: [Int: Names] = [:] - - /// The mapping from proto/text names to field/enum-case numbers. - private var protoToNumberMap: [Name: Int] = [:] - - /// The mapping from JSON names to field/enum-case numbers. - /// Note that this also contains all of the proto/text names, - /// as required by Google's spec for protobuf JSON. - private var jsonToNumberMap: [Name: Int] = [:] - - /// Creates a new empty field/enum-case name/number mapping. - public init() {} - - /// Build the bidirectional maps between numbers and proto/JSON names. - public init(dictionaryLiteral elements: (Int, NameDescription)...) { - for (number, description) in elements { - switch description { - - case .same(proto: let p): - let protoName = Name(staticString: p, pool: internPool) - let names = Names(json: protoName, proto: protoName) - numberToNameMap[number] = names - protoToNumberMap[protoName] = number - jsonToNumberMap[protoName] = number - - case .standard(proto: let p): - let protoName = Name(staticString: p, pool: internPool) - let jsonString = toJsonFieldName(protoName.description) - let jsonName = Name(string: jsonString, pool: internPool) - let names = Names(json: jsonName, proto: protoName) - numberToNameMap[number] = names - protoToNumberMap[protoName] = number - jsonToNumberMap[protoName] = number - jsonToNumberMap[jsonName] = number - - case .unique(proto: let p, json: let j): - let jsonName = Name(staticString: j, pool: internPool) - let protoName = Name(staticString: p, pool: internPool) - let names = Names(json: jsonName, proto: protoName) - numberToNameMap[number] = names - protoToNumberMap[protoName] = number - jsonToNumberMap[protoName] = number - jsonToNumberMap[jsonName] = number - - case .aliased(proto: let p, aliases: let aliases): - let protoName = Name(staticString: p, pool: internPool) - let names = Names(json: protoName, proto: protoName) - numberToNameMap[number] = names - protoToNumberMap[protoName] = number - jsonToNumberMap[protoName] = number - for alias in aliases { - let protoName = Name(staticString: alias, pool: internPool) - protoToNumberMap[protoName] = number - jsonToNumberMap[protoName] = number + + /// Returns the field/enum-case number that has the given JSON name, + /// or `nil` if there is no match. + /// + /// This accepts a regular `String` and is used in JSON parsing + /// only when a field name or enum name was decoded from a string + /// containing backslash escapes. + /// + /// JSON parsing must interpret *both* the JSON name of the + /// field/enum-case provided by the descriptor *as well as* its + /// original proto/text name. + internal func number(forJSONName name: String) -> Int? { + let utf8 = Array(name.utf8) + return utf8.withUnsafeBytes { (buffer: UnsafeRawBufferPointer) in + let n = Name(transientUtf8Buffer: buffer) + return jsonToNumberMap[n] } - } } - } - - /// Returns the name bundle for the field/enum-case with the given number, or - /// `nil` if there is no match. - internal func names(for number: Int) -> Names? { - return numberToNameMap[number] - } - - /// Returns the field/enum-case number that has the given JSON name, - /// or `nil` if there is no match. - /// - /// This is used by the Text format parser to look up field or enum - /// names using a direct reference to the un-decoded UTF8 bytes. - internal func number(forProtoName raw: UnsafeRawBufferPointer) -> Int? { - let n = Name(transientUtf8Buffer: raw) - return protoToNumberMap[n] - } - - /// Returns the field/enum-case number that has the given JSON name, - /// or `nil` if there is no match. - /// - /// This accepts a regular `String` and is used in JSON parsing - /// only when a field name or enum name was decoded from a string - /// containing backslash escapes. - /// - /// JSON parsing must interpret *both* the JSON name of the - /// field/enum-case provided by the descriptor *as well as* its - /// original proto/text name. - internal func number(forJSONName name: String) -> Int? { - let utf8 = Array(name.utf8) - return utf8.withUnsafeBytes { (buffer: UnsafeRawBufferPointer) in - let n = Name(transientUtf8Buffer: buffer) - return jsonToNumberMap[n] + + /// Returns the field/enum-case number that has the given JSON name, + /// or `nil` if there is no match. + /// + /// This is used by the JSON parser when a field name or enum name + /// required no special processing. As a result, we can avoid + /// copying the name and look up the number using a direct reference + /// to the un-decoded UTF8 bytes. + internal func number(forJSONName raw: UnsafeRawBufferPointer) -> Int? { + let n = Name(transientUtf8Buffer: raw) + return jsonToNumberMap[n] + } + + /// Returns all proto names + internal var names: [Name] { + numberToNameMap.map(\.value.proto) } - } - - /// Returns the field/enum-case number that has the given JSON name, - /// or `nil` if there is no match. - /// - /// This is used by the JSON parser when a field name or enum name - /// required no special processing. As a result, we can avoid - /// copying the name and look up the number using a direct reference - /// to the un-decoded UTF8 bytes. - internal func number(forJSONName raw: UnsafeRawBufferPointer) -> Int? { - let n = Name(transientUtf8Buffer: raw) - return jsonToNumberMap[n] - } - - /// Returns all proto names - internal var names: [Name] { - numberToNameMap.map(\.value.proto) - } } // The `_NameMap` (and supporting types) are only mutated during their initial @@ -294,6 +300,6 @@ public struct _NameMap: ExpressibleByDictionaryLiteral { // https://github.com/apple/swift-protobuf/issues/1561 is also opened to revisit // the `_NameMap` generally as it dates back to the days before Swift perferred // the UTF-8 internal encoding. -extension _NameMap : Sendable {} -extension _NameMap.Name : @unchecked Sendable {} -extension InternPool : @unchecked Sendable {} +extension _NameMap: Sendable {} +extension _NameMap.Name: @unchecked Sendable {} +extension InternPool: @unchecked Sendable {} diff --git a/Sources/SwiftProtobuf/PathDecoder.swift b/Sources/SwiftProtobuf/PathDecoder.swift index 7b7e67a9c..f486623a5 100644 --- a/Sources/SwiftProtobuf/PathDecoder.swift +++ b/Sources/SwiftProtobuf/PathDecoder.swift @@ -17,424 +17,427 @@ import Foundation /// Describes errors can occure during decoding a proto by path. public enum PathDecodingError: Error { - /// Describes a mismatch in type of the fields. - /// - /// If a value of type A is applied to a path with type B. - /// this error will be thrown. - case typeMismatch - - /// Describes path is not found in message type. - /// - /// If a message has no field with the given path this - /// error will be thrown. - case pathNotFound + /// Describes a mismatch in type of the fields. + /// + /// If a value of type A is applied to a path with type B. + /// this error will be thrown. + case typeMismatch + + /// Describes path is not found in message type. + /// + /// If a message has no field with the given path this + /// error will be thrown. + case pathNotFound } extension Message { - static func number(for field: String) -> Int? { - guard let type = Self.self as? any _ProtoNameProviding.Type else { - return nil + static func number(for field: String) -> Int? { + guard let type = Self.self as? any _ProtoNameProviding.Type else { + return nil + } + guard + let number = Array(field.utf8).withUnsafeBytes({ bytes in + type._protobuf_nameMap.number(forProtoName: bytes) + }) + else { + return nil + } + if type._protobuf_nameMap.names(for: number)?.proto.description != field { + return nil + } + return number } - guard let number = Array(field.utf8).withUnsafeBytes({ bytes in - type._protobuf_nameMap.number(forProtoName: bytes) - }) else { - return nil - } - if type._protobuf_nameMap.names(for: number)?.proto.description != field { - return nil - } - return number - } - static func name(for field: Int) -> String? { - guard let type = Self.self as? any _ProtoNameProviding.Type else { - return nil + static func name(for field: Int) -> String? { + guard let type = Self.self as? any _ProtoNameProviding.Type else { + return nil + } + return type._protobuf_nameMap.names(for: field)?.proto.description } - return type._protobuf_nameMap.names(for: field)?.proto.description - } } // Decoder that set value of a message field by the given path struct PathDecoder: Decoder { - // The value should be set to the path - private let value: Any? - - // Field number should be overriden by decoder - private var number: Int? - - // The path only including sub-paths - private let nextPath: [String] - - // Merge options to be concidered while setting value - private let mergeOption: Google_Protobuf_FieldMask.MergeOptions - - private var replaceRepeatedFields: Bool { - mergeOption.replaceRepeatedFields - } - - init( - path: [String], - value: Any?, - mergeOption: Google_Protobuf_FieldMask.MergeOptions - ) throws { - if let firstComponent = path.first, - let number = T.number(for: firstComponent) { - self.number = number - self.nextPath = .init(path.dropFirst()) - } else { - throw PathDecodingError.pathNotFound - } - self.value = value - self.mergeOption = mergeOption - } - - private func setValue(_ value: inout V, defaultValue: V) throws { - if !nextPath.isEmpty { - throw PathDecodingError.pathNotFound - } - if self.value == nil { - value = defaultValue - return - } - guard let castedValue = self.value as? V else { - throw PathDecodingError.typeMismatch - } - value = castedValue - } - - private func setRepeatedValue(_ value: inout [V]) throws { - if !nextPath.isEmpty { - throw PathDecodingError.pathNotFound - } - var castedValue: [V] = [] - if self.value != nil { - guard let v = self.value as? [V] else { - throw PathDecodingError.typeMismatch - } - castedValue = v - } - if replaceRepeatedFields { - value = castedValue - } else { - value.append(contentsOf: castedValue) - } - } - - private func setMapValue( - _ value: inout Dictionary - ) throws { - if !nextPath.isEmpty { - throw PathDecodingError.pathNotFound - } - var castedValue: [K: V] = [:] - if self.value != nil { - guard let v = self.value as? Dictionary else { - throw PathDecodingError.typeMismatch - } - castedValue = v - } - if replaceRepeatedFields { - value = castedValue - } else { - value.merge(castedValue) { _, new in - new - } - } - } - - private func setMessageValue( - _ value: inout M? - ) throws { - if nextPath.isEmpty { - try setValue(&value, defaultValue: nil) - return - } - var decoder = try PathDecoder( - path: nextPath, - value: self.value, - mergeOption: mergeOption - ) - if value == nil { - value = .init() - } - try value?.decodeMessage(decoder: &decoder) - } - - mutating func handleConflictingOneOf() throws {} - - mutating func nextFieldNumber() throws -> Int? { - defer { number = nil } - return number - } - - mutating func decodeSingularFloatField(value: inout Float) throws { - try setValue(&value, defaultValue: .init()) - } - - mutating func decodeSingularFloatField(value: inout Float?) throws { - try setValue(&value, defaultValue: nil) - } - - mutating func decodeRepeatedFloatField(value: inout [Float]) throws { - try setRepeatedValue(&value) - } - - mutating func decodeSingularDoubleField(value: inout Double) throws { - try setValue(&value, defaultValue: .init()) - } - - mutating func decodeSingularDoubleField(value: inout Double?) throws { - try setValue(&value, defaultValue: nil) - } - - mutating func decodeRepeatedDoubleField(value: inout [Double]) throws { - try setRepeatedValue(&value) - } - - mutating func decodeSingularInt32Field(value: inout Int32) throws { - try setValue(&value, defaultValue: .init()) - } - - mutating func decodeSingularInt32Field(value: inout Int32?) throws { - try setValue(&value, defaultValue: nil) - } - - mutating func decodeRepeatedInt32Field(value: inout [Int32]) throws { - try setRepeatedValue(&value) - } - - mutating func decodeSingularInt64Field(value: inout Int64) throws { - try setValue(&value, defaultValue: .init()) - } - - mutating func decodeSingularInt64Field(value: inout Int64?) throws { - try setValue(&value, defaultValue: nil) - } - - mutating func decodeRepeatedInt64Field(value: inout [Int64]) throws { - try setRepeatedValue(&value) - } - - mutating func decodeSingularUInt32Field(value: inout UInt32) throws { - try setValue(&value, defaultValue: .init()) - } - - mutating func decodeSingularUInt32Field(value: inout UInt32?) throws { - try setValue(&value, defaultValue: nil) - } - - mutating func decodeRepeatedUInt32Field(value: inout [UInt32]) throws { - try setRepeatedValue(&value) - } - - mutating func decodeSingularUInt64Field(value: inout UInt64) throws { - try setValue(&value, defaultValue: .init()) - } - - mutating func decodeSingularUInt64Field(value: inout UInt64?) throws { - try setValue(&value, defaultValue: nil) - } - - mutating func decodeRepeatedUInt64Field(value: inout [UInt64]) throws { - try setRepeatedValue(&value) - } + // The value should be set to the path + private let value: Any? - mutating func decodeSingularSInt32Field(value: inout Int32) throws { - try setValue(&value, defaultValue: .init()) - } - - mutating func decodeSingularSInt32Field(value: inout Int32?) throws { - try setValue(&value, defaultValue: nil) - } + // Field number should be overriden by decoder + private var number: Int? - mutating func decodeRepeatedSInt32Field(value: inout [Int32]) throws { - try setRepeatedValue(&value) - } - - mutating func decodeSingularSInt64Field(value: inout Int64) throws { - try setValue(&value, defaultValue: .init()) - } + // The path only including sub-paths + private let nextPath: [String] - mutating func decodeSingularSInt64Field(value: inout Int64?) throws { - try setValue(&value, defaultValue: nil) - } + // Merge options to be concidered while setting value + private let mergeOption: Google_Protobuf_FieldMask.MergeOptions - mutating func decodeRepeatedSInt64Field(value: inout [Int64]) throws { - try setRepeatedValue(&value) - } - - mutating func decodeSingularFixed32Field(value: inout UInt32) throws { - try setValue(&value, defaultValue: .init()) - } - - mutating func decodeSingularFixed32Field(value: inout UInt32?) throws { - try setValue(&value, defaultValue: nil) - } - - mutating func decodeRepeatedFixed32Field(value: inout [UInt32]) throws { - try setRepeatedValue(&value) - } - - mutating func decodeSingularFixed64Field(value: inout UInt64) throws { - try setValue(&value, defaultValue: .init()) - } - - mutating func decodeSingularFixed64Field(value: inout UInt64?) throws { - try setValue(&value, defaultValue: nil) - } - - mutating func decodeRepeatedFixed64Field(value: inout [UInt64]) throws { - try setRepeatedValue(&value) - } - - mutating func decodeSingularSFixed32Field(value: inout Int32) throws { - try setValue(&value, defaultValue: .init()) - } - - mutating func decodeSingularSFixed32Field(value: inout Int32?) throws { - try setValue(&value, defaultValue: nil) - } - - mutating func decodeRepeatedSFixed32Field(value: inout [Int32]) throws { - try setRepeatedValue(&value) - } - - mutating func decodeSingularSFixed64Field(value: inout Int64) throws { - try setValue(&value, defaultValue: .init()) - } - - mutating func decodeSingularSFixed64Field(value: inout Int64?) throws { - try setValue(&value, defaultValue: nil) - } - - mutating func decodeRepeatedSFixed64Field(value: inout [Int64]) throws { - try setRepeatedValue(&value) - } - - mutating func decodeSingularBoolField(value: inout Bool) throws { - try setValue(&value, defaultValue: .init()) - } - - mutating func decodeSingularBoolField(value: inout Bool?) throws { - try setValue(&value, defaultValue: nil) - } - - mutating func decodeRepeatedBoolField(value: inout [Bool]) throws { - try setRepeatedValue(&value) - } - - mutating func decodeSingularStringField(value: inout String) throws { - try setValue(&value, defaultValue: .init()) - } - - mutating func decodeSingularStringField(value: inout String?) throws { - try setValue(&value, defaultValue: nil) - } - - mutating func decodeRepeatedStringField(value: inout [String]) throws { - try setRepeatedValue(&value) - } - - mutating func decodeSingularBytesField(value: inout Data) throws { - try setValue(&value, defaultValue: .init()) - } - - mutating func decodeSingularBytesField(value: inout Data?) throws { - try setValue(&value, defaultValue: nil) - } - - mutating func decodeRepeatedBytesField(value: inout [Data]) throws { - try setRepeatedValue(&value) - } - - mutating func decodeSingularEnumField( - value: inout E - ) throws where E : Enum, E.RawValue == Int { - try setValue(&value, defaultValue: .init()) - } - - mutating func decodeSingularEnumField( - value: inout E? - ) throws where E : Enum, E.RawValue == Int { - try setValue(&value, defaultValue: nil) - } - - mutating func decodeRepeatedEnumField( - value: inout [E] - ) throws where E : Enum, E.RawValue == Int { - try setRepeatedValue(&value) - } - - mutating func decodeSingularMessageField( - value: inout M? - ) throws where M : Message { - try setMessageValue(&value) - } - - mutating func decodeRepeatedMessageField( - value: inout [M] - ) throws where M : Message { - try setRepeatedValue(&value) - } - - mutating func decodeSingularGroupField( - value: inout G? - ) throws where G : Message { - try setMessageValue(&value) - } - - mutating func decodeRepeatedGroupField( - value: inout [G] - ) throws where G : Message { - try setRepeatedValue(&value) - } - - mutating func decodeMapField( - fieldType: _ProtobufMap.Type, - value: inout _ProtobufMap.BaseType - ) throws where KeyType : MapKeyType, ValueType : MapValueType { - try setMapValue(&value) - } - - mutating func decodeMapField( - fieldType: _ProtobufEnumMap.Type, - value: inout _ProtobufEnumMap.BaseType - ) throws where KeyType : MapKeyType, ValueType : Enum, ValueType.RawValue == Int { - try setMapValue(&value) - } - - mutating func decodeMapField( - fieldType: _ProtobufMessageMap.Type, - value: inout _ProtobufMessageMap.BaseType - ) throws where KeyType : MapKeyType, ValueType : Hashable, ValueType : Message { - try setMapValue(&value) - } - - mutating func decodeExtensionField( - values: inout ExtensionFieldValueSet, - messageType: any Message.Type, - fieldNumber: Int - ) throws { - preconditionFailure( - "Internal Error: Path decoder should never decode an extension field" - ) - } + private var replaceRepeatedFields: Bool { + mergeOption.replaceRepeatedFields + } + + init( + path: [String], + value: Any?, + mergeOption: Google_Protobuf_FieldMask.MergeOptions + ) throws { + if let firstComponent = path.first, + let number = T.number(for: firstComponent) + { + self.number = number + self.nextPath = .init(path.dropFirst()) + } else { + throw PathDecodingError.pathNotFound + } + self.value = value + self.mergeOption = mergeOption + } + + private func setValue(_ value: inout V, defaultValue: V) throws { + if !nextPath.isEmpty { + throw PathDecodingError.pathNotFound + } + if self.value == nil { + value = defaultValue + return + } + guard let castedValue = self.value as? V else { + throw PathDecodingError.typeMismatch + } + value = castedValue + } + + private func setRepeatedValue(_ value: inout [V]) throws { + if !nextPath.isEmpty { + throw PathDecodingError.pathNotFound + } + var castedValue: [V] = [] + if self.value != nil { + guard let v = self.value as? [V] else { + throw PathDecodingError.typeMismatch + } + castedValue = v + } + if replaceRepeatedFields { + value = castedValue + } else { + value.append(contentsOf: castedValue) + } + } + + private func setMapValue( + _ value: inout [K: V] + ) throws { + if !nextPath.isEmpty { + throw PathDecodingError.pathNotFound + } + var castedValue: [K: V] = [:] + if self.value != nil { + guard let v = self.value as? [K: V] else { + throw PathDecodingError.typeMismatch + } + castedValue = v + } + if replaceRepeatedFields { + value = castedValue + } else { + value.merge(castedValue) { _, new in + new + } + } + } + + private func setMessageValue( + _ value: inout M? + ) throws { + if nextPath.isEmpty { + try setValue(&value, defaultValue: nil) + return + } + var decoder = try PathDecoder( + path: nextPath, + value: self.value, + mergeOption: mergeOption + ) + if value == nil { + value = .init() + } + try value?.decodeMessage(decoder: &decoder) + } + + mutating func handleConflictingOneOf() throws {} + + mutating func nextFieldNumber() throws -> Int? { + defer { number = nil } + return number + } + + mutating func decodeSingularFloatField(value: inout Float) throws { + try setValue(&value, defaultValue: .init()) + } + + mutating func decodeSingularFloatField(value: inout Float?) throws { + try setValue(&value, defaultValue: nil) + } + + mutating func decodeRepeatedFloatField(value: inout [Float]) throws { + try setRepeatedValue(&value) + } + + mutating func decodeSingularDoubleField(value: inout Double) throws { + try setValue(&value, defaultValue: .init()) + } + + mutating func decodeSingularDoubleField(value: inout Double?) throws { + try setValue(&value, defaultValue: nil) + } + + mutating func decodeRepeatedDoubleField(value: inout [Double]) throws { + try setRepeatedValue(&value) + } + + mutating func decodeSingularInt32Field(value: inout Int32) throws { + try setValue(&value, defaultValue: .init()) + } + + mutating func decodeSingularInt32Field(value: inout Int32?) throws { + try setValue(&value, defaultValue: nil) + } + + mutating func decodeRepeatedInt32Field(value: inout [Int32]) throws { + try setRepeatedValue(&value) + } + + mutating func decodeSingularInt64Field(value: inout Int64) throws { + try setValue(&value, defaultValue: .init()) + } + + mutating func decodeSingularInt64Field(value: inout Int64?) throws { + try setValue(&value, defaultValue: nil) + } + + mutating func decodeRepeatedInt64Field(value: inout [Int64]) throws { + try setRepeatedValue(&value) + } + + mutating func decodeSingularUInt32Field(value: inout UInt32) throws { + try setValue(&value, defaultValue: .init()) + } + + mutating func decodeSingularUInt32Field(value: inout UInt32?) throws { + try setValue(&value, defaultValue: nil) + } + + mutating func decodeRepeatedUInt32Field(value: inout [UInt32]) throws { + try setRepeatedValue(&value) + } + + mutating func decodeSingularUInt64Field(value: inout UInt64) throws { + try setValue(&value, defaultValue: .init()) + } + + mutating func decodeSingularUInt64Field(value: inout UInt64?) throws { + try setValue(&value, defaultValue: nil) + } + + mutating func decodeRepeatedUInt64Field(value: inout [UInt64]) throws { + try setRepeatedValue(&value) + } + + mutating func decodeSingularSInt32Field(value: inout Int32) throws { + try setValue(&value, defaultValue: .init()) + } + + mutating func decodeSingularSInt32Field(value: inout Int32?) throws { + try setValue(&value, defaultValue: nil) + } + + mutating func decodeRepeatedSInt32Field(value: inout [Int32]) throws { + try setRepeatedValue(&value) + } + + mutating func decodeSingularSInt64Field(value: inout Int64) throws { + try setValue(&value, defaultValue: .init()) + } + + mutating func decodeSingularSInt64Field(value: inout Int64?) throws { + try setValue(&value, defaultValue: nil) + } + + mutating func decodeRepeatedSInt64Field(value: inout [Int64]) throws { + try setRepeatedValue(&value) + } + + mutating func decodeSingularFixed32Field(value: inout UInt32) throws { + try setValue(&value, defaultValue: .init()) + } + + mutating func decodeSingularFixed32Field(value: inout UInt32?) throws { + try setValue(&value, defaultValue: nil) + } + + mutating func decodeRepeatedFixed32Field(value: inout [UInt32]) throws { + try setRepeatedValue(&value) + } + + mutating func decodeSingularFixed64Field(value: inout UInt64) throws { + try setValue(&value, defaultValue: .init()) + } + + mutating func decodeSingularFixed64Field(value: inout UInt64?) throws { + try setValue(&value, defaultValue: nil) + } + + mutating func decodeRepeatedFixed64Field(value: inout [UInt64]) throws { + try setRepeatedValue(&value) + } + + mutating func decodeSingularSFixed32Field(value: inout Int32) throws { + try setValue(&value, defaultValue: .init()) + } + + mutating func decodeSingularSFixed32Field(value: inout Int32?) throws { + try setValue(&value, defaultValue: nil) + } + + mutating func decodeRepeatedSFixed32Field(value: inout [Int32]) throws { + try setRepeatedValue(&value) + } + + mutating func decodeSingularSFixed64Field(value: inout Int64) throws { + try setValue(&value, defaultValue: .init()) + } + + mutating func decodeSingularSFixed64Field(value: inout Int64?) throws { + try setValue(&value, defaultValue: nil) + } + + mutating func decodeRepeatedSFixed64Field(value: inout [Int64]) throws { + try setRepeatedValue(&value) + } + + mutating func decodeSingularBoolField(value: inout Bool) throws { + try setValue(&value, defaultValue: .init()) + } + + mutating func decodeSingularBoolField(value: inout Bool?) throws { + try setValue(&value, defaultValue: nil) + } + + mutating func decodeRepeatedBoolField(value: inout [Bool]) throws { + try setRepeatedValue(&value) + } + + mutating func decodeSingularStringField(value: inout String) throws { + try setValue(&value, defaultValue: .init()) + } + + mutating func decodeSingularStringField(value: inout String?) throws { + try setValue(&value, defaultValue: nil) + } + + mutating func decodeRepeatedStringField(value: inout [String]) throws { + try setRepeatedValue(&value) + } + + mutating func decodeSingularBytesField(value: inout Data) throws { + try setValue(&value, defaultValue: .init()) + } + + mutating func decodeSingularBytesField(value: inout Data?) throws { + try setValue(&value, defaultValue: nil) + } + + mutating func decodeRepeatedBytesField(value: inout [Data]) throws { + try setRepeatedValue(&value) + } + + mutating func decodeSingularEnumField( + value: inout E + ) throws where E: Enum, E.RawValue == Int { + try setValue(&value, defaultValue: .init()) + } + + mutating func decodeSingularEnumField( + value: inout E? + ) throws where E: Enum, E.RawValue == Int { + try setValue(&value, defaultValue: nil) + } + + mutating func decodeRepeatedEnumField( + value: inout [E] + ) throws where E: Enum, E.RawValue == Int { + try setRepeatedValue(&value) + } + + mutating func decodeSingularMessageField( + value: inout M? + ) throws where M: Message { + try setMessageValue(&value) + } + + mutating func decodeRepeatedMessageField( + value: inout [M] + ) throws where M: Message { + try setRepeatedValue(&value) + } + + mutating func decodeSingularGroupField( + value: inout G? + ) throws where G: Message { + try setMessageValue(&value) + } + + mutating func decodeRepeatedGroupField( + value: inout [G] + ) throws where G: Message { + try setRepeatedValue(&value) + } + + mutating func decodeMapField( + fieldType: _ProtobufMap.Type, + value: inout _ProtobufMap.BaseType + ) throws where KeyType: MapKeyType, ValueType: MapValueType { + try setMapValue(&value) + } + + mutating func decodeMapField( + fieldType: _ProtobufEnumMap.Type, + value: inout _ProtobufEnumMap.BaseType + ) throws where KeyType: MapKeyType, ValueType: Enum, ValueType.RawValue == Int { + try setMapValue(&value) + } + + mutating func decodeMapField( + fieldType: _ProtobufMessageMap.Type, + value: inout _ProtobufMessageMap.BaseType + ) throws where KeyType: MapKeyType, ValueType: Hashable, ValueType: Message { + try setMapValue(&value) + } + + mutating func decodeExtensionField( + values: inout ExtensionFieldValueSet, + messageType: any Message.Type, + fieldNumber: Int + ) throws { + preconditionFailure( + "Internal Error: Path decoder should never decode an extension field" + ) + } } extension Message { - mutating func `set`( - path: String, - value: Any?, - mergeOption: Google_Protobuf_FieldMask.MergeOptions - ) throws { - let _path = path.components(separatedBy: ".") - var decoder = try PathDecoder( - path: _path, - value: value, - mergeOption: mergeOption - ) - try decodeMessage(decoder: &decoder) - } + mutating func `set`( + path: String, + value: Any?, + mergeOption: Google_Protobuf_FieldMask.MergeOptions + ) throws { + let _path = path.components(separatedBy: ".") + var decoder = try PathDecoder( + path: _path, + value: value, + mergeOption: mergeOption + ) + try decodeMessage(decoder: &decoder) + } } diff --git a/Sources/SwiftProtobuf/PathVisitor.swift b/Sources/SwiftProtobuf/PathVisitor.swift index 868fed574..54069c9a4 100644 --- a/Sources/SwiftProtobuf/PathVisitor.swift +++ b/Sources/SwiftProtobuf/PathVisitor.swift @@ -17,268 +17,268 @@ import Foundation // Visitor captures all values of message with their paths struct PathVisitor: Visitor { - // The path contains parent components - private let prevPath: String? + // The path contains parent components + private let prevPath: String? - // Captured values after visiting will be stored in this property - private(set) var values: [String: Any] = [:] + // Captured values after visiting will be stored in this property + private(set) var values: [String: Any] = [:] - internal init(prevPath: String? = nil) { - self.prevPath = prevPath - } + internal init(prevPath: String? = nil) { + self.prevPath = prevPath + } + + mutating private func visit(_ value: Any, fieldNumber: Int) { + guard let name = T.name(for: fieldNumber) else { + return + } + if let prevPath { + values["\(prevPath).\(name)"] = value + } else { + values[name] = value + } + } + + mutating private func visitMessageField( + _ value: M, + fieldNumber: Int + ) { + guard var path = T.name(for: fieldNumber) else { + return + } + if let prevPath { + path = "\(prevPath).\(path)" + } + values[path] = value + var visitor = PathVisitor(prevPath: path) + try? value.traverse(visitor: &visitor) + values.merge(visitor.values) { _, new in + new + } + } + + mutating func visitUnknown(bytes: Data) throws {} + + mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } + + mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } + + mutating func visitSingularInt32Field(value: Int32, fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } + + mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } + + mutating func visitSingularUInt32Field(value: UInt32, fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } + + mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } + + mutating func visitSingularSInt32Field(value: Int32, fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } + + mutating func visitSingularSInt64Field(value: Int64, fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } + + mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } + + mutating func visitSingularFixed64Field(value: UInt64, fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } + + mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } + + mutating func visitSingularSFixed64Field(value: Int64, fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } + + mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } + + mutating func visitSingularStringField(value: String, fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } + + mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } + + mutating func visitSingularEnumField(value: E, fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } - mutating private func visit(_ value: Any, fieldNumber: Int) { - guard let name = T.name(for: fieldNumber) else { - return + mutating func visitSingularMessageField(value: M, fieldNumber: Int) throws { + visitMessageField(value, fieldNumber: fieldNumber) } - if let prevPath { - values["\(prevPath).\(name)"] = value - } else { - values[name] = value + + mutating func visitSingularGroupField(value: G, fieldNumber: Int) throws { + visitMessageField(value, fieldNumber: fieldNumber) } - } - mutating private func visitMessageField( - _ value: M, - fieldNumber: Int - ) { - guard var path = T.name(for: fieldNumber) else { - return + mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) } - if let prevPath { - path = "\(prevPath).\(path)" + + mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) } - values[path] = value - var visitor = PathVisitor(prevPath: path) - try? value.traverse(visitor: &visitor) - values.merge(visitor.values) { _, new in - new + + mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) } - } - mutating func visitUnknown(bytes: Data) throws {} + mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } - mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } + mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } - mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } + mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } - mutating func visitSingularInt32Field(value: Int32, fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } + mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } - mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } + mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } - mutating func visitSingularUInt32Field(value: UInt32, fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } + mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } - mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } + mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } - mutating func visitSingularSInt32Field(value: Int32, fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } + mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } - mutating func visitSingularSInt64Field(value: Int64, fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } + mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } - mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } + mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } + + mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } + + mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } + + mutating func visitRepeatedEnumField(value: [E], fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } - mutating func visitSingularFixed64Field(value: UInt64, fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } + mutating func visitRepeatedMessageField(value: [M], fieldNumber: Int) throws where M: Message { + visit(value, fieldNumber: fieldNumber) + } - mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } + mutating func visitRepeatedGroupField(value: [G], fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } - mutating func visitSingularSFixed64Field(value: Int64, fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } + mutating func visitPackedFloatField(value: [Float], fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } - mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } + mutating func visitPackedDoubleField(value: [Double], fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } - mutating func visitSingularStringField(value: String, fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } + mutating func visitPackedInt32Field(value: [Int32], fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } - mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } + mutating func visitPackedInt64Field(value: [Int64], fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } - mutating func visitSingularEnumField(value: E, fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } + mutating func visitPackedUInt32Field(value: [UInt32], fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } - mutating func visitSingularMessageField(value: M, fieldNumber: Int) throws { - visitMessageField(value, fieldNumber: fieldNumber) - } + mutating func visitPackedUInt64Field(value: [UInt64], fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } - mutating func visitSingularGroupField(value: G, fieldNumber: Int) throws { - visitMessageField(value, fieldNumber: fieldNumber) - } + mutating func visitPackedSInt32Field(value: [Int32], fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } - mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } + mutating func visitPackedSInt64Field(value: [Int64], fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } - mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } + mutating func visitPackedFixed32Field(value: [UInt32], fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } - mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } + mutating func visitPackedFixed64Field(value: [UInt64], fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } - mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } + mutating func visitPackedSFixed32Field(value: [Int32], fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } - mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } + mutating func visitPackedSFixed64Field(value: [Int64], fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } - mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } + mutating func visitPackedBoolField(value: [Bool], fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } - mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } + mutating func visitPackedEnumField(value: [E], fieldNumber: Int) throws { + visit(value, fieldNumber: fieldNumber) + } - mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } + mutating func visitMapField( + fieldType: _ProtobufMap.Type, + value: _ProtobufMap.BaseType, + fieldNumber: Int + ) throws where KeyType: MapKeyType, ValueType: MapValueType { + visit(value, fieldNumber: fieldNumber) + } - mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } + mutating func visitMapField( + fieldType: _ProtobufEnumMap.Type, + value: _ProtobufEnumMap.BaseType, + fieldNumber: Int + ) throws where KeyType: MapKeyType, ValueType: Enum, ValueType.RawValue == Int { + visit(value, fieldNumber: fieldNumber) + } - mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } - - mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } - - mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } - - mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } - - mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } - - mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } - - mutating func visitRepeatedEnumField(value: [E], fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } - - mutating func visitRepeatedMessageField(value: [M], fieldNumber: Int) throws where M : Message { - visit(value, fieldNumber: fieldNumber) - } - - mutating func visitRepeatedGroupField(value: [G], fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } - - mutating func visitPackedFloatField(value: [Float], fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } - - mutating func visitPackedDoubleField(value: [Double], fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } - - mutating func visitPackedInt32Field(value: [Int32], fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } - - mutating func visitPackedInt64Field(value: [Int64], fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } - - mutating func visitPackedUInt32Field(value: [UInt32], fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } - - mutating func visitPackedUInt64Field(value: [UInt64], fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } - - mutating func visitPackedSInt32Field(value: [Int32], fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } - - mutating func visitPackedSInt64Field(value: [Int64], fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } - - mutating func visitPackedFixed32Field(value: [UInt32], fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } - - mutating func visitPackedFixed64Field(value: [UInt64], fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } - - mutating func visitPackedSFixed32Field(value: [Int32], fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } - - mutating func visitPackedSFixed64Field(value: [Int64], fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } - - mutating func visitPackedBoolField(value: [Bool], fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } - - mutating func visitPackedEnumField(value: [E], fieldNumber: Int) throws { - visit(value, fieldNumber: fieldNumber) - } - - mutating func visitMapField( - fieldType: _ProtobufMap.Type, - value: _ProtobufMap.BaseType, - fieldNumber: Int - ) throws where KeyType : MapKeyType, ValueType : MapValueType { - visit(value, fieldNumber: fieldNumber) - } - - mutating func visitMapField( - fieldType: _ProtobufEnumMap.Type, - value: _ProtobufEnumMap.BaseType, - fieldNumber: Int - ) throws where KeyType : MapKeyType, ValueType : Enum, ValueType.RawValue == Int { - visit(value, fieldNumber: fieldNumber) - } - - mutating func visitMapField( - fieldType: _ProtobufMessageMap.Type, - value: _ProtobufMessageMap.BaseType, - fieldNumber: Int - ) throws where KeyType : MapKeyType, ValueType : Hashable, ValueType : Message { - visit(value, fieldNumber: fieldNumber) - } + mutating func visitMapField( + fieldType: _ProtobufMessageMap.Type, + value: _ProtobufMessageMap.BaseType, + fieldNumber: Int + ) throws where KeyType: MapKeyType, ValueType: Hashable, ValueType: Message { + visit(value, fieldNumber: fieldNumber) + } } diff --git a/Sources/SwiftProtobuf/ProtoNameProviding.swift b/Sources/SwiftProtobuf/ProtoNameProviding.swift index 8b5a81c15..e0eb4dd5b 100644 --- a/Sources/SwiftProtobuf/ProtoNameProviding.swift +++ b/Sources/SwiftProtobuf/ProtoNameProviding.swift @@ -8,7 +8,6 @@ // // ----------------------------------------------------------------------------- - /// SwiftProtobuf Internal: Common support looking up field names. /// /// Messages conform to this protocol to provide the proto/text and JSON field @@ -17,7 +16,7 @@ /// (reducing bloat and minimizing leaks of field names). public protocol _ProtoNameProviding { - /// The mapping between field numbers and proto/JSON field names defined in - /// the conforming message type. - static var _protobuf_nameMap: _NameMap { get } + /// The mapping between field numbers and proto/JSON field names defined in + /// the conforming message type. + static var _protobuf_nameMap: _NameMap { get } } diff --git a/Sources/SwiftProtobuf/ProtobufAPIVersionCheck.swift b/Sources/SwiftProtobuf/ProtobufAPIVersionCheck.swift index 480305e68..4ef92dd03 100644 --- a/Sources/SwiftProtobuf/ProtobufAPIVersionCheck.swift +++ b/Sources/SwiftProtobuf/ProtobufAPIVersionCheck.swift @@ -19,7 +19,6 @@ /// // ----------------------------------------------------------------------------- - /// An empty protocol that encodes the version of the runtime library. /// /// This protocol will be replaced with one containing a different version @@ -39,5 +38,5 @@ public protocol ProtobufAPIVersion_2 {} /// causing a compile-time error (with reasonable diagnostics) if they are /// incompatible. public protocol ProtobufAPIVersionCheck { - associatedtype Version: ProtobufAPIVersion_2 + associatedtype Version: ProtobufAPIVersion_2 } diff --git a/Sources/SwiftProtobuf/ProtobufMap.swift b/Sources/SwiftProtobuf/ProtobufMap.swift index a9c4d80ef..d1181287a 100644 --- a/Sources/SwiftProtobuf/ProtobufMap.swift +++ b/Sources/SwiftProtobuf/ProtobufMap.swift @@ -15,25 +15,22 @@ import Foundation /// SwiftProtobuf Internal: Support for Encoding/Decoding. -public struct _ProtobufMap -{ +public struct _ProtobufMap { public typealias Key = KeyType.BaseType public typealias Value = ValueType.BaseType - public typealias BaseType = Dictionary + public typealias BaseType = [Key: Value] } /// SwiftProtobuf Internal: Support for Encoding/Decoding. -public struct _ProtobufMessageMap -{ +public struct _ProtobufMessageMap { public typealias Key = KeyType.BaseType public typealias Value = ValueType - public typealias BaseType = Dictionary + public typealias BaseType = [Key: Value] } /// SwiftProtobuf Internal: Support for Encoding/Decoding. -public struct _ProtobufEnumMap -{ +public struct _ProtobufEnumMap { public typealias Key = KeyType.BaseType public typealias Value = ValueType - public typealias BaseType = Dictionary + public typealias BaseType = [Key: Value] } diff --git a/Sources/SwiftProtobuf/SelectiveVisitor.swift b/Sources/SwiftProtobuf/SelectiveVisitor.swift index e8dc4073a..a85dc674a 100644 --- a/Sources/SwiftProtobuf/SelectiveVisitor.swift +++ b/Sources/SwiftProtobuf/SelectiveVisitor.swift @@ -16,7 +16,7 @@ import Foundation /// A base for Visitors that only expects a subset of things to called. internal protocol SelectiveVisitor: Visitor { - // Adds nothing. + // Adds nothing. } /// Default impls for everything so things using this only have to write the @@ -27,242 +27,243 @@ internal protocol SelectiveVisitor: Visitor { /// provided by Visitor to bridge packed->repeated, repeated->singular, etc /// won't kick in. extension SelectiveVisitor { - internal mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws { - assert(false) - } + internal mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws { + assert(false) + } - internal mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws { - assert(false) - } + internal mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws { + assert(false) + } - internal mutating func visitSingularInt32Field(value: Int32, fieldNumber: Int) throws { - assert(false) - } + internal mutating func visitSingularInt32Field(value: Int32, fieldNumber: Int) throws { + assert(false) + } - internal mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws { - assert(false) - } + internal mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws { + assert(false) + } - internal mutating func visitSingularUInt32Field(value: UInt32, fieldNumber: Int) throws { - assert(false) - } + internal mutating func visitSingularUInt32Field(value: UInt32, fieldNumber: Int) throws { + assert(false) + } - internal mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws { - assert(false) - } + internal mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws { + assert(false) + } - internal mutating func visitSingularSInt32Field(value: Int32, fieldNumber: Int) throws { - assert(false) - } + internal mutating func visitSingularSInt32Field(value: Int32, fieldNumber: Int) throws { + assert(false) + } - internal mutating func visitSingularSInt64Field(value: Int64, fieldNumber: Int) throws { - assert(false) - } + internal mutating func visitSingularSInt64Field(value: Int64, fieldNumber: Int) throws { + assert(false) + } - internal mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws { - assert(false) - } + internal mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws { + assert(false) + } - internal mutating func visitSingularFixed64Field(value: UInt64, fieldNumber: Int) throws { - assert(false) - } + internal mutating func visitSingularFixed64Field(value: UInt64, fieldNumber: Int) throws { + assert(false) + } - internal mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws { - assert(false) - } + internal mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws { + assert(false) + } - internal mutating func visitSingularSFixed64Field(value: Int64, fieldNumber: Int) throws { - assert(false) - } + internal mutating func visitSingularSFixed64Field(value: Int64, fieldNumber: Int) throws { + assert(false) + } - internal mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws { - assert(false) - } + internal mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws { + assert(false) + } - internal mutating func visitSingularStringField(value: String, fieldNumber: Int) throws { - assert(false) - } + internal mutating func visitSingularStringField(value: String, fieldNumber: Int) throws { + assert(false) + } - internal mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws { - assert(false) - } + internal mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws { + assert(false) + } - internal mutating func visitSingularEnumField(value: E, fieldNumber: Int) throws { - assert(false) - } + internal mutating func visitSingularEnumField(value: E, fieldNumber: Int) throws { + assert(false) + } - internal mutating func visitSingularMessageField(value: M, fieldNumber: Int) throws { - assert(false) - } + internal mutating func visitSingularMessageField(value: M, fieldNumber: Int) throws { + assert(false) + } - internal mutating func visitSingularGroupField(value: G, fieldNumber: Int) throws { - assert(false) - } + internal mutating func visitSingularGroupField(value: G, fieldNumber: Int) throws { + assert(false) + } - internal mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws { - assert(false) - } + internal mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws { + assert(false) + } - internal mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws { - assert(false) - } + internal mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws { + assert(false) + } - internal mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws { - assert(false) - } + internal mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws { + assert(false) + } - internal mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws { - assert(false) - } + internal mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws { + assert(false) + } - internal mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws { - assert(false) - } + internal mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws { + assert(false) + } - internal mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws { - assert(false) - } + internal mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws { + assert(false) + } - internal mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws { - assert(false) - } + internal mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws { + assert(false) + } - internal mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws { - assert(false) - } + internal mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws { + assert(false) + } - internal mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws { - assert(false) - } + internal mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws { + assert(false) + } - internal mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws { - assert(false) - } - - internal mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws { - assert(false) - } - - internal mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws { - assert(false) - } - - internal mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws { - assert(false) - } - - internal mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws { - assert(false) - } - - internal mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws { - assert(false) - } - - internal mutating func visitRepeatedEnumField(value: [E], fieldNumber: Int) throws { - assert(false) - } - - internal mutating func visitRepeatedMessageField(value: [M], fieldNumber: Int) throws { - assert(false) - } - - internal mutating func visitRepeatedGroupField(value: [G], fieldNumber: Int) throws { - assert(false) - } - - internal mutating func visitPackedFloatField(value: [Float], fieldNumber: Int) throws { - assert(false) - } - - internal mutating func visitPackedDoubleField(value: [Double], fieldNumber: Int) throws { - assert(false) - } - - internal mutating func visitPackedInt32Field(value: [Int32], fieldNumber: Int) throws { - assert(false) - } - - internal mutating func visitPackedInt64Field(value: [Int64], fieldNumber: Int) throws { - assert(false) - } - - internal mutating func visitPackedUInt32Field(value: [UInt32], fieldNumber: Int) throws { - assert(false) - } - - internal mutating func visitPackedUInt64Field(value: [UInt64], fieldNumber: Int) throws { - assert(false) - } - - internal mutating func visitPackedSInt32Field(value: [Int32], fieldNumber: Int) throws { - assert(false) - } - - internal mutating func visitPackedSInt64Field(value: [Int64], fieldNumber: Int) throws { - assert(false) - } - - internal mutating func visitPackedFixed32Field(value: [UInt32], fieldNumber: Int) throws { - assert(false) - } - - internal mutating func visitPackedFixed64Field(value: [UInt64], fieldNumber: Int) throws { - assert(false) - } - - internal mutating func visitPackedSFixed32Field(value: [Int32], fieldNumber: Int) throws { - assert(false) - } - - internal mutating func visitPackedSFixed64Field(value: [Int64], fieldNumber: Int) throws { - assert(false) - } - - internal mutating func visitPackedBoolField(value: [Bool], fieldNumber: Int) throws { - assert(false) - } - - internal mutating func visitPackedEnumField(value: [E], fieldNumber: Int) throws { - assert(false) - } - - internal mutating func visitMapField( - fieldType: _ProtobufMap.Type, - value: _ProtobufMap.BaseType, - fieldNumber: Int) throws { - assert(false) - } - - internal mutating func visitMapField( - fieldType: _ProtobufEnumMap.Type, - value: _ProtobufEnumMap.BaseType, - fieldNumber: Int - ) throws where ValueType.RawValue == Int { - assert(false) - } - - internal mutating func visitMapField( - fieldType: _ProtobufMessageMap.Type, - value: _ProtobufMessageMap.BaseType, - fieldNumber: Int - ) throws { - assert(false) - } - - internal mutating func visitExtensionFields(fields: ExtensionFieldValueSet, start: Int, end: Int) throws { - assert(false) - } - - internal mutating func visitExtensionFieldsAsMessageSet( - fields: ExtensionFieldValueSet, - start: Int, - end: Int - ) throws { - assert(false) - } - - internal mutating func visitUnknown(bytes: Data) throws { - assert(false) - } + internal mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws { + assert(false) + } + + internal mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws { + assert(false) + } + + internal mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws { + assert(false) + } + + internal mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws { + assert(false) + } + + internal mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws { + assert(false) + } + + internal mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws { + assert(false) + } + + internal mutating func visitRepeatedEnumField(value: [E], fieldNumber: Int) throws { + assert(false) + } + + internal mutating func visitRepeatedMessageField(value: [M], fieldNumber: Int) throws { + assert(false) + } + + internal mutating func visitRepeatedGroupField(value: [G], fieldNumber: Int) throws { + assert(false) + } + + internal mutating func visitPackedFloatField(value: [Float], fieldNumber: Int) throws { + assert(false) + } + + internal mutating func visitPackedDoubleField(value: [Double], fieldNumber: Int) throws { + assert(false) + } + + internal mutating func visitPackedInt32Field(value: [Int32], fieldNumber: Int) throws { + assert(false) + } + + internal mutating func visitPackedInt64Field(value: [Int64], fieldNumber: Int) throws { + assert(false) + } + + internal mutating func visitPackedUInt32Field(value: [UInt32], fieldNumber: Int) throws { + assert(false) + } + + internal mutating func visitPackedUInt64Field(value: [UInt64], fieldNumber: Int) throws { + assert(false) + } + + internal mutating func visitPackedSInt32Field(value: [Int32], fieldNumber: Int) throws { + assert(false) + } + + internal mutating func visitPackedSInt64Field(value: [Int64], fieldNumber: Int) throws { + assert(false) + } + + internal mutating func visitPackedFixed32Field(value: [UInt32], fieldNumber: Int) throws { + assert(false) + } + + internal mutating func visitPackedFixed64Field(value: [UInt64], fieldNumber: Int) throws { + assert(false) + } + + internal mutating func visitPackedSFixed32Field(value: [Int32], fieldNumber: Int) throws { + assert(false) + } + + internal mutating func visitPackedSFixed64Field(value: [Int64], fieldNumber: Int) throws { + assert(false) + } + + internal mutating func visitPackedBoolField(value: [Bool], fieldNumber: Int) throws { + assert(false) + } + + internal mutating func visitPackedEnumField(value: [E], fieldNumber: Int) throws { + assert(false) + } + + internal mutating func visitMapField( + fieldType: _ProtobufMap.Type, + value: _ProtobufMap.BaseType, + fieldNumber: Int + ) throws { + assert(false) + } + + internal mutating func visitMapField( + fieldType: _ProtobufEnumMap.Type, + value: _ProtobufEnumMap.BaseType, + fieldNumber: Int + ) throws where ValueType.RawValue == Int { + assert(false) + } + + internal mutating func visitMapField( + fieldType: _ProtobufMessageMap.Type, + value: _ProtobufMessageMap.BaseType, + fieldNumber: Int + ) throws { + assert(false) + } + + internal mutating func visitExtensionFields(fields: ExtensionFieldValueSet, start: Int, end: Int) throws { + assert(false) + } + + internal mutating func visitExtensionFieldsAsMessageSet( + fields: ExtensionFieldValueSet, + start: Int, + end: Int + ) throws { + assert(false) + } + + internal mutating func visitUnknown(bytes: Data) throws { + assert(false) + } } diff --git a/Sources/SwiftProtobuf/SimpleExtensionMap.swift b/Sources/SwiftProtobuf/SimpleExtensionMap.swift index 7c83bc5b5..0a17e88bf 100644 --- a/Sources/SwiftProtobuf/SimpleExtensionMap.swift +++ b/Sources/SwiftProtobuf/SimpleExtensionMap.swift @@ -12,13 +12,12 @@ /// // ----------------------------------------------------------------------------- - // Note: The generated code only relies on ExpressibleByArrayLiteral public struct SimpleExtensionMap: ExtensionMap, ExpressibleByArrayLiteral { public typealias Element = AnyMessageExtension // Since type objects aren't Hashable, we can't do much better than this... - internal var fields = [Int: Array]() + internal var fields = [Int: [any AnyMessageExtension]]() public init() {} @@ -27,9 +26,9 @@ public struct SimpleExtensionMap: ExtensionMap, ExpressibleByArrayLiteral { } public init(_ others: SimpleExtensionMap...) { - for other in others { - formUnion(other) - } + for other in others { + formUnion(other) + } } public subscript(messageType: any Message.Type, fieldNumber: Int) -> (any AnyMessageExtension)? { diff --git a/Sources/SwiftProtobuf/StringUtils.swift b/Sources/SwiftProtobuf/StringUtils.swift index aea5feef6..9bbf5421c 100644 --- a/Sources/SwiftProtobuf/StringUtils.swift +++ b/Sources/SwiftProtobuf/StringUtils.swift @@ -26,43 +26,42 @@ import Foundation // Note: We're trying to avoid Foundation's String(format:) since that's not // universally available. -fileprivate func formatZeroPaddedInt(_ value: Int32, digits: Int) -> String { - precondition(value >= 0) - let s = String(value) - if s.count >= digits { - return s - } else { - let pad = String(repeating: "0", count: digits - s.count) - return pad + s - } +private func formatZeroPaddedInt(_ value: Int32, digits: Int) -> String { + precondition(value >= 0) + let s = String(value) + if s.count >= digits { + return s + } else { + let pad = String(repeating: "0", count: digits - s.count) + return pad + s + } } internal func twoDigit(_ value: Int32) -> String { - return formatZeroPaddedInt(value, digits: 2) + formatZeroPaddedInt(value, digits: 2) } internal func threeDigit(_ value: Int32) -> String { - return formatZeroPaddedInt(value, digits: 3) + formatZeroPaddedInt(value, digits: 3) } internal func fourDigit(_ value: Int32) -> String { - return formatZeroPaddedInt(value, digits: 4) + formatZeroPaddedInt(value, digits: 4) } internal func sixDigit(_ value: Int32) -> String { - return formatZeroPaddedInt(value, digits: 6) + formatZeroPaddedInt(value, digits: 6) } internal func nineDigit(_ value: Int32) -> String { - return formatZeroPaddedInt(value, digits: 9) + formatZeroPaddedInt(value, digits: 9) } // Wrapper that takes a buffer and start/end offsets internal func utf8ToString( - bytes: UnsafeRawBufferPointer, - start: UnsafeRawBufferPointer.Index, - end: UnsafeRawBufferPointer.Index + bytes: UnsafeRawBufferPointer, + start: UnsafeRawBufferPointer.Index, + end: UnsafeRawBufferPointer.Index ) -> String? { - return utf8ToString(bytes: bytes.baseAddress! + start, count: end - start) + utf8ToString(bytes: bytes.baseAddress! + start, count: end - start) } - // Swift 4 introduced new faster String facilities // that seem to work consistently across all platforms. @@ -79,28 +78,27 @@ internal func utf8ToString( // slower than on macOS, so this is a much bigger // win there. internal func utf8ToString(bytes: UnsafeRawPointer, count: Int) -> String? { - if count == 0 { - return String() - } - let codeUnits = UnsafeRawBufferPointer(start: bytes, count: count) - let sourceEncoding = Unicode.UTF8.self + if count == 0 { + return String() + } + let codeUnits = UnsafeRawBufferPointer(start: bytes, count: count) + let sourceEncoding = Unicode.UTF8.self - // Verify that the UTF-8 is valid. - var p = sourceEncoding.ForwardParser() - var i = codeUnits.makeIterator() - Loop: - while true { - switch p.parseScalar(from: &i) { - case .valid(_): - break - case .error: - return nil - case .emptyInput: - break Loop + // Verify that the UTF-8 is valid. + var p = sourceEncoding.ForwardParser() + var i = codeUnits.makeIterator() + Loop: while true { + switch p.parseScalar(from: &i) { + case .valid(_): + break + case .error: + return nil + case .emptyInput: + break Loop + } } - } - // This initializer is fast but does not reject broken - // UTF-8 (which is why we validate the UTF-8 above). - return String(decoding: codeUnits, as: sourceEncoding) - } + // This initializer is fast but does not reject broken + // UTF-8 (which is why we validate the UTF-8 above). + return String(decoding: codeUnits, as: sourceEncoding) +} diff --git a/Sources/SwiftProtobuf/SwiftProtobufContiguousBytes.swift b/Sources/SwiftProtobuf/SwiftProtobufContiguousBytes.swift index c5565a000..380a592d7 100644 --- a/Sources/SwiftProtobuf/SwiftProtobufContiguousBytes.swift +++ b/Sources/SwiftProtobuf/SwiftProtobufContiguousBytes.swift @@ -14,7 +14,7 @@ import Foundation /// to be used for serialization and deserialization of protobufs. /// It provides a general interface for bytes since the Swift Standard Library currently does not /// provide such a protocol. -/// +/// /// By conforming your own types to this protocol, you will be able to pass instances of said types /// directly to `SwiftProtobuf.Message`'s deserialisation methods /// (i.e. `init(contiguousBytes:)` for binary format and `init(jsonUTF8Bytes:)` for JSON). diff --git a/Sources/SwiftProtobuf/SwiftProtobufError.swift b/Sources/SwiftProtobuf/SwiftProtobufError.swift index e9151e202..947f6ac39 100644 --- a/Sources/SwiftProtobuf/SwiftProtobufError.swift +++ b/Sources/SwiftProtobuf/SwiftProtobufError.swift @@ -25,7 +25,7 @@ public struct SwiftProtobufError: Error, @unchecked Sendable { self.storage = self.storage.copy() } } - + private final class Storage { var code: Code var message: String @@ -42,14 +42,14 @@ public struct SwiftProtobufError: Error, @unchecked Sendable { } func copy() -> Self { - return Self( + Self( code: self.code, message: self.message, location: self.location ) } } - + /// A high-level error code to provide broad a classification. public var code: Code { get { self.storage.code } @@ -67,7 +67,7 @@ public struct SwiftProtobufError: Error, @unchecked Sendable { self.storage.message = newValue } } - + private var location: SourceLocation { get { self.storage.location } set { @@ -111,12 +111,12 @@ extension SwiftProtobufError { private init(_ code: Wrapped) { self.code = code } - + /// Errors arising from binary decoding of data into protobufs. public static var binaryDecodingError: Self { Self(.binaryDecodingError) } - + /// Errors arising from decoding streams of binary messages. These errors have to do with the framing /// of the messages in the stream, or the stream as a whole. public static var binaryStreamDecodingError: Self { @@ -147,7 +147,7 @@ extension SwiftProtobufError { file: String = #fileID, line: Int = #line ) -> Self { - return SourceLocation(function: function, file: file, line: line) + SourceLocation(function: function, file: file, line: line) } } } @@ -182,60 +182,60 @@ extension SwiftProtobufError { ) } } - + /// Errors arising from decoding streams of binary messages. These errors have to do with the framing /// of the messages in the stream, or the stream as a whole. public enum BinaryStreamDecoding { - /// Message is too large. Bytes and Strings have a max size of 2GB. - public static func tooLarge( - function: String = #function, - file: String = #fileID, - line: Int = #line - ) -> SwiftProtobufError { - SwiftProtobufError( - code: .binaryStreamDecodingError, - message: "Message too large: Bytes and Strings have a max size of 2GB.", - location: SourceLocation(function: function, file: file, line: line) - ) - } - - /// While attempting to read the length of a message on the stream, the - /// bytes were malformed for the protobuf format. - public static func malformedLength( - function: String = #function, - file: String = #fileID, - line: Int = #line - ) -> SwiftProtobufError { - SwiftProtobufError( - code: .binaryStreamDecodingError, - message: """ - While attempting to read the length of a binary-delimited message \ - on the stream, the bytes were malformed for the protobuf format. - """, - location: .init(function: function, file: file, line: line) - ) - } - - /// This isn't really an error. `InputStream` documents that - /// `hasBytesAvailable` _may_ return `True` if a read is needed to - /// determine if there really are bytes available. So this "error" is thrown - /// when a `parse` or `merge` fails because there were no bytes available. - /// If this is raised, the callers should decide via what ever other means - /// are correct if the stream has completely ended or if more bytes might - /// eventually show up. - public static func noBytesAvailable( - function: String = #function, - file: String = #fileID, - line: Int = #line - ) -> SwiftProtobufError { - SwiftProtobufError( - code: .binaryStreamDecodingError, - message: """ - This is not really an error: please read the documentation for - `SwiftProtobufError/BinaryStreamDecoding/noBytesAvailable` for more information. - """, - location: .init(function: function, file: file, line: line) - ) - } + /// Message is too large. Bytes and Strings have a max size of 2GB. + public static func tooLarge( + function: String = #function, + file: String = #fileID, + line: Int = #line + ) -> SwiftProtobufError { + SwiftProtobufError( + code: .binaryStreamDecodingError, + message: "Message too large: Bytes and Strings have a max size of 2GB.", + location: SourceLocation(function: function, file: file, line: line) + ) + } + + /// While attempting to read the length of a message on the stream, the + /// bytes were malformed for the protobuf format. + public static func malformedLength( + function: String = #function, + file: String = #fileID, + line: Int = #line + ) -> SwiftProtobufError { + SwiftProtobufError( + code: .binaryStreamDecodingError, + message: """ + While attempting to read the length of a binary-delimited message \ + on the stream, the bytes were malformed for the protobuf format. + """, + location: .init(function: function, file: file, line: line) + ) + } + + /// This isn't really an error. `InputStream` documents that + /// `hasBytesAvailable` _may_ return `True` if a read is needed to + /// determine if there really are bytes available. So this "error" is thrown + /// when a `parse` or `merge` fails because there were no bytes available. + /// If this is raised, the callers should decide via what ever other means + /// are correct if the stream has completely ended or if more bytes might + /// eventually show up. + public static func noBytesAvailable( + function: String = #function, + file: String = #fileID, + line: Int = #line + ) -> SwiftProtobufError { + SwiftProtobufError( + code: .binaryStreamDecodingError, + message: """ + This is not really an error: please read the documentation for + `SwiftProtobufError/BinaryStreamDecoding/noBytesAvailable` for more information. + """, + location: .init(function: function, file: file, line: line) + ) + } } } diff --git a/Sources/SwiftProtobuf/TextFormatDecoder.swift b/Sources/SwiftProtobuf/TextFormatDecoder.swift index e96d59c05..1a80cdc8b 100644 --- a/Sources/SwiftProtobuf/TextFormatDecoder.swift +++ b/Sources/SwiftProtobuf/TextFormatDecoder.swift @@ -28,21 +28,21 @@ internal struct TextFormatDecoder: Decoder { private var messageType: any Message.Type internal var options: TextFormatDecodingOptions { - return scanner.options + scanner.options } internal var complete: Bool { mutating get { - return scanner.complete + scanner.complete } } internal init( - messageType: any Message.Type, - utf8Pointer: UnsafeRawPointer, - count: Int, - options: TextFormatDecodingOptions, - extensions: (any ExtensionMap)? + messageType: any Message.Type, + utf8Pointer: UnsafeRawPointer, + count: Int, + options: TextFormatDecodingOptions, + extensions: (any ExtensionMap)? ) throws { scanner = TextFormatScanner(utf8Pointer: utf8Pointer, count: count, options: options, extensions: extensions) guard let nameProviding = (messageType as? any _ProtoNameProviding.Type) else { @@ -70,9 +70,11 @@ internal struct TextFormatDecoder: Decoder { if fieldCount > 0 { scanner.skipOptionalSeparator() } - if let fieldNumber = try scanner.nextFieldNumber(names: fieldNameMap!, - messageType: messageType, - terminator: terminator) { + if let fieldNumber = try scanner.nextFieldNumber( + names: fieldNameMap!, + messageType: messageType, + terminator: terminator + ) { fieldCount += 1 return fieldNumber } else { @@ -549,7 +551,10 @@ internal struct TextFormatDecoder: Decoder { try decodeRepeatedMessageField(value: &value) } - private mutating func decodeMapEntry(mapType: _ProtobufMap.Type, value: inout _ProtobufMap.BaseType) throws { + private mutating func decodeMapEntry( + mapType: _ProtobufMap.Type, + value: inout _ProtobufMap.BaseType + ) throws { var keyField: KeyType.BaseType? var valueField: ValueType.BaseType? let terminator = try scanner.skipObjectStart() @@ -585,7 +590,10 @@ internal struct TextFormatDecoder: Decoder { } } - mutating func decodeMapField(fieldType: _ProtobufMap.Type, value: inout _ProtobufMap.BaseType) throws { + mutating func decodeMapField( + fieldType: _ProtobufMap.Type, + value: inout _ProtobufMap.BaseType + ) throws { _ = scanner.skipOptionalColon() if scanner.skipOptionalBeginArray() { var firstItem = true @@ -605,7 +613,10 @@ internal struct TextFormatDecoder: Decoder { } } - private mutating func decodeMapEntry(mapType: _ProtobufEnumMap.Type, value: inout _ProtobufEnumMap.BaseType) throws where ValueType.RawValue == Int { + private mutating func decodeMapEntry( + mapType: _ProtobufEnumMap.Type, + value: inout _ProtobufEnumMap.BaseType + ) throws where ValueType.RawValue == Int { var keyField: KeyType.BaseType? var valueField: ValueType? let terminator = try scanner.skipObjectStart() @@ -641,7 +652,10 @@ internal struct TextFormatDecoder: Decoder { } } - mutating func decodeMapField(fieldType: _ProtobufEnumMap.Type, value: inout _ProtobufEnumMap.BaseType) throws where ValueType.RawValue == Int { + mutating func decodeMapField( + fieldType: _ProtobufEnumMap.Type, + value: inout _ProtobufEnumMap.BaseType + ) throws where ValueType.RawValue == Int { _ = scanner.skipOptionalColon() if scanner.skipOptionalBeginArray() { var firstItem = true @@ -661,7 +675,10 @@ internal struct TextFormatDecoder: Decoder { } } - private mutating func decodeMapEntry(mapType: _ProtobufMessageMap.Type, value: inout _ProtobufMessageMap.BaseType) throws { + private mutating func decodeMapEntry( + mapType: _ProtobufMessageMap.Type, + value: inout _ProtobufMessageMap.BaseType + ) throws { var keyField: KeyType.BaseType? var valueField: ValueType? let terminator = try scanner.skipObjectStart() @@ -697,7 +714,10 @@ internal struct TextFormatDecoder: Decoder { } } - mutating func decodeMapField(fieldType: _ProtobufMessageMap.Type, value: inout _ProtobufMessageMap.BaseType) throws { + mutating func decodeMapField( + fieldType: _ProtobufMessageMap.Type, + value: inout _ProtobufMessageMap.BaseType + ) throws { _ = scanner.skipOptionalColon() if scanner.skipOptionalBeginArray() { var firstItem = true @@ -717,7 +737,11 @@ internal struct TextFormatDecoder: Decoder { } } - mutating func decodeExtensionField(values: inout ExtensionFieldValueSet, messageType: any Message.Type, fieldNumber: Int) throws { + mutating func decodeExtensionField( + values: inout ExtensionFieldValueSet, + messageType: any Message.Type, + fieldNumber: Int + ) throws { if let ext = scanner.extensions?[messageType, fieldNumber] { try values.modify(index: fieldNumber) { fieldValue in if fieldValue != nil { diff --git a/Sources/SwiftProtobuf/TextFormatDecodingOptions.swift b/Sources/SwiftProtobuf/TextFormatDecodingOptions.swift index 06ede25e4..fbd5cc9e3 100644 --- a/Sources/SwiftProtobuf/TextFormatDecodingOptions.swift +++ b/Sources/SwiftProtobuf/TextFormatDecodingOptions.swift @@ -14,26 +14,26 @@ /// Options for TextFormatDecoding. public struct TextFormatDecodingOptions: Sendable { - /// The maximum nesting of message with messages. The default is 100. - /// - /// To prevent corrupt or malicious messages from causing stack overflows, - /// this controls how deep messages can be nested within other messages - /// while parsing. - public var messageDepthLimit: Int = 100 + /// The maximum nesting of message with messages. The default is 100. + /// + /// To prevent corrupt or malicious messages from causing stack overflows, + /// this controls how deep messages can be nested within other messages + /// while parsing. + public var messageDepthLimit: Int = 100 - /// If unknown fields in the TextFormat should be ignored. If they aren't - /// ignored, an error will be raised if one is encountered. - /// - /// Note: This is a lossy option, enabling it means part of the TextFormat - /// is silently skipped. - public var ignoreUnknownFields: Bool = false + /// If unknown fields in the TextFormat should be ignored. If they aren't + /// ignored, an error will be raised if one is encountered. + /// + /// Note: This is a lossy option, enabling it means part of the TextFormat + /// is silently skipped. + public var ignoreUnknownFields: Bool = false - /// If unknown extension fields in the TextFormat should be ignored. If they - /// aren't ignored, an error will be raised if one is encountered. - /// - /// Note: This is a lossy option, enabling it means part of the TextFormat - /// is silently skipped. - public var ignoreUnknownExtensionFields: Bool = false + /// If unknown extension fields in the TextFormat should be ignored. If they + /// aren't ignored, an error will be raised if one is encountered. + /// + /// Note: This is a lossy option, enabling it means part of the TextFormat + /// is silently skipped. + public var ignoreUnknownExtensionFields: Bool = false - public init() {} + public init() {} } diff --git a/Sources/SwiftProtobuf/TextFormatEncoder.swift b/Sources/SwiftProtobuf/TextFormatEncoder.swift index 6fe217cdf..56fe66899 100644 --- a/Sources/SwiftProtobuf/TextFormatEncoder.swift +++ b/Sources/SwiftProtobuf/TextFormatEncoder.swift @@ -37,7 +37,7 @@ internal struct TextFormatEncoder { private var indentString: [UInt8] = [] var stringResult: String { get { - return String(bytes: data, encoding: String.Encoding.utf8)! + String(bytes: data, encoding: String.Encoding.utf8)! } } @@ -231,7 +231,7 @@ internal struct TextFormatEncoder { append(staticText: "\\\"") case 92: append(staticText: "\\\\") - case 0...31, 127: // Octal form for C0 control chars + case 0...31, 127: // Octal form for C0 control chars data.append(asciiBackslash) data.append(asciiZero + UInt8(c.value / 64)) data.append(asciiZero + UInt8(c.value / 8 % 8)) @@ -258,39 +258,38 @@ internal struct TextFormatEncoder { mutating func putBytesValue(value: Data) { data.append(asciiDoubleQuote) value.withUnsafeBytes { (body: UnsafeRawBufferPointer) in - if let p = body.baseAddress, body.count > 0 { - for i in 0.. 0 { + for i in 0.. [UInt8] { - var bytes = [UInt8]() - if let protoName = nameMap?.names(for: fieldNumber)?.proto { - bytes.append(contentsOf: protoName.utf8Buffer) - } else if let protoName = nameResolver[fieldNumber] { - let buff = UnsafeBufferPointer(start: protoName.utf8Start, count: protoName.utf8CodeUnitCount) - bytes.append(contentsOf: buff) - } else if let extensionName = extensions?[fieldNumber]?.protobufExtension.fieldName { - bytes.append(UInt8(ascii: "[")) - bytes.append(contentsOf: extensionName.utf8) - bytes.append(UInt8(ascii: "]")) - } else { - bytes.append(contentsOf: fieldNumber.description.utf8) - } - return bytes - } - - private mutating func emitFieldName(lookingUp fieldNumber: Int) { - if let protoName = nameMap?.names(for: fieldNumber)?.proto { - encoder.emitFieldName(name: protoName.utf8Buffer) - } else if let protoName = nameResolver[fieldNumber] { - encoder.emitFieldName(name: protoName) - } else if let extensionName = extensions?[fieldNumber]?.protobufExtension.fieldName { - encoder.emitExtensionFieldName(name: extensionName) - } else { - encoder.emitFieldNumber(number: fieldNumber) - } - } - - mutating func visitUnknown(bytes: Data) throws { - if options.printUnknownFields { - try bytes.withUnsafeBytes { (body: UnsafeRawBufferPointer) -> () in - if let baseAddress = body.baseAddress, body.count > 0 { - // All fields will be directly handled, so there is no need for - // the unknown field buffering/collection (when scannings to see - // if something is a message, this would be extremely wasteful). - var binaryOptions = BinaryDecodingOptions() - binaryOptions.discardUnknownFields = true - var decoder = BinaryDecoder(forReadingFrom: baseAddress, - count: body.count, - options: binaryOptions) - try visitUnknown(decoder: &decoder) + private var encoder: TextFormatEncoder + private var nameMap: _NameMap? + private var nameResolver: [Int: StaticString] + private var extensions: ExtensionFieldValueSet? + private let options: TextFormatEncodingOptions + + /// The protobuf text produced by the visitor. + var result: String { + encoder.stringResult + } + + /// Creates a new visitor that serializes the given message to protobuf text + /// format. + init(message: any Message, options: TextFormatEncodingOptions) { + let nameMap: _NameMap? + if let nameProviding = message as? (any _ProtoNameProviding) { + nameMap = type(of: nameProviding)._protobuf_nameMap + } else { + nameMap = nil + } + let extensions = (message as? (any ExtensibleMessage))?._protobuf_extensionFieldValues + + self.nameMap = nameMap + self.nameResolver = [:] + self.extensions = extensions + self.encoder = TextFormatEncoder() + self.options = options + } + + // TODO: This largely duplicates emitFieldName() below. + // But, it's slower so we don't want to just have emitFieldName() use + // formatFieldName(). Also, we need to measure whether the optimization + // this provides to repeated fields is worth the effort; consider just + // removing this and having repeated fields just re-run emitFieldName() + // for each item. + private func formatFieldName(lookingUp fieldNumber: Int) -> [UInt8] { + var bytes = [UInt8]() + if let protoName = nameMap?.names(for: fieldNumber)?.proto { + bytes.append(contentsOf: protoName.utf8Buffer) + } else if let protoName = nameResolver[fieldNumber] { + let buff = UnsafeBufferPointer(start: protoName.utf8Start, count: protoName.utf8CodeUnitCount) + bytes.append(contentsOf: buff) + } else if let extensionName = extensions?[fieldNumber]?.protobufExtension.fieldName { + bytes.append(UInt8(ascii: "[")) + bytes.append(contentsOf: extensionName.utf8) + bytes.append(UInt8(ascii: "]")) + } else { + bytes.append(contentsOf: fieldNumber.description.utf8) + } + return bytes + } + + private mutating func emitFieldName(lookingUp fieldNumber: Int) { + if let protoName = nameMap?.names(for: fieldNumber)?.proto { + encoder.emitFieldName(name: protoName.utf8Buffer) + } else if let protoName = nameResolver[fieldNumber] { + encoder.emitFieldName(name: protoName) + } else if let extensionName = extensions?[fieldNumber]?.protobufExtension.fieldName { + encoder.emitExtensionFieldName(name: extensionName) + } else { + encoder.emitFieldNumber(number: fieldNumber) + } + } + + mutating func visitUnknown(bytes: Data) throws { + if options.printUnknownFields { + try bytes.withUnsafeBytes { (body: UnsafeRawBufferPointer) -> Void in + if let baseAddress = body.baseAddress, body.count > 0 { + // All fields will be directly handled, so there is no need for + // the unknown field buffering/collection (when scannings to see + // if something is a message, this would be extremely wasteful). + var binaryOptions = BinaryDecodingOptions() + binaryOptions.discardUnknownFields = true + var decoder = BinaryDecoder( + forReadingFrom: baseAddress, + count: body.count, + options: binaryOptions + ) + try visitUnknown(decoder: &decoder) + } } - } - } - } - - /// Helper for printing out unknowns. - /// - /// The implementation tries to be "helpful" and if a length delimited field - /// appears to be a submessage, it prints it as such. However, that opens the - /// door to someone sending a message with an unknown field that is a stack - /// bomb, i.e. - it causes this code to recurse, exhausting the stack and - /// thus opening up an attack vector. To keep this "help", but avoid the - /// attack, a limit is placed on how many times it will recurse before just - /// treating the length delimited fields as bytes and not trying to decode - /// them. - private mutating func visitUnknown( - decoder: inout BinaryDecoder, - recursionBudget: Int = 10 - ) throws { - // This stack serves to avoid recursion for groups within groups within - // groups..., this avoid the stack attack that the message detection - // hits. No limit is placed on this because there is no stack risk with - // recursion, and because if a limit was hit, there is no other way to - // encode the group (the message field can just print as length - // delimited, groups don't have an option like that). - var groupFieldNumberStack: [Int] = [] - - while let tag = try decoder.getTag() { - switch tag.wireFormat { - case .varint: - encoder.emitFieldNumber(number: tag.fieldNumber) - var value: UInt64 = 0 - encoder.startRegularField() - try decoder.decodeSingularUInt64Field(value: &value) - encoder.putUInt64(value: value) - encoder.endRegularField() - case .fixed64: - encoder.emitFieldNumber(number: tag.fieldNumber) - var value: UInt64 = 0 - encoder.startRegularField() - try decoder.decodeSingularFixed64Field(value: &value) - encoder.putUInt64Hex(value: value, digits: 16) - encoder.endRegularField() - case .lengthDelimited: - encoder.emitFieldNumber(number: tag.fieldNumber) - var bytes = Data() - try decoder.decodeSingularBytesField(value: &bytes) - var encodeAsBytes = true - if bytes.count > 0 && recursionBudget > 0 { - bytes.withUnsafeBytes { (body: UnsafeRawBufferPointer) -> () in - if let baseAddress = body.baseAddress, body.count > 0 { - do { - // Walk all the fields to test if it looks like a message - var testDecoder = BinaryDecoder(forReadingFrom: baseAddress, - count: body.count, - parent: decoder) - while let _ = try testDecoder.nextFieldNumber() { - } - // No error? Output the message body. - encodeAsBytes = false - var subDecoder = BinaryDecoder(forReadingFrom: baseAddress, - count: bytes.count, - parent: decoder) - encoder.startMessageField() - try visitUnknown(decoder: &subDecoder, - recursionBudget: recursionBudget - 1) - encoder.endMessageField() - } catch { - encodeAsBytes = true - } - } - } - } - if (encodeAsBytes) { + } + } + + /// Helper for printing out unknowns. + /// + /// The implementation tries to be "helpful" and if a length delimited field + /// appears to be a submessage, it prints it as such. However, that opens the + /// door to someone sending a message with an unknown field that is a stack + /// bomb, i.e. - it causes this code to recurse, exhausting the stack and + /// thus opening up an attack vector. To keep this "help", but avoid the + /// attack, a limit is placed on how many times it will recurse before just + /// treating the length delimited fields as bytes and not trying to decode + /// them. + private mutating func visitUnknown( + decoder: inout BinaryDecoder, + recursionBudget: Int = 10 + ) throws { + // This stack serves to avoid recursion for groups within groups within + // groups..., this avoid the stack attack that the message detection + // hits. No limit is placed on this because there is no stack risk with + // recursion, and because if a limit was hit, there is no other way to + // encode the group (the message field can just print as length + // delimited, groups don't have an option like that). + var groupFieldNumberStack: [Int] = [] + + while let tag = try decoder.getTag() { + switch tag.wireFormat { + case .varint: + encoder.emitFieldNumber(number: tag.fieldNumber) + var value: UInt64 = 0 + encoder.startRegularField() + try decoder.decodeSingularUInt64Field(value: &value) + encoder.putUInt64(value: value) + encoder.endRegularField() + case .fixed64: + encoder.emitFieldNumber(number: tag.fieldNumber) + var value: UInt64 = 0 + encoder.startRegularField() + try decoder.decodeSingularFixed64Field(value: &value) + encoder.putUInt64Hex(value: value, digits: 16) + encoder.endRegularField() + case .lengthDelimited: + encoder.emitFieldNumber(number: tag.fieldNumber) + var bytes = Data() + try decoder.decodeSingularBytesField(value: &bytes) + var encodeAsBytes = true + if bytes.count > 0 && recursionBudget > 0 { + bytes.withUnsafeBytes { (body: UnsafeRawBufferPointer) -> Void in + if let baseAddress = body.baseAddress, body.count > 0 { + do { + // Walk all the fields to test if it looks like a message + var testDecoder = BinaryDecoder( + forReadingFrom: baseAddress, + count: body.count, + parent: decoder + ) + while let _ = try testDecoder.nextFieldNumber() { + } + // No error? Output the message body. + encodeAsBytes = false + var subDecoder = BinaryDecoder( + forReadingFrom: baseAddress, + count: bytes.count, + parent: decoder + ) + encoder.startMessageField() + try visitUnknown( + decoder: &subDecoder, + recursionBudget: recursionBudget - 1 + ) + encoder.endMessageField() + } catch { + encodeAsBytes = true + } + } + } + } + if encodeAsBytes { + encoder.startRegularField() + encoder.putBytesValue(value: bytes) + encoder.endRegularField() + } + case .startGroup: + encoder.emitFieldNumber(number: tag.fieldNumber) + encoder.startMessageField() + groupFieldNumberStack.append(tag.fieldNumber) + case .endGroup: + let groupFieldNumber = groupFieldNumberStack.popLast() + // Unknown data is scanned and verified by the + // binary parser, so this can never fail. + assert(tag.fieldNumber == groupFieldNumber) + encoder.endMessageField() + case .fixed32: + encoder.emitFieldNumber(number: tag.fieldNumber) + var value: UInt32 = 0 encoder.startRegularField() - encoder.putBytesValue(value: bytes) + try decoder.decodeSingularFixed32Field(value: &value) + encoder.putUInt64Hex(value: UInt64(value), digits: 8) encoder.endRegularField() - } - case .startGroup: - encoder.emitFieldNumber(number: tag.fieldNumber) - encoder.startMessageField() - groupFieldNumberStack.append(tag.fieldNumber) - case .endGroup: - let groupFieldNumber = groupFieldNumberStack.popLast() - // Unknown data is scanned and verified by the - // binary parser, so this can never fail. - assert(tag.fieldNumber == groupFieldNumber) - encoder.endMessageField() - case .fixed32: - encoder.emitFieldNumber(number: tag.fieldNumber) - var value: UInt32 = 0 - encoder.startRegularField() - try decoder.decodeSingularFixed32Field(value: &value) - encoder.putUInt64Hex(value: UInt64(value), digits: 8) - encoder.endRegularField() - } - } - - // Unknown data is scanned and verified by the binary parser, so this can - // never fail. - assert(groupFieldNumberStack.isEmpty) - } - - // Visitor.swift defines default versions for other singular field types - // that simply widen and dispatch to one of the following. Since Text format - // does not distinguish e.g., Fixed64 vs. UInt64, this is sufficient. - - mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws { - emitFieldName(lookingUp: fieldNumber) - encoder.startRegularField() - encoder.putFloatValue(value: value) - encoder.endRegularField() - } - - mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws { - emitFieldName(lookingUp: fieldNumber) - encoder.startRegularField() - encoder.putDoubleValue(value: value) - encoder.endRegularField() - } - - mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws { - emitFieldName(lookingUp: fieldNumber) - encoder.startRegularField() - encoder.putInt64(value: value) - encoder.endRegularField() - } - - mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws { - emitFieldName(lookingUp: fieldNumber) - encoder.startRegularField() - encoder.putUInt64(value: value) - encoder.endRegularField() - } - - mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws { - emitFieldName(lookingUp: fieldNumber) - encoder.startRegularField() - encoder.putBoolValue(value: value) - encoder.endRegularField() - } - - mutating func visitSingularStringField(value: String, fieldNumber: Int) throws { - emitFieldName(lookingUp: fieldNumber) - encoder.startRegularField() - encoder.putStringValue(value: value) - encoder.endRegularField() - } - - mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws { - emitFieldName(lookingUp: fieldNumber) - encoder.startRegularField() - encoder.putBytesValue(value: value) - encoder.endRegularField() - } - - mutating func visitSingularEnumField(value: E, fieldNumber: Int) throws { - emitFieldName(lookingUp: fieldNumber) - encoder.startRegularField() - encoder.putEnumValue(value: value) - encoder.endRegularField() - } - - mutating func visitSingularMessageField(value: M, - fieldNumber: Int) throws { - emitFieldName(lookingUp: fieldNumber) - - // Cache old visitor configuration - let oldNameMap = self.nameMap - let oldNameResolver = self.nameResolver - let oldExtensions = self.extensions - // Update configuration for new message - self.nameMap = (M.self as? any _ProtoNameProviding.Type)?._protobuf_nameMap - self.nameResolver = [:] - self.extensions = (value as? (any ExtensibleMessage))?._protobuf_extensionFieldValues - // Encode submessage - encoder.startMessageField() - if let any = value as? Google_Protobuf_Any { - any.textTraverse(visitor: &self) - } else { - try! value.traverse(visitor: &self) - } - encoder.endMessageField() - // Restore configuration before returning - self.extensions = oldExtensions - self.nameResolver = oldNameResolver - self.nameMap = oldNameMap - } - - // Emit the full "verbose" form of an Any. This writes the typeURL - // as a field name in `[...]` followed by the fields of the - // contained message. - internal mutating func visitAnyVerbose(value: any Message, typeURL: String) { - encoder.emitExtensionFieldName(name: typeURL) - encoder.startMessageField() - - // Cache old visitor configuration - let oldNameMap = self.nameMap - let oldNameResolver = self.nameResolver - let oldExtensions = self.extensions - // Update configuration for new message - self.nameMap = (type(of: value) as? any _ProtoNameProviding.Type)?._protobuf_nameMap - self.nameResolver = [:] - self.extensions = (value as? (any ExtensibleMessage))?._protobuf_extensionFieldValues - - if let any = value as? Google_Protobuf_Any { - any.textTraverse(visitor: &self) - } else { - try! value.traverse(visitor: &self) - } - - // Restore configuration before returning - self.extensions = oldExtensions - self.nameResolver = oldNameResolver - self.nameMap = oldNameMap - - encoder.endMessageField() - } - - // Write a single special field called "#json". This - // is used for Any objects with undecoded JSON contents. - internal mutating func visitAnyJSONBytesField(value: Data) { - encoder.indent() - encoder.append(staticText: "#json: ") - encoder.putBytesValue(value: value) - encoder.append(staticText: "\n") - } - - // The default implementations in Visitor.swift provide the correct - // results, but we get significantly better performance by only doing - // the name lookup once for the array, rather than once for each element: - - mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws { - assert(!value.isEmpty) - let fieldName = formatFieldName(lookingUp: fieldNumber) - for v in value { - encoder.emitFieldName(name: fieldName) - encoder.startRegularField() - encoder.putFloatValue(value: v) - encoder.endRegularField() - } - } - - mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws { - assert(!value.isEmpty) - let fieldName = formatFieldName(lookingUp: fieldNumber) - for v in value { - encoder.emitFieldName(name: fieldName) - encoder.startRegularField() - encoder.putDoubleValue(value: v) - encoder.endRegularField() - } - } - - mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws { - assert(!value.isEmpty) - let fieldName = formatFieldName(lookingUp: fieldNumber) - for v in value { - encoder.emitFieldName(name: fieldName) - encoder.startRegularField() - encoder.putInt64(value: Int64(v)) - encoder.endRegularField() - } - } - - mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws { - assert(!value.isEmpty) - let fieldName = formatFieldName(lookingUp: fieldNumber) - for v in value { - encoder.emitFieldName(name: fieldName) - encoder.startRegularField() - encoder.putInt64(value: v) - encoder.endRegularField() - } - } - - mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws { - assert(!value.isEmpty) - let fieldName = formatFieldName(lookingUp: fieldNumber) - for v in value { - encoder.emitFieldName(name: fieldName) - encoder.startRegularField() - encoder.putUInt64(value: UInt64(v)) - encoder.endRegularField() - } - } - - mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws { - assert(!value.isEmpty) - let fieldName = formatFieldName(lookingUp: fieldNumber) - for v in value { - encoder.emitFieldName(name: fieldName) - encoder.startRegularField() - encoder.putUInt64(value: v) - encoder.endRegularField() - } - } - - mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws { - try visitRepeatedInt32Field(value: value, fieldNumber: fieldNumber) - } - mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws { - try visitRepeatedInt64Field(value: value, fieldNumber: fieldNumber) - } - mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws { - try visitRepeatedUInt32Field(value: value, fieldNumber: fieldNumber) - } - mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws { - try visitRepeatedUInt64Field(value: value, fieldNumber: fieldNumber) - } - mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws { - try visitRepeatedInt32Field(value: value, fieldNumber: fieldNumber) - } - mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws { - try visitRepeatedInt64Field(value: value, fieldNumber: fieldNumber) - } - - mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws { - assert(!value.isEmpty) - let fieldName = formatFieldName(lookingUp: fieldNumber) - for v in value { - encoder.emitFieldName(name: fieldName) - encoder.startRegularField() - encoder.putBoolValue(value: v) - encoder.endRegularField() - } - } - - mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws { - assert(!value.isEmpty) - let fieldName = formatFieldName(lookingUp: fieldNumber) - for v in value { - encoder.emitFieldName(name: fieldName) - encoder.startRegularField() - encoder.putStringValue(value: v) - encoder.endRegularField() - } - } - - mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws { - assert(!value.isEmpty) - let fieldName = formatFieldName(lookingUp: fieldNumber) - for v in value { - encoder.emitFieldName(name: fieldName) - encoder.startRegularField() - encoder.putBytesValue(value: v) - encoder.endRegularField() - } - } - - mutating func visitRepeatedEnumField(value: [E], fieldNumber: Int) throws { - assert(!value.isEmpty) - let fieldName = formatFieldName(lookingUp: fieldNumber) - for v in value { - encoder.emitFieldName(name: fieldName) - encoder.startRegularField() - encoder.putEnumValue(value: v) - encoder.endRegularField() - } - } - - // Messages and groups - mutating func visitRepeatedMessageField(value: [M], - fieldNumber: Int) throws { - assert(!value.isEmpty) - // Look up field name against outer message encoding state - let fieldName = formatFieldName(lookingUp: fieldNumber) - // Cache old visitor state - let oldNameMap = self.nameMap - let oldNameResolver = self.nameResolver - let oldExtensions = self.extensions - // Update encoding state for new message type - self.nameMap = (M.self as? any _ProtoNameProviding.Type)?._protobuf_nameMap - self.nameResolver = [:] - self.extensions = (value as? (any ExtensibleMessage))?._protobuf_extensionFieldValues - // Iterate and encode each message - for v in value { - encoder.emitFieldName(name: fieldName) - encoder.startMessageField() - if let any = v as? Google_Protobuf_Any { - any.textTraverse(visitor: &self) - } else { - try! v.traverse(visitor: &self) - } - encoder.endMessageField() - } - // Restore state - self.extensions = oldExtensions - self.nameResolver = oldNameResolver - self.nameMap = oldNameMap - } - - // Google's C++ implementation of Text format supports two formats - // for repeated numeric fields: "short" format writes the list as a - // single field with values enclosed in `[...]`, "long" format - // writes a separate field name/value for each item. They provide - // an option for callers to select which output version they prefer. - - // Since this distinction mirrors the difference in Protobuf Binary - // between "packed" and "non-packed", I've chosen to use the short - // format for packed fields and the long version for repeated - // fields. This provides a clear visual distinction between these - // fields (including proto3's default use of packed) without - // introducing the baggage of a separate option. - - private mutating func iterateAndEncode( - packedValue: [T], fieldNumber: Int, - encode: (T, inout TextFormatEncoder) -> () - ) throws { - assert(!packedValue.isEmpty) - emitFieldName(lookingUp: fieldNumber) - encoder.startRegularField() - var firstItem = true - encoder.startArray() - for v in packedValue { - if !firstItem { - encoder.arraySeparator() - } - encode(v, &encoder) - firstItem = false - } - encoder.endArray() - encoder.endRegularField() - } - - mutating func visitPackedFloatField(value: [Float], fieldNumber: Int) throws { - try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) { - (v: Float, encoder: inout TextFormatEncoder) in - encoder.putFloatValue(value: v) - } - } - - mutating func visitPackedDoubleField(value: [Double], fieldNumber: Int) throws { - try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) { - (v: Double, encoder: inout TextFormatEncoder) in - encoder.putDoubleValue(value: v) - } - } - - mutating func visitPackedInt32Field(value: [Int32], fieldNumber: Int) throws { - try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) { - (v: Int32, encoder: inout TextFormatEncoder) in - encoder.putInt64(value: Int64(v)) - } - } - - mutating func visitPackedInt64Field(value: [Int64], fieldNumber: Int) throws { - try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) { - (v: Int64, encoder: inout TextFormatEncoder) in - encoder.putInt64(value: v) - } - } - - mutating func visitPackedUInt32Field(value: [UInt32], fieldNumber: Int) throws { - try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) { - (v: UInt32, encoder: inout TextFormatEncoder) in - encoder.putUInt64(value: UInt64(v)) - } - } - - mutating func visitPackedUInt64Field(value: [UInt64], fieldNumber: Int) throws { - try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) { - (v: UInt64, encoder: inout TextFormatEncoder) in - encoder.putUInt64(value: v) - } - } - - mutating func visitPackedSInt32Field(value: [Int32], fieldNumber: Int) throws { - try visitPackedInt32Field(value: value, fieldNumber: fieldNumber) - } - - mutating func visitPackedSInt64Field(value: [Int64], fieldNumber: Int) throws { - try visitPackedInt64Field(value: value, fieldNumber: fieldNumber) - } - - mutating func visitPackedFixed32Field(value: [UInt32], fieldNumber: Int) throws { - try visitPackedUInt32Field(value: value, fieldNumber: fieldNumber) - } - - mutating func visitPackedFixed64Field(value: [UInt64], fieldNumber: Int) throws { - try visitPackedUInt64Field(value: value, fieldNumber: fieldNumber) - } - - mutating func visitPackedSFixed32Field(value: [Int32], fieldNumber: Int) throws { - try visitPackedInt32Field(value: value, fieldNumber: fieldNumber) - } - - mutating func visitPackedSFixed64Field(value: [Int64], fieldNumber: Int) throws { - try visitPackedInt64Field(value: value, fieldNumber: fieldNumber) - } - - mutating func visitPackedBoolField(value: [Bool], fieldNumber: Int) throws { - try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) { - (v: Bool, encoder: inout TextFormatEncoder) in - encoder.putBoolValue(value: v) - } - } - - mutating func visitPackedEnumField(value: [E], fieldNumber: Int) throws { - try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) { - (v: E, encoder: inout TextFormatEncoder) in - encoder.putEnumValue(value: v) - } - } - - /// Helper to encapsulate the common structure of iterating over a map - /// and encoding the keys and values. - private mutating func iterateAndEncode( - map: Dictionary, - fieldNumber: Int, - isOrderedBefore: (K, K) -> Bool, - encode: (inout TextFormatEncodingVisitor, K, V) throws -> () - ) throws { - // Cache old visitor configuration - let oldNameMap = self.nameMap - let oldNameResolver = self.nameResolver - let oldExtensions = self.extensions - - for (k,v) in map.sorted(by: { isOrderedBefore( $0.0, $1.0) }) { - emitFieldName(lookingUp: fieldNumber) - encoder.startMessageField() - - // Update visitor configuration for map - self.nameMap = nil - self.nameResolver = mapNameResolver - self.extensions = nil - - try encode(&self, k, v) - - // Restore configuration before resuming containing message - self.extensions = oldExtensions - self.nameResolver = oldNameResolver - self.nameMap = oldNameMap - - encoder.endMessageField() - } - } - - mutating func visitMapField( - fieldType: _ProtobufMap.Type, - value: _ProtobufMap.BaseType, - fieldNumber: Int - ) throws { - try iterateAndEncode(map: value, fieldNumber: fieldNumber, isOrderedBefore: KeyType._lessThan) { - (visitor: inout TextFormatEncodingVisitor, key, value) throws -> () in - try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor) - try ValueType.visitSingular(value: value, fieldNumber: 2, with: &visitor) - } - } - - mutating func visitMapField( - fieldType: _ProtobufEnumMap.Type, - value: _ProtobufEnumMap.BaseType, - fieldNumber: Int - ) throws where ValueType.RawValue == Int { - try iterateAndEncode(map: value, fieldNumber: fieldNumber, isOrderedBefore: KeyType._lessThan) { - (visitor: inout TextFormatEncodingVisitor, key, value) throws -> () in - try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor) - try visitor.visitSingularEnumField(value: value, fieldNumber: 2) - } - } - - mutating func visitMapField( - fieldType: _ProtobufMessageMap.Type, - value: _ProtobufMessageMap.BaseType, - fieldNumber: Int - ) throws { - try iterateAndEncode(map: value, fieldNumber: fieldNumber, isOrderedBefore: KeyType._lessThan) { - (visitor: inout TextFormatEncodingVisitor, key, value) throws -> () in - try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor) - try visitor.visitSingularMessageField(value: value, fieldNumber: 2) - } - } + } + } + + // Unknown data is scanned and verified by the binary parser, so this can + // never fail. + assert(groupFieldNumberStack.isEmpty) + } + + // Visitor.swift defines default versions for other singular field types + // that simply widen and dispatch to one of the following. Since Text format + // does not distinguish e.g., Fixed64 vs. UInt64, this is sufficient. + + mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws { + emitFieldName(lookingUp: fieldNumber) + encoder.startRegularField() + encoder.putFloatValue(value: value) + encoder.endRegularField() + } + + mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws { + emitFieldName(lookingUp: fieldNumber) + encoder.startRegularField() + encoder.putDoubleValue(value: value) + encoder.endRegularField() + } + + mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws { + emitFieldName(lookingUp: fieldNumber) + encoder.startRegularField() + encoder.putInt64(value: value) + encoder.endRegularField() + } + + mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws { + emitFieldName(lookingUp: fieldNumber) + encoder.startRegularField() + encoder.putUInt64(value: value) + encoder.endRegularField() + } + + mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws { + emitFieldName(lookingUp: fieldNumber) + encoder.startRegularField() + encoder.putBoolValue(value: value) + encoder.endRegularField() + } + + mutating func visitSingularStringField(value: String, fieldNumber: Int) throws { + emitFieldName(lookingUp: fieldNumber) + encoder.startRegularField() + encoder.putStringValue(value: value) + encoder.endRegularField() + } + + mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws { + emitFieldName(lookingUp: fieldNumber) + encoder.startRegularField() + encoder.putBytesValue(value: value) + encoder.endRegularField() + } + + mutating func visitSingularEnumField(value: E, fieldNumber: Int) throws { + emitFieldName(lookingUp: fieldNumber) + encoder.startRegularField() + encoder.putEnumValue(value: value) + encoder.endRegularField() + } + + mutating func visitSingularMessageField( + value: M, + fieldNumber: Int + ) throws { + emitFieldName(lookingUp: fieldNumber) + + // Cache old visitor configuration + let oldNameMap = self.nameMap + let oldNameResolver = self.nameResolver + let oldExtensions = self.extensions + // Update configuration for new message + self.nameMap = (M.self as? any _ProtoNameProviding.Type)?._protobuf_nameMap + self.nameResolver = [:] + self.extensions = (value as? (any ExtensibleMessage))?._protobuf_extensionFieldValues + // Encode submessage + encoder.startMessageField() + if let any = value as? Google_Protobuf_Any { + any.textTraverse(visitor: &self) + } else { + try! value.traverse(visitor: &self) + } + encoder.endMessageField() + // Restore configuration before returning + self.extensions = oldExtensions + self.nameResolver = oldNameResolver + self.nameMap = oldNameMap + } + + // Emit the full "verbose" form of an Any. This writes the typeURL + // as a field name in `[...]` followed by the fields of the + // contained message. + internal mutating func visitAnyVerbose(value: any Message, typeURL: String) { + encoder.emitExtensionFieldName(name: typeURL) + encoder.startMessageField() + + // Cache old visitor configuration + let oldNameMap = self.nameMap + let oldNameResolver = self.nameResolver + let oldExtensions = self.extensions + // Update configuration for new message + self.nameMap = (type(of: value) as? any _ProtoNameProviding.Type)?._protobuf_nameMap + self.nameResolver = [:] + self.extensions = (value as? (any ExtensibleMessage))?._protobuf_extensionFieldValues + + if let any = value as? Google_Protobuf_Any { + any.textTraverse(visitor: &self) + } else { + try! value.traverse(visitor: &self) + } + + // Restore configuration before returning + self.extensions = oldExtensions + self.nameResolver = oldNameResolver + self.nameMap = oldNameMap + + encoder.endMessageField() + } + + // Write a single special field called "#json". This + // is used for Any objects with undecoded JSON contents. + internal mutating func visitAnyJSONBytesField(value: Data) { + encoder.indent() + encoder.append(staticText: "#json: ") + encoder.putBytesValue(value: value) + encoder.append(staticText: "\n") + } + + // The default implementations in Visitor.swift provide the correct + // results, but we get significantly better performance by only doing + // the name lookup once for the array, rather than once for each element: + + mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws { + assert(!value.isEmpty) + let fieldName = formatFieldName(lookingUp: fieldNumber) + for v in value { + encoder.emitFieldName(name: fieldName) + encoder.startRegularField() + encoder.putFloatValue(value: v) + encoder.endRegularField() + } + } + + mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws { + assert(!value.isEmpty) + let fieldName = formatFieldName(lookingUp: fieldNumber) + for v in value { + encoder.emitFieldName(name: fieldName) + encoder.startRegularField() + encoder.putDoubleValue(value: v) + encoder.endRegularField() + } + } + + mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws { + assert(!value.isEmpty) + let fieldName = formatFieldName(lookingUp: fieldNumber) + for v in value { + encoder.emitFieldName(name: fieldName) + encoder.startRegularField() + encoder.putInt64(value: Int64(v)) + encoder.endRegularField() + } + } + + mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws { + assert(!value.isEmpty) + let fieldName = formatFieldName(lookingUp: fieldNumber) + for v in value { + encoder.emitFieldName(name: fieldName) + encoder.startRegularField() + encoder.putInt64(value: v) + encoder.endRegularField() + } + } + + mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws { + assert(!value.isEmpty) + let fieldName = formatFieldName(lookingUp: fieldNumber) + for v in value { + encoder.emitFieldName(name: fieldName) + encoder.startRegularField() + encoder.putUInt64(value: UInt64(v)) + encoder.endRegularField() + } + } + + mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws { + assert(!value.isEmpty) + let fieldName = formatFieldName(lookingUp: fieldNumber) + for v in value { + encoder.emitFieldName(name: fieldName) + encoder.startRegularField() + encoder.putUInt64(value: v) + encoder.endRegularField() + } + } + + mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws { + try visitRepeatedInt32Field(value: value, fieldNumber: fieldNumber) + } + mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws { + try visitRepeatedInt64Field(value: value, fieldNumber: fieldNumber) + } + mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws { + try visitRepeatedUInt32Field(value: value, fieldNumber: fieldNumber) + } + mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws { + try visitRepeatedUInt64Field(value: value, fieldNumber: fieldNumber) + } + mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws { + try visitRepeatedInt32Field(value: value, fieldNumber: fieldNumber) + } + mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws { + try visitRepeatedInt64Field(value: value, fieldNumber: fieldNumber) + } + + mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws { + assert(!value.isEmpty) + let fieldName = formatFieldName(lookingUp: fieldNumber) + for v in value { + encoder.emitFieldName(name: fieldName) + encoder.startRegularField() + encoder.putBoolValue(value: v) + encoder.endRegularField() + } + } + + mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws { + assert(!value.isEmpty) + let fieldName = formatFieldName(lookingUp: fieldNumber) + for v in value { + encoder.emitFieldName(name: fieldName) + encoder.startRegularField() + encoder.putStringValue(value: v) + encoder.endRegularField() + } + } + + mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws { + assert(!value.isEmpty) + let fieldName = formatFieldName(lookingUp: fieldNumber) + for v in value { + encoder.emitFieldName(name: fieldName) + encoder.startRegularField() + encoder.putBytesValue(value: v) + encoder.endRegularField() + } + } + + mutating func visitRepeatedEnumField(value: [E], fieldNumber: Int) throws { + assert(!value.isEmpty) + let fieldName = formatFieldName(lookingUp: fieldNumber) + for v in value { + encoder.emitFieldName(name: fieldName) + encoder.startRegularField() + encoder.putEnumValue(value: v) + encoder.endRegularField() + } + } + + // Messages and groups + mutating func visitRepeatedMessageField( + value: [M], + fieldNumber: Int + ) throws { + assert(!value.isEmpty) + // Look up field name against outer message encoding state + let fieldName = formatFieldName(lookingUp: fieldNumber) + // Cache old visitor state + let oldNameMap = self.nameMap + let oldNameResolver = self.nameResolver + let oldExtensions = self.extensions + // Update encoding state for new message type + self.nameMap = (M.self as? any _ProtoNameProviding.Type)?._protobuf_nameMap + self.nameResolver = [:] + self.extensions = (value as? (any ExtensibleMessage))?._protobuf_extensionFieldValues + // Iterate and encode each message + for v in value { + encoder.emitFieldName(name: fieldName) + encoder.startMessageField() + if let any = v as? Google_Protobuf_Any { + any.textTraverse(visitor: &self) + } else { + try! v.traverse(visitor: &self) + } + encoder.endMessageField() + } + // Restore state + self.extensions = oldExtensions + self.nameResolver = oldNameResolver + self.nameMap = oldNameMap + } + + // Google's C++ implementation of Text format supports two formats + // for repeated numeric fields: "short" format writes the list as a + // single field with values enclosed in `[...]`, "long" format + // writes a separate field name/value for each item. They provide + // an option for callers to select which output version they prefer. + + // Since this distinction mirrors the difference in Protobuf Binary + // between "packed" and "non-packed", I've chosen to use the short + // format for packed fields and the long version for repeated + // fields. This provides a clear visual distinction between these + // fields (including proto3's default use of packed) without + // introducing the baggage of a separate option. + + private mutating func iterateAndEncode( + packedValue: [T], + fieldNumber: Int, + encode: (T, inout TextFormatEncoder) -> Void + ) throws { + assert(!packedValue.isEmpty) + emitFieldName(lookingUp: fieldNumber) + encoder.startRegularField() + var firstItem = true + encoder.startArray() + for v in packedValue { + if !firstItem { + encoder.arraySeparator() + } + encode(v, &encoder) + firstItem = false + } + encoder.endArray() + encoder.endRegularField() + } + + mutating func visitPackedFloatField(value: [Float], fieldNumber: Int) throws { + try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) { + (v: Float, encoder: inout TextFormatEncoder) in + encoder.putFloatValue(value: v) + } + } + + mutating func visitPackedDoubleField(value: [Double], fieldNumber: Int) throws { + try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) { + (v: Double, encoder: inout TextFormatEncoder) in + encoder.putDoubleValue(value: v) + } + } + + mutating func visitPackedInt32Field(value: [Int32], fieldNumber: Int) throws { + try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) { + (v: Int32, encoder: inout TextFormatEncoder) in + encoder.putInt64(value: Int64(v)) + } + } + + mutating func visitPackedInt64Field(value: [Int64], fieldNumber: Int) throws { + try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) { + (v: Int64, encoder: inout TextFormatEncoder) in + encoder.putInt64(value: v) + } + } + + mutating func visitPackedUInt32Field(value: [UInt32], fieldNumber: Int) throws { + try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) { + (v: UInt32, encoder: inout TextFormatEncoder) in + encoder.putUInt64(value: UInt64(v)) + } + } + + mutating func visitPackedUInt64Field(value: [UInt64], fieldNumber: Int) throws { + try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) { + (v: UInt64, encoder: inout TextFormatEncoder) in + encoder.putUInt64(value: v) + } + } + + mutating func visitPackedSInt32Field(value: [Int32], fieldNumber: Int) throws { + try visitPackedInt32Field(value: value, fieldNumber: fieldNumber) + } + + mutating func visitPackedSInt64Field(value: [Int64], fieldNumber: Int) throws { + try visitPackedInt64Field(value: value, fieldNumber: fieldNumber) + } + + mutating func visitPackedFixed32Field(value: [UInt32], fieldNumber: Int) throws { + try visitPackedUInt32Field(value: value, fieldNumber: fieldNumber) + } + + mutating func visitPackedFixed64Field(value: [UInt64], fieldNumber: Int) throws { + try visitPackedUInt64Field(value: value, fieldNumber: fieldNumber) + } + + mutating func visitPackedSFixed32Field(value: [Int32], fieldNumber: Int) throws { + try visitPackedInt32Field(value: value, fieldNumber: fieldNumber) + } + + mutating func visitPackedSFixed64Field(value: [Int64], fieldNumber: Int) throws { + try visitPackedInt64Field(value: value, fieldNumber: fieldNumber) + } + + mutating func visitPackedBoolField(value: [Bool], fieldNumber: Int) throws { + try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) { + (v: Bool, encoder: inout TextFormatEncoder) in + encoder.putBoolValue(value: v) + } + } + + mutating func visitPackedEnumField(value: [E], fieldNumber: Int) throws { + try iterateAndEncode(packedValue: value, fieldNumber: fieldNumber) { + (v: E, encoder: inout TextFormatEncoder) in + encoder.putEnumValue(value: v) + } + } + + /// Helper to encapsulate the common structure of iterating over a map + /// and encoding the keys and values. + private mutating func iterateAndEncode( + map: [K: V], + fieldNumber: Int, + isOrderedBefore: (K, K) -> Bool, + encode: (inout TextFormatEncodingVisitor, K, V) throws -> Void + ) throws { + // Cache old visitor configuration + let oldNameMap = self.nameMap + let oldNameResolver = self.nameResolver + let oldExtensions = self.extensions + + for (k, v) in map.sorted(by: { isOrderedBefore($0.0, $1.0) }) { + emitFieldName(lookingUp: fieldNumber) + encoder.startMessageField() + + // Update visitor configuration for map + self.nameMap = nil + self.nameResolver = mapNameResolver + self.extensions = nil + + try encode(&self, k, v) + + // Restore configuration before resuming containing message + self.extensions = oldExtensions + self.nameResolver = oldNameResolver + self.nameMap = oldNameMap + + encoder.endMessageField() + } + } + + mutating func visitMapField( + fieldType: _ProtobufMap.Type, + value: _ProtobufMap.BaseType, + fieldNumber: Int + ) throws { + try iterateAndEncode(map: value, fieldNumber: fieldNumber, isOrderedBefore: KeyType._lessThan) { + (visitor: inout TextFormatEncodingVisitor, key, value) throws -> Void in + try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor) + try ValueType.visitSingular(value: value, fieldNumber: 2, with: &visitor) + } + } + + mutating func visitMapField( + fieldType: _ProtobufEnumMap.Type, + value: _ProtobufEnumMap.BaseType, + fieldNumber: Int + ) throws where ValueType.RawValue == Int { + try iterateAndEncode(map: value, fieldNumber: fieldNumber, isOrderedBefore: KeyType._lessThan) { + (visitor: inout TextFormatEncodingVisitor, key, value) throws -> Void in + try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor) + try visitor.visitSingularEnumField(value: value, fieldNumber: 2) + } + } + + mutating func visitMapField( + fieldType: _ProtobufMessageMap.Type, + value: _ProtobufMessageMap.BaseType, + fieldNumber: Int + ) throws { + try iterateAndEncode(map: value, fieldNumber: fieldNumber, isOrderedBefore: KeyType._lessThan) { + (visitor: inout TextFormatEncodingVisitor, key, value) throws -> Void in + try KeyType.visitSingular(value: key, fieldNumber: 1, with: &visitor) + try visitor.visitSingularMessageField(value: value, fieldNumber: 2) + } + } } diff --git a/Sources/SwiftProtobuf/TextFormatScanner.swift b/Sources/SwiftProtobuf/TextFormatScanner.swift index f523625a7..df390c5d8 100644 --- a/Sources/SwiftProtobuf/TextFormatScanner.swift +++ b/Sources/SwiftProtobuf/TextFormatScanner.swift @@ -74,23 +74,23 @@ private let asciiUpperZ = UInt8(ascii: "Z") private let maxFieldNumLength: Int = 9 private func fromHexDigit(_ c: UInt8) -> UInt8? { - if c >= asciiZero && c <= asciiNine { - return c - asciiZero - } - if c >= asciiUpperA && c <= asciiUpperF { - return c - asciiUpperA + UInt8(10) - } - if c >= asciiLowerA && c <= asciiLowerF { - return c - asciiLowerA + UInt8(10) - } - return nil + if c >= asciiZero && c <= asciiNine { + return c - asciiZero + } + if c >= asciiUpperA && c <= asciiUpperF { + return c - asciiUpperA + UInt8(10) + } + if c >= asciiLowerA && c <= asciiLowerF { + return c - asciiLowerA + UInt8(10) + } + return nil } private func uint32FromHexDigit(_ c: UInt8) -> UInt32? { - guard let u8 = fromHexDigit(c) else { - return nil - } - return UInt32(u8) + guard let u8 = fromHexDigit(c) else { + return nil + } + return UInt32(u8) } // Protobuf Text encoding assumes that you're working directly @@ -99,138 +99,141 @@ private func uint32FromHexDigit(_ c: UInt8) -> UInt32? { // it back into a string. private func decodeString(_ s: String) -> String? { - // Helper to read 4 hex digits as a UInt32 - func read4HexDigits(_ i: inout String.UTF8View.Iterator) -> UInt32? { - if let digit1 = i.next(), - let d1 = uint32FromHexDigit(digit1), - let digit2 = i.next(), - let d2 = uint32FromHexDigit(digit2), - let digit3 = i.next(), - let d3 = uint32FromHexDigit(digit3), - let digit4 = i.next(), - let d4 = uint32FromHexDigit(digit4) { - return (d1 << 12) + (d2 << 8) + (d3 << 4) + d4 + // Helper to read 4 hex digits as a UInt32 + func read4HexDigits(_ i: inout String.UTF8View.Iterator) -> UInt32? { + if let digit1 = i.next(), + let d1 = uint32FromHexDigit(digit1), + let digit2 = i.next(), + let d2 = uint32FromHexDigit(digit2), + let digit3 = i.next(), + let d3 = uint32FromHexDigit(digit3), + let digit4 = i.next(), + let d4 = uint32FromHexDigit(digit4) + { + return (d1 << 12) + (d2 << 8) + (d3 << 4) + d4 + } + return nil } - return nil - } - - var out = [UInt8]() - var bytes = s.utf8.makeIterator() - while let byte = bytes.next() { - switch byte { - case asciiBackslash: // backslash - if let escaped = bytes.next() { - switch escaped { - case asciiZero...asciiSeven: // 0...7 - // C standard allows 1, 2, or 3 octal digits. - let savedPosition = bytes - let digit1 = escaped - let digit1Value = digit1 - asciiZero - if let digit2 = bytes.next(), - digit2 >= asciiZero && digit2 <= asciiSeven { - let digit2Value = digit2 - asciiZero - let innerSavedPosition = bytes - if let digit3 = bytes.next(), - digit3 >= asciiZero && digit3 <= asciiSeven { - let digit3Value = digit3 - asciiZero - // The max octal digit is actually \377, but looking at the C++ - // protobuf code in strutil.cc:UnescapeCEscapeSequences(), it - // decodes with rollover, so just duplicate that behavior for - // consistency between languages. - let n = digit1Value &* 64 &+ digit2Value &* 8 &+ digit3Value - out.append(n) - } else { - let n = digit1Value * 8 + digit2Value - out.append(n) - bytes = innerSavedPosition - } - } else { - let n = digit1Value - out.append(n) - bytes = savedPosition - } - case asciiLowerU, asciiUpperU: // "u" - // \u - 4 hex digits, \U 8 hex digits: - guard let first = read4HexDigits(&bytes) else { return nil } - var codePoint = first - if escaped == asciiUpperU { - guard let second = read4HexDigits(&bytes) else { return nil } - codePoint = (codePoint << 16) + second - } - switch codePoint { - case 0...0x7f: - // 1 byte encoding - out.append(UInt8(truncatingIfNeeded: codePoint)) - case 0x80...0x7ff: - // 2 byte encoding - out.append(0xC0 + UInt8(truncatingIfNeeded: codePoint >> 6)) - out.append(0x80 + UInt8(truncatingIfNeeded: codePoint & 0x3F)) - case 0x800...0xffff: - // 3 byte encoding - out.append(0xE0 + UInt8(truncatingIfNeeded: codePoint >> 12)) - out.append(0x80 + UInt8(truncatingIfNeeded: (codePoint >> 6) & 0x3F)) - out.append(0x80 + UInt8(truncatingIfNeeded: codePoint & 0x3F)) - case 0x10000...0x10FFFF: - // 4 byte encoding - out.append(0xF0 + UInt8(truncatingIfNeeded: codePoint >> 18)) - out.append(0x80 + UInt8(truncatingIfNeeded: (codePoint >> 12) & 0x3F)) - out.append(0x80 + UInt8(truncatingIfNeeded: (codePoint >> 6) & 0x3F)) - out.append(0x80 + UInt8(truncatingIfNeeded: codePoint & 0x3F)) - default: - return nil - } - case asciiLowerX: // "x" - // Unlike C/C++, protobuf only allows 1 or 2 digits here: - if let byte = bytes.next(), let digit = fromHexDigit(byte) { - var n = digit - let savedPosition = bytes - if let byte = bytes.next(), let digit = fromHexDigit(byte) { - n = n &* 16 + digit + + var out = [UInt8]() + var bytes = s.utf8.makeIterator() + while let byte = bytes.next() { + switch byte { + case asciiBackslash: // backslash + if let escaped = bytes.next() { + switch escaped { + case asciiZero...asciiSeven: // 0...7 + // C standard allows 1, 2, or 3 octal digits. + let savedPosition = bytes + let digit1 = escaped + let digit1Value = digit1 - asciiZero + if let digit2 = bytes.next(), + digit2 >= asciiZero && digit2 <= asciiSeven + { + let digit2Value = digit2 - asciiZero + let innerSavedPosition = bytes + if let digit3 = bytes.next(), + digit3 >= asciiZero && digit3 <= asciiSeven + { + let digit3Value = digit3 - asciiZero + // The max octal digit is actually \377, but looking at the C++ + // protobuf code in strutil.cc:UnescapeCEscapeSequences(), it + // decodes with rollover, so just duplicate that behavior for + // consistency between languages. + let n = digit1Value &* 64 &+ digit2Value &* 8 &+ digit3Value + out.append(n) + } else { + let n = digit1Value * 8 + digit2Value + out.append(n) + bytes = innerSavedPosition + } + } else { + let n = digit1Value + out.append(n) + bytes = savedPosition + } + case asciiLowerU, asciiUpperU: // "u" + // \u - 4 hex digits, \U 8 hex digits: + guard let first = read4HexDigits(&bytes) else { return nil } + var codePoint = first + if escaped == asciiUpperU { + guard let second = read4HexDigits(&bytes) else { return nil } + codePoint = (codePoint << 16) + second + } + switch codePoint { + case 0...0x7f: + // 1 byte encoding + out.append(UInt8(truncatingIfNeeded: codePoint)) + case 0x80...0x7ff: + // 2 byte encoding + out.append(0xC0 + UInt8(truncatingIfNeeded: codePoint >> 6)) + out.append(0x80 + UInt8(truncatingIfNeeded: codePoint & 0x3F)) + case 0x800...0xffff: + // 3 byte encoding + out.append(0xE0 + UInt8(truncatingIfNeeded: codePoint >> 12)) + out.append(0x80 + UInt8(truncatingIfNeeded: (codePoint >> 6) & 0x3F)) + out.append(0x80 + UInt8(truncatingIfNeeded: codePoint & 0x3F)) + case 0x10000...0x10FFFF: + // 4 byte encoding + out.append(0xF0 + UInt8(truncatingIfNeeded: codePoint >> 18)) + out.append(0x80 + UInt8(truncatingIfNeeded: (codePoint >> 12) & 0x3F)) + out.append(0x80 + UInt8(truncatingIfNeeded: (codePoint >> 6) & 0x3F)) + out.append(0x80 + UInt8(truncatingIfNeeded: codePoint & 0x3F)) + default: + return nil + } + case asciiLowerX: // "x" + // Unlike C/C++, protobuf only allows 1 or 2 digits here: + if let byte = bytes.next(), let digit = fromHexDigit(byte) { + var n = digit + let savedPosition = bytes + if let byte = bytes.next(), let digit = fromHexDigit(byte) { + n = n &* 16 + digit + } else { + // No second digit; reset the iterator + bytes = savedPosition + } + out.append(n) + } else { + return nil // Hex escape must have at least 1 digit + } + case asciiLowerA: // \a + out.append(asciiBell) + case asciiLowerB: // \b + out.append(asciiBackspace) + case asciiLowerF: // \f + out.append(asciiFormFeed) + case asciiLowerN: // \n + out.append(asciiNewLine) + case asciiLowerR: // \r + out.append(asciiCarriageReturn) + case asciiLowerT: // \t + out.append(asciiTab) + case asciiLowerV: // \v + out.append(asciiVerticalTab) + case asciiDoubleQuote, + asciiSingleQuote, + asciiQuestionMark, + asciiBackslash: // " ' ? \ + out.append(escaped) + default: + return nil // Unrecognized escape + } } else { - // No second digit; reset the iterator - bytes = savedPosition + return nil // Input ends with backslash } - out.append(n) - } else { - return nil // Hex escape must have at least 1 digit - } - case asciiLowerA: // \a - out.append(asciiBell) - case asciiLowerB: // \b - out.append(asciiBackspace) - case asciiLowerF: // \f - out.append(asciiFormFeed) - case asciiLowerN: // \n - out.append(asciiNewLine) - case asciiLowerR: // \r - out.append(asciiCarriageReturn) - case asciiLowerT: // \t - out.append(asciiTab) - case asciiLowerV: // \v - out.append(asciiVerticalTab) - case asciiDoubleQuote, - asciiSingleQuote, - asciiQuestionMark, - asciiBackslash: // " ' ? \ - out.append(escaped) default: - return nil // Unrecognized escape + out.append(byte) } - } else { - return nil // Input ends with backslash - } - default: - out.append(byte) } - } - // There has got to be an easier way to convert a [UInt8] into a String. - return out.withUnsafeBufferPointer { ptr in - if let addr = ptr.baseAddress { - return utf8ToString(bytes: addr, count: ptr.count) - } else { - return String() + // There has got to be an easier way to convert a [UInt8] into a String. + return out.withUnsafeBufferPointer { ptr in + if let addr = ptr.baseAddress { + return utf8ToString(bytes: addr, count: ptr.count) + } else { + return String() + } } - } } /// @@ -247,15 +250,15 @@ internal struct TextFormatScanner { internal var complete: Bool { mutating get { - return p == end + p == end } } internal init( - utf8Pointer: UnsafeRawPointer, - count: Int, - options: TextFormatDecodingOptions, - extensions: (any ExtensionMap)? = nil + utf8Pointer: UnsafeRawPointer, + count: Int, + options: TextFormatDecodingOptions, + extensions: (any ExtensionMap)? = nil ) { p = utf8Pointer end = p + count @@ -289,11 +292,11 @@ internal struct TextFormatScanner { let u = p[0] switch u { case asciiSpace, - asciiTab, - asciiNewLine, - asciiCarriageReturn: // space, tab, NL, CR + asciiTab, + asciiNewLine, + asciiCarriageReturn: // space, tab, NL, CR p += 1 - case asciiHash: // # comment + case asciiHash: // # comment p += 1 while p != end { // Skip until end of line @@ -318,9 +321,9 @@ internal struct TextFormatScanner { let c = p[0] switch c { case asciiLowerA...asciiLowerZ, - asciiUpperA...asciiUpperZ, - asciiZero...asciiNine, - asciiUnderscore: + asciiUpperA...asciiUpperZ, + asciiZero...asciiNine, + asciiUnderscore: p += 1 default: break loop @@ -342,103 +345,103 @@ internal struct TextFormatScanner { /// Scan a string that encodes a byte field, return a count of /// the number of bytes that should be decoded from it private mutating func validateAndCountBytesFromString(terminator: UInt8, sawBackslash: inout Bool) throws -> Int { - var count = 0 - let start = p - sawBackslash = false - while p != end { - let byte = p[0] - p += 1 - if byte == terminator { - p = start - return count - } - switch byte { - case asciiNewLine, asciiCarriageReturn: - // Can't have a newline in the middle of a bytes string. - throw TextFormatDecodingError.malformedText - case asciiBackslash: // "\\" - sawBackslash = true - if p != end { - let escaped = p[0] + var count = 0 + let start = p + sawBackslash = false + while p != end { + let byte = p[0] p += 1 - switch escaped { - case asciiZero...asciiSeven: // '0'...'7' - // C standard allows 1, 2, or 3 octal digits. - if p != end, p[0] >= asciiZero, p[0] <= asciiSeven { - p += 1 - if p != end, p[0] >= asciiZero, p[0] <= asciiSeven { - if escaped > asciiThree { - // Out of range octal: three digits and first digit is greater than 3 - throw TextFormatDecodingError.malformedText - } - p += 1 - } - } - count += 1 - case asciiLowerU, asciiUpperU: // 'u' or 'U' unicode escape - let numDigits = (escaped == asciiLowerU) ? 4 : 8 - guard (end - p) >= numDigits else { - throw TextFormatDecodingError.malformedText // unicode escape must 4/8 digits - } - var codePoint: UInt32 = 0 - for i in 0..= asciiZero, p[0] <= asciiSeven { + p += 1 + if p != end, p[0] >= asciiZero, p[0] <= asciiSeven { + if escaped > asciiThree { + // Out of range octal: three digits and first digit is greater than 3 + throw TextFormatDecodingError.malformedText + } + p += 1 + } + } + count += 1 + case asciiLowerU, asciiUpperU: // 'u' or 'U' unicode escape + let numDigits = (escaped == asciiLowerU) ? 4 : 8 + guard (end - p) >= numDigits else { + throw TextFormatDecodingError.malformedText // unicode escape must 4/8 digits + } + var codePoint: UInt32 = 0 + for i in 0.. 0 { - while p[0] != terminator { - let byte = p[0] - p += 1 - switch byte { - case asciiBackslash: // "\\" - let escaped = p[0] - p += 1 - switch escaped { - case asciiZero...asciiSeven: // '0'...'7' - // C standard allows 1, 2, or 3 octal digits. - let digit1Value = escaped - asciiZero - let digit2 = p[0] - if digit2 >= asciiZero, digit2 <= asciiSeven { - p += 1 - let digit2Value = digit2 - asciiZero - let digit3 = p[0] - if digit3 >= asciiZero, digit3 <= asciiSeven { + data.withUnsafeMutableBytes { + (body: UnsafeMutableRawBufferPointer) in + if var out = body.baseAddress, body.count > 0 { + while p[0] != terminator { + let byte = p[0] p += 1 - let digit3Value = digit3 - asciiZero - out[0] = digit1Value &* 64 + digit2Value * 8 + digit3Value - out += 1 - } else { - out[0] = digit1Value * 8 + digit2Value - out += 1 - } - } else { - out[0] = digit1Value - out += 1 - } - case asciiLowerU, asciiUpperU: - let numDigits = (escaped == asciiLowerU) ? 4 : 8 - var codePoint: UInt32 = 0 - for i in 0..> 6) - out[1] = 0x80 + UInt8(truncatingIfNeeded: codePoint & 0x3F) - out += 2 - case 0x800...0xffff: - // 3 byte encoding - out[0] = 0xE0 + UInt8(truncatingIfNeeded: codePoint >> 12) - out[1] = 0x80 + UInt8(truncatingIfNeeded: (codePoint >> 6) & 0x3F) - out[2] = 0x80 + UInt8(truncatingIfNeeded: codePoint & 0x3F) - out += 3 - case 0x10000...0x10FFFF: - // 4 byte encoding - out[0] = 0xF0 + UInt8(truncatingIfNeeded: codePoint >> 18) - out[1] = 0x80 + UInt8(truncatingIfNeeded: (codePoint >> 12) & 0x3F) - out[2] = 0x80 + UInt8(truncatingIfNeeded: (codePoint >> 6) & 0x3F) - out[3] = 0x80 + UInt8(truncatingIfNeeded: codePoint & 0x3F) - out += 4 - default: - preconditionFailure() // Already validated, can't happen - } - case asciiLowerX: // 'x' hexadecimal escape - // We already validated, so we know there's at least one digit: - var n = fromHexDigit(p[0])! - p += 1 - if let digit = fromHexDigit(p[0]) { - n = n &* 16 &+ digit - p += 1 + switch byte { + case asciiBackslash: // "\\" + let escaped = p[0] + p += 1 + switch escaped { + case asciiZero...asciiSeven: // '0'...'7' + // C standard allows 1, 2, or 3 octal digits. + let digit1Value = escaped - asciiZero + let digit2 = p[0] + if digit2 >= asciiZero, digit2 <= asciiSeven { + p += 1 + let digit2Value = digit2 - asciiZero + let digit3 = p[0] + if digit3 >= asciiZero, digit3 <= asciiSeven { + p += 1 + let digit3Value = digit3 - asciiZero + out[0] = digit1Value &* 64 + digit2Value * 8 + digit3Value + out += 1 + } else { + out[0] = digit1Value * 8 + digit2Value + out += 1 + } + } else { + out[0] = digit1Value + out += 1 + } + case asciiLowerU, asciiUpperU: + let numDigits = (escaped == asciiLowerU) ? 4 : 8 + var codePoint: UInt32 = 0 + for i in 0..> 6) + out[1] = 0x80 + UInt8(truncatingIfNeeded: codePoint & 0x3F) + out += 2 + case 0x800...0xffff: + // 3 byte encoding + out[0] = 0xE0 + UInt8(truncatingIfNeeded: codePoint >> 12) + out[1] = 0x80 + UInt8(truncatingIfNeeded: (codePoint >> 6) & 0x3F) + out[2] = 0x80 + UInt8(truncatingIfNeeded: codePoint & 0x3F) + out += 3 + case 0x10000...0x10FFFF: + // 4 byte encoding + out[0] = 0xF0 + UInt8(truncatingIfNeeded: codePoint >> 18) + out[1] = 0x80 + UInt8(truncatingIfNeeded: (codePoint >> 12) & 0x3F) + out[2] = 0x80 + UInt8(truncatingIfNeeded: (codePoint >> 6) & 0x3F) + out[3] = 0x80 + UInt8(truncatingIfNeeded: codePoint & 0x3F) + out += 4 + default: + preconditionFailure() // Already validated, can't happen + } + case asciiLowerX: // 'x' hexadecimal escape + // We already validated, so we know there's at least one digit: + var n = fromHexDigit(p[0])! + p += 1 + if let digit = fromHexDigit(p[0]) { + n = n &* 16 &+ digit + p += 1 + } + out[0] = n + out += 1 + case asciiLowerA: // \a ("alert") + out[0] = asciiBell + out += 1 + case asciiLowerB: // \b + out[0] = asciiBackspace + out += 1 + case asciiLowerF: // \f + out[0] = asciiFormFeed + out += 1 + case asciiLowerN: // \n + out[0] = asciiNewLine + out += 1 + case asciiLowerR: // \r + out[0] = asciiCarriageReturn + out += 1 + case asciiLowerT: // \t + out[0] = asciiTab + out += 1 + case asciiLowerV: // \v + out[0] = asciiVerticalTab + out += 1 + default: + out[0] = escaped + out += 1 + } + default: + out[0] = byte + out += 1 + } } - out[0] = n - out += 1 - case asciiLowerA: // \a ("alert") - out[0] = asciiBell - out += 1 - case asciiLowerB: // \b - out[0] = asciiBackspace - out += 1 - case asciiLowerF: // \f - out[0] = asciiFormFeed - out += 1 - case asciiLowerN: // \n - out[0] = asciiNewLine - out += 1 - case asciiLowerR: // \r - out[0] = asciiCarriageReturn - out += 1 - case asciiLowerT: // \t - out[0] = asciiTab - out += 1 - case asciiLowerV: // \v - out[0] = asciiVerticalTab - out += 1 - default: - out[0] = escaped - out += 1 - } - default: - out[0] = byte - out += 1 + p += 1 // Consume terminator } - } - p += 1 // Consume terminator } - } } /// Assumes the leading quote has already been consumed @@ -575,7 +578,7 @@ internal struct TextFormatScanner { } } p += 1 - if c == asciiBackslash { // \ + if c == asciiBackslash { // \ if p == end { return nil } @@ -587,7 +590,7 @@ internal struct TextFormatScanner { return nil } } - return nil // Unterminated quoted string + return nil // Unterminated quoted string } internal mutating func nextUInt() throws -> UInt64 { @@ -596,21 +599,21 @@ internal struct TextFormatScanner { } let c = p[0] p += 1 - if c == asciiZero { // leading '0' precedes octal or hex + if c == asciiZero { // leading '0' precedes octal or hex if p == end { // The TextFormat ended with a field value of zero. return 0 } - if p[0] == asciiLowerX { // 'x' => hex + if p[0] == asciiLowerX { // 'x' => hex p += 1 var n: UInt64 = 0 while p != end { let digit = p[0] let val: UInt64 switch digit { - case asciiZero...asciiNine: // 0...9 + case asciiZero...asciiNine: // 0...9 val = UInt64(digit - asciiZero) - case asciiLowerA...asciiLowerF: // a...f + case asciiLowerA...asciiLowerF: // a...f val = UInt64(digit - asciiLowerA + 10) case asciiUpperA...asciiUpperF: val = UInt64(digit - asciiUpperA + 10) @@ -626,13 +629,13 @@ internal struct TextFormatScanner { } skipWhitespace() return n - } else { // octal + } else { // octal var n: UInt64 = 0 while p != end { let digit = p[0] if digit < asciiZero || digit > asciiSeven { skipWhitespace() - return n // not octal digit + return n // not octal digit } let val = UInt64(digit - asciiZero) if n > UInt64.max / 8 { @@ -644,13 +647,13 @@ internal struct TextFormatScanner { skipWhitespace() return n } - } else if c > asciiZero && c <= asciiNine { // 1...9 + } else if c > asciiZero && c <= asciiNine { // 1...9 var n = UInt64(c - asciiZero) while p != end { let digit = p[0] if digit < asciiZero || digit > asciiNine { skipWhitespace() - return n // not a digit + return n // not a digit } let val = UInt64(digit - asciiZero) if n > UInt64.max / 10 || n * 10 > UInt64.max - val { @@ -670,7 +673,7 @@ internal struct TextFormatScanner { throw TextFormatDecodingError.malformedNumber } let c = p[0] - if c == asciiMinus { // - + if c == asciiMinus { // - p += 1 if p == end { throw TextFormatDecodingError.malformedNumber @@ -681,13 +684,13 @@ internal struct TextFormatScanner { throw TextFormatDecodingError.malformedNumber } let n = try nextUInt() - let limit: UInt64 = 0x8000000000000000 // -Int64.min + let limit: UInt64 = 0x8000_0000_0000_0000 // -Int64.min if n >= limit { if n > limit { // Too large negative number throw TextFormatDecodingError.malformedNumber } else { - return Int64.min // Special case for Int64.min + return Int64.min // Special case for Int64.min } } return -Int64(bitPattern: n) @@ -754,11 +757,11 @@ internal struct TextFormatScanner { var sawBackslash = false let n = try validateAndCountBytesFromString(terminator: c, sawBackslash: &sawBackslash) if sawBackslash { - result = Data(count: n) - parseBytesFromString(terminator: c, into: &result) + result = Data(count: n) + parseBytesFromString(terminator: c, into: &result) } else { - result = Data(bytes: p, count: n) - p += n + 1 // Skip string body + close quote + result = Data(bytes: p, count: n) + p += n + 1 // Skip string body + close quote } // If there are more strings, decode them @@ -776,12 +779,12 @@ internal struct TextFormatScanner { var sawBackslash = false let n = try validateAndCountBytesFromString(terminator: c, sawBackslash: &sawBackslash) if sawBackslash { - var b = Data(count: n) - parseBytesFromString(terminator: c, into: &b) - result.append(b) + var b = Data(count: n) + parseBytesFromString(terminator: c, into: &b) + result.append(b) } else { - result.append(Data(bytes: p, count: n)) - p += n + 1 // Skip string body + close quote + result.append(Data(bytes: p, count: n)) + p += n + 1 // Skip string body + close quote } } } @@ -789,26 +792,32 @@ internal struct TextFormatScanner { // Tries to identify a sequence of UTF8 characters // that represent a numeric floating-point value. private mutating func tryParseFloatString() -> Double? { - guard p != end else {return nil} + guard p != end else { return nil } let start = p var c = p[0] if c == asciiMinus { p += 1 - guard p != end else {p = start; return nil} + guard p != end else { + p = start + return nil + } c = p[0] } switch c { - case asciiZero: // '0' as first character is not allowed followed by digit + case asciiZero: // '0' as first character is not allowed followed by digit p += 1 - guard p != end else {break} + guard p != end else { break } c = p[0] if c >= asciiZero && c <= asciiNine { p = start return nil } - case asciiPeriod: // '.' as first char only if followed by digit + case asciiPeriod: // '.' as first char only if followed by digit p += 1 - guard p != end else {p = start; return nil} + guard p != end else { + p = start + return nil + } c = p[0] if c < asciiZero || c > asciiNine { p = start @@ -824,16 +833,20 @@ internal struct TextFormatScanner { let c = p[0] switch c { case asciiZero...asciiNine, - asciiPeriod, - asciiPlus, - asciiMinus, - asciiLowerE, - asciiUpperE: // 0...9, ., +, -, e, E + asciiPeriod, + asciiPlus, + asciiMinus, + asciiLowerE, + asciiUpperE: // 0...9, ., +, -, e, E p += 1 - case asciiLowerF, asciiUpperF: // f or F - let d = doubleParser.utf8ToDouble(bytes: UnsafeRawBufferPointer(start: start, - count: p - start), - finiteOnly: false) + case asciiLowerF, asciiUpperF: // f or F + let d = doubleParser.utf8ToDouble( + bytes: UnsafeRawBufferPointer( + start: start, + count: p - start + ), + finiteOnly: false + ) // Just skip the 'f'/'F' p += 1 skipWhitespace() @@ -842,9 +855,13 @@ internal struct TextFormatScanner { break loop } } - let d = doubleParser.utf8ToDouble(bytes: UnsafeRawBufferPointer(start: start, - count: p - start), - finiteOnly: false) + let d = doubleParser.utf8ToDouble( + bytes: UnsafeRawBufferPointer( + start: start, + count: p - start + ), + finiteOnly: false + ) skipWhitespace() return d } @@ -886,8 +903,9 @@ internal struct TextFormatScanner { return true } let c = p[0] - if ((c >= asciiUpperA && c <= asciiUpperZ) - || (c >= asciiLowerA && c <= asciiLowerZ)) { + if (c >= asciiUpperA && c <= asciiUpperZ) + || (c >= asciiLowerA && c <= asciiLowerZ) + { p = start return false } @@ -927,10 +945,13 @@ internal struct TextFormatScanner { negated = false } let inf = [asciiLowerI, asciiLowerN, asciiLowerF] - let infinity = [asciiLowerI, asciiLowerN, asciiLowerF, asciiLowerI, - asciiLowerN, asciiLowerI, asciiLowerT, asciiLowerY] - if (skipOptionalKeyword(bytes: inf) - || skipOptionalKeyword(bytes: infinity)) { + let infinity = [ + asciiLowerI, asciiLowerN, asciiLowerF, asciiLowerI, + asciiLowerN, asciiLowerI, asciiLowerT, asciiLowerY, + ] + if skipOptionalKeyword(bytes: inf) + || skipOptionalKeyword(bytes: infinity) + { return negated ? -Float.infinity : Float.infinity } p = start @@ -996,15 +1017,15 @@ internal struct TextFormatScanner { } switch p[0] { case asciiSpace, - asciiTab, - asciiNewLine, - asciiCarriageReturn, - asciiHash, - asciiComma, - asciiSemicolon, - asciiCloseSquareBracket, - asciiCloseCurlyBracket, - asciiCloseAngleBracket: + asciiTab, + asciiNewLine, + asciiCarriageReturn, + asciiHash, + asciiComma, + asciiSemicolon, + asciiCloseSquareBracket, + asciiCloseCurlyBracket, + asciiCloseAngleBracket: skipWhitespace() return result default: @@ -1028,7 +1049,7 @@ internal struct TextFormatScanner { /// Any URLs are syntactically (almost) identical to extension /// keys, so we share the code for those. internal mutating func nextOptionalAnyURL() throws -> String? { - return try nextOptionalExtensionKey() + try nextOptionalExtensionKey() } /// Returns next extension key or nil if end-of-input or @@ -1047,7 +1068,7 @@ internal struct TextFormatScanner { if p == end { return nil } - guard p[0] == asciiOpenSquareBracket else { // [ + guard p[0] == asciiOpenSquareBracket else { // [ return nil } return try parseExtensionKey() @@ -1072,13 +1093,13 @@ internal struct TextFormatScanner { loop: while p != end { switch p[0] { case asciiLowerA...asciiLowerZ, - asciiUpperA...asciiUpperZ, - asciiZero...asciiNine, - asciiUnderscore, - asciiPeriod, - asciiForwardSlash: + asciiUpperA...asciiUpperZ, + asciiZero...asciiNine, + asciiUnderscore, + asciiPeriod, + asciiForwardSlash: p += 1 - case asciiCloseSquareBracket: // ] + case asciiCloseSquareBracket: // ] break loop default: throw TextFormatDecodingError.malformedText @@ -1103,13 +1124,13 @@ internal struct TextFormatScanner { } let c = p[0] switch c { - case asciiOpenSquareBracket: // [ + case asciiOpenSquareBracket: // [ if allowExtensions { return "[\(try parseExtensionKey())]" } throw TextFormatDecodingError.unknownField case asciiLowerA...asciiLowerZ, - asciiUpperA...asciiUpperZ: // a...z, A...Z + asciiUpperA...asciiUpperZ: // a...z, A...Z return parseIdentifier() case asciiOne...asciiNine: // 1...9 (field numbers are 123, not 0123) let start = p @@ -1162,7 +1183,7 @@ internal struct TextFormatScanner { let c = p[0] switch c { case asciiLowerA...asciiLowerZ, - asciiUpperA...asciiUpperZ: // a...z, A...Z + asciiUpperA...asciiUpperZ: // a...z, A...Z let key = parseUTF8Identifier() if let fieldNumber = names.number(forProtoName: key) { return fieldNumber @@ -1172,7 +1193,7 @@ internal struct TextFormatScanner { } // Unknown field name break - case asciiOpenSquareBracket: // Start of an extension field + case asciiOpenSquareBracket: // Start of an extension field let key = try parseExtensionKey() if let fieldNumber = extensions?.fieldNumberForProto(messageType: messageType, protoFieldName: key) { return fieldNumber @@ -1235,7 +1256,7 @@ internal struct TextFormatScanner { // to be a message or the input is ill-formed. skipWhitespace() - if (skipOptionalColon()) { + if skipOptionalColon() { if p == end { // Nothing after the ':'? throw TextFormatDecodingError.malformedText @@ -1274,8 +1295,9 @@ internal struct TextFormatScanner { scan += 1 if scan == end { return true } // "(-)0[end]" : parse it as decimal c = scan[0] - if c == asciiLowerX || // "(-)0x" : hex - must parse as decimal - (c >= asciiZero && c <= asciiSeven) { // "(-)0[0-7]" : octal - must parse as decimal + if c == asciiLowerX // "(-)0x" : hex - must parse as decimal + || (c >= asciiZero && c <= asciiSeven) + { // "(-)0[0-7]" : octal - must parse as decimal return true } if c == asciiPeriod { @@ -1390,15 +1412,15 @@ internal struct TextFormatScanner { } internal mutating func skipOptionalColon() -> Bool { - return skipOptionalCharacter(asciiColon) + skipOptionalCharacter(asciiColon) } internal mutating func skipOptionalEndArray() -> Bool { - return skipOptionalCharacter(asciiCloseSquareBracket) + skipOptionalCharacter(asciiCloseSquareBracket) } internal mutating func skipOptionalBeginArray() -> Bool { - return skipOptionalCharacter(asciiOpenSquareBracket) + skipOptionalCharacter(asciiOpenSquareBracket) } internal mutating func skipOptionalObjectEnd(_ c: UInt8) -> Bool { @@ -1412,7 +1434,7 @@ internal struct TextFormatScanner { internal mutating func skipOptionalSeparator() { if p != end { let c = p[0] - if c == asciiComma || c == asciiSemicolon { // comma or semicolon + if c == asciiComma || c == asciiSemicolon { // comma or semicolon p += 1 skipWhitespace() } @@ -1428,10 +1450,10 @@ internal struct TextFormatScanner { p += 1 skipWhitespace() switch c { - case asciiOpenCurlyBracket: // { - return asciiCloseCurlyBracket // } - case asciiOpenAngleBracket: // < - return asciiCloseAngleBracket // > + case asciiOpenCurlyBracket: // { + return asciiCloseCurlyBracket // } + case asciiOpenAngleBracket: // < + return asciiCloseAngleBracket // > default: break } diff --git a/Sources/SwiftProtobuf/TimeUtils.swift b/Sources/SwiftProtobuf/TimeUtils.swift index a7d142b40..e21d31c3b 100644 --- a/Sources/SwiftProtobuf/TimeUtils.swift +++ b/Sources/SwiftProtobuf/TimeUtils.swift @@ -17,7 +17,7 @@ let minutesPerHour: Int32 = 60 let secondsPerDay: Int32 = 86400 let secondsPerHour: Int32 = 3600 let secondsPerMinute: Int32 = 60 -let nanosPerSecond: Int32 = 1000000000 +let nanosPerSecond: Int32 = 1_000_000_000 internal func timeOfDayFromSecondsSince1970(seconds: Int64) -> (hh: Int32, mm: Int32, ss: Int32) { let secondsSinceMidnight = Int32(mod(seconds, Int64(secondsPerDay))) @@ -31,7 +31,7 @@ internal func timeOfDayFromSecondsSince1970(seconds: Int64) -> (hh: Int32, mm: I internal func julianDayNumberFromSecondsSince1970(seconds: Int64) -> Int64 { // January 1, 1970 is Julian Day Number 2440588. // See http://aa.usno.navy.mil/faq/docs/JD_Formula.php - return div(seconds + 2440588 * Int64(secondsPerDay), Int64(secondsPerDay)) + div(seconds + 2_440_588 * Int64(secondsPerDay), Int64(secondsPerDay)) } internal func gregorianDateFromSecondsSince1970(seconds: Int64) -> (YY: Int32, MM: Int32, DD: Int32) { @@ -53,13 +53,13 @@ internal func gregorianDateFromSecondsSince1970(seconds: Int64) -> (YY: Int32, M } internal func nanosToString(nanos: Int32) -> String { - if nanos == 0 { - return "" - } else if nanos % 1000000 == 0 { - return ".\(threeDigit(abs(nanos) / 1000000))" - } else if nanos % 1000 == 0 { - return ".\(sixDigit(abs(nanos) / 1000))" - } else { - return ".\(nineDigit(abs(nanos)))" - } -} \ No newline at end of file + if nanos == 0 { + return "" + } else if nanos % 1_000_000 == 0 { + return ".\(threeDigit(abs(nanos) / 1_000_000))" + } else if nanos % 1000 == 0 { + return ".\(sixDigit(abs(nanos) / 1000))" + } else { + return ".\(nineDigit(abs(nanos)))" + } +} diff --git a/Sources/SwiftProtobuf/UnknownStorage.swift b/Sources/SwiftProtobuf/UnknownStorage.swift index b7329d6ae..9adc1db85 100644 --- a/Sources/SwiftProtobuf/UnknownStorage.swift +++ b/Sources/SwiftProtobuf/UnknownStorage.swift @@ -22,22 +22,22 @@ import Foundation /// formats (for example, a field encoded as a varint when a fixed32 integer /// was expected). public struct UnknownStorage: Equatable, @unchecked Sendable { - // Once swift(>=5.9) the '@unchecked' can be removed, it is needed for Data in - // linux builds. + // Once swift(>=5.9) the '@unchecked' can be removed, it is needed for Data in + // linux builds. - /// The raw protocol buffer binary-encoded bytes that represent the unknown - /// fields of a decoded message. - public private(set) var data = Data() + /// The raw protocol buffer binary-encoded bytes that represent the unknown + /// fields of a decoded message. + public private(set) var data = Data() - public init() {} + public init() {} - internal mutating func append(protobufData: Data) { - data.append(protobufData) - } + internal mutating func append(protobufData: Data) { + data.append(protobufData) + } - public func traverse(visitor: inout V) throws { - if !data.isEmpty { - try visitor.visitUnknown(bytes: data) + public func traverse(visitor: inout V) throws { + if !data.isEmpty { + try visitor.visitUnknown(bytes: data) + } } - } } diff --git a/Sources/SwiftProtobuf/UnsafeRawPointer+Shims.swift b/Sources/SwiftProtobuf/UnsafeRawPointer+Shims.swift index 6b6fb452b..da70bd8ce 100644 --- a/Sources/SwiftProtobuf/UnsafeRawPointer+Shims.swift +++ b/Sources/SwiftProtobuf/UnsafeRawPointer+Shims.swift @@ -12,14 +12,13 @@ /// // ----------------------------------------------------------------------------- - extension UnsafeRawPointer { /// A shim subscript for UnsafeRawPointer aiming to maintain code consistency. /// /// We can remove this shim when we rewrite the code to use buffer pointers. internal subscript(_ offset: Int) -> UInt8 { get { - return self.load(fromByteOffset: offset, as: UInt8.self) + self.load(fromByteOffset: offset, as: UInt8.self) } } } @@ -30,7 +29,7 @@ extension UnsafeMutableRawPointer { /// We can remove this shim when we rewrite the code to use buffer pointers. internal subscript(_ offset: Int) -> UInt8 { get { - return self.load(fromByteOffset: offset, as: UInt8.self) + self.load(fromByteOffset: offset, as: UInt8.self) } set { self.storeBytes(of: newValue, toByteOffset: offset, as: UInt8.self) diff --git a/Sources/SwiftProtobuf/Varint.swift b/Sources/SwiftProtobuf/Varint.swift index d82c69695..dccfb531d 100644 --- a/Sources/SwiftProtobuf/Varint.swift +++ b/Sources/SwiftProtobuf/Varint.swift @@ -12,97 +12,96 @@ /// // ----------------------------------------------------------------------------- - /// Contains helper methods to varint-encode and decode integers. internal enum Varint { - /// Computes the number of bytes that would be needed to store a 32-bit varint. - /// - /// - Parameter value: The number whose varint size should be calculated. - /// - Returns: The size, in bytes, of the 32-bit varint. - static func encodedSize(of value: UInt32) -> Int { - if (value & (~0 << 7)) == 0 { - return 1 - } - if (value & (~0 << 14)) == 0 { - return 2 - } - if (value & (~0 << 21)) == 0 { - return 3 + /// Computes the number of bytes that would be needed to store a 32-bit varint. + /// + /// - Parameter value: The number whose varint size should be calculated. + /// - Returns: The size, in bytes, of the 32-bit varint. + static func encodedSize(of value: UInt32) -> Int { + if (value & (~0 << 7)) == 0 { + return 1 + } + if (value & (~0 << 14)) == 0 { + return 2 + } + if (value & (~0 << 21)) == 0 { + return 3 + } + if (value & (~0 << 28)) == 0 { + return 4 + } + return 5 } - if (value & (~0 << 28)) == 0 { - return 4 - } - return 5 - } - /// Computes the number of bytes that would be needed to store a signed 32-bit varint, if it were - /// treated as an unsigned integer with the same bit pattern. - /// - /// - Parameter value: The number whose varint size should be calculated. - /// - Returns: The size, in bytes, of the 32-bit varint. - static func encodedSize(of value: Int32) -> Int { - if value >= 0 { - return encodedSize(of: UInt32(bitPattern: value)) - } else { - // Must sign-extend. - return encodedSize(of: Int64(value)) + /// Computes the number of bytes that would be needed to store a signed 32-bit varint, if it were + /// treated as an unsigned integer with the same bit pattern. + /// + /// - Parameter value: The number whose varint size should be calculated. + /// - Returns: The size, in bytes, of the 32-bit varint. + static func encodedSize(of value: Int32) -> Int { + if value >= 0 { + return encodedSize(of: UInt32(bitPattern: value)) + } else { + // Must sign-extend. + return encodedSize(of: Int64(value)) + } } - } - /// Computes the number of bytes that would be needed to store a 64-bit varint. - /// - /// - Parameter value: The number whose varint size should be calculated. - /// - Returns: The size, in bytes, of the 64-bit varint. - static func encodedSize(of value: Int64) -> Int { - // Handle two common special cases up front. - if (value & (~0 << 7)) == 0 { - return 1 - } - if value < 0 { - return 10 - } + /// Computes the number of bytes that would be needed to store a 64-bit varint. + /// + /// - Parameter value: The number whose varint size should be calculated. + /// - Returns: The size, in bytes, of the 64-bit varint. + static func encodedSize(of value: Int64) -> Int { + // Handle two common special cases up front. + if (value & (~0 << 7)) == 0 { + return 1 + } + if value < 0 { + return 10 + } - // Divide and conquer the remaining eight cases. - var value = value - var n = 2 + // Divide and conquer the remaining eight cases. + var value = value + var n = 2 - if (value & (~0 << 35)) != 0 { - n += 4 - value >>= 28 - } - if (value & (~0 << 21)) != 0 { - n += 2 - value >>= 14 + if (value & (~0 << 35)) != 0 { + n += 4 + value >>= 28 + } + if (value & (~0 << 21)) != 0 { + n += 2 + value >>= 14 + } + if (value & (~0 << 14)) != 0 { + n += 1 + } + return n } - if (value & (~0 << 14)) != 0 { - n += 1 - } - return n - } - /// Computes the number of bytes that would be needed to store an unsigned 64-bit varint, if it - /// were treated as a signed integer with the same bit pattern. - /// - /// - Parameter value: The number whose varint size should be calculated. - /// - Returns: The size, in bytes, of the 64-bit varint. - static func encodedSize(of value: UInt64) -> Int { - return encodedSize(of: Int64(bitPattern: value)) - } + /// Computes the number of bytes that would be needed to store an unsigned 64-bit varint, if it + /// were treated as a signed integer with the same bit pattern. + /// + /// - Parameter value: The number whose varint size should be calculated. + /// - Returns: The size, in bytes, of the 64-bit varint. + static func encodedSize(of value: UInt64) -> Int { + encodedSize(of: Int64(bitPattern: value)) + } - /// Counts the number of distinct varints in a packed byte buffer. - static func countVarintsInBuffer(start: UnsafeRawPointer, count: Int) -> Int { - // We don't need to decode all the varints to count how many there - // are. Just observe that every varint has exactly one byte with - // value < 128. So we just count those... - var n = 0 - var ints = 0 - while n < count { - if start.load(fromByteOffset: n, as: UInt8.self) < 128 { - ints += 1 - } - n += 1 + /// Counts the number of distinct varints in a packed byte buffer. + static func countVarintsInBuffer(start: UnsafeRawPointer, count: Int) -> Int { + // We don't need to decode all the varints to count how many there + // are. Just observe that every varint has exactly one byte with + // value < 128. So we just count those... + var n = 0 + var ints = 0 + while n < count { + if start.load(fromByteOffset: n, as: UInt8.self) < 128 { + ints += 1 + } + n += 1 + } + return ints } - return ints - } } diff --git a/Sources/SwiftProtobuf/Version.swift b/Sources/SwiftProtobuf/Version.swift index 2b389d424..efb6e98bd 100644 --- a/Sources/SwiftProtobuf/Version.swift +++ b/Sources/SwiftProtobuf/Version.swift @@ -16,13 +16,13 @@ import Foundation // Expose version information about the library. public struct Version { - /// Major version. - public static let major = 1 - /// Minor version. - public static let minor = 28 - /// Revision number. - public static let revision = 1 + /// Major version. + public static let major = 1 + /// Minor version. + public static let minor = 28 + /// Revision number. + public static let revision = 1 - /// String form of the version number. - public static let versionString = "\(major).\(minor).\(revision)" + /// String form of the version number. + public static let versionString = "\(major).\(minor).\(revision)" } diff --git a/Sources/SwiftProtobuf/Visitor.swift b/Sources/SwiftProtobuf/Visitor.swift index b41c7c949..5955e5672 100644 --- a/Sources/SwiftProtobuf/Visitor.swift +++ b/Sources/SwiftProtobuf/Visitor.swift @@ -32,694 +32,705 @@ import Foundation /// Protobuf Binary, Protobuf Text, JSON, and the Hash encoder. public protocol Visitor { - /// Called for each non-repeated float field - /// - /// A default implementation is provided that just widens the value - /// and calls `visitSingularDoubleField` - mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws - - /// Called for each non-repeated double field - /// - /// There is no default implementation. This must be implemented. - mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws - - /// Called for each non-repeated int32 field - /// - /// A default implementation is provided that just widens the value - /// and calls `visitSingularInt64Field` - mutating func visitSingularInt32Field(value: Int32, fieldNumber: Int) throws - - /// Called for each non-repeated int64 field - /// - /// There is no default implementation. This must be implemented. - mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws - - /// Called for each non-repeated uint32 field - /// - /// A default implementation is provided that just widens the value - /// and calls `visitSingularUInt64Field` - mutating func visitSingularUInt32Field(value: UInt32, fieldNumber: Int) throws - - /// Called for each non-repeated uint64 field - /// - /// There is no default implementation. This must be implemented. - mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws - - /// Called for each non-repeated sint32 field - /// - /// A default implementation is provided that just forwards to - /// `visitSingularInt32Field` - mutating func visitSingularSInt32Field(value: Int32, fieldNumber: Int) throws - - /// Called for each non-repeated sint64 field - /// - /// A default implementation is provided that just forwards to - /// `visitSingularInt64Field` - mutating func visitSingularSInt64Field(value: Int64, fieldNumber: Int) throws - - /// Called for each non-repeated fixed32 field - /// - /// A default implementation is provided that just forwards to - /// `visitSingularUInt32Field` - mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws - - /// Called for each non-repeated fixed64 field - /// - /// A default implementation is provided that just forwards to - /// `visitSingularUInt64Field` - mutating func visitSingularFixed64Field(value: UInt64, fieldNumber: Int) throws - - /// Called for each non-repeated sfixed32 field - /// - /// A default implementation is provided that just forwards to - /// `visitSingularInt32Field` - mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws - - /// Called for each non-repeated sfixed64 field - /// - /// A default implementation is provided that just forwards to - /// `visitSingularInt64Field` - mutating func visitSingularSFixed64Field(value: Int64, fieldNumber: Int) throws - - /// Called for each non-repeated bool field - /// - /// There is no default implementation. This must be implemented. - mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws - - /// Called for each non-repeated string field - /// - /// There is no default implementation. This must be implemented. - mutating func visitSingularStringField(value: String, fieldNumber: Int) throws - - /// Called for each non-repeated bytes field - /// - /// There is no default implementation. This must be implemented. - mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws - - /// Called for each non-repeated enum field - /// - /// There is no default implementation. This must be implemented. - mutating func visitSingularEnumField(value: E, fieldNumber: Int) throws - - /// Called for each non-repeated nested message field. - /// - /// There is no default implementation. This must be implemented. - mutating func visitSingularMessageField(value: M, fieldNumber: Int) throws - - /// Called for each non-repeated proto2 group field. - /// - /// A default implementation is provided that simply forwards to - /// `visitSingularMessageField`. Implementors who need to handle groups - /// differently than nested messages can override this and provide distinct - /// implementations. - mutating func visitSingularGroupField(value: G, fieldNumber: Int) throws - - // Called for each non-packed repeated float field. - /// The method is called once with the complete array of values for - /// the field. - /// - /// A default implementation is provided that simply calls - /// `visitSingularFloatField` once for each item in the array. - mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws - - // Called for each non-packed repeated double field. - /// The method is called once with the complete array of values for - /// the field. - /// - /// A default implementation is provided that simply calls - /// `visitSingularDoubleField` once for each item in the array. - mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws - - // Called for each non-packed repeated int32 field. - /// The method is called once with the complete array of values for - /// the field. - /// - /// A default implementation is provided that simply calls - /// `visitSingularInt32Field` once for each item in the array. - mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws - - // Called for each non-packed repeated int64 field. - /// The method is called once with the complete array of values for - /// the field. - /// - /// A default implementation is provided that simply calls - /// `visitSingularInt64Field` once for each item in the array. - mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws - - // Called for each non-packed repeated uint32 field. - /// The method is called once with the complete array of values for - /// the field. - /// - /// A default implementation is provided that simply calls - /// `visitSingularUInt32Field` once for each item in the array. - mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws - - // Called for each non-packed repeated uint64 field. - /// The method is called once with the complete array of values for - /// the field. - /// - /// A default implementation is provided that simply calls - /// `visitSingularUInt64Field` once for each item in the array. - mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws - - // Called for each non-packed repeated sint32 field. - /// The method is called once with the complete array of values for - /// the field. - /// - /// A default implementation is provided that simply calls - /// `visitSingularSInt32Field` once for each item in the array. - mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws - - // Called for each non-packed repeated sint64 field. - /// The method is called once with the complete array of values for - /// the field. - /// - /// A default implementation is provided that simply calls - /// `visitSingularSInt64Field` once for each item in the array. - mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws - - // Called for each non-packed repeated fixed32 field. - /// The method is called once with the complete array of values for - /// the field. - /// - /// A default implementation is provided that simply calls - /// `visitSingularFixed32Field` once for each item in the array. - mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws - - // Called for each non-packed repeated fixed64 field. - /// The method is called once with the complete array of values for - /// the field. - /// - /// A default implementation is provided that simply calls - /// `visitSingularFixed64Field` once for each item in the array. - mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws - - // Called for each non-packed repeated sfixed32 field. - /// The method is called once with the complete array of values for - /// the field. - /// - /// A default implementation is provided that simply calls - /// `visitSingularSFixed32Field` once for each item in the array. - mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws - - // Called for each non-packed repeated sfixed64 field. - /// The method is called once with the complete array of values for - /// the field. - /// - /// A default implementation is provided that simply calls - /// `visitSingularSFixed64Field` once for each item in the array. - mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws - - // Called for each non-packed repeated bool field. - /// The method is called once with the complete array of values for - /// the field. - /// - /// A default implementation is provided that simply calls - /// `visitSingularBoolField` once for each item in the array. - mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws - - // Called for each non-packed repeated string field. - /// The method is called once with the complete array of values for - /// the field. - /// - /// A default implementation is provided that simply calls - /// `visitSingularStringField` once for each item in the array. - mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws - - // Called for each non-packed repeated bytes field. - /// The method is called once with the complete array of values for - /// the field. - /// - /// A default implementation is provided that simply calls - /// `visitSingularBytesField` once for each item in the array. - mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws - - /// Called for each repeated, unpacked enum field. - /// The method is called once with the complete array of values for - /// the field. - /// - /// A default implementation is provided that simply calls - /// `visitSingularEnumField` once for each item in the array. - mutating func visitRepeatedEnumField(value: [E], fieldNumber: Int) throws - - /// Called for each repeated nested message field. The method is called once - /// with the complete array of values for the field. - /// - /// A default implementation is provided that simply calls - /// `visitSingularMessageField` once for each item in the array. - mutating func visitRepeatedMessageField(value: [M], - fieldNumber: Int) throws - - /// Called for each repeated proto2 group field. - /// - /// A default implementation is provided that simply calls - /// `visitSingularGroupField` once for each item in the array. - mutating func visitRepeatedGroupField(value: [G], fieldNumber: Int) throws - - // Called for each packed, repeated float field. - /// - /// This is called once with the complete array of values for - /// the field. - /// - /// There is a default implementation that forwards to the non-packed - /// function. - mutating func visitPackedFloatField(value: [Float], fieldNumber: Int) throws - - // Called for each packed, repeated double field. - /// - /// This is called once with the complete array of values for - /// the field. - /// - /// There is a default implementation that forwards to the non-packed - /// function. - mutating func visitPackedDoubleField(value: [Double], fieldNumber: Int) throws - - // Called for each packed, repeated int32 field. - /// - /// This is called once with the complete array of values for - /// the field. - /// - /// There is a default implementation that forwards to the non-packed - /// function. - mutating func visitPackedInt32Field(value: [Int32], fieldNumber: Int) throws - - // Called for each packed, repeated int64 field. - /// - /// This is called once with the complete array of values for - /// the field. - /// - /// There is a default implementation that forwards to the non-packed - /// function. - mutating func visitPackedInt64Field(value: [Int64], fieldNumber: Int) throws - - // Called for each packed, repeated uint32 field. - /// - /// This is called once with the complete array of values for - /// the field. - /// - /// There is a default implementation that forwards to the non-packed - /// function. - mutating func visitPackedUInt32Field(value: [UInt32], fieldNumber: Int) throws - - // Called for each packed, repeated uint64 field. - /// - /// This is called once with the complete array of values for - /// the field. - /// - /// There is a default implementation that forwards to the non-packed - /// function. - mutating func visitPackedUInt64Field(value: [UInt64], fieldNumber: Int) throws - - // Called for each packed, repeated sint32 field. - /// - /// This is called once with the complete array of values for - /// the field. - /// - /// There is a default implementation that forwards to the non-packed - /// function. - mutating func visitPackedSInt32Field(value: [Int32], fieldNumber: Int) throws - - // Called for each packed, repeated sint64 field. - /// - /// This is called once with the complete array of values for - /// the field. - /// - /// There is a default implementation that forwards to the non-packed - /// function. - mutating func visitPackedSInt64Field(value: [Int64], fieldNumber: Int) throws - - // Called for each packed, repeated fixed32 field. - /// - /// This is called once with the complete array of values for - /// the field. - /// - /// There is a default implementation that forwards to the non-packed - /// function. - mutating func visitPackedFixed32Field(value: [UInt32], fieldNumber: Int) throws - - // Called for each packed, repeated fixed64 field. - /// - /// This is called once with the complete array of values for - /// the field. - /// - /// There is a default implementation that forwards to the non-packed - /// function. - mutating func visitPackedFixed64Field(value: [UInt64], fieldNumber: Int) throws - - // Called for each packed, repeated sfixed32 field. - /// - /// This is called once with the complete array of values for - /// the field. - /// - /// There is a default implementation that forwards to the non-packed - /// function. - mutating func visitPackedSFixed32Field(value: [Int32], fieldNumber: Int) throws - - // Called for each packed, repeated sfixed64 field. - /// - /// This is called once with the complete array of values for - /// the field. - /// - /// There is a default implementation that forwards to the non-packed - /// function. - mutating func visitPackedSFixed64Field(value: [Int64], fieldNumber: Int) throws - - // Called for each packed, repeated bool field. - /// - /// This is called once with the complete array of values for - /// the field. - /// - /// There is a default implementation that forwards to the non-packed - /// function. - mutating func visitPackedBoolField(value: [Bool], fieldNumber: Int) throws - - /// Called for each repeated, packed enum field. - /// The method is called once with the complete array of values for - /// the field. - /// - /// A default implementation is provided that simply forwards to - /// `visitRepeatedEnumField`. Implementors who need to handle packed fields - /// differently than unpacked fields can override this and provide distinct - /// implementations. - mutating func visitPackedEnumField(value: [E], fieldNumber: Int) throws - - /// Called for each map field with primitive values. The method is - /// called once with the complete dictionary of keys/values for the - /// field. - /// - /// There is no default implementation. This must be implemented. - mutating func visitMapField( - fieldType: _ProtobufMap.Type, - value: _ProtobufMap.BaseType, - fieldNumber: Int) throws - - /// Called for each map field with enum values. The method is called - /// once with the complete dictionary of keys/values for the field. - /// - /// There is no default implementation. This must be implemented. - mutating func visitMapField( - fieldType: _ProtobufEnumMap.Type, - value: _ProtobufEnumMap.BaseType, - fieldNumber: Int) throws where ValueType.RawValue == Int - - /// Called for each map field with message values. The method is - /// called once with the complete dictionary of keys/values for the - /// field. - /// - /// There is no default implementation. This must be implemented. - mutating func visitMapField( - fieldType: _ProtobufMessageMap.Type, - value: _ProtobufMessageMap.BaseType, - fieldNumber: Int) throws - - /// Called for each extension range. - mutating func visitExtensionFields(fields: ExtensionFieldValueSet, start: Int, end: Int) throws - - /// Called for each extension range. - mutating func visitExtensionFieldsAsMessageSet( - fields: ExtensionFieldValueSet, - start: Int, - end: Int) throws - - /// Called with the raw bytes that represent any unknown fields. - mutating func visitUnknown(bytes: Data) throws + /// Called for each non-repeated float field + /// + /// A default implementation is provided that just widens the value + /// and calls `visitSingularDoubleField` + mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws + + /// Called for each non-repeated double field + /// + /// There is no default implementation. This must be implemented. + mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws + + /// Called for each non-repeated int32 field + /// + /// A default implementation is provided that just widens the value + /// and calls `visitSingularInt64Field` + mutating func visitSingularInt32Field(value: Int32, fieldNumber: Int) throws + + /// Called for each non-repeated int64 field + /// + /// There is no default implementation. This must be implemented. + mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws + + /// Called for each non-repeated uint32 field + /// + /// A default implementation is provided that just widens the value + /// and calls `visitSingularUInt64Field` + mutating func visitSingularUInt32Field(value: UInt32, fieldNumber: Int) throws + + /// Called for each non-repeated uint64 field + /// + /// There is no default implementation. This must be implemented. + mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws + + /// Called for each non-repeated sint32 field + /// + /// A default implementation is provided that just forwards to + /// `visitSingularInt32Field` + mutating func visitSingularSInt32Field(value: Int32, fieldNumber: Int) throws + + /// Called for each non-repeated sint64 field + /// + /// A default implementation is provided that just forwards to + /// `visitSingularInt64Field` + mutating func visitSingularSInt64Field(value: Int64, fieldNumber: Int) throws + + /// Called for each non-repeated fixed32 field + /// + /// A default implementation is provided that just forwards to + /// `visitSingularUInt32Field` + mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws + + /// Called for each non-repeated fixed64 field + /// + /// A default implementation is provided that just forwards to + /// `visitSingularUInt64Field` + mutating func visitSingularFixed64Field(value: UInt64, fieldNumber: Int) throws + + /// Called for each non-repeated sfixed32 field + /// + /// A default implementation is provided that just forwards to + /// `visitSingularInt32Field` + mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws + + /// Called for each non-repeated sfixed64 field + /// + /// A default implementation is provided that just forwards to + /// `visitSingularInt64Field` + mutating func visitSingularSFixed64Field(value: Int64, fieldNumber: Int) throws + + /// Called for each non-repeated bool field + /// + /// There is no default implementation. This must be implemented. + mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws + + /// Called for each non-repeated string field + /// + /// There is no default implementation. This must be implemented. + mutating func visitSingularStringField(value: String, fieldNumber: Int) throws + + /// Called for each non-repeated bytes field + /// + /// There is no default implementation. This must be implemented. + mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws + + /// Called for each non-repeated enum field + /// + /// There is no default implementation. This must be implemented. + mutating func visitSingularEnumField(value: E, fieldNumber: Int) throws + + /// Called for each non-repeated nested message field. + /// + /// There is no default implementation. This must be implemented. + mutating func visitSingularMessageField(value: M, fieldNumber: Int) throws + + /// Called for each non-repeated proto2 group field. + /// + /// A default implementation is provided that simply forwards to + /// `visitSingularMessageField`. Implementors who need to handle groups + /// differently than nested messages can override this and provide distinct + /// implementations. + mutating func visitSingularGroupField(value: G, fieldNumber: Int) throws + + // Called for each non-packed repeated float field. + /// The method is called once with the complete array of values for + /// the field. + /// + /// A default implementation is provided that simply calls + /// `visitSingularFloatField` once for each item in the array. + mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws + + // Called for each non-packed repeated double field. + /// The method is called once with the complete array of values for + /// the field. + /// + /// A default implementation is provided that simply calls + /// `visitSingularDoubleField` once for each item in the array. + mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws + + // Called for each non-packed repeated int32 field. + /// The method is called once with the complete array of values for + /// the field. + /// + /// A default implementation is provided that simply calls + /// `visitSingularInt32Field` once for each item in the array. + mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws + + // Called for each non-packed repeated int64 field. + /// The method is called once with the complete array of values for + /// the field. + /// + /// A default implementation is provided that simply calls + /// `visitSingularInt64Field` once for each item in the array. + mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws + + // Called for each non-packed repeated uint32 field. + /// The method is called once with the complete array of values for + /// the field. + /// + /// A default implementation is provided that simply calls + /// `visitSingularUInt32Field` once for each item in the array. + mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws + + // Called for each non-packed repeated uint64 field. + /// The method is called once with the complete array of values for + /// the field. + /// + /// A default implementation is provided that simply calls + /// `visitSingularUInt64Field` once for each item in the array. + mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws + + // Called for each non-packed repeated sint32 field. + /// The method is called once with the complete array of values for + /// the field. + /// + /// A default implementation is provided that simply calls + /// `visitSingularSInt32Field` once for each item in the array. + mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws + + // Called for each non-packed repeated sint64 field. + /// The method is called once with the complete array of values for + /// the field. + /// + /// A default implementation is provided that simply calls + /// `visitSingularSInt64Field` once for each item in the array. + mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws + + // Called for each non-packed repeated fixed32 field. + /// The method is called once with the complete array of values for + /// the field. + /// + /// A default implementation is provided that simply calls + /// `visitSingularFixed32Field` once for each item in the array. + mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws + + // Called for each non-packed repeated fixed64 field. + /// The method is called once with the complete array of values for + /// the field. + /// + /// A default implementation is provided that simply calls + /// `visitSingularFixed64Field` once for each item in the array. + mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws + + // Called for each non-packed repeated sfixed32 field. + /// The method is called once with the complete array of values for + /// the field. + /// + /// A default implementation is provided that simply calls + /// `visitSingularSFixed32Field` once for each item in the array. + mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws + + // Called for each non-packed repeated sfixed64 field. + /// The method is called once with the complete array of values for + /// the field. + /// + /// A default implementation is provided that simply calls + /// `visitSingularSFixed64Field` once for each item in the array. + mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws + + // Called for each non-packed repeated bool field. + /// The method is called once with the complete array of values for + /// the field. + /// + /// A default implementation is provided that simply calls + /// `visitSingularBoolField` once for each item in the array. + mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws + + // Called for each non-packed repeated string field. + /// The method is called once with the complete array of values for + /// the field. + /// + /// A default implementation is provided that simply calls + /// `visitSingularStringField` once for each item in the array. + mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws + + // Called for each non-packed repeated bytes field. + /// The method is called once with the complete array of values for + /// the field. + /// + /// A default implementation is provided that simply calls + /// `visitSingularBytesField` once for each item in the array. + mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws + + /// Called for each repeated, unpacked enum field. + /// The method is called once with the complete array of values for + /// the field. + /// + /// A default implementation is provided that simply calls + /// `visitSingularEnumField` once for each item in the array. + mutating func visitRepeatedEnumField(value: [E], fieldNumber: Int) throws + + /// Called for each repeated nested message field. The method is called once + /// with the complete array of values for the field. + /// + /// A default implementation is provided that simply calls + /// `visitSingularMessageField` once for each item in the array. + mutating func visitRepeatedMessageField( + value: [M], + fieldNumber: Int + ) throws + + /// Called for each repeated proto2 group field. + /// + /// A default implementation is provided that simply calls + /// `visitSingularGroupField` once for each item in the array. + mutating func visitRepeatedGroupField(value: [G], fieldNumber: Int) throws + + // Called for each packed, repeated float field. + /// + /// This is called once with the complete array of values for + /// the field. + /// + /// There is a default implementation that forwards to the non-packed + /// function. + mutating func visitPackedFloatField(value: [Float], fieldNumber: Int) throws + + // Called for each packed, repeated double field. + /// + /// This is called once with the complete array of values for + /// the field. + /// + /// There is a default implementation that forwards to the non-packed + /// function. + mutating func visitPackedDoubleField(value: [Double], fieldNumber: Int) throws + + // Called for each packed, repeated int32 field. + /// + /// This is called once with the complete array of values for + /// the field. + /// + /// There is a default implementation that forwards to the non-packed + /// function. + mutating func visitPackedInt32Field(value: [Int32], fieldNumber: Int) throws + + // Called for each packed, repeated int64 field. + /// + /// This is called once with the complete array of values for + /// the field. + /// + /// There is a default implementation that forwards to the non-packed + /// function. + mutating func visitPackedInt64Field(value: [Int64], fieldNumber: Int) throws + + // Called for each packed, repeated uint32 field. + /// + /// This is called once with the complete array of values for + /// the field. + /// + /// There is a default implementation that forwards to the non-packed + /// function. + mutating func visitPackedUInt32Field(value: [UInt32], fieldNumber: Int) throws + + // Called for each packed, repeated uint64 field. + /// + /// This is called once with the complete array of values for + /// the field. + /// + /// There is a default implementation that forwards to the non-packed + /// function. + mutating func visitPackedUInt64Field(value: [UInt64], fieldNumber: Int) throws + + // Called for each packed, repeated sint32 field. + /// + /// This is called once with the complete array of values for + /// the field. + /// + /// There is a default implementation that forwards to the non-packed + /// function. + mutating func visitPackedSInt32Field(value: [Int32], fieldNumber: Int) throws + + // Called for each packed, repeated sint64 field. + /// + /// This is called once with the complete array of values for + /// the field. + /// + /// There is a default implementation that forwards to the non-packed + /// function. + mutating func visitPackedSInt64Field(value: [Int64], fieldNumber: Int) throws + + // Called for each packed, repeated fixed32 field. + /// + /// This is called once with the complete array of values for + /// the field. + /// + /// There is a default implementation that forwards to the non-packed + /// function. + mutating func visitPackedFixed32Field(value: [UInt32], fieldNumber: Int) throws + + // Called for each packed, repeated fixed64 field. + /// + /// This is called once with the complete array of values for + /// the field. + /// + /// There is a default implementation that forwards to the non-packed + /// function. + mutating func visitPackedFixed64Field(value: [UInt64], fieldNumber: Int) throws + + // Called for each packed, repeated sfixed32 field. + /// + /// This is called once with the complete array of values for + /// the field. + /// + /// There is a default implementation that forwards to the non-packed + /// function. + mutating func visitPackedSFixed32Field(value: [Int32], fieldNumber: Int) throws + + // Called for each packed, repeated sfixed64 field. + /// + /// This is called once with the complete array of values for + /// the field. + /// + /// There is a default implementation that forwards to the non-packed + /// function. + mutating func visitPackedSFixed64Field(value: [Int64], fieldNumber: Int) throws + + // Called for each packed, repeated bool field. + /// + /// This is called once with the complete array of values for + /// the field. + /// + /// There is a default implementation that forwards to the non-packed + /// function. + mutating func visitPackedBoolField(value: [Bool], fieldNumber: Int) throws + + /// Called for each repeated, packed enum field. + /// The method is called once with the complete array of values for + /// the field. + /// + /// A default implementation is provided that simply forwards to + /// `visitRepeatedEnumField`. Implementors who need to handle packed fields + /// differently than unpacked fields can override this and provide distinct + /// implementations. + mutating func visitPackedEnumField(value: [E], fieldNumber: Int) throws + + /// Called for each map field with primitive values. The method is + /// called once with the complete dictionary of keys/values for the + /// field. + /// + /// There is no default implementation. This must be implemented. + mutating func visitMapField( + fieldType: _ProtobufMap.Type, + value: _ProtobufMap.BaseType, + fieldNumber: Int + ) throws + + /// Called for each map field with enum values. The method is called + /// once with the complete dictionary of keys/values for the field. + /// + /// There is no default implementation. This must be implemented. + mutating func visitMapField( + fieldType: _ProtobufEnumMap.Type, + value: _ProtobufEnumMap.BaseType, + fieldNumber: Int + ) throws where ValueType.RawValue == Int + + /// Called for each map field with message values. The method is + /// called once with the complete dictionary of keys/values for the + /// field. + /// + /// There is no default implementation. This must be implemented. + mutating func visitMapField( + fieldType: _ProtobufMessageMap.Type, + value: _ProtobufMessageMap.BaseType, + fieldNumber: Int + ) throws + + /// Called for each extension range. + mutating func visitExtensionFields(fields: ExtensionFieldValueSet, start: Int, end: Int) throws + + /// Called for each extension range. + mutating func visitExtensionFieldsAsMessageSet( + fields: ExtensionFieldValueSet, + start: Int, + end: Int + ) throws + + /// Called with the raw bytes that represent any unknown fields. + mutating func visitUnknown(bytes: Data) throws } /// Forwarding default implementations of some visitor methods, for convenience. extension Visitor { - // Default definitions of numeric serializations. - // - // The 32-bit versions widen and delegate to 64-bit versions. - // The specialized integer codings delegate to standard Int/UInt. - // - // These "just work" for Hash and Text formats. Most of these work - // for JSON (32-bit integers are overridden to suppress quoting), - // and a few even work for Protobuf Binary (thanks to varint coding - // which erases the size difference between 32-bit and 64-bit ints). - - public mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws { - try visitSingularDoubleField(value: Double(value), fieldNumber: fieldNumber) - } - public mutating func visitSingularInt32Field(value: Int32, fieldNumber: Int) throws { - try visitSingularInt64Field(value: Int64(value), fieldNumber: fieldNumber) - } - public mutating func visitSingularUInt32Field(value: UInt32, fieldNumber: Int) throws { - try visitSingularUInt64Field(value: UInt64(value), fieldNumber: fieldNumber) - } - public mutating func visitSingularSInt32Field(value: Int32, fieldNumber: Int) throws { - try visitSingularInt32Field(value: value, fieldNumber: fieldNumber) - } - public mutating func visitSingularSInt64Field(value: Int64, fieldNumber: Int) throws { - try visitSingularInt64Field(value: value, fieldNumber: fieldNumber) - } - public mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws { - try visitSingularUInt32Field(value: value, fieldNumber: fieldNumber) - } - public mutating func visitSingularFixed64Field(value: UInt64, fieldNumber: Int) throws { - try visitSingularUInt64Field(value: value, fieldNumber: fieldNumber) - } - public mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws { - try visitSingularInt32Field(value: value, fieldNumber: fieldNumber) - } - public mutating func visitSingularSFixed64Field(value: Int64, fieldNumber: Int) throws { - try visitSingularInt64Field(value: value, fieldNumber: fieldNumber) - } - - // Default definitions of repeated serializations that just iterate and - // invoke the singular encoding. These "just work" for Protobuf Binary (encoder - // and size visitor), Protobuf Text, and Hash visitors. JSON format stores - // repeated values differently from singular, so overrides these. - - public mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws { - assert(!value.isEmpty) - for v in value { - try visitSingularFloatField(value: v, fieldNumber: fieldNumber) - } - } - - public mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws { - assert(!value.isEmpty) - for v in value { - try visitSingularDoubleField(value: v, fieldNumber: fieldNumber) - } - } - - public mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws { - assert(!value.isEmpty) - for v in value { - try visitSingularInt32Field(value: v, fieldNumber: fieldNumber) - } - } - - public mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws { - assert(!value.isEmpty) - for v in value { - try visitSingularInt64Field(value: v, fieldNumber: fieldNumber) - } - } - - public mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws { - assert(!value.isEmpty) - for v in value { - try visitSingularUInt32Field(value: v, fieldNumber: fieldNumber) - } - } - - public mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws { - assert(!value.isEmpty) - for v in value { - try visitSingularUInt64Field(value: v, fieldNumber: fieldNumber) - } - } - - public mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws { - assert(!value.isEmpty) - for v in value { - try visitSingularSInt32Field(value: v, fieldNumber: fieldNumber) - } - } - - public mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws { - assert(!value.isEmpty) - for v in value { - try visitSingularSInt64Field(value: v, fieldNumber: fieldNumber) - } - } - - public mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws { - assert(!value.isEmpty) - for v in value { - try visitSingularFixed32Field(value: v, fieldNumber: fieldNumber) - } - } - - public mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws { - assert(!value.isEmpty) - for v in value { - try visitSingularFixed64Field(value: v, fieldNumber: fieldNumber) - } - } - - public mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws { - assert(!value.isEmpty) - for v in value { - try visitSingularSFixed32Field(value: v, fieldNumber: fieldNumber) - } - } - - public mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws { - assert(!value.isEmpty) - for v in value { - try visitSingularSFixed64Field(value: v, fieldNumber: fieldNumber) - } - } - - public mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws { - assert(!value.isEmpty) - for v in value { - try visitSingularBoolField(value: v, fieldNumber: fieldNumber) - } - } - - public mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws { - assert(!value.isEmpty) - for v in value { - try visitSingularStringField(value: v, fieldNumber: fieldNumber) - } - } - - public mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws { - assert(!value.isEmpty) - for v in value { - try visitSingularBytesField(value: v, fieldNumber: fieldNumber) - } - } - - public mutating func visitRepeatedEnumField(value: [E], fieldNumber: Int) throws { - assert(!value.isEmpty) - for v in value { - try visitSingularEnumField(value: v, fieldNumber: fieldNumber) - } - } - - public mutating func visitRepeatedMessageField(value: [M], fieldNumber: Int) throws { - assert(!value.isEmpty) - for v in value { - try visitSingularMessageField(value: v, fieldNumber: fieldNumber) - } - } - - public mutating func visitRepeatedGroupField(value: [G], fieldNumber: Int) throws { - assert(!value.isEmpty) - for v in value { - try visitSingularGroupField(value: v, fieldNumber: fieldNumber) - } - } - - // Default definitions of packed serialization just defer to the - // repeated implementation. This works for Hash and JSON visitors - // (which do not distinguish packed vs. non-packed) but are - // overridden by Protobuf Binary and Text. - - public mutating func visitPackedFloatField(value: [Float], fieldNumber: Int) throws { - assert(!value.isEmpty) - try visitRepeatedFloatField(value: value, fieldNumber: fieldNumber) - } - - public mutating func visitPackedDoubleField(value: [Double], fieldNumber: Int) throws { - assert(!value.isEmpty) - try visitRepeatedDoubleField(value: value, fieldNumber: fieldNumber) - } - - public mutating func visitPackedInt32Field(value: [Int32], fieldNumber: Int) throws { - assert(!value.isEmpty) - try visitRepeatedInt32Field(value: value, fieldNumber: fieldNumber) - } - - public mutating func visitPackedInt64Field(value: [Int64], fieldNumber: Int) throws { - assert(!value.isEmpty) - try visitRepeatedInt64Field(value: value, fieldNumber: fieldNumber) - } - - public mutating func visitPackedUInt32Field(value: [UInt32], fieldNumber: Int) throws { - assert(!value.isEmpty) - try visitRepeatedUInt32Field(value: value, fieldNumber: fieldNumber) - } - - public mutating func visitPackedUInt64Field(value: [UInt64], fieldNumber: Int) throws { - assert(!value.isEmpty) - try visitRepeatedUInt64Field(value: value, fieldNumber: fieldNumber) - } - - public mutating func visitPackedSInt32Field(value: [Int32], fieldNumber: Int) throws { - assert(!value.isEmpty) - try visitPackedInt32Field(value: value, fieldNumber: fieldNumber) - } - - public mutating func visitPackedSInt64Field(value: [Int64], fieldNumber: Int) throws { - assert(!value.isEmpty) - try visitPackedInt64Field(value: value, fieldNumber: fieldNumber) - } - - public mutating func visitPackedFixed32Field(value: [UInt32], fieldNumber: Int) throws { - assert(!value.isEmpty) - try visitPackedUInt32Field(value: value, fieldNumber: fieldNumber) - } - - public mutating func visitPackedFixed64Field(value: [UInt64], fieldNumber: Int) throws { - assert(!value.isEmpty) - try visitPackedUInt64Field(value: value, fieldNumber: fieldNumber) - } - - public mutating func visitPackedSFixed32Field(value: [Int32], fieldNumber: Int) throws { - assert(!value.isEmpty) - try visitPackedInt32Field(value: value, fieldNumber: fieldNumber) - } - - public mutating func visitPackedSFixed64Field(value: [Int64], fieldNumber: Int) throws { - assert(!value.isEmpty) - try visitPackedInt64Field(value: value, fieldNumber: fieldNumber) - } - - public mutating func visitPackedBoolField(value: [Bool], fieldNumber: Int) throws { - assert(!value.isEmpty) - try visitRepeatedBoolField(value: value, fieldNumber: fieldNumber) - } - - public mutating func visitPackedEnumField(value: [E], - fieldNumber: Int) throws { - assert(!value.isEmpty) - try visitRepeatedEnumField(value: value, fieldNumber: fieldNumber) - } - - // Default handling for Groups is to treat them just like messages. - // This works for Text and Hash, but is overridden by Protobuf Binary - // format (which has a different encoding for groups) and JSON - // (which explicitly ignores all groups). - - public mutating func visitSingularGroupField(value: G, - fieldNumber: Int) throws { - try visitSingularMessageField(value: value, fieldNumber: fieldNumber) - } - - // Default handling of Extensions as a MessageSet to handing them just - // as plain extensions. Formats that what custom behavior can override - // it. - - public mutating func visitExtensionFieldsAsMessageSet( - fields: ExtensionFieldValueSet, - start: Int, - end: Int) throws { - try visitExtensionFields(fields: fields, start: start, end: end) - } - - // Default handling for Extensions is to forward the traverse to - // the ExtensionFieldValueSet. Formats that don't care about extensions - // can override to avoid it. - - /// Called for each extension range. - public mutating func visitExtensionFields(fields: ExtensionFieldValueSet, start: Int, end: Int) throws { - try fields.traverse(visitor: &self, start: start, end: end) - } + // Default definitions of numeric serializations. + // + // The 32-bit versions widen and delegate to 64-bit versions. + // The specialized integer codings delegate to standard Int/UInt. + // + // These "just work" for Hash and Text formats. Most of these work + // for JSON (32-bit integers are overridden to suppress quoting), + // and a few even work for Protobuf Binary (thanks to varint coding + // which erases the size difference between 32-bit and 64-bit ints). + + public mutating func visitSingularFloatField(value: Float, fieldNumber: Int) throws { + try visitSingularDoubleField(value: Double(value), fieldNumber: fieldNumber) + } + public mutating func visitSingularInt32Field(value: Int32, fieldNumber: Int) throws { + try visitSingularInt64Field(value: Int64(value), fieldNumber: fieldNumber) + } + public mutating func visitSingularUInt32Field(value: UInt32, fieldNumber: Int) throws { + try visitSingularUInt64Field(value: UInt64(value), fieldNumber: fieldNumber) + } + public mutating func visitSingularSInt32Field(value: Int32, fieldNumber: Int) throws { + try visitSingularInt32Field(value: value, fieldNumber: fieldNumber) + } + public mutating func visitSingularSInt64Field(value: Int64, fieldNumber: Int) throws { + try visitSingularInt64Field(value: value, fieldNumber: fieldNumber) + } + public mutating func visitSingularFixed32Field(value: UInt32, fieldNumber: Int) throws { + try visitSingularUInt32Field(value: value, fieldNumber: fieldNumber) + } + public mutating func visitSingularFixed64Field(value: UInt64, fieldNumber: Int) throws { + try visitSingularUInt64Field(value: value, fieldNumber: fieldNumber) + } + public mutating func visitSingularSFixed32Field(value: Int32, fieldNumber: Int) throws { + try visitSingularInt32Field(value: value, fieldNumber: fieldNumber) + } + public mutating func visitSingularSFixed64Field(value: Int64, fieldNumber: Int) throws { + try visitSingularInt64Field(value: value, fieldNumber: fieldNumber) + } + + // Default definitions of repeated serializations that just iterate and + // invoke the singular encoding. These "just work" for Protobuf Binary (encoder + // and size visitor), Protobuf Text, and Hash visitors. JSON format stores + // repeated values differently from singular, so overrides these. + + public mutating func visitRepeatedFloatField(value: [Float], fieldNumber: Int) throws { + assert(!value.isEmpty) + for v in value { + try visitSingularFloatField(value: v, fieldNumber: fieldNumber) + } + } + + public mutating func visitRepeatedDoubleField(value: [Double], fieldNumber: Int) throws { + assert(!value.isEmpty) + for v in value { + try visitSingularDoubleField(value: v, fieldNumber: fieldNumber) + } + } + + public mutating func visitRepeatedInt32Field(value: [Int32], fieldNumber: Int) throws { + assert(!value.isEmpty) + for v in value { + try visitSingularInt32Field(value: v, fieldNumber: fieldNumber) + } + } + + public mutating func visitRepeatedInt64Field(value: [Int64], fieldNumber: Int) throws { + assert(!value.isEmpty) + for v in value { + try visitSingularInt64Field(value: v, fieldNumber: fieldNumber) + } + } + + public mutating func visitRepeatedUInt32Field(value: [UInt32], fieldNumber: Int) throws { + assert(!value.isEmpty) + for v in value { + try visitSingularUInt32Field(value: v, fieldNumber: fieldNumber) + } + } + + public mutating func visitRepeatedUInt64Field(value: [UInt64], fieldNumber: Int) throws { + assert(!value.isEmpty) + for v in value { + try visitSingularUInt64Field(value: v, fieldNumber: fieldNumber) + } + } + + public mutating func visitRepeatedSInt32Field(value: [Int32], fieldNumber: Int) throws { + assert(!value.isEmpty) + for v in value { + try visitSingularSInt32Field(value: v, fieldNumber: fieldNumber) + } + } + + public mutating func visitRepeatedSInt64Field(value: [Int64], fieldNumber: Int) throws { + assert(!value.isEmpty) + for v in value { + try visitSingularSInt64Field(value: v, fieldNumber: fieldNumber) + } + } + + public mutating func visitRepeatedFixed32Field(value: [UInt32], fieldNumber: Int) throws { + assert(!value.isEmpty) + for v in value { + try visitSingularFixed32Field(value: v, fieldNumber: fieldNumber) + } + } + + public mutating func visitRepeatedFixed64Field(value: [UInt64], fieldNumber: Int) throws { + assert(!value.isEmpty) + for v in value { + try visitSingularFixed64Field(value: v, fieldNumber: fieldNumber) + } + } + + public mutating func visitRepeatedSFixed32Field(value: [Int32], fieldNumber: Int) throws { + assert(!value.isEmpty) + for v in value { + try visitSingularSFixed32Field(value: v, fieldNumber: fieldNumber) + } + } + + public mutating func visitRepeatedSFixed64Field(value: [Int64], fieldNumber: Int) throws { + assert(!value.isEmpty) + for v in value { + try visitSingularSFixed64Field(value: v, fieldNumber: fieldNumber) + } + } + + public mutating func visitRepeatedBoolField(value: [Bool], fieldNumber: Int) throws { + assert(!value.isEmpty) + for v in value { + try visitSingularBoolField(value: v, fieldNumber: fieldNumber) + } + } + + public mutating func visitRepeatedStringField(value: [String], fieldNumber: Int) throws { + assert(!value.isEmpty) + for v in value { + try visitSingularStringField(value: v, fieldNumber: fieldNumber) + } + } + + public mutating func visitRepeatedBytesField(value: [Data], fieldNumber: Int) throws { + assert(!value.isEmpty) + for v in value { + try visitSingularBytesField(value: v, fieldNumber: fieldNumber) + } + } + + public mutating func visitRepeatedEnumField(value: [E], fieldNumber: Int) throws { + assert(!value.isEmpty) + for v in value { + try visitSingularEnumField(value: v, fieldNumber: fieldNumber) + } + } + + public mutating func visitRepeatedMessageField(value: [M], fieldNumber: Int) throws { + assert(!value.isEmpty) + for v in value { + try visitSingularMessageField(value: v, fieldNumber: fieldNumber) + } + } + + public mutating func visitRepeatedGroupField(value: [G], fieldNumber: Int) throws { + assert(!value.isEmpty) + for v in value { + try visitSingularGroupField(value: v, fieldNumber: fieldNumber) + } + } + + // Default definitions of packed serialization just defer to the + // repeated implementation. This works for Hash and JSON visitors + // (which do not distinguish packed vs. non-packed) but are + // overridden by Protobuf Binary and Text. + + public mutating func visitPackedFloatField(value: [Float], fieldNumber: Int) throws { + assert(!value.isEmpty) + try visitRepeatedFloatField(value: value, fieldNumber: fieldNumber) + } + + public mutating func visitPackedDoubleField(value: [Double], fieldNumber: Int) throws { + assert(!value.isEmpty) + try visitRepeatedDoubleField(value: value, fieldNumber: fieldNumber) + } + + public mutating func visitPackedInt32Field(value: [Int32], fieldNumber: Int) throws { + assert(!value.isEmpty) + try visitRepeatedInt32Field(value: value, fieldNumber: fieldNumber) + } + + public mutating func visitPackedInt64Field(value: [Int64], fieldNumber: Int) throws { + assert(!value.isEmpty) + try visitRepeatedInt64Field(value: value, fieldNumber: fieldNumber) + } + + public mutating func visitPackedUInt32Field(value: [UInt32], fieldNumber: Int) throws { + assert(!value.isEmpty) + try visitRepeatedUInt32Field(value: value, fieldNumber: fieldNumber) + } + + public mutating func visitPackedUInt64Field(value: [UInt64], fieldNumber: Int) throws { + assert(!value.isEmpty) + try visitRepeatedUInt64Field(value: value, fieldNumber: fieldNumber) + } + + public mutating func visitPackedSInt32Field(value: [Int32], fieldNumber: Int) throws { + assert(!value.isEmpty) + try visitPackedInt32Field(value: value, fieldNumber: fieldNumber) + } + + public mutating func visitPackedSInt64Field(value: [Int64], fieldNumber: Int) throws { + assert(!value.isEmpty) + try visitPackedInt64Field(value: value, fieldNumber: fieldNumber) + } + + public mutating func visitPackedFixed32Field(value: [UInt32], fieldNumber: Int) throws { + assert(!value.isEmpty) + try visitPackedUInt32Field(value: value, fieldNumber: fieldNumber) + } + + public mutating func visitPackedFixed64Field(value: [UInt64], fieldNumber: Int) throws { + assert(!value.isEmpty) + try visitPackedUInt64Field(value: value, fieldNumber: fieldNumber) + } + + public mutating func visitPackedSFixed32Field(value: [Int32], fieldNumber: Int) throws { + assert(!value.isEmpty) + try visitPackedInt32Field(value: value, fieldNumber: fieldNumber) + } + + public mutating func visitPackedSFixed64Field(value: [Int64], fieldNumber: Int) throws { + assert(!value.isEmpty) + try visitPackedInt64Field(value: value, fieldNumber: fieldNumber) + } + + public mutating func visitPackedBoolField(value: [Bool], fieldNumber: Int) throws { + assert(!value.isEmpty) + try visitRepeatedBoolField(value: value, fieldNumber: fieldNumber) + } + + public mutating func visitPackedEnumField( + value: [E], + fieldNumber: Int + ) throws { + assert(!value.isEmpty) + try visitRepeatedEnumField(value: value, fieldNumber: fieldNumber) + } + + // Default handling for Groups is to treat them just like messages. + // This works for Text and Hash, but is overridden by Protobuf Binary + // format (which has a different encoding for groups) and JSON + // (which explicitly ignores all groups). + + public mutating func visitSingularGroupField( + value: G, + fieldNumber: Int + ) throws { + try visitSingularMessageField(value: value, fieldNumber: fieldNumber) + } + + // Default handling of Extensions as a MessageSet to handing them just + // as plain extensions. Formats that what custom behavior can override + // it. + + public mutating func visitExtensionFieldsAsMessageSet( + fields: ExtensionFieldValueSet, + start: Int, + end: Int + ) throws { + try visitExtensionFields(fields: fields, start: start, end: end) + } + + // Default handling for Extensions is to forward the traverse to + // the ExtensionFieldValueSet. Formats that don't care about extensions + // can override to avoid it. + + /// Called for each extension range. + public mutating func visitExtensionFields(fields: ExtensionFieldValueSet, start: Int, end: Int) throws { + try fields.traverse(visitor: &self, start: start, end: end) + } } diff --git a/Sources/SwiftProtobuf/WireFormat.swift b/Sources/SwiftProtobuf/WireFormat.swift index d1e67ac4b..25cd6f75c 100644 --- a/Sources/SwiftProtobuf/WireFormat.swift +++ b/Sources/SwiftProtobuf/WireFormat.swift @@ -14,57 +14,55 @@ /// Denotes the wire format by which a value is encoded in binary form. internal enum WireFormat: UInt8 { - case varint = 0 - case fixed64 = 1 - case lengthDelimited = 2 - case startGroup = 3 - case endGroup = 4 - case fixed32 = 5 + case varint = 0 + case fixed64 = 1 + case lengthDelimited = 2 + case startGroup = 3 + case endGroup = 4 + case fixed32 = 5 } extension WireFormat { - /// Information about the "MessageSet" format. Used when a Message has - /// the message_set_wire_format option enabled. - /// - /// Writing in MessageSet form means instead of writing the Extesions - /// normally as a simple fields, each gets written wrapped in a group: - /// repeated group Item = 1 { - /// required int32 type_id = 2; - /// required bytes message = 3; - /// } - /// Where the field number is the type_id, and the message is serilaized - /// into the bytes. - /// - /// The handling of unknown fields is ill defined. In proto1, they were - /// dropped. In the C++ for proto2, since it stores them in the unknowns - /// storage, if preserves any that are length delimited data (since that's - /// how the message also goes out). While the C++ is parsing, where the - /// unknowns fall in the flow of the group, sorta decides what happens. - /// Since it is ill defined, currently SwiftProtobuf will reflect out - /// anything set in the unknownStorage. During parsing, unknowns on the - /// message are preserved, but unknowns within the group are dropped (like - /// map items). Any extension in the MessageSet that isn't in the Regisry - /// being used at parse time will remain in a group and go into the - /// Messages's unknown fields (this way it reflects back out correctly). - internal enum MessageSet { + /// Information about the "MessageSet" format. Used when a Message has + /// the message_set_wire_format option enabled. + /// + /// Writing in MessageSet form means instead of writing the Extesions + /// normally as a simple fields, each gets written wrapped in a group: + /// repeated group Item = 1 { + /// required int32 type_id = 2; + /// required bytes message = 3; + /// } + /// Where the field number is the type_id, and the message is serilaized + /// into the bytes. + /// + /// The handling of unknown fields is ill defined. In proto1, they were + /// dropped. In the C++ for proto2, since it stores them in the unknowns + /// storage, if preserves any that are length delimited data (since that's + /// how the message also goes out). While the C++ is parsing, where the + /// unknowns fall in the flow of the group, sorta decides what happens. + /// Since it is ill defined, currently SwiftProtobuf will reflect out + /// anything set in the unknownStorage. During parsing, unknowns on the + /// message are preserved, but unknowns within the group are dropped (like + /// map items). Any extension in the MessageSet that isn't in the Regisry + /// being used at parse time will remain in a group and go into the + /// Messages's unknown fields (this way it reflects back out correctly). + internal enum MessageSet { - enum FieldNumbers { - static let item = 1; - static let typeId = 2; - static let message = 3; - } + enum FieldNumbers { + static let item = 1 + static let typeId = 2 + static let message = 3 + } - enum Tags { - static let itemStart = FieldTag(fieldNumber: FieldNumbers.item, wireFormat: .startGroup) - static let itemEnd = FieldTag(fieldNumber: FieldNumbers.item, wireFormat: .endGroup) - static let typeId = FieldTag(fieldNumber: FieldNumbers.typeId, wireFormat: .varint) - static let message = FieldTag(fieldNumber: FieldNumbers.message, wireFormat: .lengthDelimited) - } + enum Tags { + static let itemStart = FieldTag(fieldNumber: FieldNumbers.item, wireFormat: .startGroup) + static let itemEnd = FieldTag(fieldNumber: FieldNumbers.item, wireFormat: .endGroup) + static let typeId = FieldTag(fieldNumber: FieldNumbers.typeId, wireFormat: .varint) + static let message = FieldTag(fieldNumber: FieldNumbers.message, wireFormat: .lengthDelimited) + } - // The size of all the tags needed to write out an Extension in MessageSet format. - static let itemTagsEncodedSize = - Tags.itemStart.encodedSize + Tags.itemEnd.encodedSize + - Tags.typeId.encodedSize + - Tags.message.encodedSize - } + // The size of all the tags needed to write out an Extension in MessageSet format. + static let itemTagsEncodedSize = + Tags.itemStart.encodedSize + Tags.itemEnd.encodedSize + Tags.typeId.encodedSize + Tags.message.encodedSize + } } diff --git a/Sources/SwiftProtobuf/ZigZag.swift b/Sources/SwiftProtobuf/ZigZag.swift index e5a534bb3..93c30c4d6 100644 --- a/Sources/SwiftProtobuf/ZigZag.swift +++ b/Sources/SwiftProtobuf/ZigZag.swift @@ -12,7 +12,6 @@ /// // ----------------------------------------------------------------------------- - /// Contains helper methods to ZigZag encode and decode signed integers. internal enum ZigZag { @@ -25,7 +24,7 @@ internal enum ZigZag { /// - Parameter value: A signed 32-bit integer. /// - Returns: An unsigned 32-bit integer representing the ZigZag-encoded value. static func encoded(_ value: Int32) -> UInt32 { - return UInt32(bitPattern: (value << 1) ^ (value >> 31)) + UInt32(bitPattern: (value << 1) ^ (value >> 31)) } /// Return a 64-bit ZigZag-encoded value. @@ -37,7 +36,7 @@ internal enum ZigZag { /// - Parameter value: A signed 64-bit integer. /// - Returns: An unsigned 64-bit integer representing the ZigZag-encoded value. static func encoded(_ value: Int64) -> UInt64 { - return UInt64(bitPattern: (value << 1) ^ (value >> 63)) + UInt64(bitPattern: (value << 1) ^ (value >> 63)) } /// Return a 32-bit ZigZag-decoded value. @@ -49,7 +48,7 @@ internal enum ZigZag { /// - Parameter value: An unsigned 32-bit ZagZag-encoded integer. /// - Returns: The signed 32-bit decoded value. static func decoded(_ value: UInt32) -> Int32 { - return Int32(value >> 1) ^ -Int32(value & 1) + Int32(value >> 1) ^ -Int32(value & 1) } /// Return a 64-bit ZigZag-decoded value. @@ -61,6 +60,6 @@ internal enum ZigZag { /// - Parameter value: An unsigned 64-bit ZigZag-encoded integer. /// - Returns: The signed 64-bit decoded value. static func decoded(_ value: UInt64) -> Int64 { - return Int64(value >> 1) ^ -Int64(value & 1) + Int64(value >> 1) ^ -Int64(value & 1) } } diff --git a/Sources/SwiftProtobufPluginLibrary/CodeGenerator.swift b/Sources/SwiftProtobufPluginLibrary/CodeGenerator.swift index ef767f55b..2fdba052e 100644 --- a/Sources/SwiftProtobufPluginLibrary/CodeGenerator.swift +++ b/Sources/SwiftProtobufPluginLibrary/CodeGenerator.swift @@ -13,187 +13,191 @@ // ----------------------------------------------------------------------------- import Foundation - import SwiftProtobuf /// A protocol that generator should conform to then get easy support for /// being a protocol buffer compiler pluign. public protocol CodeGenerator { - init() - - /// Generates code for the given proto files. - /// - /// - Parameters: - /// - parameter: The parameter (or paramenters) passed for the generator. - /// This is for parameters specific to this generator, - /// `parse(parameter:)` (below) can be used to split back out - /// multiple parameters into the combined for the protocol buffer - /// compiler uses. - /// - protoCompilerContext: Context information about the protocol buffer - /// compiler being used. - /// - generatorOutputs: A object that can be used to send back the - /// generated outputs. - /// - /// - Throws: Can throw any `Error` to fail generate. `String(describing:)` - /// will be called on the error to provide the error string reported - /// to the user attempting to generate sources. - func generate( - files: [FileDescriptor], - parameter: any CodeGeneratorParameter, - protoCompilerContext: any ProtoCompilerContext, - generatorOutputs: any GeneratorOutputs) throws - - /// The list of features this CodeGenerator support to be reported back to - /// the protocol buffer compiler. - var supportedFeatures: [Google_Protobuf_Compiler_CodeGeneratorResponse.Feature] { get } - - /// The Protobuf Edition range that this generator can handle. Attempting - /// to generate for an Edition outside this range will cause protoc to - /// error. - var supportedEditionRange: ClosedRange { get } - - /// A list of extensions that define Custom Options - /// (https://protobuf.dev/programming-guides/proto2/#customoptions) for this generator so - /// they will be exposed on the `Descriptor` options. - var customOptionExtensions: [any AnyMessageExtension] { get } - - /// If provided, the argument parsing will support `--version` and report - /// this value. - var version: String? { get } - - /// If provided and `printHelp` isn't provide, this value will be including in - /// default output for the `--help` output. - var projectURL: String? { get } - - /// If provided and `printHelp` isn't provide, this value will be including in - /// default output for the `--help` output. - var copyrightLine: String? { get } - - /// Will be called for `-h` or `--help`, should `print()` out whatever is - /// desired; there is a default implementation that uses the above info - /// when provided. - func printHelp() + init() + + /// Generates code for the given proto files. + /// + /// - Parameters: + /// - parameter: The parameter (or paramenters) passed for the generator. + /// This is for parameters specific to this generator, + /// `parse(parameter:)` (below) can be used to split back out + /// multiple parameters into the combined for the protocol buffer + /// compiler uses. + /// - protoCompilerContext: Context information about the protocol buffer + /// compiler being used. + /// - generatorOutputs: A object that can be used to send back the + /// generated outputs. + /// + /// - Throws: Can throw any `Error` to fail generate. `String(describing:)` + /// will be called on the error to provide the error string reported + /// to the user attempting to generate sources. + func generate( + files: [FileDescriptor], + parameter: any CodeGeneratorParameter, + protoCompilerContext: any ProtoCompilerContext, + generatorOutputs: any GeneratorOutputs + ) throws + + /// The list of features this CodeGenerator support to be reported back to + /// the protocol buffer compiler. + var supportedFeatures: [Google_Protobuf_Compiler_CodeGeneratorResponse.Feature] { get } + + /// The Protobuf Edition range that this generator can handle. Attempting + /// to generate for an Edition outside this range will cause protoc to + /// error. + var supportedEditionRange: ClosedRange { get } + + /// A list of extensions that define Custom Options + /// (https://protobuf.dev/programming-guides/proto2/#customoptions) for this generator so + /// they will be exposed on the `Descriptor` options. + var customOptionExtensions: [any AnyMessageExtension] { get } + + /// If provided, the argument parsing will support `--version` and report + /// this value. + var version: String? { get } + + /// If provided and `printHelp` isn't provide, this value will be including in + /// default output for the `--help` output. + var projectURL: String? { get } + + /// If provided and `printHelp` isn't provide, this value will be including in + /// default output for the `--help` output. + var copyrightLine: String? { get } + + /// Will be called for `-h` or `--help`, should `print()` out whatever is + /// desired; there is a default implementation that uses the above info + /// when provided. + func printHelp() } extension CommandLine { - /// Get the command-line arguments passed to this process in a non mutable - /// form. Idea from https://github.com/swiftlang/swift/issues/66213 - /// - /// - Returns: An array of command-line arguments. - fileprivate static let safeArguments: [String] = - UnsafeBufferPointer(start: unsafeArgv, count: Int(argc)).compactMap { - String(validatingUTF8: $0!) - } + /// Get the command-line arguments passed to this process in a non mutable + /// form. Idea from https://github.com/swiftlang/swift/issues/66213 + /// + /// - Returns: An array of command-line arguments. + fileprivate static let safeArguments: [String] = + UnsafeBufferPointer(start: unsafeArgv, count: Int(argc)).compactMap { + String(validatingUTF8: $0!) + } } extension CodeGenerator { - var programName: String { - guard let name = CommandLine.safeArguments.first?.split(separator: "/").last else { - return "" - } - return String(name) - } - - /// Runs as a protocol buffer compiler plugin based on the given arguments - /// or falls back to `CommandLine.arguments`. - public func main(_ args: [String]?) { - let args = args ?? Array(CommandLine.safeArguments.dropFirst()) - - for arg in args { - if arg == "--version", let version = version { - print("\(programName) \(version)") - return - } - if arg == "-h" || arg == "--help" { - printHelp() - return - } - // Could look at bringing back the support for recorded requests, but - // haven't needed it in a long time. - var stderr = StandardErrorOutputStream() - print("Unknown argument: \(arg)", to: &stderr) - return + var programName: String { + guard let name = CommandLine.safeArguments.first?.split(separator: "/").last else { + return "" + } + return String(name) } - var extensionMap = SimpleExtensionMap() - if !customOptionExtensions.isEmpty { - for e in customOptionExtensions { - // Don't include Google_Protobuf_FeatureSet, that will be handing via custom features. - precondition(e.messageType == Google_Protobuf_EnumOptions.self || - e.messageType == Google_Protobuf_EnumValueOptions.self || - e.messageType == Google_Protobuf_ExtensionRangeOptions.self || - e.messageType == Google_Protobuf_FieldOptions.self || - e.messageType == Google_Protobuf_FileOptions.self || - e.messageType == Google_Protobuf_MessageOptions.self || - e.messageType == Google_Protobuf_MethodOptions.self || - e.messageType == Google_Protobuf_OneofOptions.self || - e.messageType == Google_Protobuf_ServiceOptions.self, - "CodeGenerator `customOptionExtensions` must only extend the descriptor.proto 'Options' messages \(e.messageType).") - } - extensionMap.insert(contentsOf: customOptionExtensions) + /// Runs as a protocol buffer compiler plugin based on the given arguments + /// or falls back to `CommandLine.arguments`. + public func main(_ args: [String]?) { + let args = args ?? Array(CommandLine.safeArguments.dropFirst()) + + for arg in args { + if arg == "--version", let version = version { + print("\(programName) \(version)") + return + } + if arg == "-h" || arg == "--help" { + printHelp() + return + } + // Could look at bringing back the support for recorded requests, but + // haven't needed it in a long time. + var stderr = StandardErrorOutputStream() + print("Unknown argument: \(arg)", to: &stderr) + return + } + + var extensionMap = SimpleExtensionMap() + if !customOptionExtensions.isEmpty { + for e in customOptionExtensions { + // Don't include Google_Protobuf_FeatureSet, that will be handing via custom features. + precondition( + e.messageType == Google_Protobuf_EnumOptions.self + || e.messageType == Google_Protobuf_EnumValueOptions.self + || e.messageType == Google_Protobuf_ExtensionRangeOptions.self + || e.messageType == Google_Protobuf_FieldOptions.self + || e.messageType == Google_Protobuf_FileOptions.self + || e.messageType == Google_Protobuf_MessageOptions.self + || e.messageType == Google_Protobuf_MethodOptions.self + || e.messageType == Google_Protobuf_OneofOptions.self + || e.messageType == Google_Protobuf_ServiceOptions.self, + "CodeGenerator `customOptionExtensions` must only extend the descriptor.proto 'Options' messages \(e.messageType)." + ) + } + extensionMap.insert(contentsOf: customOptionExtensions) + } + + let response: Google_Protobuf_Compiler_CodeGeneratorResponse + do { + let request = try Google_Protobuf_Compiler_CodeGeneratorRequest( + serializedBytes: FileHandle.standardInput.readDataToEndOfFile(), + extensions: extensionMap + ) + response = generateCode(request: request, generator: self) + } catch let e { + response = Google_Protobuf_Compiler_CodeGeneratorResponse( + error: "Received an unparsable request from the compiler: \(e)" + ) + } + + let serializedResponse: Data + do { + serializedResponse = try response.serializedBytes() + } catch let e { + var stderr = StandardErrorOutputStream() + print("\(programName): Failure while serializing response: \(e)", to: &stderr) + return + } + FileHandle.standardOutput.write(serializedResponse) } - let response: Google_Protobuf_Compiler_CodeGeneratorResponse - do { - let request = try Google_Protobuf_Compiler_CodeGeneratorRequest( - serializedBytes: FileHandle.standardInput.readDataToEndOfFile(), - extensions: extensionMap - ) - response = generateCode(request: request, generator: self) - } catch let e { - response = Google_Protobuf_Compiler_CodeGeneratorResponse( - error: "Received an unparsable request from the compiler: \(e)") + /// Runs as a protocol buffer compiler plugin; reading the generation request + /// off stdin and sending the response on stdout. + /// + /// Instead of calling this, just add `@main` to your `CodeGenerator`. + public static func main() { + let generator = Self() + generator.main(nil) } - - let serializedResponse: Data - do { - serializedResponse = try response.serializedBytes() - } catch let e { - var stderr = StandardErrorOutputStream() - print("\(programName): Failure while serializing response: \(e)", to: &stderr) - return - } - FileHandle.standardOutput.write(serializedResponse) - } - - /// Runs as a protocol buffer compiler plugin; reading the generation request - /// off stdin and sending the response on stdout. - /// - /// Instead of calling this, just add `@main` to your `CodeGenerator`. - public static func main() { - let generator = Self() - generator.main(nil) - } } // Provide default implementation for things so `CodeGenerator`s only have to // provide them if they wish too. extension CodeGenerator { - public var supportedEditionRange: ClosedRange { - // Default impl of unknown so generator don't have to provide this until - // they support editions. - return Google_Protobuf_Edition.unknown...Google_Protobuf_Edition.unknown - } - public var customOptionExtensions: [any AnyMessageExtension] { return [] } - public var version: String? { return nil } - public var projectURL: String? { return nil } - public var copyrightLine: String? { return nil } - - public func printHelp() { - print("\(programName): A plugin for protoc and should not normally be run directly.") - if let copyright = copyrightLine { - print("\(copyright)") + public var supportedEditionRange: ClosedRange { + // Default impl of unknown so generator don't have to provide this until + // they support editions. + Google_Protobuf_Edition.unknown...Google_Protobuf_Edition.unknown } - if let projectURL = projectURL { - print( - """ - - For more information on the usage of this plugin, please see: - \(projectURL) - - """) + public var customOptionExtensions: [any AnyMessageExtension] { [] } + public var version: String? { nil } + public var projectURL: String? { nil } + public var copyrightLine: String? { nil } + + public func printHelp() { + print("\(programName): A plugin for protoc and should not normally be run directly.") + if let copyright = copyrightLine { + print("\(copyright)") + } + if let projectURL = projectURL { + print( + """ + + For more information on the usage of this plugin, please see: + \(projectURL) + + """ + ) + } } - } } /// Uses the given `Google_Protobuf_Compiler_CodeGeneratorRequest` and @@ -209,62 +213,71 @@ extension CodeGenerator { /// - Returns a filled out response with the success or failure of the /// generation. public func generateCode( - request: Google_Protobuf_Compiler_CodeGeneratorRequest, - generator: any CodeGenerator + request: Google_Protobuf_Compiler_CodeGeneratorRequest, + generator: any CodeGenerator ) -> Google_Protobuf_Compiler_CodeGeneratorResponse { - // TODO: This will need update to language specific features. + // TODO: This will need update to language specific features. + + let descriptorSet = DescriptorSet(protos: request.protoFile) + + var files = [FileDescriptor]() + for name in request.fileToGenerate { + guard let fileDescriptor = descriptorSet.fileDescriptor(named: name) else { + return Google_Protobuf_Compiler_CodeGeneratorResponse( + error: + "protoc asked plugin to generate a file but did not provide a descriptor for the file: \(name)" + ) + } + files.append(fileDescriptor) + } + + let context = InternalProtoCompilerContext(request: request) + let outputs = InternalGeneratorOutputs() + let parameter = InternalCodeGeneratorParameter(request.parameter) - let descriptorSet = DescriptorSet(protos: request.protoFile) + do { + try generator.generate( + files: files, + parameter: parameter, + protoCompilerContext: context, + generatorOutputs: outputs + ) + } catch let e { + return Google_Protobuf_Compiler_CodeGeneratorResponse(error: String(describing: e)) + } - var files = [FileDescriptor]() - for name in request.fileToGenerate { - guard let fileDescriptor = descriptorSet.fileDescriptor(named: name) else { - return Google_Protobuf_Compiler_CodeGeneratorResponse( - error: - "protoc asked plugin to generate a file but did not provide a descriptor for the file: \(name)" - ) + var response = Google_Protobuf_Compiler_CodeGeneratorResponse() + response.file = outputs.files + + // TODO: Could supportedFeatures be completely handled within library? + // - The only "hard" part around hiding the proto3 optional support is making + // sure the oneof index related bits aren't leaked from FieldDescriptors. + // Otherwise the oneof related apis could likely take over the "realOneof" + // jobs and just never vend the synthetic information. + // - The editions support bit likely could be computed based on the values + // `supportedEditionRange` having been overridden. + let supportedFeatures = generator.supportedFeatures + response.supportedFeatures = supportedFeatures.reduce(0) { $0 | UInt64($1.rawValue) } + + if supportedFeatures.contains(.supportsEditions) { + let supportedEditions = generator.supportedEditionRange + precondition( + supportedEditions.upperBound != .unknown, + "For a CodeGenerator to support Editions, it must override `supportedEditionRange`" + ) + precondition( + DescriptorSet.bundledEditionsSupport.contains(supportedEditions.lowerBound), + "A CodeGenerator can't claim to support an Edition before what the library supports: \(supportedEditions.lowerBound) vs \(DescriptorSet.bundledEditionsSupport)" + ) + precondition( + DescriptorSet.bundledEditionsSupport.contains(supportedEditions.upperBound), + "A CodeGenerator can't claim to support an Edition after what the library supports: \(supportedEditions.upperBound) vs \(DescriptorSet.bundledEditionsSupport)" + ) + response.minimumEdition = Int32(supportedEditions.lowerBound.rawValue) + response.maximumEdition = Int32(supportedEditions.upperBound.rawValue) } - files.append(fileDescriptor) - } - - let context = InternalProtoCompilerContext(request: request) - let outputs = InternalGeneratorOutputs() - let parameter = InternalCodeGeneratorParameter(request.parameter) - - do { - try generator.generate( - files: files, parameter: parameter, protoCompilerContext: context, - generatorOutputs: outputs) - } catch let e { - return Google_Protobuf_Compiler_CodeGeneratorResponse(error: String(describing: e)) - } - - var response = Google_Protobuf_Compiler_CodeGeneratorResponse() - response.file = outputs.files - - // TODO: Could supportedFeatures be completely handled within library? - // - The only "hard" part around hiding the proto3 optional support is making - // sure the oneof index related bits aren't leaked from FieldDescriptors. - // Otherwise the oneof related apis could likely take over the "realOneof" - // jobs and just never vend the synthetic information. - // - The editions support bit likely could be computed based on the values - // `supportedEditionRange` having been overridden. - let supportedFeatures = generator.supportedFeatures - response.supportedFeatures = supportedFeatures.reduce(0) { $0 | UInt64($1.rawValue) } - - if supportedFeatures.contains(.supportsEditions) { - let supportedEditions = generator.supportedEditionRange - precondition(supportedEditions.upperBound != .unknown, - "For a CodeGenerator to support Editions, it must override `supportedEditionRange`") - precondition(DescriptorSet.bundledEditionsSupport.contains(supportedEditions.lowerBound), - "A CodeGenerator can't claim to support an Edition before what the library supports: \(supportedEditions.lowerBound) vs \(DescriptorSet.bundledEditionsSupport)") - precondition(DescriptorSet.bundledEditionsSupport.contains(supportedEditions.upperBound), - "A CodeGenerator can't claim to support an Edition after what the library supports: \(supportedEditions.upperBound) vs \(DescriptorSet.bundledEditionsSupport)") - response.minimumEdition = Int32(supportedEditions.lowerBound.rawValue) - response.maximumEdition = Int32(supportedEditions.upperBound.rawValue) - } - - return response + + return response } // MARK: Internal supporting types @@ -272,67 +285,69 @@ public func generateCode( /// Internal implementation of `CodeGeneratorParameter` for /// `generateCode(request:generator:)` struct InternalCodeGeneratorParameter: CodeGeneratorParameter { - let parameter: String - - init(_ parameter: String) { - self.parameter = parameter - } + let parameter: String - var parsedPairs: [(key: String, value: String)] { - guard !parameter.isEmpty else { - return [] + init(_ parameter: String) { + self.parameter = parameter } - let parts = parameter.components(separatedBy: ",") - return parts.map { s -> (key: String, value: String) in - guard let index = s.range(of: "=")?.lowerBound else { - // Key only, no value ("baz" in example). - return (trimWhitespace(s), "") - } - return ( - key: trimWhitespace(s[.. (key: String, value: String) in + guard let index = s.range(of: "=")?.lowerBound else { + // Key only, no value ("baz" in example). + return (trimWhitespace(s), "") + } + return ( + key: trimWhitespace(s[.. = [] - func add(fileName: String, contents: String) throws { - guard !fileNames.contains(fileName) else { - throw OutputError.duplicateName(fileName) + var files: [Google_Protobuf_Compiler_CodeGeneratorResponse.File] = [] + private var fileNames: Set = [] + + func add(fileName: String, contents: String) throws { + guard !fileNames.contains(fileName) else { + throw OutputError.duplicateName(fileName) + } + fileNames.insert(fileName) + files.append( + Google_Protobuf_Compiler_CodeGeneratorResponse.File( + name: fileName, + content: contents + ) + ) } - fileNames.insert(fileName) - files.append( - Google_Protobuf_Compiler_CodeGeneratorResponse.File( - name: fileName, - content: contents)) - } } diff --git a/Sources/SwiftProtobufPluginLibrary/CodeGeneratorParameter.swift b/Sources/SwiftProtobufPluginLibrary/CodeGeneratorParameter.swift index 2511f815b..e21565cf7 100644 --- a/Sources/SwiftProtobufPluginLibrary/CodeGeneratorParameter.swift +++ b/Sources/SwiftProtobufPluginLibrary/CodeGeneratorParameter.swift @@ -20,21 +20,21 @@ import Foundation /// parameters via the `--[LANG]_out` or `--[LANG]_opt` command line flags. /// The compiler will relay those through as a _parameter_ string. public protocol CodeGeneratorParameter { - /// The raw value from the compiler as a single string, if multiple values - /// were passed, they are joined into a single string. See `parsedPairs` as - /// that is likely a better option for consuming the parameters. - var parameter: String { get } + /// The raw value from the compiler as a single string, if multiple values + /// were passed, they are joined into a single string. See `parsedPairs` as + /// that is likely a better option for consuming the parameters. + var parameter: String { get } - /// The protocol buffer compiler will combine multiple `--[LANG]_opt` - /// directives into a "single" parameter by joining them with commas. This - /// vends the parameter split back back out into the individual arguments: - /// i.e., - /// "foo=bar,baz,mumble=blah" - /// becomes: - /// [ - /// (key: "foo", value: "bar"), - /// (key: "baz", value: ""), - /// (key: "mumble", value: "blah") - /// ] - var parsedPairs: [(key: String, value: String)] { get } + /// The protocol buffer compiler will combine multiple `--[LANG]_opt` + /// directives into a "single" parameter by joining them with commas. This + /// vends the parameter split back back out into the individual arguments: + /// i.e., + /// "foo=bar,baz,mumble=blah" + /// becomes: + /// [ + /// (key: "foo", value: "bar"), + /// (key: "baz", value: ""), + /// (key: "mumble", value: "blah") + /// ] + var parsedPairs: [(key: String, value: String)] { get } } diff --git a/Sources/SwiftProtobufPluginLibrary/CodePrinter.swift b/Sources/SwiftProtobufPluginLibrary/CodePrinter.swift index 2841c9514..348f860ec 100644 --- a/Sources/SwiftProtobufPluginLibrary/CodePrinter.swift +++ b/Sources/SwiftProtobufPluginLibrary/CodePrinter.swift @@ -17,210 +17,212 @@ /// `outdent`. public struct CodePrinter { - /// Reserve an initial buffer of 64KB scalars to eliminate some reallocations - /// in smaller files. - private static let initialBufferSize = 65536 - - private static let kNewline : String.UnicodeScalarView.Element = "\n" - - /// The string content that was printed. - public var content: String { - return String(contentScalars) - } - - /// See if anything was printed. - public var isEmpty: Bool { return contentScalars.isEmpty } - - /// The Unicode scalar buffer used to build up the printed contents. - private var contentScalars = String.UnicodeScalarView() - - /// The `UnicodeScalarView` representing a single indentation step. - private let singleIndent: String.UnicodeScalarView - - /// The current indentation level (a collection of spaces). - private var indentation = String.UnicodeScalarView() - - /// Keeps track of whether the printer is currently sitting at the beginning - /// of a line. - private var atLineStart = true - - /// Keeps track of if a newline should be added after each string to the - /// print apis. - private let newlines: Bool - - public init(indent: String.UnicodeScalarView = " ".unicodeScalars) { - contentScalars.reserveCapacity(CodePrinter.initialBufferSize) - singleIndent = indent - newlines = false - } - - /// Initialize the printer for use. - /// - /// - Parameters: - /// - indent: A string (usually spaces) to use for the indentation amount. - /// - newlines: A boolean indicating if every `print` and `printIndented` - /// should automatically add newlines to the end of the strings. - public init( - indent: String.UnicodeScalarView = " ".unicodeScalars, - addNewlines newlines: Bool - ) { - contentScalars.reserveCapacity(CodePrinter.initialBufferSize) - singleIndent = indent - self.newlines = newlines - } - - /// Initialize a new printer using the existing state from another printer. - /// - /// This can be useful to use with generation subtasks, so see if they - /// actually generate something (via `isEmpty`) to then optionally add it - /// back into the parent with whatever surounding content. - /// - /// This is most useful to then use `append` to add the new content. - /// - /// - Parameter parent: The other printer to copy the configuration/state - /// from. - public init(_ parent: CodePrinter) { - self.init(parent, addNewlines: parent.newlines) - } - - /// Initialize a new printer using the existing state from another printer - /// but with support to control the behavior of `addNewlines`. - /// - /// This can be useful to use with generation subtasks, so see if they - /// actually generate something (via `isEmpty`) to then optionally add it - /// back into the parent with whatever surounding content. - /// - /// This is most useful to then use `append` to add the new content. - /// - /// - Parameters: - /// - parent: The other printer to copy the configuration/state - /// from. - /// - newlines: A boolean indicating if every `print` and `printIndented` - /// should automatically add newlines to the end of the strings. - public init(_ parent: CodePrinter, addNewlines newlines: Bool) { - self.init(indent: parent.singleIndent, addNewlines: newlines) - indentation = parent.indentation - } - - /// Writes the given strings to the printer, adding a newline after each - /// string. - /// - /// Newlines within the strings are honored and indentention is applied. - /// - /// The `addNewlines` value from initializing the printer controls if - /// newlines are appended after each string. - /// - /// If called with no strings, a blank line is added to the printer - /// (even is `addNewlines` was false at initialization of the printer. - /// - /// - Parameter text: A variable-length list of strings to be printed. - public mutating func print(_ text: String...) { - if text.isEmpty { - contentScalars.append(CodePrinter.kNewline) - atLineStart = true - } else { - for t in text { - printInternal(t.unicodeScalars, addNewline: newlines) - } + /// Reserve an initial buffer of 64KB scalars to eliminate some reallocations + /// in smaller files. + private static let initialBufferSize = 65536 + + private static let kNewline: String.UnicodeScalarView.Element = "\n" + + /// The string content that was printed. + public var content: String { + String(contentScalars) + } + + /// See if anything was printed. + public var isEmpty: Bool { contentScalars.isEmpty } + + /// The Unicode scalar buffer used to build up the printed contents. + private var contentScalars = String.UnicodeScalarView() + + /// The `UnicodeScalarView` representing a single indentation step. + private let singleIndent: String.UnicodeScalarView + + /// The current indentation level (a collection of spaces). + private var indentation = String.UnicodeScalarView() + + /// Keeps track of whether the printer is currently sitting at the beginning + /// of a line. + private var atLineStart = true + + /// Keeps track of if a newline should be added after each string to the + /// print apis. + private let newlines: Bool + + public init(indent: String.UnicodeScalarView = " ".unicodeScalars) { + contentScalars.reserveCapacity(CodePrinter.initialBufferSize) + singleIndent = indent + newlines = false } - } - - /// Writes the given strings to the printer, optionally adding a newline - /// after each string. If called with no strings, a blank line is added to - /// the printer. - /// - /// Newlines within the strings are honored and indentention is applied. - /// - /// - Parameters - /// - text: A variable-length list of strings to be printed. - /// - newlines: Boolean to control adding newlines after each string. This - /// is an explicit override of the `addNewlines` value using to - /// initialize this `CodePrinter`. - public mutating func print(_ text: String..., newlines: Bool) { - if text.isEmpty { - assert(newlines, - "Disabling newlines with no strings doesn't make sense.") - contentScalars.append(CodePrinter.kNewline) - atLineStart = true - } else { - for t in text { - printInternal(t.unicodeScalars, addNewline: newlines) - } + + /// Initialize the printer for use. + /// + /// - Parameters: + /// - indent: A string (usually spaces) to use for the indentation amount. + /// - newlines: A boolean indicating if every `print` and `printIndented` + /// should automatically add newlines to the end of the strings. + public init( + indent: String.UnicodeScalarView = " ".unicodeScalars, + addNewlines newlines: Bool + ) { + contentScalars.reserveCapacity(CodePrinter.initialBufferSize) + singleIndent = indent + self.newlines = newlines } - } - - /// Indents, writes the given strings to the printer, and then outdents. - /// - /// Newlines within the strings are honored and indentention is applied. - /// - /// The `addNewlines` value from initializing the printer controls if - /// newlines are appended after each string. - /// - /// - Parameter text: A variable-length list of strings to be printed. - public mutating func printIndented(_ text: String...) { - indent() - for t in text { - printInternal(t.unicodeScalars, addNewline: newlines) + + /// Initialize a new printer using the existing state from another printer. + /// + /// This can be useful to use with generation subtasks, so see if they + /// actually generate something (via `isEmpty`) to then optionally add it + /// back into the parent with whatever surounding content. + /// + /// This is most useful to then use `append` to add the new content. + /// + /// - Parameter parent: The other printer to copy the configuration/state + /// from. + public init(_ parent: CodePrinter) { + self.init(parent, addNewlines: parent.newlines) } - outdent() - } - - private mutating func printInternal( - _ scalars: String.UnicodeScalarView, - addNewline: Bool - ) { - for scalar in scalars { - // Indent at the start of a new line, unless it's a blank line. - if atLineStart && scalar != CodePrinter.kNewline { - contentScalars.append(contentsOf: indentation) - } - contentScalars.append(scalar) - atLineStart = (scalar == CodePrinter.kNewline) + + /// Initialize a new printer using the existing state from another printer + /// but with support to control the behavior of `addNewlines`. + /// + /// This can be useful to use with generation subtasks, so see if they + /// actually generate something (via `isEmpty`) to then optionally add it + /// back into the parent with whatever surounding content. + /// + /// This is most useful to then use `append` to add the new content. + /// + /// - Parameters: + /// - parent: The other printer to copy the configuration/state + /// from. + /// - newlines: A boolean indicating if every `print` and `printIndented` + /// should automatically add newlines to the end of the strings. + public init(_ parent: CodePrinter, addNewlines newlines: Bool) { + self.init(indent: parent.singleIndent, addNewlines: newlines) + indentation = parent.indentation } - if addNewline { - contentScalars.append(CodePrinter.kNewline) - atLineStart = true + + /// Writes the given strings to the printer, adding a newline after each + /// string. + /// + /// Newlines within the strings are honored and indentention is applied. + /// + /// The `addNewlines` value from initializing the printer controls if + /// newlines are appended after each string. + /// + /// If called with no strings, a blank line is added to the printer + /// (even is `addNewlines` was false at initialization of the printer. + /// + /// - Parameter text: A variable-length list of strings to be printed. + public mutating func print(_ text: String...) { + if text.isEmpty { + contentScalars.append(CodePrinter.kNewline) + atLineStart = true + } else { + for t in text { + printInternal(t.unicodeScalars, addNewline: newlines) + } + } } - } - - /// Appended the content of another `CodePrinter`to this one. - /// - /// - Parameters: - /// - printer: The other `CodePrinter` to copy from. - /// - indenting: Boolean, if the text being appended should be reindented - /// to the current state of this printer. If the `printer` was - /// initialized off of this printer, there isn't a need to reindent. - public mutating func append(_ printer: CodePrinter, indenting: Bool = false) { - if indenting { - printInternal(printer.contentScalars, addNewline: false) - } else { - contentScalars.append(contentsOf: printer.contentScalars) - atLineStart = printer.atLineStart + + /// Writes the given strings to the printer, optionally adding a newline + /// after each string. If called with no strings, a blank line is added to + /// the printer. + /// + /// Newlines within the strings are honored and indentention is applied. + /// + /// - Parameters + /// - text: A variable-length list of strings to be printed. + /// - newlines: Boolean to control adding newlines after each string. This + /// is an explicit override of the `addNewlines` value using to + /// initialize this `CodePrinter`. + public mutating func print(_ text: String..., newlines: Bool) { + if text.isEmpty { + assert( + newlines, + "Disabling newlines with no strings doesn't make sense." + ) + contentScalars.append(CodePrinter.kNewline) + atLineStart = true + } else { + for t in text { + printInternal(t.unicodeScalars, addNewline: newlines) + } + } + } + + /// Indents, writes the given strings to the printer, and then outdents. + /// + /// Newlines within the strings are honored and indentention is applied. + /// + /// The `addNewlines` value from initializing the printer controls if + /// newlines are appended after each string. + /// + /// - Parameter text: A variable-length list of strings to be printed. + public mutating func printIndented(_ text: String...) { + indent() + for t in text { + printInternal(t.unicodeScalars, addNewline: newlines) + } + outdent() + } + + private mutating func printInternal( + _ scalars: String.UnicodeScalarView, + addNewline: Bool + ) { + for scalar in scalars { + // Indent at the start of a new line, unless it's a blank line. + if atLineStart && scalar != CodePrinter.kNewline { + contentScalars.append(contentsOf: indentation) + } + contentScalars.append(scalar) + atLineStart = (scalar == CodePrinter.kNewline) + } + if addNewline { + contentScalars.append(CodePrinter.kNewline) + atLineStart = true + } + } + + /// Appended the content of another `CodePrinter`to this one. + /// + /// - Parameters: + /// - printer: The other `CodePrinter` to copy from. + /// - indenting: Boolean, if the text being appended should be reindented + /// to the current state of this printer. If the `printer` was + /// initialized off of this printer, there isn't a need to reindent. + public mutating func append(_ printer: CodePrinter, indenting: Bool = false) { + if indenting { + printInternal(printer.contentScalars, addNewline: false) + } else { + contentScalars.append(contentsOf: printer.contentScalars) + atLineStart = printer.atLineStart + } + } + + /// Increases the printer's indentation level. + public mutating func indent() { + indentation.append(contentsOf: singleIndent) + } + + /// Decreases the printer's indentation level. + /// + /// - Precondition: The printer must not have an indentation level. + public mutating func outdent() { + let indentCount = singleIndent.count + precondition(indentation.count >= indentCount, "Cannot outdent past the left margin") + indentation.removeLast(indentCount) + } + + /// Indents, calls `body` to do other work relaying along the printer, and + /// the outdents after wards. + /// + /// - Parameter body: A closure that is invoked after the indent is + /// increasted. + public mutating func withIndentation(body: (_ p: inout CodePrinter) -> Void) { + indent() + body(&self) + outdent() } - } - - /// Increases the printer's indentation level. - public mutating func indent() { - indentation.append(contentsOf: singleIndent) - } - - /// Decreases the printer's indentation level. - /// - /// - Precondition: The printer must not have an indentation level. - public mutating func outdent() { - let indentCount = singleIndent.count - precondition(indentation.count >= indentCount, "Cannot outdent past the left margin") - indentation.removeLast(indentCount) - } - - /// Indents, calls `body` to do other work relaying along the printer, and - /// the outdents after wards. - /// - /// - Parameter body: A closure that is invoked after the indent is - /// increasted. - public mutating func withIndentation(body: (_ p: inout CodePrinter) -> Void) { - indent() - body(&self) - outdent() - } } diff --git a/Sources/SwiftProtobufPluginLibrary/Descriptor+Extensions.swift b/Sources/SwiftProtobufPluginLibrary/Descriptor+Extensions.swift index 4034a9623..337e30224 100644 --- a/Sources/SwiftProtobufPluginLibrary/Descriptor+Extensions.swift +++ b/Sources/SwiftProtobufPluginLibrary/Descriptor+Extensions.swift @@ -12,174 +12,176 @@ import Foundation import SwiftProtobuf extension FileDescriptor: ProvidesSourceCodeLocation { - public var sourceCodeInfoLocation: Google_Protobuf_SourceCodeInfo.Location? { - // google/protobuf's descriptor.cc says it should be an empty path. - return sourceCodeInfoLocation(path: IndexPath()) - } + public var sourceCodeInfoLocation: Google_Protobuf_SourceCodeInfo.Location? { + // google/protobuf's descriptor.cc says it should be an empty path. + sourceCodeInfoLocation(path: IndexPath()) + } } extension Descriptor: ProvidesLocationPath, ProvidesSourceCodeLocation, TypeOrFileProvidesDeprecationComment { - public func getLocationPath(path: inout IndexPath) { - if let containingType = containingType { - containingType.getLocationPath(path: &path) - path.append(Google_Protobuf_DescriptorProto.FieldNumbers.nestedType) - } else { - path.append(Google_Protobuf_FileDescriptorProto.FieldNumbers.messageType) + public func getLocationPath(path: inout IndexPath) { + if let containingType = containingType { + containingType.getLocationPath(path: &path) + path.append(Google_Protobuf_DescriptorProto.FieldNumbers.nestedType) + } else { + path.append(Google_Protobuf_FileDescriptorProto.FieldNumbers.messageType) + } + path.append(index) } - path.append(index) - } - public var typeName: String { "message" } - public var isDeprecated: Bool { options.deprecated } + public var typeName: String { "message" } + public var isDeprecated: Bool { options.deprecated } } extension Descriptor.ExtensionRange: ProvidesLocationPath, ProvidesSourceCodeLocation { - public func getLocationPath(path: inout IndexPath) { - containingType.getLocationPath(path: &path) - path.append(Google_Protobuf_DescriptorProto.FieldNumbers.extensionRange) - path.append(index) - } + public func getLocationPath(path: inout IndexPath) { + containingType.getLocationPath(path: &path) + path.append(Google_Protobuf_DescriptorProto.FieldNumbers.extensionRange) + path.append(index) + } } extension EnumDescriptor: ProvidesLocationPath, ProvidesSourceCodeLocation, TypeOrFileProvidesDeprecationComment { - public func getLocationPath(path: inout IndexPath) { - if let containingType = containingType { - containingType.getLocationPath(path: &path) - path.append(Google_Protobuf_DescriptorProto.FieldNumbers.enumType) - } else { - path.append(Google_Protobuf_FileDescriptorProto.FieldNumbers.enumType) + public func getLocationPath(path: inout IndexPath) { + if let containingType = containingType { + containingType.getLocationPath(path: &path) + path.append(Google_Protobuf_DescriptorProto.FieldNumbers.enumType) + } else { + path.append(Google_Protobuf_FileDescriptorProto.FieldNumbers.enumType) + } + path.append(index) } - path.append(index) - } - public var typeName: String { "enum" } - public var isDeprecated: Bool { options.deprecated } + public var typeName: String { "enum" } + public var isDeprecated: Bool { options.deprecated } } extension EnumValueDescriptor: ProvidesLocationPath, ProvidesSourceCodeLocation, SimpleProvidesDeprecationComment { - public func getLocationPath(path: inout IndexPath) { - enumType.getLocationPath(path: &path) - path.append(Google_Protobuf_EnumDescriptorProto.FieldNumbers.value) - path.append(index) - } - - public var typeName: String { "enum value" } - public var isDeprecated: Bool { options.deprecated } + public func getLocationPath(path: inout IndexPath) { + enumType.getLocationPath(path: &path) + path.append(Google_Protobuf_EnumDescriptorProto.FieldNumbers.value) + path.append(index) + } + + public var typeName: String { "enum value" } + public var isDeprecated: Bool { options.deprecated } } extension OneofDescriptor: ProvidesLocationPath, ProvidesSourceCodeLocation { - public func getLocationPath(path: inout IndexPath) { - containingType.getLocationPath(path: &path) - path.append(Google_Protobuf_DescriptorProto.FieldNumbers.oneofDecl) - path.append(index) - } + public func getLocationPath(path: inout IndexPath) { + containingType.getLocationPath(path: &path) + path.append(Google_Protobuf_DescriptorProto.FieldNumbers.oneofDecl) + path.append(index) + } } extension FieldDescriptor: ProvidesLocationPath, ProvidesSourceCodeLocation, ProvidesDeprecationComment { - public func getLocationPath(path: inout IndexPath) { - if isExtension { - if let extensionScope = extensionScope { - extensionScope.getLocationPath(path: &path) - path.append(Google_Protobuf_DescriptorProto.FieldNumbers.extension) - } else { - path.append(Google_Protobuf_FileDescriptorProto.FieldNumbers.extension) - } - } else { - containingType.getLocationPath(path: &path) - path.append(Google_Protobuf_DescriptorProto.FieldNumbers.field) - } - path.append(index) - } - - public func deprecationComment(commentPrefix: String) -> String { - // FieldDesciptor can be an extension field or a normal field, so it needs - // a custom imply to only look at the file for extentsion fields. - if options.deprecated { - return "\(commentPrefix) NOTE: This \(isExtension ? "extension field" : "field") was marked as deprecated in the .proto file.\n" + public func getLocationPath(path: inout IndexPath) { + if isExtension { + if let extensionScope = extensionScope { + extensionScope.getLocationPath(path: &path) + path.append(Google_Protobuf_DescriptorProto.FieldNumbers.extension) + } else { + path.append(Google_Protobuf_FileDescriptorProto.FieldNumbers.extension) + } + } else { + containingType.getLocationPath(path: &path) + path.append(Google_Protobuf_DescriptorProto.FieldNumbers.field) + } + path.append(index) } - if isExtension && file.options.deprecated { - return "\(commentPrefix) NOTE: The whole .proto file that defined this extension field was marked as deprecated.\n" - } - return String() - } - - /// Returns true if the type can be used for a Packed field. - static func isPackable(type: Google_Protobuf_FieldDescriptorProto.TypeEnum) -> Bool { - // This logic comes from the C++ FieldDescriptor::IsTypePackable() impl. - switch type { - case .string, .group, .message, .bytes: - return false - default: - return true + + public func deprecationComment(commentPrefix: String) -> String { + // FieldDesciptor can be an extension field or a normal field, so it needs + // a custom imply to only look at the file for extentsion fields. + if options.deprecated { + return + "\(commentPrefix) NOTE: This \(isExtension ? "extension field" : "field") was marked as deprecated in the .proto file.\n" + } + if isExtension && file.options.deprecated { + return + "\(commentPrefix) NOTE: The whole .proto file that defined this extension field was marked as deprecated.\n" + } + return String() } - } - - /// Helper to return the name to as the "base" for naming of generated fields. - /// - /// Groups use the underlying message's name. The way groups are declared in - /// proto files, the filed names is derived by lowercasing the Group's name, - /// so there are no underscores, etc. to rebuild a camel case name from. - var namingBase: String { - return internal_isGroupLike ? messageType!.name : name - } - - /// Helper to see if this is "group-like". Edition 2024 will likely provide - /// a new feature to better deal with this. See upsteam protobuf for more - /// details on the problem. - /// - /// This models upstream internal::cpp::IsGroupLike(). - /// - /// TODO(thomasvl): make this `package` instead of `public` and drop the - /// "internal" part from the name when 5.9 is the baseline. - public var internal_isGroupLike: Bool { - guard type == .group else { - return false + + /// Returns true if the type can be used for a Packed field. + static func isPackable(type: Google_Protobuf_FieldDescriptorProto.TypeEnum) -> Bool { + // This logic comes from the C++ FieldDescriptor::IsTypePackable() impl. + switch type { + case .string, .group, .message, .bytes: + return false + default: + return true + } } - // `messageType` can't realy be nil once we know it's a group. - let messageType = messageType! - // The original proto2 syntax concept of a group always has a field name - // that is the exact lowercasing of the message name. - guard name == messageType.name.lowercased() else { - return false; + /// Helper to return the name to as the "base" for naming of generated fields. + /// + /// Groups use the underlying message's name. The way groups are declared in + /// proto files, the filed names is derived by lowercasing the Group's name, + /// so there are no underscores, etc. to rebuild a camel case name from. + var namingBase: String { + internal_isGroupLike ? messageType!.name : name } - // The message defined by a group is at the same scope as the field. So... - if isExtension { - if extensionScope == nil { - // Top level extension, so the message made by the group has to be the - // same file and also a type level type. - return messageType.file === file && messageType.containingType == nil - } else { - // Extension field was scoped to a message, so the group will be also - // nested under that same message. - return messageType.containingType === extensionScope - } - } else { - // A regular message field, the message made by the group has to be - // nested under this same message. - return messageType.containingType === containingType + /// Helper to see if this is "group-like". Edition 2024 will likely provide + /// a new feature to better deal with this. See upsteam protobuf for more + /// details on the problem. + /// + /// This models upstream internal::cpp::IsGroupLike(). + /// + /// TODO(thomasvl): make this `package` instead of `public` and drop the + /// "internal" part from the name when 5.9 is the baseline. + public var internal_isGroupLike: Bool { + guard type == .group else { + return false + } + // `messageType` can't realy be nil once we know it's a group. + let messageType = messageType! + + // The original proto2 syntax concept of a group always has a field name + // that is the exact lowercasing of the message name. + guard name == messageType.name.lowercased() else { + return false + } + + // The message defined by a group is at the same scope as the field. So... + if isExtension { + if extensionScope == nil { + // Top level extension, so the message made by the group has to be the + // same file and also a type level type. + return messageType.file === file && messageType.containingType == nil + } else { + // Extension field was scoped to a message, so the group will be also + // nested under that same message. + return messageType.containingType === extensionScope + } + } else { + // A regular message field, the message made by the group has to be + // nested under this same message. + return messageType.containingType === containingType + } } - } } extension ServiceDescriptor: ProvidesLocationPath, ProvidesSourceCodeLocation, TypeOrFileProvidesDeprecationComment { - public func getLocationPath(path: inout IndexPath) { - path.append(Google_Protobuf_FileDescriptorProto.FieldNumbers.service) - path.append(index) - } + public func getLocationPath(path: inout IndexPath) { + path.append(Google_Protobuf_FileDescriptorProto.FieldNumbers.service) + path.append(index) + } - public var typeName: String { "service" } - public var isDeprecated: Bool { options.deprecated } + public var typeName: String { "service" } + public var isDeprecated: Bool { options.deprecated } } extension MethodDescriptor: ProvidesLocationPath, ProvidesSourceCodeLocation, SimpleProvidesDeprecationComment { - public func getLocationPath(path: inout IndexPath) { - service.getLocationPath(path: &path) - path.append(Google_Protobuf_ServiceDescriptorProto.FieldNumbers.method) - path.append(index) - } - - public var typeName: String { "method" } - public var isDeprecated: Bool { options.deprecated } + public func getLocationPath(path: inout IndexPath) { + service.getLocationPath(path: &path) + path.append(Google_Protobuf_ServiceDescriptorProto.FieldNumbers.method) + path.append(index) + } + + public var typeName: String { "method" } + public var isDeprecated: Bool { options.deprecated } } diff --git a/Sources/SwiftProtobufPluginLibrary/Descriptor.swift b/Sources/SwiftProtobufPluginLibrary/Descriptor.swift index 86eb72155..135f448e7 100644 --- a/Sources/SwiftProtobufPluginLibrary/Descriptor.swift +++ b/Sources/SwiftProtobufPluginLibrary/Descriptor.swift @@ -33,153 +33,162 @@ import SwiftProtobuf /// /// This is like the `DescriptorPool` class in the C++ protobuf library. public final class DescriptorSet { - /// The list of `FileDescriptor`s in this set. - public let files: [FileDescriptor] - private let registry = Registry() - - // Construct out of a `Google_Protobuf_FileDescriptorSet` likely - // created by protoc. - public convenience init(proto: Google_Protobuf_FileDescriptorSet) { - self.init(protos: proto.file) - } - - /// The bundled in `google.protobuf.FeatureSetDefault` that defines what - /// the plugin library can support. - private static let bundledFeatureSetDefaults = - // Decoding the bundle defaults better never fail - try! Google_Protobuf_FeatureSetDefaults(serializedBytes: bundledFeatureSetDefaultBytes) - - /// The range of Editions that the library can support. - /// - /// This will limit what edition versions a plugin can claim to support. - public static var bundledEditionsSupport: ClosedRange { - return bundledFeatureSetDefaults.minimumEdition...bundledFeatureSetDefaults.maximumEdition - } - - /// Construct out of a ordered list of - /// `Google_Protobuf_FileDescriptorProto`s likely created by protoc. - public convenience init(protos: [Google_Protobuf_FileDescriptorProto]) { - self.init(protos: protos, - featureSetDefaults: DescriptorSet.bundledFeatureSetDefaults) - } - - /// Construct out of a ordered list of - /// `Google_Protobuf_FileDescriptorProto`s likely created by protoc. Since - /// .proto files can import other .proto files, the imports have to be - /// listed before the things that use them so the graph can be - /// reconstructed. - /// - /// - Parameters: - /// - protos: An ordered list of `Google_Protobuf_FileDescriptorProto`. - /// They must be order such that a file is provided before another file - /// that depends on it. - /// - featureSetDefaults: A `Google_Protobuf_FeatureSetDefaults` that provides - /// the Feature defaults to use when parsing the give File protos. - /// - featureExtensions: A list of Protobuf Extension extensions to - /// `google.protobuf.FeatureSet` that define custom features. If used, the - /// `defaults` should have been parsed with the extensions being - /// supported. - public init( - protos: [Google_Protobuf_FileDescriptorProto], - featureSetDefaults: Google_Protobuf_FeatureSetDefaults, - featureExtensions: [any AnyMessageExtension] = [] - ) { - precondition(Self.bundledEditionsSupport.contains(featureSetDefaults.minimumEdition), - "Attempt to use a FeatureSetDefault minimumEdition that isn't supported by the library.") - precondition(Self.bundledEditionsSupport.contains(featureSetDefaults.maximumEdition), - "Attempt to use a FeatureSetDefault maximumEdition that isn't supported by the library.") - // If a protoc is too old ≤v26, it might have `features` instead of `overridable_features` and - // `fixed_features`, try to catch that. - precondition( - nil == featureSetDefaults.defaults.first(where: { !$0.hasOverridableFeatures && !$0.hasFixedFeatures }), - "These FeatureSetDefault don't appear valid, make sure you are using a new enough protoc to generate them. ") - let registry = self.registry - self.files = protos.map { - return FileDescriptor(proto: $0, - featureSetDefaults: featureSetDefaults, - featureExtensions: featureExtensions, - registry: registry) - } - } - - /// Lookup a specific file. The names for files are what was captured in - /// the `Google_Protobuf_FileDescriptorProto` when it was created, protoc - /// uses the path name for how the file was found. - /// - /// This is a legacy api since it requires the file to be found or it aborts. - /// Mainly kept for grpc-swift compatibility. - @available(*, deprecated, renamed: "fileDescriptor(named:)") - public func lookupFileDescriptor(protoName name: String) -> FileDescriptor { - return registry.fileDescriptor(named: name)! - } - - /// Find a specific file. The names for files are what was captured in - /// the `Google_Protobuf_FileDescriptorProto` when it was created, protoc - /// uses the path name for how the file was found. - public func fileDescriptor(named name: String) -> FileDescriptor? { - return registry.fileDescriptor(named: name) - } - - /// Find the `Descriptor` for a named proto message. - /// - /// This is a legacy api since it requires the proto to be found or it aborts. - /// Mainly kept for grpc-swift compatibility. - @available(*, deprecated, renamed: "descriptor(named:)") - public func lookupDescriptor(protoName: String) -> Descriptor { - self.descriptor(named: protoName)! - } - - /// Find the `Descriptor` for a named proto message. - public func descriptor(named fullName: String) -> Descriptor? { - return registry.descriptor(named: ".\(fullName)") - } - - /// Find the `EnumDescriptor` for a named proto enum. - /// - /// This is a legacy api since it requires the enum to be found or it aborts. - /// Mainly kept for grpc-swift compatibility. - @available(*, deprecated, renamed: "enumDescriptor(named:)") - public func lookupEnumDescriptor(protoName: String) -> EnumDescriptor { - return enumDescriptor(named: protoName)! - } - - /// Find the `EnumDescriptor` for a named proto enum. - public func enumDescriptor(named fullName: String) -> EnumDescriptor? { - return registry.enumDescriptor(named: ".\(fullName)") - } - - /// Find the `ServiceDescriptor` for a named proto service. - /// - /// This is a legacy api since it requires the enum to be found or it aborts. - /// Mainly kept for grpc-swift compatibility. - @available(*, deprecated, renamed: "serviceDescriptor(named:)") - public func lookupServiceDescriptor(protoName: String) -> ServiceDescriptor { - return serviceDescriptor(named: protoName)! - } - - /// Find the `ServiceDescriptor` for a named proto service. - public func serviceDescriptor(named fullName: String) -> ServiceDescriptor? { - return registry.serviceDescriptor(named: ".\(fullName)") - } + /// The list of `FileDescriptor`s in this set. + public let files: [FileDescriptor] + private let registry = Registry() + + // Construct out of a `Google_Protobuf_FileDescriptorSet` likely + // created by protoc. + public convenience init(proto: Google_Protobuf_FileDescriptorSet) { + self.init(protos: proto.file) + } + + /// The bundled in `google.protobuf.FeatureSetDefault` that defines what + /// the plugin library can support. + private static let bundledFeatureSetDefaults = + // Decoding the bundle defaults better never fail + try! Google_Protobuf_FeatureSetDefaults(serializedBytes: bundledFeatureSetDefaultBytes) + + /// The range of Editions that the library can support. + /// + /// This will limit what edition versions a plugin can claim to support. + public static var bundledEditionsSupport: ClosedRange { + bundledFeatureSetDefaults.minimumEdition...bundledFeatureSetDefaults.maximumEdition + } + + /// Construct out of a ordered list of + /// `Google_Protobuf_FileDescriptorProto`s likely created by protoc. + public convenience init(protos: [Google_Protobuf_FileDescriptorProto]) { + self.init( + protos: protos, + featureSetDefaults: DescriptorSet.bundledFeatureSetDefaults + ) + } + + /// Construct out of a ordered list of + /// `Google_Protobuf_FileDescriptorProto`s likely created by protoc. Since + /// .proto files can import other .proto files, the imports have to be + /// listed before the things that use them so the graph can be + /// reconstructed. + /// + /// - Parameters: + /// - protos: An ordered list of `Google_Protobuf_FileDescriptorProto`. + /// They must be order such that a file is provided before another file + /// that depends on it. + /// - featureSetDefaults: A `Google_Protobuf_FeatureSetDefaults` that provides + /// the Feature defaults to use when parsing the give File protos. + /// - featureExtensions: A list of Protobuf Extension extensions to + /// `google.protobuf.FeatureSet` that define custom features. If used, the + /// `defaults` should have been parsed with the extensions being + /// supported. + public init( + protos: [Google_Protobuf_FileDescriptorProto], + featureSetDefaults: Google_Protobuf_FeatureSetDefaults, + featureExtensions: [any AnyMessageExtension] = [] + ) { + precondition( + Self.bundledEditionsSupport.contains(featureSetDefaults.minimumEdition), + "Attempt to use a FeatureSetDefault minimumEdition that isn't supported by the library." + ) + precondition( + Self.bundledEditionsSupport.contains(featureSetDefaults.maximumEdition), + "Attempt to use a FeatureSetDefault maximumEdition that isn't supported by the library." + ) + // If a protoc is too old ≤v26, it might have `features` instead of `overridable_features` and + // `fixed_features`, try to catch that. + precondition( + nil == featureSetDefaults.defaults.first(where: { !$0.hasOverridableFeatures && !$0.hasFixedFeatures }), + "These FeatureSetDefault don't appear valid, make sure you are using a new enough protoc to generate them. " + ) + let registry = self.registry + self.files = protos.map { + FileDescriptor( + proto: $0, + featureSetDefaults: featureSetDefaults, + featureExtensions: featureExtensions, + registry: registry + ) + } + } + + /// Lookup a specific file. The names for files are what was captured in + /// the `Google_Protobuf_FileDescriptorProto` when it was created, protoc + /// uses the path name for how the file was found. + /// + /// This is a legacy api since it requires the file to be found or it aborts. + /// Mainly kept for grpc-swift compatibility. + @available(*, deprecated, renamed: "fileDescriptor(named:)") + public func lookupFileDescriptor(protoName name: String) -> FileDescriptor { + registry.fileDescriptor(named: name)! + } + + /// Find a specific file. The names for files are what was captured in + /// the `Google_Protobuf_FileDescriptorProto` when it was created, protoc + /// uses the path name for how the file was found. + public func fileDescriptor(named name: String) -> FileDescriptor? { + registry.fileDescriptor(named: name) + } + + /// Find the `Descriptor` for a named proto message. + /// + /// This is a legacy api since it requires the proto to be found or it aborts. + /// Mainly kept for grpc-swift compatibility. + @available(*, deprecated, renamed: "descriptor(named:)") + public func lookupDescriptor(protoName: String) -> Descriptor { + self.descriptor(named: protoName)! + } + + /// Find the `Descriptor` for a named proto message. + public func descriptor(named fullName: String) -> Descriptor? { + registry.descriptor(named: ".\(fullName)") + } + + /// Find the `EnumDescriptor` for a named proto enum. + /// + /// This is a legacy api since it requires the enum to be found or it aborts. + /// Mainly kept for grpc-swift compatibility. + @available(*, deprecated, renamed: "enumDescriptor(named:)") + public func lookupEnumDescriptor(protoName: String) -> EnumDescriptor { + enumDescriptor(named: protoName)! + } + + /// Find the `EnumDescriptor` for a named proto enum. + public func enumDescriptor(named fullName: String) -> EnumDescriptor? { + registry.enumDescriptor(named: ".\(fullName)") + } + + /// Find the `ServiceDescriptor` for a named proto service. + /// + /// This is a legacy api since it requires the enum to be found or it aborts. + /// Mainly kept for grpc-swift compatibility. + @available(*, deprecated, renamed: "serviceDescriptor(named:)") + public func lookupServiceDescriptor(protoName: String) -> ServiceDescriptor { + serviceDescriptor(named: protoName)! + } + + /// Find the `ServiceDescriptor` for a named proto service. + public func serviceDescriptor(named fullName: String) -> ServiceDescriptor? { + registry.serviceDescriptor(named: ".\(fullName)") + } } /// Options for collected a proto object from a Descriptor. public struct ExtractProtoOptions { - /// If the `SourceCodeInfo` should also be included in the proto file. - /// - /// If embedding the descriptor in a binary for some reason, normally the `SourceCodeInfo` - /// isn't needed and would just be an increas in binary size. - public var includeSourceCodeInfo: Bool = false + /// If the `SourceCodeInfo` should also be included in the proto file. + /// + /// If embedding the descriptor in a binary for some reason, normally the `SourceCodeInfo` + /// isn't needed and would just be an increas in binary size. + public var includeSourceCodeInfo: Bool = false - /// Copy on the _header_ for the descriptor. This mainly means leave out any of the nested - /// descriptors (messages, enums, etc.). - public var headerOnly: Bool = false + /// Copy on the _header_ for the descriptor. This mainly means leave out any of the nested + /// descriptors (messages, enums, etc.). + public var headerOnly: Bool = false - // NOTE: in the future maybe add toggles to model the behavior of the *Descriptor::Copy*To() - // apis. + // NOTE: in the future maybe add toggles to model the behavior of the *Descriptor::Copy*To() + // apis. - public init() {} + public init() {} } /// Models a .proto file. `FileDescriptor`s are not directly created, @@ -187,222 +196,233 @@ public struct ExtractProtoOptions { /// they are directly accessed via a `file` property on all the other /// types of descriptors. public final class FileDescriptor { - @available(*, deprecated, message: "This enum has been deprecated. Use `Google_Protobuf_Edition` instead.") - public enum Syntax: String { - case proto2 - case proto3 - - public init?(rawValue: String) { - switch rawValue { - case "proto2", "": - self = .proto2 - case "proto3": - self = .proto3 - default: - return nil - } - } - } - - /// The filename used with protoc. - public let name: String - /// The proto package. - public let package: String - - @available(*, deprecated, message: "This property has been deprecated. Use `edition` instead.") - public var syntax: Syntax { - Syntax(rawValue: self._proto.syntax)! - } - - /// The edition of the file. - public let edition: Google_Protobuf_Edition - - /// The resolved features for this File. - public let features: Google_Protobuf_FeatureSet - - /// The imports for this file. - public let dependencies: [FileDescriptor] - /// The subset of the imports that were declared `public`. - public let publicDependencies: [FileDescriptor] - /// The subset of the imports that were declared `weak`. - public let weakDependencies: [FileDescriptor] - - /// The enum defintions at the file scope level. - public let enums: [EnumDescriptor] - /// The message defintions at the file scope level. - public let messages: [Descriptor] - /// The extension field defintions at the file scope level. - public let extensions: [FieldDescriptor] - /// The service defintions at the file scope level. - public let services: [ServiceDescriptor] - - /// The `Google_Protobuf_FileOptions` set on this file. - @available(*, deprecated, renamed: "options") - public var fileOptions: Google_Protobuf_FileOptions { self.options } - - /// The `Google_Protobuf_FileOptions` set on this file. - public let options: Google_Protobuf_FileOptions - - private let sourceCodeInfo: Google_Protobuf_SourceCodeInfo - - /// Extract contents of this descriptor in Proto form. - /// - /// - Parameters: - /// - options: Controls what information is include/excluded when creating the Proto version. - /// - /// - Returns: A `Google_Protobuf_FileDescriptorProto`. - public func extractProto( - options: ExtractProtoOptions = ExtractProtoOptions() - ) -> Google_Protobuf_FileDescriptorProto { - // In the future it might make sense to model this like the C++, where the protos is built up - // on demand instead of keeping the object around. - var result = _proto - - if !options.includeSourceCodeInfo { - result.clearSourceCodeInfo() - } - - if options.headerOnly { - // For FileDescriptor, make `headerOnly` mean the same things as C++ - // `FileDescriptor::CopyHeaderTo()`. - result.dependency = [] - result.publicDependency = [] - result.weakDependency = [] - result.messageType = [] - result.enumType = [] - result.messageType = [] - result.service = [] - result.extension = [] - } - - return result - } - - /// The proto version of the descriptor that defines this File. - @available(*, deprecated, renamed: "extractProto()") - public var proto: Google_Protobuf_FileDescriptorProto { return _proto } - private let _proto: Google_Protobuf_FileDescriptorProto - - @available(*, deprecated, message: "Use `fileOptions/deprecated` instead.") - public var isDeprecated: Bool { return proto.options.deprecated } - - fileprivate init( - proto: Google_Protobuf_FileDescriptorProto, - featureSetDefaults: Google_Protobuf_FeatureSetDefaults, - featureExtensions: [any AnyMessageExtension], - registry: Registry - ) { - self.name = proto.name - self.package = proto.package - - // This logic comes from upstream `DescriptorBuilder::BuildFileImpl()`. - if proto.hasEdition { - self.edition = proto.edition - } else { - switch proto.syntax { - case "", "proto2": - self.edition = .proto2 - case "proto3": - self.edition = .proto3 - default: - self.edition = .unknown - fatalError( - "protoc provided an expected value (\"\(proto.syntax)\") for syntax/edition: \(proto.name)") - } - } - // TODO: Revsit capturing the error here and see about exposing it out - // to be reported via plugins. - let featureResolver: FeatureResolver - do { - featureResolver = try FeatureResolver(edition: self.edition, - featureSetDefaults: featureSetDefaults, - featureExtensions: featureExtensions) - } catch let e { - fatalError("Failed to make a FeatureResolver for \(self.name): \(e)") - } - let resolvedFeatures = featureResolver.resolve(proto.options) - self.features = resolvedFeatures - self.options = proto.options - - let protoPackage = proto.package - self.enums = proto.enumType.enumerated().map { - return EnumDescriptor(proto: $0.element, - index: $0.offset, - parentFeatures: resolvedFeatures, - featureResolver: featureResolver, - registry: registry, - scope: protoPackage) - } - self.messages = proto.messageType.enumerated().map { - return Descriptor(proto: $0.element, - index: $0.offset, - parentFeatures: resolvedFeatures, - featureResolver: featureResolver, - registry: registry, - scope: protoPackage) - } - self.extensions = proto.extension.enumerated().map { - return FieldDescriptor(extension: $0.element, - index: $0.offset, - parentFeatures: resolvedFeatures, - featureResolver: featureResolver, - registry: registry) - } - self.services = proto.service.enumerated().map { - return ServiceDescriptor(proto: $0.element, - index: $0.offset, - fileFeatures: resolvedFeatures, - featureResolver: featureResolver, - registry: registry, - scope: protoPackage) - } - - // The compiler ensures there aren't cycles between a file and dependencies, so - // this doesn't run the risk of creating any retain cycles that would force these - // to have to be weak. - let dependencies = proto.dependency.map { return registry.fileDescriptor(named: $0)! } - self.dependencies = dependencies - self.publicDependencies = proto.publicDependency.map { dependencies[Int($0)] } - self.weakDependencies = proto.weakDependency.map { dependencies[Int($0)] } - - self.sourceCodeInfo = proto.sourceCodeInfo - - self._proto = proto - - // Done initializing, register ourselves. - registry.register(file: self) - - // descriptor.proto documents the files will be in deps order. That means we - // any external reference will have been in the previous files in the set. - self.enums.forEach { $0.bind(file: self, registry: registry, containingType: nil) } - self.messages.forEach { $0.bind(file: self, registry: registry, containingType: nil) } - self.extensions.forEach { $0.bind(file: self, registry: registry, containingType: nil) } - self.services.forEach { $0.bind(file: self, registry: registry) } - } - - /// Fetch the source information for a give path. For more details on the paths - /// and what this information is, see `Google_Protobuf_SourceCodeInfo`. - /// - /// For simpler access to the comments for give message, fields, enums; see - /// `Descriptor+Extensions.swift` and the `ProvidesLocationPath` and - /// `ProvidesSourceCodeLocation` protocols. - public func sourceCodeInfoLocation(path: IndexPath) -> Google_Protobuf_SourceCodeInfo.Location? { - guard let location = locationMap[path] else { - return nil - } - return location - } - - // Lazy so this can be computed on demand, as the imported files won't need - // comments during generation. - private lazy var locationMap: [IndexPath:Google_Protobuf_SourceCodeInfo.Location] = { - var result: [IndexPath:Google_Protobuf_SourceCodeInfo.Location] = [:] - for loc in sourceCodeInfo.location { - let intList = loc.path.map { return Int($0) } - result[IndexPath(indexes: intList)] = loc - } - return result - }() + @available(*, deprecated, message: "This enum has been deprecated. Use `Google_Protobuf_Edition` instead.") + public enum Syntax: String { + case proto2 + case proto3 + + public init?(rawValue: String) { + switch rawValue { + case "proto2", "": + self = .proto2 + case "proto3": + self = .proto3 + default: + return nil + } + } + } + + /// The filename used with protoc. + public let name: String + /// The proto package. + public let package: String + + @available(*, deprecated, message: "This property has been deprecated. Use `edition` instead.") + public var syntax: Syntax { + Syntax(rawValue: self._proto.syntax)! + } + + /// The edition of the file. + public let edition: Google_Protobuf_Edition + + /// The resolved features for this File. + public let features: Google_Protobuf_FeatureSet + + /// The imports for this file. + public let dependencies: [FileDescriptor] + /// The subset of the imports that were declared `public`. + public let publicDependencies: [FileDescriptor] + /// The subset of the imports that were declared `weak`. + public let weakDependencies: [FileDescriptor] + + /// The enum defintions at the file scope level. + public let enums: [EnumDescriptor] + /// The message defintions at the file scope level. + public let messages: [Descriptor] + /// The extension field defintions at the file scope level. + public let extensions: [FieldDescriptor] + /// The service defintions at the file scope level. + public let services: [ServiceDescriptor] + + /// The `Google_Protobuf_FileOptions` set on this file. + @available(*, deprecated, renamed: "options") + public var fileOptions: Google_Protobuf_FileOptions { self.options } + + /// The `Google_Protobuf_FileOptions` set on this file. + public let options: Google_Protobuf_FileOptions + + private let sourceCodeInfo: Google_Protobuf_SourceCodeInfo + + /// Extract contents of this descriptor in Proto form. + /// + /// - Parameters: + /// - options: Controls what information is include/excluded when creating the Proto version. + /// + /// - Returns: A `Google_Protobuf_FileDescriptorProto`. + public func extractProto( + options: ExtractProtoOptions = ExtractProtoOptions() + ) -> Google_Protobuf_FileDescriptorProto { + // In the future it might make sense to model this like the C++, where the protos is built up + // on demand instead of keeping the object around. + var result = _proto + + if !options.includeSourceCodeInfo { + result.clearSourceCodeInfo() + } + + if options.headerOnly { + // For FileDescriptor, make `headerOnly` mean the same things as C++ + // `FileDescriptor::CopyHeaderTo()`. + result.dependency = [] + result.publicDependency = [] + result.weakDependency = [] + result.messageType = [] + result.enumType = [] + result.messageType = [] + result.service = [] + result.extension = [] + } + + return result + } + + /// The proto version of the descriptor that defines this File. + @available(*, deprecated, renamed: "extractProto()") + public var proto: Google_Protobuf_FileDescriptorProto { _proto } + private let _proto: Google_Protobuf_FileDescriptorProto + + @available(*, deprecated, message: "Use `fileOptions/deprecated` instead.") + public var isDeprecated: Bool { proto.options.deprecated } + + fileprivate init( + proto: Google_Protobuf_FileDescriptorProto, + featureSetDefaults: Google_Protobuf_FeatureSetDefaults, + featureExtensions: [any AnyMessageExtension], + registry: Registry + ) { + self.name = proto.name + self.package = proto.package + + // This logic comes from upstream `DescriptorBuilder::BuildFileImpl()`. + if proto.hasEdition { + self.edition = proto.edition + } else { + switch proto.syntax { + case "", "proto2": + self.edition = .proto2 + case "proto3": + self.edition = .proto3 + default: + self.edition = .unknown + fatalError( + "protoc provided an expected value (\"\(proto.syntax)\") for syntax/edition: \(proto.name)" + ) + } + } + // TODO: Revsit capturing the error here and see about exposing it out + // to be reported via plugins. + let featureResolver: FeatureResolver + do { + featureResolver = try FeatureResolver( + edition: self.edition, + featureSetDefaults: featureSetDefaults, + featureExtensions: featureExtensions + ) + } catch let e { + fatalError("Failed to make a FeatureResolver for \(self.name): \(e)") + } + let resolvedFeatures = featureResolver.resolve(proto.options) + self.features = resolvedFeatures + self.options = proto.options + + let protoPackage = proto.package + self.enums = proto.enumType.enumerated().map { + EnumDescriptor( + proto: $0.element, + index: $0.offset, + parentFeatures: resolvedFeatures, + featureResolver: featureResolver, + registry: registry, + scope: protoPackage + ) + } + self.messages = proto.messageType.enumerated().map { + Descriptor( + proto: $0.element, + index: $0.offset, + parentFeatures: resolvedFeatures, + featureResolver: featureResolver, + registry: registry, + scope: protoPackage + ) + } + self.extensions = proto.extension.enumerated().map { + FieldDescriptor( + extension: $0.element, + index: $0.offset, + parentFeatures: resolvedFeatures, + featureResolver: featureResolver, + registry: registry + ) + } + self.services = proto.service.enumerated().map { + ServiceDescriptor( + proto: $0.element, + index: $0.offset, + fileFeatures: resolvedFeatures, + featureResolver: featureResolver, + registry: registry, + scope: protoPackage + ) + } + + // The compiler ensures there aren't cycles between a file and dependencies, so + // this doesn't run the risk of creating any retain cycles that would force these + // to have to be weak. + let dependencies = proto.dependency.map { return registry.fileDescriptor(named: $0)! } + self.dependencies = dependencies + self.publicDependencies = proto.publicDependency.map { dependencies[Int($0)] } + self.weakDependencies = proto.weakDependency.map { dependencies[Int($0)] } + + self.sourceCodeInfo = proto.sourceCodeInfo + + self._proto = proto + + // Done initializing, register ourselves. + registry.register(file: self) + + // descriptor.proto documents the files will be in deps order. That means we + // any external reference will have been in the previous files in the set. + self.enums.forEach { $0.bind(file: self, registry: registry, containingType: nil) } + self.messages.forEach { $0.bind(file: self, registry: registry, containingType: nil) } + self.extensions.forEach { $0.bind(file: self, registry: registry, containingType: nil) } + self.services.forEach { $0.bind(file: self, registry: registry) } + } + + /// Fetch the source information for a give path. For more details on the paths + /// and what this information is, see `Google_Protobuf_SourceCodeInfo`. + /// + /// For simpler access to the comments for give message, fields, enums; see + /// `Descriptor+Extensions.swift` and the `ProvidesLocationPath` and + /// `ProvidesSourceCodeLocation` protocols. + public func sourceCodeInfoLocation(path: IndexPath) -> Google_Protobuf_SourceCodeInfo.Location? { + guard let location = locationMap[path] else { + return nil + } + return location + } + + // Lazy so this can be computed on demand, as the imported files won't need + // comments during generation. + private lazy var locationMap: [IndexPath: Google_Protobuf_SourceCodeInfo.Location] = { + var result: [IndexPath: Google_Protobuf_SourceCodeInfo.Location] = [:] + for loc in sourceCodeInfo.location { + let intList = loc.path.map { return Int($0) } + result[IndexPath(indexes: intList)] = loc + } + return result + }() } /// Describes a type of protocol message, or a particular group within a @@ -410,336 +430,354 @@ public final class FileDescriptor { /// constructed/fetched via the `DescriptorSet` or they are directly accessed /// via a `messageType` property on `FieldDescriptor`s, etc. public final class Descriptor { - // We can't assign a value directly to `proto` in the init because we get the - // deprecation warning. This private prop only exists as a workaround to avoid - // this warning and preserve backwards compatibility - it should be removed - // when removing `proto`. - private let _proto: Google_Protobuf_DescriptorProto - @available(*, deprecated, message: "Please open a GitHub issue if you think functionality is missing.") - public var proto: Google_Protobuf_DescriptorProto { - _proto - } - - /// The type of this Message. - public enum WellKnownType: String { - /// An instance of google.protobuf.DoubleValue. - case doubleValue = "google.protobuf.DoubleValue" - /// An instance of google.protobuf.FloatValue. - case floatValue = "google.protobuf.FloatValue" - /// An instance of google.protobuf.Int64Value. - case int64Value = "google.protobuf.Int64Value" - /// An instance of google.protobuf.UInt64Value. - case uint64Value = "google.protobuf.UInt64Value" - /// An instance of google.protobuf.Int32Value. - case int32Value = "google.protobuf.Int32Value" - /// An instance of google.protobuf.UInt32Value. - case uint32Value = "google.protobuf.UInt32Value" - /// An instance of google.protobuf.StringValue. - case stringValue = "google.protobuf.StringValue" - /// An instance of google.protobuf.BytesValue. - case bytesValue = "google.protobuf.BytesValue" - /// An instance of google.protobuf.BoolValue. - case boolValue = "google.protobuf.BoolValue" - - /// An instance of google.protobuf.Any. - case any = "google.protobuf.Any" - /// An instance of google.protobuf.FieldMask. - case fieldMask = "google.protobuf.FieldMask" - /// An instance of google.protobuf.Duration. - case duration = "google.protobuf.Duration" - /// An instance of google.protobuf.Timestamp. - case timestamp = "google.protobuf.Timestamp" - /// An instance of google.protobuf.Value. - case value = "google.protobuf.Value" - /// An instance of google.protobuf.ListValue. - case listValue = "google.protobuf.ListValue" - /// An instance of google.protobuf.Struct. - case `struct` = "google.protobuf.Struct" - } - - /// Describes an extension range of a message. `ExtensionRange`s are not - /// directly created, instead they are constructed/fetched via the - /// `Descriptor`. - public final class ExtensionRange { - /// The start field number of this range (inclusive). - public let start: Int32 - - // The end field number of this range (exclusive). - public fileprivate(set) var end: Int32 - - // Tndex of this extension range within the message's extension range array. + // We can't assign a value directly to `proto` in the init because we get the + // deprecation warning. This private prop only exists as a workaround to avoid + // this warning and preserve backwards compatibility - it should be removed + // when removing `proto`. + private let _proto: Google_Protobuf_DescriptorProto + @available(*, deprecated, message: "Please open a GitHub issue if you think functionality is missing.") + public var proto: Google_Protobuf_DescriptorProto { + _proto + } + + /// The type of this Message. + public enum WellKnownType: String { + /// An instance of google.protobuf.DoubleValue. + case doubleValue = "google.protobuf.DoubleValue" + /// An instance of google.protobuf.FloatValue. + case floatValue = "google.protobuf.FloatValue" + /// An instance of google.protobuf.Int64Value. + case int64Value = "google.protobuf.Int64Value" + /// An instance of google.protobuf.UInt64Value. + case uint64Value = "google.protobuf.UInt64Value" + /// An instance of google.protobuf.Int32Value. + case int32Value = "google.protobuf.Int32Value" + /// An instance of google.protobuf.UInt32Value. + case uint32Value = "google.protobuf.UInt32Value" + /// An instance of google.protobuf.StringValue. + case stringValue = "google.protobuf.StringValue" + /// An instance of google.protobuf.BytesValue. + case bytesValue = "google.protobuf.BytesValue" + /// An instance of google.protobuf.BoolValue. + case boolValue = "google.protobuf.BoolValue" + + /// An instance of google.protobuf.Any. + case any = "google.protobuf.Any" + /// An instance of google.protobuf.FieldMask. + case fieldMask = "google.protobuf.FieldMask" + /// An instance of google.protobuf.Duration. + case duration = "google.protobuf.Duration" + /// An instance of google.protobuf.Timestamp. + case timestamp = "google.protobuf.Timestamp" + /// An instance of google.protobuf.Value. + case value = "google.protobuf.Value" + /// An instance of google.protobuf.ListValue. + case listValue = "google.protobuf.ListValue" + /// An instance of google.protobuf.Struct. + case `struct` = "google.protobuf.Struct" + } + + /// Describes an extension range of a message. `ExtensionRange`s are not + /// directly created, instead they are constructed/fetched via the + /// `Descriptor`. + public final class ExtensionRange { + /// The start field number of this range (inclusive). + public let start: Int32 + + // The end field number of this range (exclusive). + public fileprivate(set) var end: Int32 + + // Tndex of this extension range within the message's extension range array. + public let index: Int + + /// The resolved features for this ExtensionRange. + public let features: Google_Protobuf_FeatureSet + + /// The `Google_Protobuf_ExtensionRangeOptions` set on this ExtensionRange. + public let options: Google_Protobuf_ExtensionRangeOptions + + /// The name of the containing type, not including its scope. + public var name: String { containingType.name } + /// The fully-qualified name of the containing type, scope delimited by + /// periods. + public var fullName: String { containingType.fullName } + + /// The .proto file in which this ExtensionRange was defined. + public var file: FileDescriptor! { containingType.file } + /// The descriptor that owns with ExtensionRange. + public var containingType: Descriptor { _containingType! } + + // Storage for `containingType`, will be set by bind() + private unowned var _containingType: Descriptor? + + fileprivate init( + proto: Google_Protobuf_DescriptorProto.ExtensionRange, + index: Int, + features: Google_Protobuf_FeatureSet + ) { + self.start = proto.start + self.end = proto.end + self.index = index + self.features = features + self.options = proto.options + } + + fileprivate func bind(containingType: Descriptor, registry: Registry) { + self._containingType = containingType + } + } + + /// The name of the message type, not including its scope. + public let name: String + /// The fully-qualified name of the message type, scope delimited by + /// periods. For example, message type "Foo" which is declared in package + /// "bar" has full name "bar.Foo". If a type "Baz" is nested within + /// Foo, Baz's `fullName` is "bar.Foo.Baz". To get only the part that + /// comes after the last '.', use name(). + public let fullName: String + /// Index of this descriptor within the file or containing type's message + /// type array. public let index: Int - /// The resolved features for this ExtensionRange. - public let features: Google_Protobuf_FeatureSet + /// The .proto file in which this message type was defined. + public var file: FileDescriptor! { _file! } + /// If this Descriptor describes a nested type, this returns the type + /// in which it is nested. + public private(set) unowned var containingType: Descriptor? - /// The `Google_Protobuf_ExtensionRangeOptions` set on this ExtensionRange. - public let options: Google_Protobuf_ExtensionRangeOptions + /// The resolved features for this Descriptor. + public let features: Google_Protobuf_FeatureSet - /// The name of the containing type, not including its scope. - public var name: String { return containingType.name } - /// The fully-qualified name of the containing type, scope delimited by - /// periods. - public var fullName: String { return containingType.fullName } + /// The `Google_Protobuf_MessageOptions` set on this Message. + public let options: Google_Protobuf_MessageOptions + + // If this descriptor represents a well known type, which type it is. + public let wellKnownType: WellKnownType? + + /// The enum defintions under this message. + public let enums: [EnumDescriptor] + /// The message defintions under this message. In the C++ Descriptor this + /// is `nested_type`. + public let messages: [Descriptor] + /// The fields of this message. + public let fields: [FieldDescriptor] + /// The oneofs in this message. This can include synthetic oneofs. + public let oneofs: [OneofDescriptor] + /// Non synthetic oneofs. + /// + /// These always come first (enforced by the C++ Descriptor code). So this is always a + /// leading subset of `oneofs` (or the same if there are no synthetic entries). + public private(set) lazy var realOneofs: [OneofDescriptor] = { + // Lazy because `isSynthetic` can't be called until after `bind()`. + return self.oneofs.filter { !$0._isSynthetic } + }() + /// The extension field defintions under this message. + public let extensions: [FieldDescriptor] + + /// The extension ranges declared for this message. They are returned in + /// the order they are defined in the .proto file. + public let messageExtensionRanges: [ExtensionRange] + + /// The extension ranges declared for this message. They are returned in + /// the order they are defined in the .proto file. + @available(*, deprecated, message: "This property is now deprecated: please use proto.extensionRange instead.") + public var extensionRanges: [Google_Protobuf_DescriptorProto.ExtensionRange] { + proto.extensionRange + } - /// The .proto file in which this ExtensionRange was defined. - public var file: FileDescriptor! { return containingType.file } - /// The descriptor that owns with ExtensionRange. - public var containingType: Descriptor { return _containingType! } + /// The `extensionRanges` are in the order they appear in the original .proto + /// file; this orders them and then merges any ranges that are actually + /// contiguious (i.e. - [(21,30),(10,20)] -> [(10,30)]) + @available(*, deprecated, message: "Please open a GitHub issue if you think functionality is missing.") + public private(set) lazy var normalizedExtensionRanges: [Google_Protobuf_DescriptorProto.ExtensionRange] = { + var ordered = self.extensionRanges.sorted(by: { return $0.start < $1.start }) + if ordered.count > 1 { + for i in (0..<(ordered.count - 1)).reversed() { + if ordered[i].end == ordered[i + 1].start { + // This is why we need `end`'s setter to be `fileprivate` instead of + // having it be a `let`. + // We should turn it back into a let once we get rid of this prop. + ordered[i].end = ordered[i + 1].end + ordered.remove(at: i + 1) + } + } + } + return ordered + }() + + /// The `extensionRanges` from `normalizedExtensionRanges`, but takes a step + /// further in that any ranges that do _not_ have any fields inbetween them + /// are also merged together. These can then be used in context where it is + /// ok to include field numbers that have to be extension or unknown fields. + @available(*, deprecated, message: "Please open a GitHub issue if you think functionality is missing.") + public private(set) lazy var ambitiousExtensionRanges: [Google_Protobuf_DescriptorProto.ExtensionRange] = { + var merged = self.normalizedExtensionRanges + var sortedFields = self.fields.sorted { $0.number < $1.number } + if merged.count > 1 { + var fieldNumbersReversedIterator = + self.fields.map({ Int($0.number) }).sorted(by: { $0 > $1 }).makeIterator() + var nextFieldNumber = fieldNumbersReversedIterator.next() + while nextFieldNumber != nil && merged.last!.start < nextFieldNumber! { + nextFieldNumber = fieldNumbersReversedIterator.next() + } + + for i in (0..<(merged.count - 1)).reversed() { + if nextFieldNumber == nil || merged[i].start > nextFieldNumber! { + // No fields left or range starts after the next field, merge it with + // the previous one. + merged[i].end = merged[i + 1].end + merged.remove(at: i + 1) + } else { + // can't merge, find the next field number below this range. + while nextFieldNumber != nil && merged[i].start < nextFieldNumber! { + nextFieldNumber = fieldNumbersReversedIterator.next() + } + } + } + } + return merged + }() + + /// The reserved field number ranges for this message. These are returned + /// in the order they are defined in the .proto file. + public let reservedRanges: [Range] + /// The reserved field names for this message. These are returned in the + /// order they are defined in the .proto file. + public let reservedNames: [String] + + /// True/False if this Message is just for a `map<>` entry. + @available(*, deprecated, renamed: "options.mapEntry") + public var isMapEntry: Bool { options.mapEntry } + + /// Returns the `FieldDescriptor`s for the "key" and "value" fields. If + /// this isn't a map entry field, returns nil. + /// + /// This is like the C++ Descriptor `map_key()` and `map_value()` methods. + public var mapKeyAndValue: (key: FieldDescriptor, value: FieldDescriptor)? { + guard options.mapEntry else { return nil } + precondition(fields.count == 2) + return (key: fields[0], value: fields[1]) + } - // Storage for `containingType`, will be set by bind() - private unowned var _containingType: Descriptor? + // Storage for `file`, will be set by bind() + private unowned var _file: FileDescriptor? + + @available(*, deprecated, renamed: "options.messageSetWireFormat") + public var useMessageSetWireFormat: Bool { options.messageSetWireFormat } + + fileprivate init( + proto: Google_Protobuf_DescriptorProto, + index: Int, + parentFeatures: Google_Protobuf_FeatureSet, + featureResolver: FeatureResolver, + registry: Registry, + scope: String + ) { + self._proto = proto + self.name = proto.name + let fullName = scope.isEmpty ? proto.name : "\(scope).\(proto.name)" + self.fullName = fullName + self.index = index + let resolvedFeatures = featureResolver.resolve(proto.options, resolvedParent: parentFeatures) + self.features = resolvedFeatures + self.options = proto.options + self.wellKnownType = WellKnownType(rawValue: fullName) + self.reservedRanges = proto.reservedRange.map { $0.start..<$0.end } + self.reservedNames = proto.reservedName + + // TODO: This can skip the synthetic oneofs as no features can be set on + // them to inherrit things. + let oneofFeatures = proto.oneofDecl.map { + return featureResolver.resolve($0.options, resolvedParent: resolvedFeatures) + } - fileprivate init(proto: Google_Protobuf_DescriptorProto.ExtensionRange, - index: Int, - features: Google_Protobuf_FeatureSet) { - self.start = proto.start - self.end = proto.end - self.index = index - self.features = features - self.options = proto.options - } - - fileprivate func bind(containingType: Descriptor, registry: Registry) { - self._containingType = containingType - } - } - - /// The name of the message type, not including its scope. - public let name: String - /// The fully-qualified name of the message type, scope delimited by - /// periods. For example, message type "Foo" which is declared in package - /// "bar" has full name "bar.Foo". If a type "Baz" is nested within - /// Foo, Baz's `fullName` is "bar.Foo.Baz". To get only the part that - /// comes after the last '.', use name(). - public let fullName: String - /// Index of this descriptor within the file or containing type's message - /// type array. - public let index: Int - - /// The .proto file in which this message type was defined. - public var file: FileDescriptor! { return _file! } - /// If this Descriptor describes a nested type, this returns the type - /// in which it is nested. - public private(set) unowned var containingType: Descriptor? - - /// The resolved features for this Descriptor. - public let features: Google_Protobuf_FeatureSet - - /// The `Google_Protobuf_MessageOptions` set on this Message. - public let options: Google_Protobuf_MessageOptions - - // If this descriptor represents a well known type, which type it is. - public let wellKnownType: WellKnownType? - - /// The enum defintions under this message. - public let enums: [EnumDescriptor] - /// The message defintions under this message. In the C++ Descriptor this - /// is `nested_type`. - public let messages: [Descriptor] - /// The fields of this message. - public let fields: [FieldDescriptor] - /// The oneofs in this message. This can include synthetic oneofs. - public let oneofs: [OneofDescriptor] - /// Non synthetic oneofs. - /// - /// These always come first (enforced by the C++ Descriptor code). So this is always a - /// leading subset of `oneofs` (or the same if there are no synthetic entries). - public private(set) lazy var realOneofs: [OneofDescriptor] = { - // Lazy because `isSynthetic` can't be called until after `bind()`. - return self.oneofs.filter { !$0._isSynthetic } - }() - /// The extension field defintions under this message. - public let extensions: [FieldDescriptor] - - /// The extension ranges declared for this message. They are returned in - /// the order they are defined in the .proto file. - public let messageExtensionRanges: [ExtensionRange] - - /// The extension ranges declared for this message. They are returned in - /// the order they are defined in the .proto file. - @available(*, deprecated, message: "This property is now deprecated: please use proto.extensionRange instead.") - public var extensionRanges: [Google_Protobuf_DescriptorProto.ExtensionRange] { - proto.extensionRange - } - - /// The `extensionRanges` are in the order they appear in the original .proto - /// file; this orders them and then merges any ranges that are actually - /// contiguious (i.e. - [(21,30),(10,20)] -> [(10,30)]) - @available(*, deprecated, message: "Please open a GitHub issue if you think functionality is missing.") - public private(set) lazy var normalizedExtensionRanges: [Google_Protobuf_DescriptorProto.ExtensionRange] = { - var ordered = self.extensionRanges.sorted(by: { return $0.start < $1.start }) - if ordered.count > 1 { - for i in (0..<(ordered.count - 1)).reversed() { - if ordered[i].end == ordered[i+1].start { - // This is why we need `end`'s setter to be `fileprivate` instead of - // having it be a `let`. - // We should turn it back into a let once we get rid of this prop. - ordered[i].end = ordered[i+1].end - ordered.remove(at: i + 1) + self.messageExtensionRanges = proto.extensionRange.enumerated().map { + ExtensionRange( + proto: $0.element, + index: $0.offset, + features: featureResolver.resolve( + $0.element.options, + resolvedParent: resolvedFeatures + ) + ) } - } - } - return ordered - }() - - /// The `extensionRanges` from `normalizedExtensionRanges`, but takes a step - /// further in that any ranges that do _not_ have any fields inbetween them - /// are also merged together. These can then be used in context where it is - /// ok to include field numbers that have to be extension or unknown fields. - @available(*, deprecated, message: "Please open a GitHub issue if you think functionality is missing.") - public private(set) lazy var ambitiousExtensionRanges: [Google_Protobuf_DescriptorProto.ExtensionRange] = { - var merged = self.normalizedExtensionRanges - var sortedFields = self.fields.sorted {$0.number < $1.number} - if merged.count > 1 { - var fieldNumbersReversedIterator = - self.fields.map({ Int($0.number) }).sorted(by: { $0 > $1 }).makeIterator() - var nextFieldNumber = fieldNumbersReversedIterator.next() - while nextFieldNumber != nil && merged.last!.start < nextFieldNumber! { - nextFieldNumber = fieldNumbersReversedIterator.next() - } - - for i in (0..<(merged.count - 1)).reversed() { - if nextFieldNumber == nil || merged[i].start > nextFieldNumber! { - // No fields left or range starts after the next field, merge it with - // the previous one. - merged[i].end = merged[i+1].end - merged.remove(at: i + 1) - } else { - // can't merge, find the next field number below this range. - while nextFieldNumber != nil && merged[i].start < nextFieldNumber! { - nextFieldNumber = fieldNumbersReversedIterator.next() - } + self.enums = proto.enumType.enumerated().map { + EnumDescriptor( + proto: $0.element, + index: $0.offset, + parentFeatures: resolvedFeatures, + featureResolver: featureResolver, + registry: registry, + scope: fullName + ) + } + self.messages = proto.nestedType.enumerated().map { + Descriptor( + proto: $0.element, + index: $0.offset, + parentFeatures: resolvedFeatures, + featureResolver: featureResolver, + registry: registry, + scope: fullName + ) + } + self.fields = proto.field.enumerated().map { + // For field Features inherrit from the parent oneof or message. A + // synthetic oneof (for proto3 optional) can't get features, so those + // don't come into play. + let inRealOneof = $0.element.hasOneofIndex && !$0.element.proto3Optional + return FieldDescriptor( + messageField: $0.element, + index: $0.offset, + parentFeatures: inRealOneof ? oneofFeatures[Int($0.element.oneofIndex)] : resolvedFeatures, + featureResolver: featureResolver, + registry: registry + ) } - } - } - return merged - }() - - /// The reserved field number ranges for this message. These are returned - /// in the order they are defined in the .proto file. - public let reservedRanges: [Range] - /// The reserved field names for this message. These are returned in the - /// order they are defined in the .proto file. - public let reservedNames: [String] - - /// True/False if this Message is just for a `map<>` entry. - @available(*, deprecated, renamed: "options.mapEntry") - public var isMapEntry: Bool { return options.mapEntry } - - /// Returns the `FieldDescriptor`s for the "key" and "value" fields. If - /// this isn't a map entry field, returns nil. - /// - /// This is like the C++ Descriptor `map_key()` and `map_value()` methods. - public var mapKeyAndValue: (key: FieldDescriptor, value: FieldDescriptor)? { - guard options.mapEntry else { return nil } - precondition(fields.count == 2) - return (key: fields[0], value: fields[1]) - } - - // Storage for `file`, will be set by bind() - private unowned var _file: FileDescriptor? - - @available(*, deprecated, renamed: "options.messageSetWireFormat") - public var useMessageSetWireFormat: Bool { return options.messageSetWireFormat } - - fileprivate init(proto: Google_Protobuf_DescriptorProto, - index: Int, - parentFeatures: Google_Protobuf_FeatureSet, - featureResolver: FeatureResolver, - registry: Registry, - scope: String) { - self._proto = proto - self.name = proto.name - let fullName = scope.isEmpty ? proto.name : "\(scope).\(proto.name)" - self.fullName = fullName - self.index = index - let resolvedFeatures = featureResolver.resolve(proto.options, resolvedParent: parentFeatures) - self.features = resolvedFeatures - self.options = proto.options - self.wellKnownType = WellKnownType(rawValue: fullName) - self.reservedRanges = proto.reservedRange.map { return $0.start ..< $0.end } - self.reservedNames = proto.reservedName - - // TODO: This can skip the synthetic oneofs as no features can be set on - // them to inherrit things. - let oneofFeatures = proto.oneofDecl.map { - return featureResolver.resolve($0.options, resolvedParent: resolvedFeatures) - } - - self.messageExtensionRanges = proto.extensionRange.enumerated().map { - return ExtensionRange(proto: $0.element, - index: $0.offset, - features: featureResolver.resolve($0.element.options, - resolvedParent: resolvedFeatures)) - } - self.enums = proto.enumType.enumerated().map { - return EnumDescriptor(proto: $0.element, - index: $0.offset, - parentFeatures: resolvedFeatures, - featureResolver: featureResolver, - registry: registry, - scope: fullName) - } - self.messages = proto.nestedType.enumerated().map { - return Descriptor(proto: $0.element, - index: $0.offset, - parentFeatures: resolvedFeatures, - featureResolver: featureResolver, - registry: registry, - scope: fullName) - } - self.fields = proto.field.enumerated().map { - // For field Features inherrit from the parent oneof or message. A - // synthetic oneof (for proto3 optional) can't get features, so those - // don't come into play. - let inRealOneof = $0.element.hasOneofIndex && !$0.element.proto3Optional - return FieldDescriptor(messageField: $0.element, - index: $0.offset, - parentFeatures: inRealOneof ? oneofFeatures[Int($0.element.oneofIndex)] : resolvedFeatures, - featureResolver: featureResolver, - registry: registry) - } - self.oneofs = proto.oneofDecl.enumerated().map { - return OneofDescriptor(proto: $0.element, - index: $0.offset, - features: oneofFeatures[$0.offset], - registry: registry) - } - self.extensions = proto.extension.enumerated().map { - return FieldDescriptor(extension: $0.element, - index: $0.offset, - parentFeatures: resolvedFeatures, - featureResolver: featureResolver, - registry: registry) - } - - // Done initializing, register ourselves. - registry.register(message: self) - } - - fileprivate func bind(file: FileDescriptor, registry: Registry, containingType: Descriptor?) { - _file = file - self.containingType = containingType - messageExtensionRanges.forEach { $0.bind(containingType: self, registry: registry) } - enums.forEach { $0.bind(file: file, registry: registry, containingType: self) } - messages.forEach { $0.bind(file: file, registry: registry, containingType: self) } - fields.forEach { $0.bind(file: file, registry: registry, containingType: self) } - oneofs.forEach { $0.bind(registry: registry, containingType: self) } - extensions.forEach { $0.bind(file: file, registry: registry, containingType: self) } - - // Synthetic oneofs come after normal oneofs. The C++ Descriptor enforces this, only - // here as a secondary validation because other code can rely on it. - var seenSynthetic = false - for o in oneofs { - if seenSynthetic { - // Once we've seen one synthetic, all the rest must also be synthetic. - precondition(o._isSynthetic) - } else { - seenSynthetic = o._isSynthetic - } - } - } + self.oneofs = proto.oneofDecl.enumerated().map { + OneofDescriptor( + proto: $0.element, + index: $0.offset, + features: oneofFeatures[$0.offset], + registry: registry + ) + } + self.extensions = proto.extension.enumerated().map { + FieldDescriptor( + extension: $0.element, + index: $0.offset, + parentFeatures: resolvedFeatures, + featureResolver: featureResolver, + registry: registry + ) + } + + // Done initializing, register ourselves. + registry.register(message: self) + } + + fileprivate func bind(file: FileDescriptor, registry: Registry, containingType: Descriptor?) { + _file = file + self.containingType = containingType + messageExtensionRanges.forEach { $0.bind(containingType: self, registry: registry) } + enums.forEach { $0.bind(file: file, registry: registry, containingType: self) } + messages.forEach { $0.bind(file: file, registry: registry, containingType: self) } + fields.forEach { $0.bind(file: file, registry: registry, containingType: self) } + oneofs.forEach { $0.bind(registry: registry, containingType: self) } + extensions.forEach { $0.bind(file: file, registry: registry, containingType: self) } + + // Synthetic oneofs come after normal oneofs. The C++ Descriptor enforces this, only + // here as a secondary validation because other code can rely on it. + var seenSynthetic = false + for o in oneofs { + if seenSynthetic { + // Once we've seen one synthetic, all the rest must also be synthetic. + precondition(o._isSynthetic) + } else { + seenSynthetic = o._isSynthetic + } + } + } } /// Describes a type of protocol enum. `EnumDescriptor`s are not directly @@ -747,241 +785,251 @@ public final class Descriptor { /// they are directly accessed via a `EnumType` property on `FieldDescriptor`s, /// etc. public final class EnumDescriptor { - // We can't assign a value directly to `proto` in the init because we get the - // deprecation warning. This private prop only exists as a workaround to avoid - // this warning and preserve backwards compatibility - it should be removed - // when removing `proto`. - private let _proto: Google_Protobuf_EnumDescriptorProto - @available(*, deprecated, message: "Please open a GitHub issue if you think functionality is missing.") - public var proto: Google_Protobuf_EnumDescriptorProto { - _proto - } - - /// The name of this enum type in the containing scope. - public let name: String - /// The fully-qualified name of the enum type, scope delimited by periods. - public let fullName: String - /// Index of this enum within the file or containing message's enums. - public let index: Int - - /// The .proto file in which this message type was defined. - public var file: FileDescriptor! { return _file! } - /// If this Descriptor describes a nested type, this returns the type - /// in which it is nested. - public private(set) unowned var containingType: Descriptor? - - /// The resolved features for this Enum. - public let features: Google_Protobuf_FeatureSet - - /// The values defined for this enum. Guaranteed (by protoc) to be atleast - /// one item. These are returned in the order they were defined in the .proto - /// file. - public let values: [EnumValueDescriptor] - - /// The `Google_Protobuf_MessageOptions` set on this enum. - public let options: Google_Protobuf_EnumOptions - - /// The reserved value ranges for this enum. These are returned in the order - /// they are defined in the .proto file. - public let reservedRanges: [ClosedRange] - /// The reserved value names for this enum. These are returned in the order - /// they are defined in the .proto file. - public let reservedNames: [String] - - /// Returns true whether this is a "closed" enum, meaning that it: - /// - Has a fixed set of named values. - /// - Encountering values not in this set causes them to be treated as unknown - /// fields. - /// - The first value (i.e., the default) may be nonzero. - public var isClosed: Bool { - // Implementation comes from C++ EnumDescriptor::is_closed(). - return features.enumType == .closed - } - - // Storage for `file`, will be set by bind() - private unowned var _file: FileDescriptor? - - @available(*, deprecated, message: "Please open a GitHub issue if you think functionality is missing.") - public var defaultValue: EnumValueDescriptor { - // The compiler requires the be atleast one value, so force unwrap is safe. - return values.first! - } - - fileprivate init(proto: Google_Protobuf_EnumDescriptorProto, - index: Int, - parentFeatures: Google_Protobuf_FeatureSet, - featureResolver: FeatureResolver, - registry: Registry, - scope: String) { - self._proto = proto - self.name = proto.name - self.fullName = scope.isEmpty ? proto.name : "\(scope).\(proto.name)" - self.index = index - let resolvedFeatures = featureResolver.resolve(proto.options, resolvedParent: parentFeatures) - self.features = resolvedFeatures - self.options = proto.options - self.reservedRanges = proto.reservedRange.map { return $0.start ... $0.end } - self.reservedNames = proto.reservedName - - self.values = proto.value.enumerated().map { - return EnumValueDescriptor(proto: $0.element, - index: $0.offset, - features: featureResolver.resolve($0.element.options, - resolvedParent: resolvedFeatures), - scope: scope) - } - - // Done initializing, register ourselves. - registry.register(enum: self) - - values.forEach { $0.bind(enumType: self) } - } - - fileprivate func bind(file: FileDescriptor, registry: Registry, containingType: Descriptor?) { - _file = file - self.containingType = containingType - } + // We can't assign a value directly to `proto` in the init because we get the + // deprecation warning. This private prop only exists as a workaround to avoid + // this warning and preserve backwards compatibility - it should be removed + // when removing `proto`. + private let _proto: Google_Protobuf_EnumDescriptorProto + @available(*, deprecated, message: "Please open a GitHub issue if you think functionality is missing.") + public var proto: Google_Protobuf_EnumDescriptorProto { + _proto + } + + /// The name of this enum type in the containing scope. + public let name: String + /// The fully-qualified name of the enum type, scope delimited by periods. + public let fullName: String + /// Index of this enum within the file or containing message's enums. + public let index: Int + + /// The .proto file in which this message type was defined. + public var file: FileDescriptor! { _file! } + /// If this Descriptor describes a nested type, this returns the type + /// in which it is nested. + public private(set) unowned var containingType: Descriptor? + + /// The resolved features for this Enum. + public let features: Google_Protobuf_FeatureSet + + /// The values defined for this enum. Guaranteed (by protoc) to be atleast + /// one item. These are returned in the order they were defined in the .proto + /// file. + public let values: [EnumValueDescriptor] + + /// The `Google_Protobuf_MessageOptions` set on this enum. + public let options: Google_Protobuf_EnumOptions + + /// The reserved value ranges for this enum. These are returned in the order + /// they are defined in the .proto file. + public let reservedRanges: [ClosedRange] + /// The reserved value names for this enum. These are returned in the order + /// they are defined in the .proto file. + public let reservedNames: [String] + + /// Returns true whether this is a "closed" enum, meaning that it: + /// - Has a fixed set of named values. + /// - Encountering values not in this set causes them to be treated as unknown + /// fields. + /// - The first value (i.e., the default) may be nonzero. + public var isClosed: Bool { + // Implementation comes from C++ EnumDescriptor::is_closed(). + features.enumType == .closed + } + + // Storage for `file`, will be set by bind() + private unowned var _file: FileDescriptor? + + @available(*, deprecated, message: "Please open a GitHub issue if you think functionality is missing.") + public var defaultValue: EnumValueDescriptor { + // The compiler requires the be atleast one value, so force unwrap is safe. + values.first! + } + + fileprivate init( + proto: Google_Protobuf_EnumDescriptorProto, + index: Int, + parentFeatures: Google_Protobuf_FeatureSet, + featureResolver: FeatureResolver, + registry: Registry, + scope: String + ) { + self._proto = proto + self.name = proto.name + self.fullName = scope.isEmpty ? proto.name : "\(scope).\(proto.name)" + self.index = index + let resolvedFeatures = featureResolver.resolve(proto.options, resolvedParent: parentFeatures) + self.features = resolvedFeatures + self.options = proto.options + self.reservedRanges = proto.reservedRange.map { $0.start...$0.end } + self.reservedNames = proto.reservedName + + self.values = proto.value.enumerated().map { + EnumValueDescriptor( + proto: $0.element, + index: $0.offset, + features: featureResolver.resolve( + $0.element.options, + resolvedParent: resolvedFeatures + ), + scope: scope + ) + } + + // Done initializing, register ourselves. + registry.register(enum: self) + + values.forEach { $0.bind(enumType: self) } + } + + fileprivate func bind(file: FileDescriptor, registry: Registry, containingType: Descriptor?) { + _file = file + self.containingType = containingType + } } /// Describes an individual enum constant of a particular type. To get the /// `EnumValueDescriptor` for a given enum value, first get the `EnumDescriptor` /// for its type. public final class EnumValueDescriptor { - // We can't assign a value directly to `proto` in the init because we get the - // deprecation warning. This private prop only exists as a workaround to avoid - // this warning and preserve backwards compatibility - it should be removed - // when removing `proto`. - private let _proto: Google_Protobuf_EnumValueDescriptorProto - @available(*, deprecated, message: "Please open a GitHub issue if you think functionality is missing.") - public var proto: Google_Protobuf_EnumValueDescriptorProto { - _proto - } - - /// Name of this enum constant. - public let name: String - - private var _fullName: String - /// The full_name of an enum value is a sibling symbol of the enum type. - /// e.g. the full name of FieldDescriptorProto::TYPE_INT32 is actually - /// "google.protobuf.FieldDescriptorProto.TYPE_INT32", NOT - /// "google.protobuf.FieldDescriptorProto.Type.TYPE_INT32". This is to conform - /// with C++ scoping rules for enums. - public var fullName: String { - get { - self._fullName - } - - @available(*, deprecated, message: "fullName is now read-only") - set { - self._fullName = newValue - } - } - /// Index within the enums's `EnumDescriptor`. - public let index: Int - /// Numeric value of this enum constant. - public let number: Int32 - - @available(*, deprecated, message: "Please open a GitHub issue if you think functionality is missing.") - public private(set) weak var aliasOf: EnumValueDescriptor? - @available(*, deprecated, message: "Please open a GitHub issue if you think functionality is missing.") - public fileprivate(set) var aliases: [EnumValueDescriptor] = [] - - /// The resolved features for this EnumValue. - public let features: Google_Protobuf_FeatureSet - - /// The .proto file in which this message type was defined. - public var file: FileDescriptor! { return enumType.file } - /// The type of this value. - public var enumType: EnumDescriptor! { return _enumType! } - - /// The `Google_Protobuf_EnumValueOptions` set on this value. - public let options: Google_Protobuf_EnumValueOptions - - // Storage for `enumType`, will be set by bind() - private unowned var _enumType: EnumDescriptor? - - fileprivate init(proto: Google_Protobuf_EnumValueDescriptorProto, - index: Int, - features: Google_Protobuf_FeatureSet, - scope: String) { - self._proto = proto - self.name = proto.name - self._fullName = scope.isEmpty ? proto.name : "\(scope).\(proto.name)" - self.index = index - self.features = features - self.number = proto.number - self.options = proto.options - } - - fileprivate func bind(enumType: EnumDescriptor) { - self._enumType = enumType - } + // We can't assign a value directly to `proto` in the init because we get the + // deprecation warning. This private prop only exists as a workaround to avoid + // this warning and preserve backwards compatibility - it should be removed + // when removing `proto`. + private let _proto: Google_Protobuf_EnumValueDescriptorProto + @available(*, deprecated, message: "Please open a GitHub issue if you think functionality is missing.") + public var proto: Google_Protobuf_EnumValueDescriptorProto { + _proto + } + + /// Name of this enum constant. + public let name: String + + private var _fullName: String + /// The full_name of an enum value is a sibling symbol of the enum type. + /// e.g. the full name of FieldDescriptorProto::TYPE_INT32 is actually + /// "google.protobuf.FieldDescriptorProto.TYPE_INT32", NOT + /// "google.protobuf.FieldDescriptorProto.Type.TYPE_INT32". This is to conform + /// with C++ scoping rules for enums. + public var fullName: String { + get { + self._fullName + } + + @available(*, deprecated, message: "fullName is now read-only") + set { + self._fullName = newValue + } + } + /// Index within the enums's `EnumDescriptor`. + public let index: Int + /// Numeric value of this enum constant. + public let number: Int32 + + @available(*, deprecated, message: "Please open a GitHub issue if you think functionality is missing.") + public private(set) weak var aliasOf: EnumValueDescriptor? + @available(*, deprecated, message: "Please open a GitHub issue if you think functionality is missing.") + public fileprivate(set) var aliases: [EnumValueDescriptor] = [] + + /// The resolved features for this EnumValue. + public let features: Google_Protobuf_FeatureSet + + /// The .proto file in which this message type was defined. + public var file: FileDescriptor! { enumType.file } + /// The type of this value. + public var enumType: EnumDescriptor! { _enumType! } + + /// The `Google_Protobuf_EnumValueOptions` set on this value. + public let options: Google_Protobuf_EnumValueOptions + + // Storage for `enumType`, will be set by bind() + private unowned var _enumType: EnumDescriptor? + + fileprivate init( + proto: Google_Protobuf_EnumValueDescriptorProto, + index: Int, + features: Google_Protobuf_FeatureSet, + scope: String + ) { + self._proto = proto + self.name = proto.name + self._fullName = scope.isEmpty ? proto.name : "\(scope).\(proto.name)" + self.index = index + self.features = features + self.number = proto.number + self.options = proto.options + } + + fileprivate func bind(enumType: EnumDescriptor) { + self._enumType = enumType + } } /// Describes a oneof defined in a message type. public final class OneofDescriptor { - // We can't assign a value directly to `proto` in the init because we get the - // deprecation warning. This private prop only exists as a workaround to avoid - // this warning and preserve backwards compatibility - it should be removed - // when removing `proto`. - private let _proto: Google_Protobuf_OneofDescriptorProto - @available(*, deprecated, message: "Please open a GitHub issue if you think functionality is missing.") - public var proto: Google_Protobuf_OneofDescriptorProto { - _proto - } - - /// Name of this oneof. - public let name: String - /// Fully-qualified name of the oneof. - public var fullName: String { return "\(containingType.fullName).\(name)" } - /// Index of this oneof within the message's oneofs. - public let index: Int - - /// The resolved features for this Oneof. - public let features: Google_Protobuf_FeatureSet - - /// Returns whether this oneof was inserted by the compiler to wrap a proto3 - /// optional field. If this returns true, code generators should *not* emit it. - var _isSynthetic: Bool { - return fields.count == 1 && fields.first!.proto3Optional - } - @available(*, deprecated, message: "Please open a GitHub issue if you think functionality is missing.") - public var isSynthetic: Bool { - _isSynthetic - } - - /// The .proto file in which this oneof type was defined. - public var file: FileDescriptor! { return containingType.file } - /// The Descriptor of the message that defines this oneof. - public var containingType: Descriptor! { return _containingType! } - - /// The `Google_Protobuf_OneofOptions` set on this oneof. - public let options: Google_Protobuf_OneofOptions - - /// The members of this oneof, in the order in which they were declared in the - /// .proto file. - public private(set) lazy var fields: [FieldDescriptor] = { - let myIndex = Int32(self.index) - return containingType.fields.filter { $0.oneofIndex == myIndex } - }() - - // Storage for `containingType`, will be set by bind() - private unowned var _containingType: Descriptor? - - fileprivate init(proto: Google_Protobuf_OneofDescriptorProto, - index: Int, - features: Google_Protobuf_FeatureSet, - registry: Registry) { - self._proto = proto - self.name = proto.name - self.index = index - self.features = features - self.options = proto.options - } - - fileprivate func bind(registry: Registry, containingType: Descriptor) { - _containingType = containingType - } + // We can't assign a value directly to `proto` in the init because we get the + // deprecation warning. This private prop only exists as a workaround to avoid + // this warning and preserve backwards compatibility - it should be removed + // when removing `proto`. + private let _proto: Google_Protobuf_OneofDescriptorProto + @available(*, deprecated, message: "Please open a GitHub issue if you think functionality is missing.") + public var proto: Google_Protobuf_OneofDescriptorProto { + _proto + } + + /// Name of this oneof. + public let name: String + /// Fully-qualified name of the oneof. + public var fullName: String { "\(containingType.fullName).\(name)" } + /// Index of this oneof within the message's oneofs. + public let index: Int + + /// The resolved features for this Oneof. + public let features: Google_Protobuf_FeatureSet + + /// Returns whether this oneof was inserted by the compiler to wrap a proto3 + /// optional field. If this returns true, code generators should *not* emit it. + var _isSynthetic: Bool { + fields.count == 1 && fields.first!.proto3Optional + } + @available(*, deprecated, message: "Please open a GitHub issue if you think functionality is missing.") + public var isSynthetic: Bool { + _isSynthetic + } + + /// The .proto file in which this oneof type was defined. + public var file: FileDescriptor! { containingType.file } + /// The Descriptor of the message that defines this oneof. + public var containingType: Descriptor! { _containingType! } + + /// The `Google_Protobuf_OneofOptions` set on this oneof. + public let options: Google_Protobuf_OneofOptions + + /// The members of this oneof, in the order in which they were declared in the + /// .proto file. + public private(set) lazy var fields: [FieldDescriptor] = { + let myIndex = Int32(self.index) + return containingType.fields.filter { $0.oneofIndex == myIndex } + }() + + // Storage for `containingType`, will be set by bind() + private unowned var _containingType: Descriptor? + + fileprivate init( + proto: Google_Protobuf_OneofDescriptorProto, + index: Int, + features: Google_Protobuf_FeatureSet, + registry: Registry + ) { + self._proto = proto + self.name = proto.name + self.index = index + self.features = features + self.options = proto.options + } + + fileprivate func bind(registry: Registry, containingType: Descriptor) { + _containingType = containingType + } } /// Describes a single field of a message. To get the descriptor for a given @@ -990,349 +1038,357 @@ public final class OneofDescriptor { /// `Descriptor` or `FileDescriptor` for its containing scope, find the /// extension. public final class FieldDescriptor { - // We can't assign a value directly to `proto` in the init because we get the - // deprecation warning. This private prop only exists as a workaround to avoid - // this warning and preserve backwards compatibility - it should be removed - // when removing `proto`. - private let _proto: Google_Protobuf_FieldDescriptorProto - @available(*, deprecated, message: "Please open a GitHub issue if you think functionality is missing.") - public var proto: Google_Protobuf_FieldDescriptorProto { - _proto - } - - /// Name of this field within the message. - public let name: String - /// Fully-qualified name of the field. - public var fullName: String { - // Since the fullName isn't needed on fields that often, compute it on demand. - guard isExtension else { - // Normal field on a message. - return "\(containingType.fullName).\(name)" - } - if let extensionScope = extensionScope { - return "\(extensionScope.fullName).\(name)" - } - let package = file.package - return package.isEmpty ? name : "\(package).\(name)" - } - /// JSON name of this field. - public let jsonName: String? - - public let features: Google_Protobuf_FeatureSet - - /// File in which this field was defined. - public var file: FileDescriptor! { return _file! } - - /// If this is an extension field. - public let isExtension: Bool - /// The field number. - public let number: Int32 - - /// Valid field numbers are positive integers up to kMaxNumber. - static let kMaxNumber: Int = (1 << 29) - 1 - - /// First field number reserved for the protocol buffer library - /// implementation. Users may not declare fields that use reserved numbers. - static let kFirstReservedNumber: Int = 19000 - /// Last field number reserved for the protocol buffer library implementation. - /// Users may not declare fields that use reserved numbers. - static let kLastReservedNumber: Int = 19999 - - /// Declared type of this field. - public private(set) var type: Google_Protobuf_FieldDescriptorProto.TypeEnum - - /// optional/required/repeated - public let label: Google_Protobuf_FieldDescriptorProto.Label - - /// Shorthand for `label` == `.required`. - /// - /// NOTE: This could also be a map as the are also repeated fields. - public var isRequired: Bool { - // Implementation comes from FieldDescriptor::is_required() - return features.fieldPresence == .legacyRequired - } - /// Shorthand for `label` == `.optional` - public var isOptional: Bool { return label == .optional } - /// Shorthand for `label` == `.repeated` - public var isRepeated: Bool { return label == .repeated } - - /// Is this field packable. - public var isPackable: Bool { - // This logic comes from the C++ FieldDescriptor::is_packable() impl. - return label == .repeated && FieldDescriptor.isPackable(type: type) - } - /// If this field is packable and packed. - public var isPacked: Bool { - // This logic comes from the C++ FieldDescriptor::is_packed() impl. - guard isPackable else { return false } - return features.repeatedFieldEncoding == .packed - } - /// True if this field is a map. - public var isMap: Bool { - // This logic comes from the C++ FieldDescriptor::is_map() impl. - return type == .message && messageType!.options.mapEntry - } - - /// Returns true if this field was syntactically written with "optional" in the - /// .proto file. Excludes singular proto3 fields that do not have a label. - var _hasOptionalKeyword: Bool { - // This logic comes from the C++ FieldDescriptor::has_optional_keyword() - // impl. - return proto3Optional || - (file.edition == .proto2 && label == .optional && oneofIndex == nil) - } - @available(*, deprecated, message: "Please open a GitHub issue if you think functionality is missing.") - public var hasOptionalKeyword: Bool { - _hasOptionalKeyword - } - - /// Returns true if this field tracks presence, ie. does the field - /// distinguish between "unset" and "present with default value." - /// This includes required, optional, and oneof fields. It excludes maps, - /// repeated fields, and singular proto3 fields without "optional". - public var hasPresence: Bool { - // This logic comes from the C++ FieldDescriptor::has_presence() impl. - guard label != .repeated else { return false } - switch type { - case .group, .message: - // Groups/messages always get field presence. - return true - default: - break - } - return isExtension || oneofIndex != nil || features.fieldPresence != .implicit - } - - /// Returns true if this is a string field and should do UTF-8 validation. - /// - /// This api is for completeness, but it likely should never be used. The - /// concept comes from the C++ FieldDescriptory::requires_utf8_validation(), - /// but doesn't make a lot of sense for Swift Protobuf because `string` fields - /// are modeled as Swift `String` objects, and thus they always have to be - /// valid UTF-8. If something were to try putting something else in the field, - /// the library won't be able to parse it. While that sounds bad, other - /// languages have similar issues with their _string_ types and thus have the - /// same issues. - public var requiresUTF8Validation: Bool { - return type == .string && features.utf8Validation == .verify - } - - /// Index of this field within the message's fields, or the file or - /// extension scope's extensions. - public let index: Int - - /// The explicitly declared default value for this field. - /// - /// This is the *raw* string value from the .proto file that was listed as - /// the default, it is up to the consumer to convert it correctly for the - /// type of this field. The C++ FieldDescriptor does offer some apis to - /// help with that, but at this time, that is not provided here. - @available(*, deprecated, renamed: "defaultValue") - public var explicitDefaultValue: String? { - return defaultValue - } - - /// The explicitly declared default value for this field. - /// - /// This is the *raw* string value from the .proto file that was listed as - /// the default, it is up to the consumer to convert it correctly for the - /// type of this field. The C++ FieldDescriptor does offer some apis to - /// help with that, but at this time, that is not provided here. - public let defaultValue: String? - - /// The `Descriptor` of the message which this is a field of. For extensions, - /// this is the extended type. - public var containingType: Descriptor! { return _containingType! } - - /// The oneof this field is a member of. - @available(*, deprecated, renamed: "containingOneof") - public var oneof: OneofDescriptor? { - return containingOneof - } - /// The oneof this field is a member of. - public var containingOneof: OneofDescriptor? { - guard let oneofIndex = oneofIndex else { return nil } - return containingType.oneofs[Int(oneofIndex)] - } - - /// The non synthetic oneof this field is a member of. - @available(*, deprecated, renamed: "realContainingOneof") - public var realOneof: OneofDescriptor? { - return realContainingOneof - } - /// The non synthetic oneof this field is a member of. - public var realContainingOneof: OneofDescriptor? { - guard let oneof = containingOneof, !oneof._isSynthetic else { return nil } - return oneof - } - /// The index in a oneof this field is in. - public let oneofIndex: Int32? - - // This builds basically a union for the storage for `extensionScope` - // and the value to look it up with. - private enum ExtensionScopeStorage { - case extendee(String) // The value to be used for looked up during `bind()`. - case message(UnownedBox) - } - private var _extensionScopeStorage: ExtensionScopeStorage? - - /// Extensions can be declared within the scope of another message. If this - /// is an extension field, then this will be the scope it was declared in - /// nil if was declared at a global scope. - public var extensionScope: Descriptor? { - guard case .message(let boxed) = _extensionScopeStorage else { return nil } - return boxed.value - } - - // This builds basically a union for the storage for `messageType` - // and `enumType` since only one can needed at a time. - private enum FieldTypeStorage { - case typeName(String) // The value to be looked up during `bind()`. - case message(UnownedBox) - case `enum`(UnownedBox) - } - private var _fieldTypeStorage: FieldTypeStorage? - - /// When this is a message/group field, that message's `Descriptor`. - public var messageType: Descriptor! { - guard case .message(let boxed) = _fieldTypeStorage else { return nil } - return boxed.value - } - /// When this is a enum field, that enum's `EnumDescriptor`. - public var enumType: EnumDescriptor! { - guard case .enum(let boxed) = _fieldTypeStorage else { return nil } - return boxed.value - } - - /// The FieldOptions for this field. - public var options: Google_Protobuf_FieldOptions - - let proto3Optional: Bool - - // Storage for `containingType`, will be set by bind() - private unowned var _containingType: Descriptor? - // Storage for `file`, will be set by bind() - private unowned var _file: FileDescriptor? - - fileprivate convenience init(messageField proto: Google_Protobuf_FieldDescriptorProto, - index: Int, - parentFeatures: Google_Protobuf_FeatureSet, - featureResolver: FeatureResolver, - registry: Registry) { - precondition(proto.extendee.isEmpty) // Only for extensions - - // On regular fields, it only makes sense to get `.proto3Optional` - // when also in a (synthetic) oneof. So...no oneof index, it better - // not be `.proto3Optional` - precondition(proto.hasOneofIndex || !proto.proto3Optional) - - self.init(proto: proto, - index: index, - parentFeatures: parentFeatures, - featureResolver: featureResolver, - registry: registry, - isExtension: false) - } - - fileprivate convenience init(extension proto: Google_Protobuf_FieldDescriptorProto, - index: Int, - parentFeatures: Google_Protobuf_FeatureSet, - featureResolver: FeatureResolver, - registry: Registry) { - precondition(!proto.extendee.isEmpty) // Required for extensions - - // FieldDescriptorProto is used for fields or extensions, generally - // .proto3Optional only makes sense on fields if it is in a oneof. But, - // it is allowed on extensions. For information on that, see - // https://github.com/protocolbuffers/protobuf/issues/8234#issuecomment-774224376 - // The C++ Descriptor code encorces the field/oneof part, but nothing - // is checked on the oneof side. - precondition(!proto.hasOneofIndex) - - self.init(proto: proto, - index: index, - parentFeatures: parentFeatures, - featureResolver: featureResolver, - registry: registry, - isExtension: true) - } - - private init(proto: Google_Protobuf_FieldDescriptorProto, - index: Int, - parentFeatures: Google_Protobuf_FeatureSet, - featureResolver: FeatureResolver, - registry: Registry, - isExtension: Bool) { - self._proto = proto - self.name = proto.name - self.index = index - self.features = featureResolver.resolve(proto, resolvedParent: parentFeatures) - self.defaultValue = proto.hasDefaultValue ? proto.defaultValue : nil - precondition(proto.hasJsonName) // protoc should always set the name - self.jsonName = proto.jsonName - self.isExtension = isExtension - self.number = proto.number - // This remapping is based follow part of what upstream - // `DescriptorBuilder::PostProcessFieldFeatures()` does. It is needed to - // help ensure basic transforms from .proto2 to .edition2023 generate the - // same code/behaviors. - if proto.type == .message && self.features.messageEncoding == .delimited { - self.type = .group - } else { - self.type = proto.type - } - // This remapping is based follow part of what upstream - // `DescriptorBuilder::PostProcessFieldFeatures()` does. If generators use - // helper instead of access `label` directly, they won't need this, but for - // consistency, remap `label` to expose the pre Editions/Features value. - if self.features.fieldPresence == .legacyRequired && proto.label == .optional { - self.label = .required - } else { - self.label = proto.label - } - self.options = proto.options - self.oneofIndex = proto.hasOneofIndex ? proto.oneofIndex : nil - self.proto3Optional = proto.proto3Optional - self._extensionScopeStorage = isExtension ? .extendee(proto.extendee) : nil - switch type { - case .group, .message, .enum: - _fieldTypeStorage = .typeName(proto.typeName) - default: - _fieldTypeStorage = nil - } - } - - fileprivate func bind(file: FileDescriptor, registry: Registry, containingType: Descriptor?) { - _file = file - - // See the defintions of `containingType` and `extensionScope`, this - // dance can otherwise be a little confusing. - if case .extendee(let extendee) = _extensionScopeStorage { - _containingType = registry.descriptor(named: extendee)! - if let containingType = containingType { - _extensionScopeStorage = .message(UnownedBox(value: containingType)) - } else { - _extensionScopeStorage = nil // Top level - } - } else { - _containingType = containingType - } - - if case .typeName(let typeName) = _fieldTypeStorage { - if type == .enum { - _fieldTypeStorage = .enum(UnownedBox(value: registry.enumDescriptor(named: typeName)!)) - } else { - let msgtype = registry.descriptor(named: typeName)! - _fieldTypeStorage = .message(UnownedBox(value: msgtype)) - if type == .group && ( - msgtype.options.mapEntry || - (_containingType != nil && _containingType!.options.mapEntry) - ) { - type = .message + // We can't assign a value directly to `proto` in the init because we get the + // deprecation warning. This private prop only exists as a workaround to avoid + // this warning and preserve backwards compatibility - it should be removed + // when removing `proto`. + private let _proto: Google_Protobuf_FieldDescriptorProto + @available(*, deprecated, message: "Please open a GitHub issue if you think functionality is missing.") + public var proto: Google_Protobuf_FieldDescriptorProto { + _proto + } + + /// Name of this field within the message. + public let name: String + /// Fully-qualified name of the field. + public var fullName: String { + // Since the fullName isn't needed on fields that often, compute it on demand. + guard isExtension else { + // Normal field on a message. + return "\(containingType.fullName).\(name)" + } + if let extensionScope = extensionScope { + return "\(extensionScope.fullName).\(name)" + } + let package = file.package + return package.isEmpty ? name : "\(package).\(name)" + } + /// JSON name of this field. + public let jsonName: String? + + public let features: Google_Protobuf_FeatureSet + + /// File in which this field was defined. + public var file: FileDescriptor! { _file! } + + /// If this is an extension field. + public let isExtension: Bool + /// The field number. + public let number: Int32 + + /// Valid field numbers are positive integers up to kMaxNumber. + static let kMaxNumber: Int = (1 << 29) - 1 + + /// First field number reserved for the protocol buffer library + /// implementation. Users may not declare fields that use reserved numbers. + static let kFirstReservedNumber: Int = 19000 + /// Last field number reserved for the protocol buffer library implementation. + /// Users may not declare fields that use reserved numbers. + static let kLastReservedNumber: Int = 19999 + + /// Declared type of this field. + public private(set) var type: Google_Protobuf_FieldDescriptorProto.TypeEnum + + /// optional/required/repeated + public let label: Google_Protobuf_FieldDescriptorProto.Label + + /// Shorthand for `label` == `.required`. + /// + /// NOTE: This could also be a map as the are also repeated fields. + public var isRequired: Bool { + // Implementation comes from FieldDescriptor::is_required() + features.fieldPresence == .legacyRequired + } + /// Shorthand for `label` == `.optional` + public var isOptional: Bool { label == .optional } + /// Shorthand for `label` == `.repeated` + public var isRepeated: Bool { label == .repeated } + + /// Is this field packable. + public var isPackable: Bool { + // This logic comes from the C++ FieldDescriptor::is_packable() impl. + label == .repeated && FieldDescriptor.isPackable(type: type) + } + /// If this field is packable and packed. + public var isPacked: Bool { + // This logic comes from the C++ FieldDescriptor::is_packed() impl. + guard isPackable else { return false } + return features.repeatedFieldEncoding == .packed + } + /// True if this field is a map. + public var isMap: Bool { + // This logic comes from the C++ FieldDescriptor::is_map() impl. + type == .message && messageType!.options.mapEntry + } + + /// Returns true if this field was syntactically written with "optional" in the + /// .proto file. Excludes singular proto3 fields that do not have a label. + var _hasOptionalKeyword: Bool { + // This logic comes from the C++ FieldDescriptor::has_optional_keyword() + // impl. + proto3Optional || (file.edition == .proto2 && label == .optional && oneofIndex == nil) + } + @available(*, deprecated, message: "Please open a GitHub issue if you think functionality is missing.") + public var hasOptionalKeyword: Bool { + _hasOptionalKeyword + } + + /// Returns true if this field tracks presence, ie. does the field + /// distinguish between "unset" and "present with default value." + /// This includes required, optional, and oneof fields. It excludes maps, + /// repeated fields, and singular proto3 fields without "optional". + public var hasPresence: Bool { + // This logic comes from the C++ FieldDescriptor::has_presence() impl. + guard label != .repeated else { return false } + switch type { + case .group, .message: + // Groups/messages always get field presence. + return true + default: + break + } + return isExtension || oneofIndex != nil || features.fieldPresence != .implicit + } + + /// Returns true if this is a string field and should do UTF-8 validation. + /// + /// This api is for completeness, but it likely should never be used. The + /// concept comes from the C++ FieldDescriptory::requires_utf8_validation(), + /// but doesn't make a lot of sense for Swift Protobuf because `string` fields + /// are modeled as Swift `String` objects, and thus they always have to be + /// valid UTF-8. If something were to try putting something else in the field, + /// the library won't be able to parse it. While that sounds bad, other + /// languages have similar issues with their _string_ types and thus have the + /// same issues. + public var requiresUTF8Validation: Bool { + type == .string && features.utf8Validation == .verify + } + + /// Index of this field within the message's fields, or the file or + /// extension scope's extensions. + public let index: Int + + /// The explicitly declared default value for this field. + /// + /// This is the *raw* string value from the .proto file that was listed as + /// the default, it is up to the consumer to convert it correctly for the + /// type of this field. The C++ FieldDescriptor does offer some apis to + /// help with that, but at this time, that is not provided here. + @available(*, deprecated, renamed: "defaultValue") + public var explicitDefaultValue: String? { + defaultValue + } + + /// The explicitly declared default value for this field. + /// + /// This is the *raw* string value from the .proto file that was listed as + /// the default, it is up to the consumer to convert it correctly for the + /// type of this field. The C++ FieldDescriptor does offer some apis to + /// help with that, but at this time, that is not provided here. + public let defaultValue: String? + + /// The `Descriptor` of the message which this is a field of. For extensions, + /// this is the extended type. + public var containingType: Descriptor! { _containingType! } + + /// The oneof this field is a member of. + @available(*, deprecated, renamed: "containingOneof") + public var oneof: OneofDescriptor? { + containingOneof + } + /// The oneof this field is a member of. + public var containingOneof: OneofDescriptor? { + guard let oneofIndex = oneofIndex else { return nil } + return containingType.oneofs[Int(oneofIndex)] + } + + /// The non synthetic oneof this field is a member of. + @available(*, deprecated, renamed: "realContainingOneof") + public var realOneof: OneofDescriptor? { + realContainingOneof + } + /// The non synthetic oneof this field is a member of. + public var realContainingOneof: OneofDescriptor? { + guard let oneof = containingOneof, !oneof._isSynthetic else { return nil } + return oneof + } + /// The index in a oneof this field is in. + public let oneofIndex: Int32? + + // This builds basically a union for the storage for `extensionScope` + // and the value to look it up with. + private enum ExtensionScopeStorage { + case extendee(String) // The value to be used for looked up during `bind()`. + case message(UnownedBox) + } + private var _extensionScopeStorage: ExtensionScopeStorage? + + /// Extensions can be declared within the scope of another message. If this + /// is an extension field, then this will be the scope it was declared in + /// nil if was declared at a global scope. + public var extensionScope: Descriptor? { + guard case .message(let boxed) = _extensionScopeStorage else { return nil } + return boxed.value + } + + // This builds basically a union for the storage for `messageType` + // and `enumType` since only one can needed at a time. + private enum FieldTypeStorage { + case typeName(String) // The value to be looked up during `bind()`. + case message(UnownedBox) + case `enum`(UnownedBox) + } + private var _fieldTypeStorage: FieldTypeStorage? + + /// When this is a message/group field, that message's `Descriptor`. + public var messageType: Descriptor! { + guard case .message(let boxed) = _fieldTypeStorage else { return nil } + return boxed.value + } + /// When this is a enum field, that enum's `EnumDescriptor`. + public var enumType: EnumDescriptor! { + guard case .enum(let boxed) = _fieldTypeStorage else { return nil } + return boxed.value + } + + /// The FieldOptions for this field. + public var options: Google_Protobuf_FieldOptions + + let proto3Optional: Bool + + // Storage for `containingType`, will be set by bind() + private unowned var _containingType: Descriptor? + // Storage for `file`, will be set by bind() + private unowned var _file: FileDescriptor? + + fileprivate convenience init( + messageField proto: Google_Protobuf_FieldDescriptorProto, + index: Int, + parentFeatures: Google_Protobuf_FeatureSet, + featureResolver: FeatureResolver, + registry: Registry + ) { + precondition(proto.extendee.isEmpty) // Only for extensions + + // On regular fields, it only makes sense to get `.proto3Optional` + // when also in a (synthetic) oneof. So...no oneof index, it better + // not be `.proto3Optional` + precondition(proto.hasOneofIndex || !proto.proto3Optional) + + self.init( + proto: proto, + index: index, + parentFeatures: parentFeatures, + featureResolver: featureResolver, + registry: registry, + isExtension: false + ) + } + + fileprivate convenience init( + extension proto: Google_Protobuf_FieldDescriptorProto, + index: Int, + parentFeatures: Google_Protobuf_FeatureSet, + featureResolver: FeatureResolver, + registry: Registry + ) { + precondition(!proto.extendee.isEmpty) // Required for extensions + + // FieldDescriptorProto is used for fields or extensions, generally + // .proto3Optional only makes sense on fields if it is in a oneof. But, + // it is allowed on extensions. For information on that, see + // https://github.com/protocolbuffers/protobuf/issues/8234#issuecomment-774224376 + // The C++ Descriptor code encorces the field/oneof part, but nothing + // is checked on the oneof side. + precondition(!proto.hasOneofIndex) + + self.init( + proto: proto, + index: index, + parentFeatures: parentFeatures, + featureResolver: featureResolver, + registry: registry, + isExtension: true + ) + } + + private init( + proto: Google_Protobuf_FieldDescriptorProto, + index: Int, + parentFeatures: Google_Protobuf_FeatureSet, + featureResolver: FeatureResolver, + registry: Registry, + isExtension: Bool + ) { + self._proto = proto + self.name = proto.name + self.index = index + self.features = featureResolver.resolve(proto, resolvedParent: parentFeatures) + self.defaultValue = proto.hasDefaultValue ? proto.defaultValue : nil + precondition(proto.hasJsonName) // protoc should always set the name + self.jsonName = proto.jsonName + self.isExtension = isExtension + self.number = proto.number + // This remapping is based follow part of what upstream + // `DescriptorBuilder::PostProcessFieldFeatures()` does. It is needed to + // help ensure basic transforms from .proto2 to .edition2023 generate the + // same code/behaviors. + if proto.type == .message && self.features.messageEncoding == .delimited { + self.type = .group + } else { + self.type = proto.type + } + // This remapping is based follow part of what upstream + // `DescriptorBuilder::PostProcessFieldFeatures()` does. If generators use + // helper instead of access `label` directly, they won't need this, but for + // consistency, remap `label` to expose the pre Editions/Features value. + if self.features.fieldPresence == .legacyRequired && proto.label == .optional { + self.label = .required + } else { + self.label = proto.label + } + self.options = proto.options + self.oneofIndex = proto.hasOneofIndex ? proto.oneofIndex : nil + self.proto3Optional = proto.proto3Optional + self._extensionScopeStorage = isExtension ? .extendee(proto.extendee) : nil + switch type { + case .group, .message, .enum: + _fieldTypeStorage = .typeName(proto.typeName) + default: + _fieldTypeStorage = nil + } + } + + fileprivate func bind(file: FileDescriptor, registry: Registry, containingType: Descriptor?) { + _file = file + + // See the defintions of `containingType` and `extensionScope`, this + // dance can otherwise be a little confusing. + if case .extendee(let extendee) = _extensionScopeStorage { + _containingType = registry.descriptor(named: extendee)! + if let containingType = containingType { + _extensionScopeStorage = .message(UnownedBox(value: containingType)) + } else { + _extensionScopeStorage = nil // Top level + } + } else { + _containingType = containingType + } + + if case .typeName(let typeName) = _fieldTypeStorage { + if type == .enum { + _fieldTypeStorage = .enum(UnownedBox(value: registry.enumDescriptor(named: typeName)!)) + } else { + let msgtype = registry.descriptor(named: typeName)! + _fieldTypeStorage = .message(UnownedBox(value: msgtype)) + if type == .group + && (msgtype.options.mapEntry || (_containingType != nil && _containingType!.options.mapEntry)) + { + type = .message + } + } } - } } - } } /// Describes an RPC service. @@ -1341,68 +1397,72 @@ public final class FieldDescriptor { /// they are here to support things that generate based off RPCs defined in /// .proto file (gRPC, etc.). public final class ServiceDescriptor { - // We can't assign a value directly to `proto` in the init because we get the - // deprecation warning. This private prop only exists as a workaround to avoid - // this warning and preserve backwards compatibility - it should be removed - // when removing `proto`. - private let _proto: Google_Protobuf_ServiceDescriptorProto - @available(*, deprecated, message: "Please open a GitHub issue if you think functionality is missing.") - public var proto: Google_Protobuf_ServiceDescriptorProto { - _proto - } - - /// The name of the service, not including its containing scope. - public let name: String - /// The fully-qualified name of the service, scope delimited by periods. - public let fullName: String - /// Index of this service within the file's services. - public let index: Int - - /// The .proto file in which this service was defined - public var file: FileDescriptor! { return _file! } - - /// The resolved features for this Service. - public let features: Google_Protobuf_FeatureSet - - /// Get `Google_Protobuf_ServiceOptions` for this service. - public let options: Google_Protobuf_ServiceOptions - - /// The methods defined on this service. These are returned in the order they - /// were defined in the .proto file. - public let methods: [MethodDescriptor] - - // Storage for `file`, will be set by bind() - private unowned var _file: FileDescriptor? - - fileprivate init(proto: Google_Protobuf_ServiceDescriptorProto, - index: Int, - fileFeatures: Google_Protobuf_FeatureSet, - featureResolver: FeatureResolver, - registry: Registry, - scope: String) { - self._proto = proto - self.name = proto.name - self.fullName = scope.isEmpty ? proto.name : "\(scope).\(proto.name)" - self.index = index - let resolvedFeatures = featureResolver.resolve(proto.options, resolvedParent: fileFeatures) - self.features = resolvedFeatures - self.options = proto.options - - self.methods = proto.method.enumerated().map { - return MethodDescriptor(proto: $0.element, - index: $0.offset, - features: featureResolver.resolve($0.element.options, resolvedParent: resolvedFeatures), - registry: registry) - } - - // Done initializing, register ourselves. - registry.register(service: self) - } - - fileprivate func bind(file: FileDescriptor, registry: Registry) { - _file = file - methods.forEach { $0.bind(service: self, registry: registry) } - } + // We can't assign a value directly to `proto` in the init because we get the + // deprecation warning. This private prop only exists as a workaround to avoid + // this warning and preserve backwards compatibility - it should be removed + // when removing `proto`. + private let _proto: Google_Protobuf_ServiceDescriptorProto + @available(*, deprecated, message: "Please open a GitHub issue if you think functionality is missing.") + public var proto: Google_Protobuf_ServiceDescriptorProto { + _proto + } + + /// The name of the service, not including its containing scope. + public let name: String + /// The fully-qualified name of the service, scope delimited by periods. + public let fullName: String + /// Index of this service within the file's services. + public let index: Int + + /// The .proto file in which this service was defined + public var file: FileDescriptor! { _file! } + + /// The resolved features for this Service. + public let features: Google_Protobuf_FeatureSet + + /// Get `Google_Protobuf_ServiceOptions` for this service. + public let options: Google_Protobuf_ServiceOptions + + /// The methods defined on this service. These are returned in the order they + /// were defined in the .proto file. + public let methods: [MethodDescriptor] + + // Storage for `file`, will be set by bind() + private unowned var _file: FileDescriptor? + + fileprivate init( + proto: Google_Protobuf_ServiceDescriptorProto, + index: Int, + fileFeatures: Google_Protobuf_FeatureSet, + featureResolver: FeatureResolver, + registry: Registry, + scope: String + ) { + self._proto = proto + self.name = proto.name + self.fullName = scope.isEmpty ? proto.name : "\(scope).\(proto.name)" + self.index = index + let resolvedFeatures = featureResolver.resolve(proto.options, resolvedParent: fileFeatures) + self.features = resolvedFeatures + self.options = proto.options + + self.methods = proto.method.enumerated().map { + MethodDescriptor( + proto: $0.element, + index: $0.offset, + features: featureResolver.resolve($0.element.options, resolvedParent: resolvedFeatures), + registry: registry + ) + } + + // Done initializing, register ourselves. + registry.register(service: self) + } + + fileprivate func bind(file: FileDescriptor, registry: Registry) { + _file = file + methods.forEach { $0.bind(service: self, registry: registry) } + } } /// Describes an individual service method. @@ -1411,63 +1471,68 @@ public final class ServiceDescriptor { /// they are here to support things that generate based off RPCs defined in /// .proto file (gRPC, etc.). public final class MethodDescriptor { - /// The name of the method, not including its containing scope. - public let name: String - /// The fully-qualified name of the method, scope delimited by periods. - public var fullName: String { return "\(service.fullName).\(name)" } - /// Index of this service within the file's services. - public let index: Int - - /// The .proto file in which this service was defined - public var file: FileDescriptor! { return service.file } - /// The service tha defines this method. - public var service: ServiceDescriptor! { return _service! } - - /// The resolved features for this Method. - public let features: Google_Protobuf_FeatureSet - - /// Get `Google_Protobuf_MethodOptions` for this method. - public let options: Google_Protobuf_MethodOptions - - /// The type of protocol message which this method accepts as input. - public private(set) var inputType: Descriptor! - /// The type of protocol message which this message produces as output. - public private(set) var outputType: Descriptor! - - /// Whether the client streams multiple requests. - public let clientStreaming: Bool - // Whether the server streams multiple responses. - public let serverStreaming: Bool - - /// The proto version of the descriptor that defines this method. - @available(*, deprecated, - message: "Use the properties directly or open a GitHub issue for something missing") - public var proto: Google_Protobuf_MethodDescriptorProto { return _proto } - private let _proto: Google_Protobuf_MethodDescriptorProto - - // Storage for `service`, will be set by bind() - private unowned var _service: ServiceDescriptor? - - fileprivate init(proto: Google_Protobuf_MethodDescriptorProto, - index: Int, - features: Google_Protobuf_FeatureSet, - registry: Registry) { - self.name = proto.name - self.index = index - self.features = features - self.options = proto.options - self.clientStreaming = proto.clientStreaming - self.serverStreaming = proto.serverStreaming - // Can look these up because all the Descriptors are already registered - self.inputType = registry.descriptor(named: proto.inputType)! - self.outputType = registry.descriptor(named: proto.outputType)! - - self._proto = proto - } - - fileprivate func bind(service: ServiceDescriptor, registry: Registry) { - self._service = service - } + /// The name of the method, not including its containing scope. + public let name: String + /// The fully-qualified name of the method, scope delimited by periods. + public var fullName: String { "\(service.fullName).\(name)" } + /// Index of this service within the file's services. + public let index: Int + + /// The .proto file in which this service was defined + public var file: FileDescriptor! { service.file } + /// The service tha defines this method. + public var service: ServiceDescriptor! { _service! } + + /// The resolved features for this Method. + public let features: Google_Protobuf_FeatureSet + + /// Get `Google_Protobuf_MethodOptions` for this method. + public let options: Google_Protobuf_MethodOptions + + /// The type of protocol message which this method accepts as input. + public private(set) var inputType: Descriptor! + /// The type of protocol message which this message produces as output. + public private(set) var outputType: Descriptor! + + /// Whether the client streams multiple requests. + public let clientStreaming: Bool + // Whether the server streams multiple responses. + public let serverStreaming: Bool + + /// The proto version of the descriptor that defines this method. + @available( + *, + deprecated, + message: "Use the properties directly or open a GitHub issue for something missing" + ) + public var proto: Google_Protobuf_MethodDescriptorProto { _proto } + private let _proto: Google_Protobuf_MethodDescriptorProto + + // Storage for `service`, will be set by bind() + private unowned var _service: ServiceDescriptor? + + fileprivate init( + proto: Google_Protobuf_MethodDescriptorProto, + index: Int, + features: Google_Protobuf_FeatureSet, + registry: Registry + ) { + self.name = proto.name + self.index = index + self.features = features + self.options = proto.options + self.clientStreaming = proto.clientStreaming + self.serverStreaming = proto.serverStreaming + // Can look these up because all the Descriptors are already registered + self.inputType = registry.descriptor(named: proto.inputType)! + self.outputType = registry.descriptor(named: proto.outputType)! + + self._proto = proto + } + + fileprivate func bind(service: ServiceDescriptor, registry: Registry) { + self._service = service + } } /// Helper used under the hood to build the mapping tables and look things up. @@ -1475,43 +1540,43 @@ public final class MethodDescriptor { /// All fullNames are like defined in descriptor.proto, they start with a /// leading '.'. This simplifies the string ops when assembling the message /// graph. -fileprivate final class Registry { - private var fileMap = [String:FileDescriptor]() - // These three are all keyed by the full_name prefixed with a '.'. - private var messageMap = [String:Descriptor]() - private var enumMap = [String:EnumDescriptor]() - private var serviceMap = [String:ServiceDescriptor]() - - init() {} - - func register(file: FileDescriptor) { - fileMap[file.name] = file - } - func register(message: Descriptor) { - messageMap[".\(message.fullName)"] = message - } - func register(enum e: EnumDescriptor) { - enumMap[".\(e.fullName)"] = e - } - func register(service: ServiceDescriptor) { - serviceMap[".\(service.fullName)"] = service - } - - func fileDescriptor(named name: String) -> FileDescriptor? { - return fileMap[name] - } - func descriptor(named fullName: String) -> Descriptor? { - return messageMap[fullName] - } - func enumDescriptor(named fullName: String) -> EnumDescriptor? { - return enumMap[fullName] - } - func serviceDescriptor(named fullName: String) -> ServiceDescriptor? { - return serviceMap[fullName] - } +private final class Registry { + private var fileMap = [String: FileDescriptor]() + // These three are all keyed by the full_name prefixed with a '.'. + private var messageMap = [String: Descriptor]() + private var enumMap = [String: EnumDescriptor]() + private var serviceMap = [String: ServiceDescriptor]() + + init() {} + + func register(file: FileDescriptor) { + fileMap[file.name] = file + } + func register(message: Descriptor) { + messageMap[".\(message.fullName)"] = message + } + func register(enum e: EnumDescriptor) { + enumMap[".\(e.fullName)"] = e + } + func register(service: ServiceDescriptor) { + serviceMap[".\(service.fullName)"] = service + } + + func fileDescriptor(named name: String) -> FileDescriptor? { + fileMap[name] + } + func descriptor(named fullName: String) -> Descriptor? { + messageMap[fullName] + } + func enumDescriptor(named fullName: String) -> EnumDescriptor? { + enumMap[fullName] + } + func serviceDescriptor(named fullName: String) -> ServiceDescriptor? { + serviceMap[fullName] + } } /// Helper for making an enum associated value `unowned`. -fileprivate struct UnownedBox { - unowned let value: T +private struct UnownedBox { + unowned let value: T } diff --git a/Sources/SwiftProtobufPluginLibrary/FeatureResolver.swift b/Sources/SwiftProtobufPluginLibrary/FeatureResolver.swift index bca927169..ebd4928ce 100644 --- a/Sources/SwiftProtobufPluginLibrary/FeatureResolver.swift +++ b/Sources/SwiftProtobufPluginLibrary/FeatureResolver.swift @@ -11,167 +11,178 @@ import SwiftProtobuf protocol ProvidesFeatureSets { - var features: Google_Protobuf_FeatureSet { get } - var hasFeatures: Bool { get } + var features: Google_Protobuf_FeatureSet { get } + var hasFeatures: Bool { get } } // Skip `Google_Protobuf_FileOptions`, special case of `resolve`. -extension Google_Protobuf_MessageOptions : ProvidesFeatureSets {} -extension Google_Protobuf_EnumOptions : ProvidesFeatureSets {} +extension Google_Protobuf_MessageOptions: ProvidesFeatureSets {} +extension Google_Protobuf_EnumOptions: ProvidesFeatureSets {} // Skip `Google_Protobuf_FieldOptions`, Field is special case of `resolve`. -extension Google_Protobuf_OneofOptions : ProvidesFeatureSets {} -extension Google_Protobuf_ExtensionRangeOptions : ProvidesFeatureSets {} -extension Google_Protobuf_EnumValueOptions : ProvidesFeatureSets {} -extension Google_Protobuf_ServiceOptions : ProvidesFeatureSets {} -extension Google_Protobuf_MethodOptions : ProvidesFeatureSets {} +extension Google_Protobuf_OneofOptions: ProvidesFeatureSets {} +extension Google_Protobuf_ExtensionRangeOptions: ProvidesFeatureSets {} +extension Google_Protobuf_EnumValueOptions: ProvidesFeatureSets {} +extension Google_Protobuf_ServiceOptions: ProvidesFeatureSets {} +extension Google_Protobuf_MethodOptions: ProvidesFeatureSets {} /// Encapsulates the process of Feature resolution, sorta like the upstream /// `feature_resolver.cpp`. class FeatureResolver { - enum Error: Swift.Error, Equatable, CustomStringConvertible { - case unsupported(edition: Google_Protobuf_Edition, - supported: ClosedRange) - case noDefault(edition: Google_Protobuf_Edition) - case invalidExtension(type: String) - - var description: String { - switch self { - case .unsupported(let edition, let supported): - return "Edition \(edition) is not in the supported range (\(supported))" - case .noDefault(let edition): - return "No default value found for edition \(edition)" - case .invalidExtension(let type): - return "Passed an extension that wasn't to google.protobuf.FeatureSet: \(type)" - } - } - } - - /// The requested Edition. - let edition: Google_Protobuf_Edition - /// The detaults to use for this edition. - let defaultFeatureSet: Google_Protobuf_FeatureSet - - private let extensionMap: (any ExtensionMap)? - - /// Construct a resolver for a given edition with the correct defaults. - /// - /// - Parameters: - /// - edition: The edition of defaults desired. - /// - defaults: A `Google_Protobuf_FeatureSetDefaults` created by protoc - /// from one or more proto files that define `Google_Protobuf_FeatureSet` - /// and any extensions. - /// - extensions: A list of Protobuf Extension extensions to - /// `google.protobuf.FeatureSet` that define custom features. If used, the - /// `defaults` should have been parsed with the extensions being - /// supported. - /// - Returns: A configured resolver for the given edition/defaults. - /// - Throws: `FeatureResolver.Error` if there edition requested can't be - /// supported by the given defaults. - init( - edition: Google_Protobuf_Edition, - featureSetDefaults defaults: Google_Protobuf_FeatureSetDefaults, - featureExtensions extensions: [any AnyMessageExtension] = [] - ) throws { - guard edition >= defaults.minimumEdition && - edition <= defaults.maximumEdition else { - throw Error.unsupported(edition: edition, - supported: defaults.minimumEdition...defaults.maximumEdition) + enum Error: Swift.Error, Equatable, CustomStringConvertible { + case unsupported( + edition: Google_Protobuf_Edition, + supported: ClosedRange + ) + case noDefault(edition: Google_Protobuf_Edition) + case invalidExtension(type: String) + + var description: String { + switch self { + case .unsupported(let edition, let supported): + return "Edition \(edition) is not in the supported range (\(supported))" + case .noDefault(let edition): + return "No default value found for edition \(edition)" + case .invalidExtension(let type): + return "Passed an extension that wasn't to google.protobuf.FeatureSet: \(type)" + } + } } - // When protoc generates defaults, they are ordered, so find the last one. - var found: Google_Protobuf_FeatureSetDefaults.FeatureSetEditionDefault? - for d in defaults.defaults { - guard d.edition <= edition else { break } - found = d - } + /// The requested Edition. + let edition: Google_Protobuf_Edition + /// The detaults to use for this edition. + let defaultFeatureSet: Google_Protobuf_FeatureSet + + private let extensionMap: (any ExtensionMap)? + + /// Construct a resolver for a given edition with the correct defaults. + /// + /// - Parameters: + /// - edition: The edition of defaults desired. + /// - defaults: A `Google_Protobuf_FeatureSetDefaults` created by protoc + /// from one or more proto files that define `Google_Protobuf_FeatureSet` + /// and any extensions. + /// - extensions: A list of Protobuf Extension extensions to + /// `google.protobuf.FeatureSet` that define custom features. If used, the + /// `defaults` should have been parsed with the extensions being + /// supported. + /// - Returns: A configured resolver for the given edition/defaults. + /// - Throws: `FeatureResolver.Error` if there edition requested can't be + /// supported by the given defaults. + init( + edition: Google_Protobuf_Edition, + featureSetDefaults defaults: Google_Protobuf_FeatureSetDefaults, + featureExtensions extensions: [any AnyMessageExtension] = [] + ) throws { + guard edition >= defaults.minimumEdition && edition <= defaults.maximumEdition else { + throw Error.unsupported( + edition: edition, + supported: defaults.minimumEdition...defaults.maximumEdition + ) + } - guard let found = found else { - throw Error.noDefault(edition: edition) - } - self.edition = edition - - if extensions.isEmpty { - extensionMap = nil - } else { - for e in extensions { - if e.messageType != Google_Protobuf_FeatureSet.self { - throw Error.invalidExtension(type: e.messageType.protoMessageName) + // When protoc generates defaults, they are ordered, so find the last one. + var found: Google_Protobuf_FeatureSetDefaults.FeatureSetEditionDefault? + for d in defaults.defaults { + guard d.edition <= edition else { break } + found = d } - } - var simpleMap = SimpleExtensionMap() - simpleMap.insert(contentsOf: extensions) - extensionMap = simpleMap - } - var features = found.fixedFeatures - // Don't yet have a message level merge, so bounce through serialization. - let bytes: [UInt8] = try! found.overridableFeatures.serializedBytes() - try! features.merge(serializedBytes: bytes, extensions: extensionMap) - defaultFeatureSet = features - } - - /// Resolve the Features for a File. - func resolve(_ options: Google_Protobuf_FileOptions) -> Google_Protobuf_FeatureSet { - /// There is no parent, so the default options are used. - return resolve(features: options.hasFeatures ? options.features : nil, - resolvedParent: defaultFeatureSet) - } - - /// Resolve the Features for a Field. - /// - /// This needs to the full FieldDescriptorProto incase it has to do fallback - /// inference. - func resolve(_ proto: Google_Protobuf_FieldDescriptorProto, - resolvedParent: Google_Protobuf_FeatureSet - ) -> Google_Protobuf_FeatureSet { - if edition >= .edition2023 { - return resolve(features: proto.options.hasFeatures ? proto.options.features : nil, - resolvedParent: resolvedParent) - } - // For `.proto2` and `.proto3`, some of the field behaviors have to be - // figured out as they can't be captured in the defaults and inherrited. - // See `InferLegacyProtoFeatures` in the C++ descriptor.cc implementation - // for this logic. - var features = Google_Protobuf_FeatureSet() - if proto.label == .required { - features.fieldPresence = .legacyRequired + guard let found = found else { + throw Error.noDefault(edition: edition) + } + self.edition = edition + + if extensions.isEmpty { + extensionMap = nil + } else { + for e in extensions { + if e.messageType != Google_Protobuf_FeatureSet.self { + throw Error.invalidExtension(type: e.messageType.protoMessageName) + } + } + var simpleMap = SimpleExtensionMap() + simpleMap.insert(contentsOf: extensions) + extensionMap = simpleMap + } + + var features = found.fixedFeatures + // Don't yet have a message level merge, so bounce through serialization. + let bytes: [UInt8] = try! found.overridableFeatures.serializedBytes() + try! features.merge(serializedBytes: bytes, extensions: extensionMap) + defaultFeatureSet = features } - if proto.type == .group { - features.messageEncoding = .delimited + + /// Resolve the Features for a File. + func resolve(_ options: Google_Protobuf_FileOptions) -> Google_Protobuf_FeatureSet { + /// There is no parent, so the default options are used. + resolve( + features: options.hasFeatures ? options.features : nil, + resolvedParent: defaultFeatureSet + ) } - let options = proto.options - if options.packed { - features.repeatedFieldEncoding = .packed + + /// Resolve the Features for a Field. + /// + /// This needs to the full FieldDescriptorProto incase it has to do fallback + /// inference. + func resolve( + _ proto: Google_Protobuf_FieldDescriptorProto, + resolvedParent: Google_Protobuf_FeatureSet + ) -> Google_Protobuf_FeatureSet { + if edition >= .edition2023 { + return resolve( + features: proto.options.hasFeatures ? proto.options.features : nil, + resolvedParent: resolvedParent + ) + } + // For `.proto2` and `.proto3`, some of the field behaviors have to be + // figured out as they can't be captured in the defaults and inherrited. + // See `InferLegacyProtoFeatures` in the C++ descriptor.cc implementation + // for this logic. + var features = Google_Protobuf_FeatureSet() + if proto.label == .required { + features.fieldPresence = .legacyRequired + } + if proto.type == .group { + features.messageEncoding = .delimited + } + let options = proto.options + if options.packed { + features.repeatedFieldEncoding = .packed + } + if edition == .proto3 && options.hasPacked && !options.packed { + features.repeatedFieldEncoding = .expanded + } + // Now now merge the rest of the inherrited info from the defaults. + return resolve(features: features, resolvedParent: defaultFeatureSet) } - if edition == .proto3 && options.hasPacked && !options.packed { - features.repeatedFieldEncoding = .expanded + + /// Resolve the Features for a given descriptor's options, the resolvedParent + /// values used to inherrit from. + func resolve( + _ options: T, + resolvedParent: Google_Protobuf_FeatureSet + ) -> Google_Protobuf_FeatureSet { + resolve( + features: options.hasFeatures ? options.features : nil, + resolvedParent: resolvedParent + ) } - // Now now merge the rest of the inherrited info from the defaults. - return resolve(features: features, resolvedParent: defaultFeatureSet) - } - - /// Resolve the Features for a given descriptor's options, the resolvedParent - /// values used to inherrit from. - func resolve( - _ options: T, - resolvedParent: Google_Protobuf_FeatureSet - ) -> Google_Protobuf_FeatureSet { - return resolve(features: options.hasFeatures ? options.features : nil, - resolvedParent: resolvedParent) - } - - /// Helper to do the merging. - func resolve(features: Google_Protobuf_FeatureSet?, - resolvedParent: Google_Protobuf_FeatureSet - ) -> Google_Protobuf_FeatureSet { - var result = resolvedParent - if let features = features { - // Don't yet have a message level merge, so bounce through serialization. - let bytes: [UInt8] = try! features.serializedBytes() - try! result.merge(serializedBytes: bytes, extensions: extensionMap) + + /// Helper to do the merging. + func resolve( + features: Google_Protobuf_FeatureSet?, + resolvedParent: Google_Protobuf_FeatureSet + ) -> Google_Protobuf_FeatureSet { + var result = resolvedParent + if let features = features { + // Don't yet have a message level merge, so bounce through serialization. + let bytes: [UInt8] = try! features.serializedBytes() + try! result.merge(serializedBytes: bytes, extensions: extensionMap) + } + return result } - return result - } } diff --git a/Sources/SwiftProtobufPluginLibrary/FieldNumbers.swift b/Sources/SwiftProtobufPluginLibrary/FieldNumbers.swift index f5a8d3a7d..731f888ca 100644 --- a/Sources/SwiftProtobufPluginLibrary/FieldNumbers.swift +++ b/Sources/SwiftProtobufPluginLibrary/FieldNumbers.swift @@ -16,33 +16,33 @@ import Foundation import SwiftProtobuf extension Google_Protobuf_FileDescriptorProto { - struct FieldNumbers { - static let messageType: Int = 4 - static let enumType: Int = 5 - static let service: Int = 6 - static let `extension`: Int = 7 - } + struct FieldNumbers { + static let messageType: Int = 4 + static let enumType: Int = 5 + static let service: Int = 6 + static let `extension`: Int = 7 + } } extension Google_Protobuf_DescriptorProto { - struct FieldNumbers { - static let field: Int = 2 - static let nestedType: Int = 3 - static let enumType: Int = 4 - static let extensionRange: Int = 4 - static let `extension`: Int = 6 - static let oneofDecl: Int = 8 - } + struct FieldNumbers { + static let field: Int = 2 + static let nestedType: Int = 3 + static let enumType: Int = 4 + static let extensionRange: Int = 4 + static let `extension`: Int = 6 + static let oneofDecl: Int = 8 + } } extension Google_Protobuf_EnumDescriptorProto { - struct FieldNumbers { - static let value: Int = 2 - } + struct FieldNumbers { + static let value: Int = 2 + } } extension Google_Protobuf_ServiceDescriptorProto { - struct FieldNumbers { - static let method: Int = 2 - } + struct FieldNumbers { + static let method: Int = 2 + } } diff --git a/Sources/SwiftProtobufPluginLibrary/GeneratorOutputs.swift b/Sources/SwiftProtobufPluginLibrary/GeneratorOutputs.swift index 4235d6809..cce1b04eb 100644 --- a/Sources/SwiftProtobufPluginLibrary/GeneratorOutputs.swift +++ b/Sources/SwiftProtobufPluginLibrary/GeneratorOutputs.swift @@ -16,17 +16,17 @@ import Foundation /// Abstract interface for receiving generation outputs. public protocol GeneratorOutputs { - /// Add the a file with the given `name` and `contents` to the outputs. - /// - /// - Parameters: - /// - fileName: The name of the file. - /// - contents: The body of the file. - /// - /// - Throws May throw errors for duplicate file names or any other problem. - /// Generally `CodeGenerator`s do *not* need to catch these, and instead - /// they are ripple all the way out to the code calling the - /// `CodeGenerator`. - func add(fileName: String, contents: String) throws + /// Add the a file with the given `name` and `contents` to the outputs. + /// + /// - Parameters: + /// - fileName: The name of the file. + /// - contents: The body of the file. + /// + /// - Throws May throw errors for duplicate file names or any other problem. + /// Generally `CodeGenerator`s do *not* need to catch these, and instead + /// they are ripple all the way out to the code calling the + /// `CodeGenerator`. + func add(fileName: String, contents: String) throws - // TODO: Consider adding apis to stream things like C++ protobuf does? + // TODO: Consider adding apis to stream things like C++ protobuf does? } diff --git a/Sources/SwiftProtobufPluginLibrary/Google_Protobuf_Compiler_CodeGeneratorResponse+Extensions.swift b/Sources/SwiftProtobufPluginLibrary/Google_Protobuf_Compiler_CodeGeneratorResponse+Extensions.swift index b9b048d84..949332abe 100644 --- a/Sources/SwiftProtobufPluginLibrary/Google_Protobuf_Compiler_CodeGeneratorResponse+Extensions.swift +++ b/Sources/SwiftProtobufPluginLibrary/Google_Protobuf_Compiler_CodeGeneratorResponse+Extensions.swift @@ -13,35 +13,35 @@ // ----------------------------------------------------------------------------- extension Google_Protobuf_Compiler_CodeGeneratorResponse { - /// Helper to make a response with an error. - public init(error: String) { - self.init() - self.error = error - } + /// Helper to make a response with an error. + public init(error: String) { + self.init() + self.error = error + } - /// Helper to make a response with a set of files - @available(*, deprecated, message: "Please move your plugin to the CodeGenerator interface") - public init(files: [Google_Protobuf_Compiler_CodeGeneratorResponse.File]) { - self.init(files: files, supportedFeatures: []) - } + /// Helper to make a response with a set of files + @available(*, deprecated, message: "Please move your plugin to the CodeGenerator interface") + public init(files: [Google_Protobuf_Compiler_CodeGeneratorResponse.File]) { + self.init(files: files, supportedFeatures: []) + } - /// Helper to make a response with a set of files and supported features. - @available(*, deprecated, message: "Please move your plugin to the CodeGenerator interface") - public init( - files: [Google_Protobuf_Compiler_CodeGeneratorResponse.File], - supportedFeatures: [Google_Protobuf_Compiler_CodeGeneratorResponse.Feature] = [] - ) { - self.init() - self.file = files - self.supportedFeatures = supportedFeatures.reduce(0) { $0 | UInt64($1.rawValue) } - } + /// Helper to make a response with a set of files and supported features. + @available(*, deprecated, message: "Please move your plugin to the CodeGenerator interface") + public init( + files: [Google_Protobuf_Compiler_CodeGeneratorResponse.File], + supportedFeatures: [Google_Protobuf_Compiler_CodeGeneratorResponse.Feature] = [] + ) { + self.init() + self.file = files + self.supportedFeatures = supportedFeatures.reduce(0) { $0 | UInt64($1.rawValue) } + } } extension Google_Protobuf_Compiler_CodeGeneratorResponse.File { - /// Helper to make a Response.File with specific content. - public init(name: String, content: String) { - self.init() - self.name = name - self.content = content - } + /// Helper to make a Response.File with specific content. + public init(name: String, content: String) { + self.init() + self.name = name + self.content = content + } } diff --git a/Sources/SwiftProtobufPluginLibrary/Google_Protobuf_Edition+Extensions.swift b/Sources/SwiftProtobufPluginLibrary/Google_Protobuf_Edition+Extensions.swift index 121d62b49..ec4e5194b 100644 --- a/Sources/SwiftProtobufPluginLibrary/Google_Protobuf_Edition+Extensions.swift +++ b/Sources/SwiftProtobufPluginLibrary/Google_Protobuf_Edition+Extensions.swift @@ -13,13 +13,12 @@ // ----------------------------------------------------------------------------- import Foundation - import SwiftProtobuf /// The spec for editions calls out them being ordered and comparable. /// https://github.com/protocolbuffers/protobuf/blob/main/docs/design/editions/edition-naming.md extension Google_Protobuf_Edition: Comparable { - public static func < (lhs: Google_Protobuf_Edition, rhs: Google_Protobuf_Edition) -> Bool { - return lhs.rawValue < rhs.rawValue - } + public static func < (lhs: Google_Protobuf_Edition, rhs: Google_Protobuf_Edition) -> Bool { + lhs.rawValue < rhs.rawValue + } } diff --git a/Sources/SwiftProtobufPluginLibrary/Google_Protobuf_SourceCodeInfo+Extensions.swift b/Sources/SwiftProtobufPluginLibrary/Google_Protobuf_SourceCodeInfo+Extensions.swift index d8560ab2d..f4c06c6f7 100644 --- a/Sources/SwiftProtobufPluginLibrary/Google_Protobuf_SourceCodeInfo+Extensions.swift +++ b/Sources/SwiftProtobufPluginLibrary/Google_Protobuf_SourceCodeInfo+Extensions.swift @@ -13,58 +13,60 @@ import SwiftProtobuf extension Google_Protobuf_SourceCodeInfo.Location { - /// Builds a source comment out of the location's comment fields. - /// - /// If leadingDetachedPrefix is not provided, those comments won't - /// be collected. - public func asSourceComment(commentPrefix: String, - leadingDetachedPrefix: String? = nil) -> String { - func escapeMarkup(_ text: String) -> String { - // Proto file comments don't really have any markup associated with - // them. Swift uses something like MarkDown: - // "Markup Formatting Reference" - // https://developer.apple.com/library/content/documentation/Xcode/Reference/xcode_markup_formatting_ref/index.html - // Sadly that format doesn't really lend itself to any form of - // escaping to ensure comments are interpreted markup when they - // really aren't. About the only thing that could be done is to - // try and escape some set of things that could start directives, - // and that gets pretty chatty/ugly pretty quickly. - return text - } + /// Builds a source comment out of the location's comment fields. + /// + /// If leadingDetachedPrefix is not provided, those comments won't + /// be collected. + public func asSourceComment( + commentPrefix: String, + leadingDetachedPrefix: String? = nil + ) -> String { + func escapeMarkup(_ text: String) -> String { + // Proto file comments don't really have any markup associated with + // them. Swift uses something like MarkDown: + // "Markup Formatting Reference" + // https://developer.apple.com/library/content/documentation/Xcode/Reference/xcode_markup_formatting_ref/index.html + // Sadly that format doesn't really lend itself to any form of + // escaping to ensure comments are interpreted markup when they + // really aren't. About the only thing that could be done is to + // try and escape some set of things that could start directives, + // and that gets pretty chatty/ugly pretty quickly. + text + } - func prefixLines(text: String, prefix: String) -> String { - var result = String() - // Protoc doesn't normalize newlines in the comments, make sure CRLF - // doesn't insert blank lines and the generated file is hopefully then - // consistent in using '\n'. - var lines = - text.replacingOccurrences(of: "\r\n", with: "\n").components(separatedBy: .newlines) - // Trim any blank lines off the end. - while !lines.isEmpty && lines.last!.trimmingCharacters(in: .whitespaces).isEmpty { - lines.removeLast() - } - for line in lines { - result.append(prefix + line + "\n") - } - return result - } + func prefixLines(text: String, prefix: String) -> String { + var result = String() + // Protoc doesn't normalize newlines in the comments, make sure CRLF + // doesn't insert blank lines and the generated file is hopefully then + // consistent in using '\n'. + var lines = + text.replacingOccurrences(of: "\r\n", with: "\n").components(separatedBy: .newlines) + // Trim any blank lines off the end. + while !lines.isEmpty && lines.last!.trimmingCharacters(in: .whitespaces).isEmpty { + lines.removeLast() + } + for line in lines { + result.append(prefix + line + "\n") + } + return result + } - var result = String() + var result = String() - if let leadingDetachedPrefix = leadingDetachedPrefix { - for detached in leadingDetachedComments { - let comment = prefixLines(text: detached, prefix: leadingDetachedPrefix) - if !comment.isEmpty { - result += comment - // Detached comments have blank lines between then (and - // anything that follows them). - result += "\n" + if let leadingDetachedPrefix = leadingDetachedPrefix { + for detached in leadingDetachedComments { + let comment = prefixLines(text: detached, prefix: leadingDetachedPrefix) + if !comment.isEmpty { + result += comment + // Detached comments have blank lines between then (and + // anything that follows them). + result += "\n" + } + } } - } - } - let comments = hasLeadingComments ? leadingComments : trailingComments - result += prefixLines(text: escapeMarkup(comments), prefix: commentPrefix) - return result - } + let comments = hasLeadingComments ? leadingComments : trailingComments + result += prefixLines(text: escapeMarkup(comments), prefix: commentPrefix) + return result + } } diff --git a/Sources/SwiftProtobufPluginLibrary/NamingUtils.swift b/Sources/SwiftProtobufPluginLibrary/NamingUtils.swift index 0817fa4db..17dd01686 100644 --- a/Sources/SwiftProtobufPluginLibrary/NamingUtils.swift +++ b/Sources/SwiftProtobufPluginLibrary/NamingUtils.swift @@ -23,151 +23,151 @@ import SwiftProtobuf /// /// We won't generate types (structs, enums) with these names: /// -fileprivate let reservedTypeNames: Set = { - () -> Set in - var names: Set = [] - - // Main SwiftProtobuf namespace - // Shadowing this leads to Bad Things. - names.insert("SwiftProtobuf") - - // Subtype of many messages, used to scope nested extensions - names.insert("Extensions") - - // Subtypes are static references, so can conflict with static - // class properties: - names.insert("protoMessageName") - - // Methods on Message that we need to avoid shadowing. Testing - // shows we do not need to avoid `serializedData` or `isEqualTo`, - // but it's not obvious to me what's different about them. Maybe - // because these two are generic? Because they throw? - names.insert("decodeMessage") - names.insert("traverse") - - // Basic Message properties we don't want to shadow: - names.insert("isInitialized") - names.insert("unknownFields") - - // Standard Swift property names we don't want - // to conflict with: - names.insert("debugDescription") - names.insert("description") - names.insert("dynamicType") - names.insert("hashValue") - - // We don't need to protect all of these keywords, just the ones - // that interfere with type expressions: - // names = names.union(swiftKeywordsReservedInParticularContexts) - names.insert("Type") - names.insert("Protocol") - - // Getting something called "Swift" would be bad as it blocks access - // to built in things. - names.insert("Swift") - - // And getting things on some of the common protocols could create - // some odd confusion. - names.insert("Equatable") - names.insert("Hashable") - names.insert("Sendable") - - names = names.union(swiftKeywordsUsedInDeclarations) - names = names.union(swiftKeywordsUsedInStatements) - names = names.union(swiftKeywordsUsedInExpressionsAndTypes) - names = names.union(swiftCommonTypes) - names = names.union(swiftSpecialVariables) - return names +private let reservedTypeNames: Set = { + () -> Set in + var names: Set = [] + + // Main SwiftProtobuf namespace + // Shadowing this leads to Bad Things. + names.insert("SwiftProtobuf") + + // Subtype of many messages, used to scope nested extensions + names.insert("Extensions") + + // Subtypes are static references, so can conflict with static + // class properties: + names.insert("protoMessageName") + + // Methods on Message that we need to avoid shadowing. Testing + // shows we do not need to avoid `serializedData` or `isEqualTo`, + // but it's not obvious to me what's different about them. Maybe + // because these two are generic? Because they throw? + names.insert("decodeMessage") + names.insert("traverse") + + // Basic Message properties we don't want to shadow: + names.insert("isInitialized") + names.insert("unknownFields") + + // Standard Swift property names we don't want + // to conflict with: + names.insert("debugDescription") + names.insert("description") + names.insert("dynamicType") + names.insert("hashValue") + + // We don't need to protect all of these keywords, just the ones + // that interfere with type expressions: + // names = names.union(swiftKeywordsReservedInParticularContexts) + names.insert("Type") + names.insert("Protocol") + + // Getting something called "Swift" would be bad as it blocks access + // to built in things. + names.insert("Swift") + + // And getting things on some of the common protocols could create + // some odd confusion. + names.insert("Equatable") + names.insert("Hashable") + names.insert("Sendable") + + names = names.union(swiftKeywordsUsedInDeclarations) + names = names.union(swiftKeywordsUsedInStatements) + names = names.union(swiftKeywordsUsedInExpressionsAndTypes) + names = names.union(swiftCommonTypes) + names = names.union(swiftSpecialVariables) + return names }() /// /// Many Swift reserved words can be used as fields names if we put backticks /// around them: /// -fileprivate let quotableFieldNames: Set = { - () -> Set in - var names: Set = [] - - names = names.union(swiftKeywordsUsedInDeclarations) - names = names.union(swiftKeywordsUsedInStatements) - names = names.union(swiftKeywordsUsedInExpressionsAndTypes) - return names +private let quotableFieldNames: Set = { + () -> Set in + var names: Set = [] + + names = names.union(swiftKeywordsUsedInDeclarations) + names = names.union(swiftKeywordsUsedInStatements) + names = names.union(swiftKeywordsUsedInExpressionsAndTypes) + return names }() -fileprivate let reservedFieldNames: Set = { - () -> Set in - var names: Set = [] - - // Properties are instance names, so can't shadow static class - // properties such as `protoMessageName`. - - // Properties can't shadow methods. For example, we don't need to - // avoid `isEqualTo` as a field name. - - // Basic Message properties that we don't want to shadow - names.insert("isInitialized") - names.insert("unknownFields") - - // Standard Swift property names we don't want - // to conflict with: - names.insert("debugDescription") - names.insert("description") - names.insert("dynamicType") - names.insert("hashValue") - names.insert("init") - names.insert("self") - - // We don't need to protect all of these keywords, just the ones - // that interfere with type expressions: - // names = names.union(swiftKeywordsReservedInParticularContexts) - names.insert("Type") - names.insert("Protocol") - - names = names.union(swiftCommonTypes) - names = names.union(swiftSpecialVariables) - return names +private let reservedFieldNames: Set = { + () -> Set in + var names: Set = [] + + // Properties are instance names, so can't shadow static class + // properties such as `protoMessageName`. + + // Properties can't shadow methods. For example, we don't need to + // avoid `isEqualTo` as a field name. + + // Basic Message properties that we don't want to shadow + names.insert("isInitialized") + names.insert("unknownFields") + + // Standard Swift property names we don't want + // to conflict with: + names.insert("debugDescription") + names.insert("description") + names.insert("dynamicType") + names.insert("hashValue") + names.insert("init") + names.insert("self") + + // We don't need to protect all of these keywords, just the ones + // that interfere with type expressions: + // names = names.union(swiftKeywordsReservedInParticularContexts) + names.insert("Type") + names.insert("Protocol") + + names = names.union(swiftCommonTypes) + names = names.union(swiftSpecialVariables) + return names }() /// /// Many Swift reserved words can be used as enum cases if we put quotes /// around them: /// -fileprivate let quotableEnumCases: Set = { - () -> Set in - var names: Set = [] - - // We don't need to protect all of these keywords, just the ones - // that interfere with enum cases: - // names = names.union(swiftKeywordsReservedInParticularContexts) - names.insert("associativity") - names.insert("dynamicType") - names.insert("optional") - names.insert("required") - - names = names.union(swiftKeywordsUsedInDeclarations) - names = names.union(swiftKeywordsUsedInStatements) - names = names.union(swiftKeywordsUsedInExpressionsAndTypes) - // Common type and variable names don't cause problems as enum - // cases, because enum case names only appear in special contexts: - // names = names.union(swiftCommonTypes) - // names = names.union(swiftSpecialVariables) - return names +private let quotableEnumCases: Set = { + () -> Set in + var names: Set = [] + + // We don't need to protect all of these keywords, just the ones + // that interfere with enum cases: + // names = names.union(swiftKeywordsReservedInParticularContexts) + names.insert("associativity") + names.insert("dynamicType") + names.insert("optional") + names.insert("required") + + names = names.union(swiftKeywordsUsedInDeclarations) + names = names.union(swiftKeywordsUsedInStatements) + names = names.union(swiftKeywordsUsedInExpressionsAndTypes) + // Common type and variable names don't cause problems as enum + // cases, because enum case names only appear in special contexts: + // names = names.union(swiftCommonTypes) + // names = names.union(swiftSpecialVariables) + return names }() /// /// Some words cannot be used for enum cases, even if they are quoted with /// backticks: /// -fileprivate let reservedEnumCases: Set = [ - // Don't conflict with standard Swift property names: - "allCases", - "debugDescription", - "description", - "dynamicType", - "hashValue", - "init", - "rawValue", - "self", +private let reservedEnumCases: Set = [ + // Don't conflict with standard Swift property names: + "allCases", + "debugDescription", + "description", + "dynamicType", + "hashValue", + "init", + "rawValue", + "self", ] /// @@ -175,437 +175,438 @@ fileprivate let reservedEnumCases: Set = [ /// Extensions { ... }`, so we resuse the same sets for backticks and reserved /// words. /// -fileprivate let quotableMessageScopedExtensionNames: Set = quotableEnumCases -fileprivate let reservedMessageScopedExtensionNames: Set = reservedEnumCases - - -fileprivate func isAllUnderscore(_ s: String) -> Bool { - if s.isEmpty { - return false - } - for c in s.unicodeScalars { - if c != "_" {return false} - } - return true +private let quotableMessageScopedExtensionNames: Set = quotableEnumCases +private let reservedMessageScopedExtensionNames: Set = reservedEnumCases + +private func isAllUnderscore(_ s: String) -> Bool { + if s.isEmpty { + return false + } + for c in s.unicodeScalars { + if c != "_" { return false } + } + return true } -fileprivate func sanitizeTypeName(_ s: String, disambiguator: String, forbiddenTypeNames: Set) -> String { - // NOTE: This code relies on the protoc validation of _identifier_ is defined - // (in Tokenizer::Next() as `[a-zA-Z_][a-zA-Z0-9_]*`, so this does not need - // any complex validation or handing of characters outside those ranges. Since - // those rules prevent a leading digit; nothing needs to be done, and any - // explicitly use Message or Enum name will be valid. The one exception is - // this code is also used for determining the OneOf enums, but that code is - // responsible for dealing with the issues in the transforms it makes. - if reservedTypeNames.contains(s) { - return s + disambiguator - } else if isAllUnderscore(s) { - return s + disambiguator - } else if s.hasSuffix(disambiguator) { - // If `foo` and `fooMessage` both exist, and `foo` gets - // expanded to `fooMessage`, then we also should expand - // `fooMessage` to `fooMessageMessage` to avoid creating a new - // conflict. This can be resolved recursively by stripping - // the disambiguator, sanitizing the root, then re-adding the - // disambiguator: - let e = s.index(s.endIndex, offsetBy: -disambiguator.count) - let truncated = String(s[..) -> String { + // NOTE: This code relies on the protoc validation of _identifier_ is defined + // (in Tokenizer::Next() as `[a-zA-Z_][a-zA-Z0-9_]*`, so this does not need + // any complex validation or handing of characters outside those ranges. Since + // those rules prevent a leading digit; nothing needs to be done, and any + // explicitly use Message or Enum name will be valid. The one exception is + // this code is also used for determining the OneOf enums, but that code is + // responsible for dealing with the issues in the transforms it makes. + if reservedTypeNames.contains(s) { + return s + disambiguator + } else if isAllUnderscore(s) { + return s + disambiguator + } else if s.hasSuffix(disambiguator) { + // If `foo` and `fooMessage` both exist, and `foo` gets + // expanded to `fooMessage`, then we also should expand + // `fooMessage` to `fooMessageMessage` to avoid creating a new + // conflict. This can be resolved recursively by stripping + // the disambiguator, sanitizing the root, then re-adding the + // disambiguator: + let e = s.index(s.endIndex, offsetBy: -disambiguator.count) + let truncated = String(s[.. Bool { - let scalars = s.unicodeScalars - let start = scalars.index(scalars.startIndex, offsetBy: index) - if start == scalars.endIndex { - // it ended, so just say the next character wasn't uppercase. - return false - } - return scalars[start].isASCUppercase +private func isCharacterUppercase(_ s: String, index: Int) -> Bool { + let scalars = s.unicodeScalars + let start = scalars.index(scalars.startIndex, offsetBy: index) + if start == scalars.endIndex { + // it ended, so just say the next character wasn't uppercase. + return false + } + return scalars[start].isASCUppercase } -fileprivate func makeUnicodeScalarView( - from unicodeScalar: UnicodeScalar +private func makeUnicodeScalarView( + from unicodeScalar: UnicodeScalar ) -> String.UnicodeScalarView { - var view = String.UnicodeScalarView() - view.append(unicodeScalar) - return view + var view = String.UnicodeScalarView() + view.append(unicodeScalar) + return view } -fileprivate enum CamelCaser { - // Abbreviation that should be all uppercase when camelcasing. Used in - // camelCased(:initialUpperCase:). - static let appreviations: Set = ["url", "http", "https", "id"] - - // The diffent "classes" a character can belong in for segmenting. - enum CharClass { - case digit - case lower - case upper - case underscore - case other - - init(_ from: UnicodeScalar) { - switch from { - case "0"..."9": - self = .digit - case "a"..."z": - self = .lower - case "A"..."Z": - self = .upper - case "_": - self = .underscore - default: - self = .other - } - } - } - - /// Transforms the input into a camelcase name that is a valid Swift - /// identifier. The input is assumed to be a protocol buffer identifier (or - /// something like that), meaning that it is a "snake_case_name" and the - /// underscores and be used to split into segements and then capitalize as - /// needed. The splits happen based on underscores and/or changes in case - /// and/or use of digits. If underscores are repeated, then the "extras" - /// (past the first) are carried over into the output. - /// - /// NOTE: protoc validation of an _identifier_ is defined (in Tokenizer::Next() - /// as `[a-zA-Z_][a-zA-Z0-9_]*`, Since leading underscores are removed, it does - /// have to handle if things would have started with a digit. If that happens, - /// then an underscore is added before it (which matches what the proto file - /// would have had to have a valid identifier also). - static func transform(_ s: String, initialUpperCase: Bool) -> String { - var result = String() - var current = String.UnicodeScalarView() // Collects in lowercase. - var lastClass = CharClass("\0") - - func addCurrent() { - guard !current.isEmpty else { - return - } - var currentAsString = String(current) - if result.isEmpty && !initialUpperCase { - // Nothing, want it to stay lowercase. - } else if appreviations.contains(currentAsString) { - currentAsString = currentAsString.uppercased() - } else { - currentAsString = NamingUtils.uppercaseFirstCharacter(currentAsString) - } - result += String(currentAsString) - current = String.UnicodeScalarView() +private enum CamelCaser { + // Abbreviation that should be all uppercase when camelcasing. Used in + // camelCased(:initialUpperCase:). + static let appreviations: Set = ["url", "http", "https", "id"] + + // The diffent "classes" a character can belong in for segmenting. + enum CharClass { + case digit + case lower + case upper + case underscore + case other + + init(_ from: UnicodeScalar) { + switch from { + case "0"..."9": + self = .digit + case "a"..."z": + self = .lower + case "A"..."Z": + self = .upper + case "_": + self = .underscore + default: + self = .other + } + } } - for scalar in s.unicodeScalars { - let scalarClass = CharClass(scalar) - switch scalarClass { - case .digit: - if lastClass != .digit { - addCurrent() - } - if result.isEmpty { - // Don't want to start with a number for the very first thing. - result += "_" + /// Transforms the input into a camelcase name that is a valid Swift + /// identifier. The input is assumed to be a protocol buffer identifier (or + /// something like that), meaning that it is a "snake_case_name" and the + /// underscores and be used to split into segements and then capitalize as + /// needed. The splits happen based on underscores and/or changes in case + /// and/or use of digits. If underscores are repeated, then the "extras" + /// (past the first) are carried over into the output. + /// + /// NOTE: protoc validation of an _identifier_ is defined (in Tokenizer::Next() + /// as `[a-zA-Z_][a-zA-Z0-9_]*`, Since leading underscores are removed, it does + /// have to handle if things would have started with a digit. If that happens, + /// then an underscore is added before it (which matches what the proto file + /// would have had to have a valid identifier also). + static func transform(_ s: String, initialUpperCase: Bool) -> String { + var result = String() + var current = String.UnicodeScalarView() // Collects in lowercase. + var lastClass = CharClass("\0") + + func addCurrent() { + guard !current.isEmpty else { + return + } + var currentAsString = String(current) + if result.isEmpty && !initialUpperCase { + // Nothing, want it to stay lowercase. + } else if appreviations.contains(currentAsString) { + currentAsString = currentAsString.uppercased() + } else { + currentAsString = NamingUtils.uppercaseFirstCharacter(currentAsString) + } + result += String(currentAsString) + current = String.UnicodeScalarView() } - current.append(scalar) - case .upper: - if lastClass != .upper { - addCurrent() - } - current.append(scalar.ascLowercased()) - case .lower: - if lastClass != .lower && lastClass != .upper { - addCurrent() + + for scalar in s.unicodeScalars { + let scalarClass = CharClass(scalar) + switch scalarClass { + case .digit: + if lastClass != .digit { + addCurrent() + } + if result.isEmpty { + // Don't want to start with a number for the very first thing. + result += "_" + } + current.append(scalar) + case .upper: + if lastClass != .upper { + addCurrent() + } + current.append(scalar.ascLowercased()) + case .lower: + if lastClass != .lower && lastClass != .upper { + addCurrent() + } + current.append(scalar) + case .underscore: + addCurrent() + if lastClass == .underscore { + result += "_" + } + case .other: + addCurrent() + let escapeIt = + result.isEmpty + ? !isSwiftIdentifierHeadCharacter(scalar) + : !isSwiftIdentifierCharacter(scalar) + if escapeIt { + result.append("_u\(scalar.value)") + } else { + current.append(scalar) + } + } + + lastClass = scalarClass } - current.append(scalar) - case .underscore: + + // Add the last segment collected. addCurrent() + + // If things end in an underscore, add one also. if lastClass == .underscore { - result += "_" - } - case .other: - addCurrent() - let escapeIt = - result.isEmpty - ? !isSwiftIdentifierHeadCharacter(scalar) - : !isSwiftIdentifierCharacter(scalar) - if escapeIt { - result.append("_u\(scalar.value)") - } else { - current.append(scalar) + result += "_" } - } - lastClass = scalarClass + return result } - - // Add the last segment collected. - addCurrent() - - // If things end in an underscore, add one also. - if lastClass == .underscore { - result += "_" - } - - return result - } } // Scope for the utilies to they are less likely to conflict when imported into // generators. public enum NamingUtils { - // Returns the type prefix to use for a given - static func typePrefix(protoPackage: String, fileOptions: Google_Protobuf_FileOptions) -> String { - // Explicit option (including blank), wins. - if fileOptions.hasSwiftPrefix { - return fileOptions.swiftPrefix - } + // Returns the type prefix to use for a given + static func typePrefix(protoPackage: String, fileOptions: Google_Protobuf_FileOptions) -> String { + // Explicit option (including blank), wins. + if fileOptions.hasSwiftPrefix { + return fileOptions.swiftPrefix + } + + if protoPackage.isEmpty { + return String() + } - if protoPackage.isEmpty { - return String() + // NOTE: This code relies on the protoc validation of proto packages. Look + // at Parser::ParsePackage() to see the logic, it comes down to reading + // _identifiers_ joined by '.'. And _identifier_ is defined (in + // Tokenizer::Next() as `[a-zA-Z_][a-zA-Z0-9_]*`, so this does not need + // any complex validation or handing of characters outside those ranges. + // It just has to deal with ended up with a leading digit after the pruning + // of '_'s. + + // Transforms: + // "package.name" -> "Package_Name" + // "package_name" -> "PackageName" + // "pacakge.some_name" -> "Package_SomeName" + var prefix = String.UnicodeScalarView() + var makeUpper = true + for c in protoPackage.unicodeScalars { + if c == "_" { + makeUpper = true + } else if c == "." { + makeUpper = true + prefix.append("_") + } else { + if prefix.isEmpty && c.isASCDigit { + // If the first character is going to be a digit, add an underscore + // to ensure it is a valid Swift identifier. + prefix.append("_") + } + if makeUpper { + prefix.append(c.ascUppercased()) + makeUpper = false + } else { + prefix.append(c) + } + } + } + // End in an underscore to split off anything that gets added to it. + return String(prefix) + "_" } - // NOTE: This code relies on the protoc validation of proto packages. Look - // at Parser::ParsePackage() to see the logic, it comes down to reading - // _identifiers_ joined by '.'. And _identifier_ is defined (in - // Tokenizer::Next() as `[a-zA-Z_][a-zA-Z0-9_]*`, so this does not need - // any complex validation or handing of characters outside those ranges. - // It just has to deal with ended up with a leading digit after the pruning - // of '_'s. - - // Transforms: - // "package.name" -> "Package_Name" - // "package_name" -> "PackageName" - // "pacakge.some_name" -> "Package_SomeName" - var prefix = String.UnicodeScalarView() - var makeUpper = true - for c in protoPackage.unicodeScalars { - if c == "_" { - makeUpper = true - } else if c == "." { - makeUpper = true - prefix.append("_") - } else { - if prefix.isEmpty && c.isASCDigit { - // If the first character is going to be a digit, add an underscore - // to ensure it is a valid Swift identifier. - prefix.append("_") + /// Helper a proto prefix from strings. A proto prefix means underscores + /// and letter case are ignored. + /// + /// NOTE: Since this is acting on proto enum names and enum cases, we know + /// the values must be _identifier_s which is defined (in Tokenizer::Next() as + /// `[a-zA-Z_][a-zA-Z0-9_]*`, so this code is based on that limited input. + struct PrefixStripper { + private let prefixChars: String.UnicodeScalarView + + init(prefix: String) { + self.prefixChars = prefix.lowercased().replacingOccurrences(of: "_", with: "").unicodeScalars } - if makeUpper { - prefix.append(c.ascUppercased()) - makeUpper = false - } else { - prefix.append(c) + + /// Strip the prefix and return the result, or return nil if it can't + /// be stripped. + func strip(from: String) -> String? { + var prefixIndex = prefixChars.startIndex + let prefixEnd = prefixChars.endIndex + + let fromChars = from.lowercased().unicodeScalars + var fromIndex = fromChars.startIndex + let fromEnd = fromChars.endIndex + + while prefixIndex != prefixEnd { + if fromIndex == fromEnd { + // Reached the end of the string while still having prefix to go + // nothing to strip. + return nil + } + + if fromChars[fromIndex] == "_" { + fromIndex = fromChars.index(after: fromIndex) + continue + } + + if prefixChars[prefixIndex] != fromChars[fromIndex] { + // They differed before the end of the prefix, can't drop. + return nil + } + + prefixIndex = prefixChars.index(after: prefixIndex) + fromIndex = fromChars.index(after: fromIndex) + } + + // Remove any more underscores. + while fromIndex != fromEnd && fromChars[fromIndex] == "_" { + fromIndex = fromChars.index(after: fromIndex) + } + + if fromIndex == fromEnd { + // They matched, can't strip. + return nil + } + + guard fromChars[fromIndex].isASCLowercase else { + // Next character isn't a lowercase letter (it must be a digit + // (fromChars was lowercased)), that would mean to make an enum value it + // would have to get prefixed with an underscore which most folks + // wouldn't consider to be a better Swift naming, so don't strip the + // prefix. + return nil + } + + let count = fromChars.distance(from: fromChars.startIndex, to: fromIndex) + let idx = from.index(from.startIndex, offsetBy: count) + return String(from[idx..) -> String { + sanitizeTypeName(s, disambiguator: "Message", forbiddenTypeNames: forbiddenTypeNames) } - /// Strip the prefix and return the result, or return nil if it can't - /// be stripped. - func strip(from: String) -> String? { - var prefixIndex = prefixChars.startIndex - let prefixEnd = prefixChars.endIndex - - let fromChars = from.lowercased().unicodeScalars - var fromIndex = fromChars.startIndex - let fromEnd = fromChars.endIndex - - while (prefixIndex != prefixEnd) { - if (fromIndex == fromEnd) { - // Reached the end of the string while still having prefix to go - // nothing to strip. - return nil - } + static func sanitize(enumName s: String, forbiddenTypeNames: Set) -> String { + sanitizeTypeName(s, disambiguator: "Enum", forbiddenTypeNames: forbiddenTypeNames) + } - if fromChars[fromIndex] == "_" { - fromIndex = fromChars.index(after: fromIndex) - continue - } + static func sanitize(oneofName s: String, forbiddenTypeNames: Set) -> String { + sanitizeTypeName(s, disambiguator: "Oneof", forbiddenTypeNames: forbiddenTypeNames) + } - if prefixChars[prefixIndex] != fromChars[fromIndex] { - // They differed before the end of the prefix, can't drop. - return nil + static func sanitize(fieldName s: String, basedOn: String) -> String { + if basedOn.hasPrefix("clear") && isCharacterUppercase(basedOn, index: 5) { + return s + "_p" + } else if basedOn.hasPrefix("has") && isCharacterUppercase(basedOn, index: 3) { + return s + "_p" + } else if reservedFieldNames.contains(basedOn) { + return s + "_p" + } else if basedOn == s && quotableFieldNames.contains(basedOn) { + // backticks are only used on the base names, if we're sanitizing based on something else + // this is skipped (the "hasFoo" doesn't get backticks just because the "foo" does). + return "`\(s)`" + } else if isAllUnderscore(basedOn) { + return s + "__" + } else { + return s } + } - prefixIndex = prefixChars.index(after: prefixIndex) - fromIndex = fromChars.index(after: fromIndex) - } - - // Remove any more underscores. - while fromIndex != fromEnd && fromChars[fromIndex] == "_" { - fromIndex = fromChars.index(after: fromIndex) - } - - if fromIndex == fromEnd { - // They matched, can't strip. - return nil - } - - guard fromChars[fromIndex].isASCLowercase else { - // Next character isn't a lowercase letter (it must be a digit - // (fromChars was lowercased)), that would mean to make an enum value it - // would have to get prefixed with an underscore which most folks - // wouldn't consider to be a better Swift naming, so don't strip the - // prefix. - return nil - } - - let count = fromChars.distance(from: fromChars.startIndex, to: fromIndex) - let idx = from.index(from.startIndex, offsetBy: count) - return String(from[idx.. String { + sanitize(fieldName: s, basedOn: s) } - } - - static func sanitize(messageName s: String, forbiddenTypeNames: Set) -> String { - return sanitizeTypeName(s, disambiguator: "Message", forbiddenTypeNames: forbiddenTypeNames) - } - - static func sanitize(enumName s: String, forbiddenTypeNames: Set) -> String { - return sanitizeTypeName(s, disambiguator: "Enum", forbiddenTypeNames: forbiddenTypeNames) - } - - static func sanitize(oneofName s: String, forbiddenTypeNames: Set) -> String { - return sanitizeTypeName(s, disambiguator: "Oneof", forbiddenTypeNames: forbiddenTypeNames) - } - - static func sanitize(fieldName s: String, basedOn: String) -> String { - if basedOn.hasPrefix("clear") && isCharacterUppercase(basedOn, index: 5) { - return s + "_p" - } else if basedOn.hasPrefix("has") && isCharacterUppercase(basedOn, index: 3) { - return s + "_p" - } else if reservedFieldNames.contains(basedOn) { - return s + "_p" - } else if basedOn == s && quotableFieldNames.contains(basedOn) { - // backticks are only used on the base names, if we're sanitizing based on something else - // this is skipped (the "hasFoo" doesn't get backticks just because the "foo" does). - return "`\(s)`" - } else if isAllUnderscore(basedOn) { - return s + "__" - } else { - return s + + static func sanitize(enumCaseName s: String) -> String { + if reservedEnumCases.contains(s) { + return "\(s)_" + } else if quotableEnumCases.contains(s) { + return "`\(s)`" + } else if isAllUnderscore(s) { + return s + "__" + } else { + return s + } } - } - static func sanitize(fieldName s: String) -> String { - return sanitize(fieldName: s, basedOn: s) - } + static func sanitize(messageScopedExtensionName s: String) -> String { + if reservedMessageScopedExtensionNames.contains(s) { + return "\(s)_" + } else if quotableMessageScopedExtensionNames.contains(s) { + return "`\(s)`" + } else if isAllUnderscore(s) { + return s + "__" + } else { + return s + } + } - static func sanitize(enumCaseName s: String) -> String { - if reservedEnumCases.contains(s) { - return "\(s)_" - } else if quotableEnumCases.contains(s) { - return "`\(s)`" - } else if isAllUnderscore(s) { - return s + "__" - } else { - return s + /// Forces the first character to be uppercase (if possible) and leaves + /// the rest of the characters in their existing case. + /// + /// Use toUpperCamelCase() to get leading "HTTP", "URL", etc. correct. + static func uppercaseFirstCharacter(_ s: String) -> String { + let out = s.unicodeScalars + if let first = out.first { + var result = makeUnicodeScalarView(from: first.ascUppercased()) + result.append( + contentsOf: out[out.index(after: out.startIndex).. String { - if reservedMessageScopedExtensionNames.contains(s) { - return "\(s)_" - } else if quotableMessageScopedExtensionNames.contains(s) { - return "`\(s)`" - } else if isAllUnderscore(s) { - return s + "__" - } else { - return s + /// Accepts any inputs and tranforms form it into a leading + /// UpperCaseCamelCased Swift identifier. It follows the same conventions as + /// that are used for mapping field names into the Message property names. + public static func toUpperCamelCase(_ s: String) -> String { + CamelCaser.transform(s, initialUpperCase: true) } - } - - /// Forces the first character to be uppercase (if possible) and leaves - /// the rest of the characters in their existing case. - /// - /// Use toUpperCamelCase() to get leading "HTTP", "URL", etc. correct. - static func uppercaseFirstCharacter(_ s: String) -> String { - let out = s.unicodeScalars - if let first = out.first { - var result = makeUnicodeScalarView(from: first.ascUppercased()) - result.append( - contentsOf: out[out.index(after: out.startIndex).. String { + CamelCaser.transform(s, initialUpperCase: false) } - } - - /// Accepts any inputs and tranforms form it into a leading - /// UpperCaseCamelCased Swift identifier. It follows the same conventions as - /// that are used for mapping field names into the Message property names. - public static func toUpperCamelCase(_ s: String) -> String { - return CamelCaser.transform(s, initialUpperCase: true) - } - - /// Accepts any inputs and tranforms form it into a leading - /// lowerCaseCamelCased Swift identifier. It follows the same conventions as - /// that are used for mapping field names into the Message property names. - public static func toLowerCamelCase(_ s: String) -> String { - return CamelCaser.transform(s, initialUpperCase: false) - } - - static func trimBackticks(_ s: String) -> String { - // This only has to deal with the backticks added when computing relative names, so - // they are always matched and a single set. - let backtick = "`" - guard s.hasPrefix(backtick) else { - assert(!s.hasSuffix(backtick)) - return s + + static func trimBackticks(_ s: String) -> String { + // This only has to deal with the backticks added when computing relative names, so + // they are always matched and a single set. + let backtick = "`" + guard s.hasPrefix(backtick) else { + assert(!s.hasSuffix(backtick)) + return s + } + assert(s.hasSuffix(backtick)) + let result = s.dropFirst().dropLast() + assert(!result.hasPrefix(backtick) && !result.hasSuffix(backtick)) + return String(result) } - assert(s.hasSuffix(backtick)) - let result = s.dropFirst().dropLast() - assert(!result.hasPrefix(backtick) && !result.hasSuffix(backtick)) - return String(result) - } - - static func periodsToUnderscores(_ s: String) -> String { - return s.replacingOccurrences(of: ".", with: "_") - } - - /// This must be exactly the same as the corresponding code in the - /// SwiftProtobuf library. Changing it will break compatibility of - /// the generated code with old library version. - public static func toJsonFieldName(_ s: String) -> String { - var result = String.UnicodeScalarView() - var capitalizeNext = false - for c in s.unicodeScalars { - if c == "_" { - capitalizeNext = true - } else if capitalizeNext { - result.append(c.ascUppercased()) - capitalizeNext = false - } else { - result.append(c) - } + static func periodsToUnderscores(_ s: String) -> String { + s.replacingOccurrences(of: ".", with: "_") + } + + /// This must be exactly the same as the corresponding code in the + /// SwiftProtobuf library. Changing it will break compatibility of + /// the generated code with old library version. + public static func toJsonFieldName(_ s: String) -> String { + var result = String.UnicodeScalarView() + var capitalizeNext = false + + for c in s.unicodeScalars { + if c == "_" { + capitalizeNext = true + } else if capitalizeNext { + result.append(c.ascUppercased()) + capitalizeNext = false + } else { + result.append(c) + } + } + return String(result) } - return String(result) - } } diff --git a/Sources/SwiftProtobufPluginLibrary/ProtoCompilerContext.swift b/Sources/SwiftProtobufPluginLibrary/ProtoCompilerContext.swift index 161fa3375..9c03ab366 100644 --- a/Sources/SwiftProtobufPluginLibrary/ProtoCompilerContext.swift +++ b/Sources/SwiftProtobufPluginLibrary/ProtoCompilerContext.swift @@ -18,7 +18,7 @@ import Foundation /// Abstact interface to get information about the protocol buffer compiler /// being used for generation. public protocol ProtoCompilerContext { - /// The version of the protocol buffer compiler (if it was provided in the - /// generation request). - var version: Google_Protobuf_Compiler_Version? { get } + /// The version of the protocol buffer compiler (if it was provided in the + /// generation request). + var version: Google_Protobuf_Compiler_Version? { get } } diff --git a/Sources/SwiftProtobufPluginLibrary/ProtoFileToModuleMappings.swift b/Sources/SwiftProtobufPluginLibrary/ProtoFileToModuleMappings.swift index d96d02c62..99391415c 100644 --- a/Sources/SwiftProtobufPluginLibrary/ProtoFileToModuleMappings.swift +++ b/Sources/SwiftProtobufPluginLibrary/ProtoFileToModuleMappings.swift @@ -19,165 +19,173 @@ private let defaultSwiftProtobufModuleName = "SwiftProtobuf" /// Handles the mapping of proto files to the modules they will be compiled into. public struct ProtoFileToModuleMappings { - /// Errors raised from parsing mappings - public enum LoadError: Error, Equatable { - /// Raised if the path wasn't found. - case failToOpen(path: String) - /// Raised if an mapping entry in the protobuf doesn't have a module name. - /// mappingIndex is the index (0-N) of the mapping. - case entryMissingModuleName(mappingIndex: Int) - /// Raised if an mapping entry in the protobuf doesn't have any proto files listed. - /// mappingIndex is the index (0-N) of the mapping. - case entryHasNoProtoPaths(mappingIndex: Int) - /// The given proto path was listed for both modules. - case duplicateProtoPathMapping(path: String, firstModule: String, secondModule: String) - } - - /// Proto file name to module name. - /// This is really `private` to this type, it is just `internal` so the tests can - /// access it to verify things. - let mappings: [String:String] - - /// A Boolean value that indicates that there were developer provided - /// mappings. - /// - /// Since `mappings` will have the bundled proto files also, this is used - /// to track whether there are any provided mappings. - public let hasMappings: Bool - - /// The name of the runtime module for SwiftProtobuf (usually "SwiftProtobuf"). - /// We expect to find the WKTs in the module named here. - public let swiftProtobufModuleName: String - - /// Loads and parses the given module mapping from disk. Raises LoadError - /// or TextFormatDecodingError. - public init(path: String) throws { - try self.init(path: path, swiftProtobufModuleName: nil) - } - - /// Loads and parses the given module mapping from disk. Raises LoadError - /// or TextFormatDecodingError. - public init(path: String, swiftProtobufModuleName: String?) throws { - let content: String - do { - content = try String(contentsOfFile: path, encoding: String.Encoding.utf8) - } catch { - throw LoadError.failToOpen(path: path) + /// Errors raised from parsing mappings + public enum LoadError: Error, Equatable { + /// Raised if the path wasn't found. + case failToOpen(path: String) + /// Raised if an mapping entry in the protobuf doesn't have a module name. + /// mappingIndex is the index (0-N) of the mapping. + case entryMissingModuleName(mappingIndex: Int) + /// Raised if an mapping entry in the protobuf doesn't have any proto files listed. + /// mappingIndex is the index (0-N) of the mapping. + case entryHasNoProtoPaths(mappingIndex: Int) + /// The given proto path was listed for both modules. + case duplicateProtoPathMapping(path: String, firstModule: String, secondModule: String) } - let mappingsProto = try SwiftProtobuf_GenSwift_ModuleMappings(textFormatString: content) - try self.init(moduleMappingsProto: mappingsProto, swiftProtobufModuleName: swiftProtobufModuleName) - } - - /// Parses the given module mapping. Raises LoadError. - public init(moduleMappingsProto mappings: SwiftProtobuf_GenSwift_ModuleMappings) throws { - try self.init(moduleMappingsProto: mappings, swiftProtobufModuleName: nil) - } - - /// Parses the given module mapping. Raises LoadError. - public init(moduleMappingsProto mappings: SwiftProtobuf_GenSwift_ModuleMappings, swiftProtobufModuleName: String?) throws { - self.swiftProtobufModuleName = swiftProtobufModuleName ?? defaultSwiftProtobufModuleName - var builder = wktMappings(swiftProtobufModuleName: self.swiftProtobufModuleName) - let initialCount = builder.count - for (idx, mapping) in mappings.mapping.lazy.enumerated() { - if mapping.moduleName.isEmpty { - throw LoadError.entryMissingModuleName(mappingIndex: idx) - } - if mapping.protoFilePath.isEmpty { - throw LoadError.entryHasNoProtoPaths(mappingIndex: idx) - } - for path in mapping.protoFilePath { - if let existing = builder[path] { - if existing != mapping.moduleName { - throw LoadError.duplicateProtoPathMapping(path: path, - firstModule: existing, - secondModule: mapping.moduleName) - } - // Was a repeat, just allow it. - } else { - builder[path] = mapping.moduleName - } - } - } - self.mappings = builder - self.hasMappings = initialCount != builder.count - } - - public init() { - try! self.init(moduleMappingsProto: SwiftProtobuf_GenSwift_ModuleMappings(), swiftProtobufModuleName: nil) - } - - public init(swiftProtobufModuleName: String?) { - try! self.init(moduleMappingsProto: SwiftProtobuf_GenSwift_ModuleMappings(), swiftProtobufModuleName: swiftProtobufModuleName) - } - - /// Looks up the module a given file is in. - public func moduleName(forFile file: FileDescriptor) -> String? { - return mappings[file.name] - } - - /// Returns the list of modules that need to be imported for a given file based on - /// the dependencies it has. - public func neededModules(forFile file: FileDescriptor) -> [String]? { - guard hasMappings else { return nil } - if file.dependencies.isEmpty { - return nil + /// Proto file name to module name. + /// This is really `private` to this type, it is just `internal` so the tests can + /// access it to verify things. + let mappings: [String: String] + + /// A Boolean value that indicates that there were developer provided + /// mappings. + /// + /// Since `mappings` will have the bundled proto files also, this is used + /// to track whether there are any provided mappings. + public let hasMappings: Bool + + /// The name of the runtime module for SwiftProtobuf (usually "SwiftProtobuf"). + /// We expect to find the WKTs in the module named here. + public let swiftProtobufModuleName: String + + /// Loads and parses the given module mapping from disk. Raises LoadError + /// or TextFormatDecodingError. + public init(path: String) throws { + try self.init(path: path, swiftProtobufModuleName: nil) } - var collector = Set() + /// Loads and parses the given module mapping from disk. Raises LoadError + /// or TextFormatDecodingError. + public init(path: String, swiftProtobufModuleName: String?) throws { + let content: String + do { + content = try String(contentsOfFile: path, encoding: String.Encoding.utf8) + } catch { + throw LoadError.failToOpen(path: path) + } + + let mappingsProto = try SwiftProtobuf_GenSwift_ModuleMappings(textFormatString: content) + try self.init(moduleMappingsProto: mappingsProto, swiftProtobufModuleName: swiftProtobufModuleName) + } - for dependency in file.dependencies { - if let depModule = mappings[dependency.name] { - collector.insert(depModule) - } + /// Parses the given module mapping. Raises LoadError. + public init(moduleMappingsProto mappings: SwiftProtobuf_GenSwift_ModuleMappings) throws { + try self.init(moduleMappingsProto: mappings, swiftProtobufModuleName: nil) } - // NOTE: This api is only used by gRPC (or things like it), with - // `import public` now re-exporting things, this likely can go away or just - // be reduced just the above loop, without the need for special casing the - // `import public` cases. It will come down to what should expectations - // be for protobuf messages, enums, and extensions with repsect to something - // that generates on top if it. i.e. - should they re-export things or - // should only the generated proto code do it? - - // Protocol Buffers has the concept of "public imports", these are imports - // into a file that expose everything from within the file to the new - // context. From the docs - - // https://protobuf.dev/programming-guides/proto/#importing - // `import public` dependencies can be transitively relied upon by anyone - // importing the proto containing the import public statement. - // To properly expose the types for use, it means in each file, the public imports - // from the dependencies have to be hoisted and also imported. - var visited = Set() - var toScan = file.dependencies - while let dep = toScan.popLast() { - for pubDep in dep.publicDependencies { - let pubDepName = pubDep.name - if visited.contains(pubDepName) { continue } - visited.insert(pubDepName) - toScan.append(pubDep) - if let pubDepModule = mappings[pubDepName] { - collector.insert(pubDepModule) + /// Parses the given module mapping. Raises LoadError. + public init( + moduleMappingsProto mappings: SwiftProtobuf_GenSwift_ModuleMappings, + swiftProtobufModuleName: String? + ) throws { + self.swiftProtobufModuleName = swiftProtobufModuleName ?? defaultSwiftProtobufModuleName + var builder = wktMappings(swiftProtobufModuleName: self.swiftProtobufModuleName) + let initialCount = builder.count + for (idx, mapping) in mappings.mapping.lazy.enumerated() { + if mapping.moduleName.isEmpty { + throw LoadError.entryMissingModuleName(mappingIndex: idx) + } + if mapping.protoFilePath.isEmpty { + throw LoadError.entryHasNoProtoPaths(mappingIndex: idx) + } + for path in mapping.protoFilePath { + if let existing = builder[path] { + if existing != mapping.moduleName { + throw LoadError.duplicateProtoPathMapping( + path: path, + firstModule: existing, + secondModule: mapping.moduleName + ) + } + // Was a repeat, just allow it. + } else { + builder[path] = mapping.moduleName + } + } } - } + self.mappings = builder + self.hasMappings = initialCount != builder.count } - if let moduleForThisFile = mappings[file.name] { - collector.remove(moduleForThisFile) + public init() { + try! self.init(moduleMappingsProto: SwiftProtobuf_GenSwift_ModuleMappings(), swiftProtobufModuleName: nil) } - // The library itself (happens if the import one of the WKTs). - collector.remove(self.swiftProtobufModuleName) + public init(swiftProtobufModuleName: String?) { + try! self.init( + moduleMappingsProto: SwiftProtobuf_GenSwift_ModuleMappings(), + swiftProtobufModuleName: swiftProtobufModuleName + ) + } - if collector.isEmpty { - return nil + /// Looks up the module a given file is in. + public func moduleName(forFile file: FileDescriptor) -> String? { + mappings[file.name] } - return collector.sorted() - } + /// Returns the list of modules that need to be imported for a given file based on + /// the dependencies it has. + public func neededModules(forFile file: FileDescriptor) -> [String]? { + guard hasMappings else { return nil } + if file.dependencies.isEmpty { + return nil + } + + var collector = Set() + + for dependency in file.dependencies { + if let depModule = mappings[dependency.name] { + collector.insert(depModule) + } + } + + // NOTE: This api is only used by gRPC (or things like it), with + // `import public` now re-exporting things, this likely can go away or just + // be reduced just the above loop, without the need for special casing the + // `import public` cases. It will come down to what should expectations + // be for protobuf messages, enums, and extensions with repsect to something + // that generates on top if it. i.e. - should they re-export things or + // should only the generated proto code do it? + + // Protocol Buffers has the concept of "public imports", these are imports + // into a file that expose everything from within the file to the new + // context. From the docs - + // https://protobuf.dev/programming-guides/proto/#importing + // `import public` dependencies can be transitively relied upon by anyone + // importing the proto containing the import public statement. + // To properly expose the types for use, it means in each file, the public imports + // from the dependencies have to be hoisted and also imported. + var visited = Set() + var toScan = file.dependencies + while let dep = toScan.popLast() { + for pubDep in dep.publicDependencies { + let pubDepName = pubDep.name + if visited.contains(pubDepName) { continue } + visited.insert(pubDepName) + toScan.append(pubDep) + if let pubDepModule = mappings[pubDepName] { + collector.insert(pubDepModule) + } + } + } + + if let moduleForThisFile = mappings[file.name] { + collector.remove(moduleForThisFile) + } + + // The library itself (happens if the import one of the WKTs). + collector.remove(self.swiftProtobufModuleName) + + if collector.isEmpty { + return nil + } + + return collector.sorted() + } } // Used to seed the mappings, the wkt are all part of the main library. -private func wktMappings(swiftProtobufModuleName: String) -> [String:String] { - return SwiftProtobufInfo.bundledProtoFiles.reduce(into: [:]) { $0[$1] = swiftProtobufModuleName } +private func wktMappings(swiftProtobufModuleName: String) -> [String: String] { + SwiftProtobufInfo.bundledProtoFiles.reduce(into: [:]) { $0[$1] = swiftProtobufModuleName } } diff --git a/Sources/SwiftProtobufPluginLibrary/ProvidesDeprecationComment.swift b/Sources/SwiftProtobufPluginLibrary/ProvidesDeprecationComment.swift index 4ecde2b40..7d0f56984 100644 --- a/Sources/SwiftProtobufPluginLibrary/ProvidesDeprecationComment.swift +++ b/Sources/SwiftProtobufPluginLibrary/ProvidesDeprecationComment.swift @@ -10,67 +10,67 @@ import Foundation - /// Protocol that all the Descriptors conform to provide deprecation comments public protocol ProvidesDeprecationComment { - /// Returns the deprecation comment to be used. - func deprecationComment(commentPrefix: String) -> String + /// Returns the deprecation comment to be used. + func deprecationComment(commentPrefix: String) -> String } /// Protocol that a Descriptor can confirm to when only the Type controls depecation. public protocol SimpleProvidesDeprecationComment: ProvidesDeprecationComment { - /// Name used in the generated message. - var typeName: String { get } - /// If the type is deprecated. - var isDeprecated: Bool { get } + /// Name used in the generated message. + var typeName: String { get } + /// If the type is deprecated. + var isDeprecated: Bool { get } } extension SimpleProvidesDeprecationComment { - /// Default implementation to provide the depectation comment. - public func deprecationComment(commentPrefix: String) -> String { - guard isDeprecated else { return String() } - return "\(commentPrefix) NOTE: This \(typeName) was marked as deprecated in the .proto file\n" - } + /// Default implementation to provide the depectation comment. + public func deprecationComment(commentPrefix: String) -> String { + guard isDeprecated else { return String() } + return "\(commentPrefix) NOTE: This \(typeName) was marked as deprecated in the .proto file\n" + } } /// Protocol that a Descriptor can confirm to when the Type or the File controls depecation. public protocol TypeOrFileProvidesDeprecationComment: ProvidesDeprecationComment { - /// Name used in the generated message. - var typeName: String { get } - /// If the type is deprecated. - var isDeprecated: Bool { get } - /// Returns the File this conforming object is in. - var file: FileDescriptor! { get } + /// Name used in the generated message. + var typeName: String { get } + /// If the type is deprecated. + var isDeprecated: Bool { get } + /// Returns the File this conforming object is in. + var file: FileDescriptor! { get } } extension TypeOrFileProvidesDeprecationComment { - /// Default implementation to provide the depectation comment. - public func deprecationComment(commentPrefix: String) -> String { - if isDeprecated { - return "\(commentPrefix) NOTE: This \(typeName) was marked as deprecated in the .proto file.\n" + /// Default implementation to provide the depectation comment. + public func deprecationComment(commentPrefix: String) -> String { + if isDeprecated { + return "\(commentPrefix) NOTE: This \(typeName) was marked as deprecated in the .proto file.\n" + } + guard file.options.deprecated else { return String() } + return "\(commentPrefix) NOTE: The whole .proto file that defined this \(typeName) was marked as deprecated.\n" } - guard file.options.deprecated else { return String() } - return "\(commentPrefix) NOTE: The whole .proto file that defined this \(typeName) was marked as deprecated.\n" - } } extension ProvidesDeprecationComment where Self: ProvidesSourceCodeLocation { - /// Helper to get the protoSourceComments combined with any depectation comment. - public func protoSourceCommentsWithDeprecation( - commentPrefix: String = "///", - leadingDetachedPrefix: String? = nil - ) -> String { - let protoSourceComments = protoSourceComments( - commentPrefix: commentPrefix, - leadingDetachedPrefix: leadingDetachedPrefix) - let deprecationComments = deprecationComment(commentPrefix: commentPrefix) + /// Helper to get the protoSourceComments combined with any depectation comment. + public func protoSourceCommentsWithDeprecation( + commentPrefix: String = "///", + leadingDetachedPrefix: String? = nil + ) -> String { + let protoSourceComments = protoSourceComments( + commentPrefix: commentPrefix, + leadingDetachedPrefix: leadingDetachedPrefix + ) + let deprecationComments = deprecationComment(commentPrefix: commentPrefix) - if deprecationComments.isEmpty { - return protoSourceComments - } - if protoSourceComments.isEmpty { - return deprecationComments + if deprecationComments.isEmpty { + return protoSourceComments + } + if protoSourceComments.isEmpty { + return deprecationComments + } + return "\(protoSourceComments)\(commentPrefix)\n\(deprecationComments)" } - return "\(protoSourceComments)\(commentPrefix)\n\(deprecationComments)" - } } diff --git a/Sources/SwiftProtobufPluginLibrary/ProvidesLocationPath.swift b/Sources/SwiftProtobufPluginLibrary/ProvidesLocationPath.swift index 4feb55cc8..727879c6a 100644 --- a/Sources/SwiftProtobufPluginLibrary/ProvidesLocationPath.swift +++ b/Sources/SwiftProtobufPluginLibrary/ProvidesLocationPath.swift @@ -13,10 +13,10 @@ import Foundation /// Protocol that all the Descriptors conform to for original .proto file /// location lookup. public protocol ProvidesLocationPath { - /// Updates `path` to the source location of the complete extent of - /// the object conforming to this protocol. This is a replacement for - /// `GetSourceLocation()` in the C++ Descriptor apis. - func getLocationPath(path: inout IndexPath) - /// Returns the File this conforming object is in. - var file: FileDescriptor! { get } + /// Updates `path` to the source location of the complete extent of + /// the object conforming to this protocol. This is a replacement for + /// `GetSourceLocation()` in the C++ Descriptor apis. + func getLocationPath(path: inout IndexPath) + /// Returns the File this conforming object is in. + var file: FileDescriptor! { get } } diff --git a/Sources/SwiftProtobufPluginLibrary/ProvidesSourceCodeLocation.swift b/Sources/SwiftProtobufPluginLibrary/ProvidesSourceCodeLocation.swift index da5465834..b80f21ed9 100644 --- a/Sources/SwiftProtobufPluginLibrary/ProvidesSourceCodeLocation.swift +++ b/Sources/SwiftProtobufPluginLibrary/ProvidesSourceCodeLocation.swift @@ -14,25 +14,29 @@ import SwiftProtobuf /// Protocol that all the Descriptors conform to for original .proto file /// location lookup. public protocol ProvidesSourceCodeLocation { - /// Returns the Location of a given object (Descriptor). - var sourceCodeInfoLocation: Google_Protobuf_SourceCodeInfo.Location? { get } + /// Returns the Location of a given object (Descriptor). + var sourceCodeInfoLocation: Google_Protobuf_SourceCodeInfo.Location? { get } } /// Default implementation for things that support ProvidesLocationPath. extension ProvidesSourceCodeLocation where Self: ProvidesLocationPath { - public var sourceCodeInfoLocation: Google_Protobuf_SourceCodeInfo.Location? { - var path = IndexPath() - getLocationPath(path: &path) - return file.sourceCodeInfoLocation(path: path) - } + public var sourceCodeInfoLocation: Google_Protobuf_SourceCodeInfo.Location? { + var path = IndexPath() + getLocationPath(path: &path) + return file.sourceCodeInfoLocation(path: path) + } } extension ProvidesSourceCodeLocation { - /// Helper to get a source comments as a string. - public func protoSourceComments(commentPrefix: String = "///", - leadingDetachedPrefix: String? = nil) -> String { - guard let loc = sourceCodeInfoLocation else { return String() } - return loc.asSourceComment(commentPrefix: commentPrefix, - leadingDetachedPrefix: leadingDetachedPrefix) - } + /// Helper to get a source comments as a string. + public func protoSourceComments( + commentPrefix: String = "///", + leadingDetachedPrefix: String? = nil + ) -> String { + guard let loc = sourceCodeInfoLocation else { return String() } + return loc.asSourceComment( + commentPrefix: commentPrefix, + leadingDetachedPrefix: leadingDetachedPrefix + ) + } } diff --git a/Sources/SwiftProtobufPluginLibrary/StandardErrorOutputStream.swift b/Sources/SwiftProtobufPluginLibrary/StandardErrorOutputStream.swift index d2c83f983..4c137a4f3 100644 --- a/Sources/SwiftProtobufPluginLibrary/StandardErrorOutputStream.swift +++ b/Sources/SwiftProtobufPluginLibrary/StandardErrorOutputStream.swift @@ -10,11 +10,11 @@ import Foundation class StandardErrorOutputStream: TextOutputStream { - func write(_ string: String) { - if #available(macOS 10.15.4, iOS 13.4, watchOS 6.2, tvOS 13.4, *) { - try! FileHandle.standardError.write(contentsOf: Data(string.utf8)) - } else { - FileHandle.standardError.write(Data(string.utf8)) + func write(_ string: String) { + if #available(macOS 10.15.4, iOS 13.4, watchOS 6.2, tvOS 13.4, *) { + try! FileHandle.standardError.write(contentsOf: Data(string.utf8)) + } else { + FileHandle.standardError.write(Data(string.utf8)) + } } - } } diff --git a/Sources/SwiftProtobufPluginLibrary/StringUtils.swift b/Sources/SwiftProtobufPluginLibrary/StringUtils.swift index 07aee8a0c..62479dfee 100644 --- a/Sources/SwiftProtobufPluginLibrary/StringUtils.swift +++ b/Sources/SwiftProtobufPluginLibrary/StringUtils.swift @@ -12,10 +12,10 @@ import Foundation @inlinable func trimWhitespace(_ s: String) -> String { - return s.trimmingCharacters(in: .whitespacesAndNewlines) + s.trimmingCharacters(in: .whitespacesAndNewlines) } @inlinable func trimWhitespace(_ s: String.SubSequence) -> String { - return s.trimmingCharacters(in: .whitespacesAndNewlines) + s.trimmingCharacters(in: .whitespacesAndNewlines) } diff --git a/Sources/SwiftProtobufPluginLibrary/SwiftLanguage.swift b/Sources/SwiftProtobufPluginLibrary/SwiftLanguage.swift index 90aab1609..7a3e12756 100644 --- a/Sources/SwiftProtobufPluginLibrary/SwiftLanguage.swift +++ b/Sources/SwiftProtobufPluginLibrary/SwiftLanguage.swift @@ -12,8 +12,8 @@ /// // ----------------------------------------------------------------------------- -import Swift import Foundation +import Swift /// Used to check if a character is a valid identifier head character. /// @@ -125,24 +125,27 @@ public let swiftKeywordsUsedInDeclarations: Set = [ "associatedtype", "class", "deinit", "enum", "extension", "fileprivate", "func", "import", "init", "inout", "internal", "let", "open", "operator", "private", "protocol", "public", - "static", "struct", "subscript", "typealias", "var" + "static", "struct", "subscript", "typealias", "var", ] -public let swiftKeywordsUsedInStatements: Set = [ "break", "case", +public let swiftKeywordsUsedInStatements: Set = [ + "break", "case", "continue", "default", "defer", "do", "else", "fallthrough", "for", "guard", "if", "in", "repeat", "return", "switch", "where", - "while" + "while", ] -public let swiftKeywordsUsedInExpressionsAndTypes: Set = [ "as", +public let swiftKeywordsUsedInExpressionsAndTypes: Set = [ + "as", "Any", "catch", "false", "is", "nil", "rethrows", "super", "self", - "Self", "throw", "throws", "true", "try" + "Self", "throw", "throws", "true", "try", ] -public let swiftKeywordsWithNumberSign: Set = [ "#available", +public let swiftKeywordsWithNumberSign: Set = [ + "#available", "#colorLiteral", "#column", "#else", "#elseif", "#endif", "#file", "#fileLiteral", "#function", "#if", "#imageLiteral", "#line", - "#selector", "#sourceLocation" + "#selector", "#sourceLocation", ] public let swiftKeywordsReservedInParticularContexts: Set = [ @@ -150,18 +153,20 @@ public let swiftKeywordsReservedInParticularContexts: Set = [ "get", "infix", "indirect", "lazy", "left", "mutating", "none", "nonmutating", "optional", "override", "postfix", "precedence", "prefix", "Protocol", "required", "right", "set", "Type", - "unowned", "weak", "willSet" + "unowned", "weak", "willSet", ] /// These are standard Swift types that are heavily used, although /// they are not technically reserved. Defining fields or structs /// with these names would break our generated code quite badly: -public let swiftCommonTypes: Set = [ "Bool", "Data", "Double", "Float", "Int", +public let swiftCommonTypes: Set = [ + "Bool", "Data", "Double", "Float", "Int", "Int32", "Int64", "String", "UInt", "UInt32", "UInt64", ] /// Special magic variables defined by the compiler that we don't /// really want to interfere with: -public let swiftSpecialVariables: Set = [ "__COLUMN__", +public let swiftSpecialVariables: Set = [ + "__COLUMN__", "__FILE__", "__FUNCTION__", "__LINE__", ] diff --git a/Sources/SwiftProtobufPluginLibrary/SwiftProtobufInfo.swift b/Sources/SwiftProtobufPluginLibrary/SwiftProtobufInfo.swift index d75a04f8f..1ca846032 100644 --- a/Sources/SwiftProtobufPluginLibrary/SwiftProtobufInfo.swift +++ b/Sources/SwiftProtobufPluginLibrary/SwiftProtobufInfo.swift @@ -17,32 +17,32 @@ import SwiftProtobuf /// Helpers about the library. public enum SwiftProtobufInfo { - /// Proto Files that ship with the library. - public static let bundledProtoFiles: Set = [ - "google/protobuf/any.proto", - "google/protobuf/api.proto", - // Even though descriptor.proto is *not* a WKT, it is included in the - // library so developers trying to compile .proto files with message, - // field, or file extensions don't have to generate it. - "google/protobuf/descriptor.proto", - "google/protobuf/duration.proto", - "google/protobuf/empty.proto", - "google/protobuf/field_mask.proto", - "google/protobuf/source_context.proto", - "google/protobuf/struct.proto", - "google/protobuf/timestamp.proto", - "google/protobuf/type.proto", - "google/protobuf/wrappers.proto", - ] + /// Proto Files that ship with the library. + public static let bundledProtoFiles: Set = [ + "google/protobuf/any.proto", + "google/protobuf/api.proto", + // Even though descriptor.proto is *not* a WKT, it is included in the + // library so developers trying to compile .proto files with message, + // field, or file extensions don't have to generate it. + "google/protobuf/descriptor.proto", + "google/protobuf/duration.proto", + "google/protobuf/empty.proto", + "google/protobuf/field_mask.proto", + "google/protobuf/source_context.proto", + "google/protobuf/struct.proto", + "google/protobuf/timestamp.proto", + "google/protobuf/type.proto", + "google/protobuf/wrappers.proto", + ] - /// Checks if a `Google_Protobuf_FileDescriptorProto` is a library bundled proto file. - @available(*, deprecated, message: "Use the version that takes a FileDescriptor instead.") - public static func isBundledProto(file: Google_Protobuf_FileDescriptorProto) -> Bool { - return file.package == "google.protobuf" && bundledProtoFiles.contains(file.name) - } + /// Checks if a `Google_Protobuf_FileDescriptorProto` is a library bundled proto file. + @available(*, deprecated, message: "Use the version that takes a FileDescriptor instead.") + public static func isBundledProto(file: Google_Protobuf_FileDescriptorProto) -> Bool { + file.package == "google.protobuf" && bundledProtoFiles.contains(file.name) + } - /// Checks if a `FileDescriptor` is a library bundled proto file. - public static func isBundledProto(file: FileDescriptor) -> Bool { - return file.package == "google.protobuf" && bundledProtoFiles.contains(file.name) - } + /// Checks if a `FileDescriptor` is a library bundled proto file. + public static func isBundledProto(file: FileDescriptor) -> Bool { + file.package == "google.protobuf" && bundledProtoFiles.contains(file.name) + } } diff --git a/Sources/SwiftProtobufPluginLibrary/SwiftProtobufNamer.swift b/Sources/SwiftProtobufPluginLibrary/SwiftProtobufNamer.swift index 362d47439..d9b7c1e87 100644 --- a/Sources/SwiftProtobufPluginLibrary/SwiftProtobufNamer.swift +++ b/Sources/SwiftProtobufPluginLibrary/SwiftProtobufNamer.swift @@ -15,347 +15,356 @@ import Foundation public final class SwiftProtobufNamer { - var filePrefixCache = [String:String]() - var enumValueRelativeNameCache = [String:String]() - public let mappings: ProtoFileToModuleMappings - public let targetModule: String + var filePrefixCache = [String: String]() + var enumValueRelativeNameCache = [String: String]() + public let mappings: ProtoFileToModuleMappings + public let targetModule: String - public var swiftProtobufModuleName: String { return mappings.swiftProtobufModuleName } + public var swiftProtobufModuleName: String { mappings.swiftProtobufModuleName } - public var swiftProtobufModulePrefix: String { - guard targetModule != mappings.swiftProtobufModuleName else { - return "" + public var swiftProtobufModulePrefix: String { + guard targetModule != mappings.swiftProtobufModuleName else { + return "" + } + return "\(mappings.swiftProtobufModuleName)." } - return "\(mappings.swiftProtobufModuleName)." - } - - /// Initializes a a new namer, assuming everything will be in the same Swift module. - public convenience init() { - self.init(protoFileToModuleMappings: ProtoFileToModuleMappings(), targetModule: "") - } - - /// Initializes a a new namer. All names will be generated as from the pov of the - /// given file using the provided file to module mapper. - public convenience init( - currentFile file: FileDescriptor, - protoFileToModuleMappings mappings: ProtoFileToModuleMappings - ) { - let targetModule = mappings.moduleName(forFile: file) ?? "" - self.init(protoFileToModuleMappings: mappings, targetModule: targetModule) - } - - /// Internal initializer. - init( - protoFileToModuleMappings mappings: ProtoFileToModuleMappings, - targetModule: String - ) { - self.mappings = mappings - self.targetModule = targetModule - } - - /// Calculate the relative name for the given message. - public func relativeName(message: Descriptor) -> String { - if message.containingType != nil { - return NamingUtils.sanitize(messageName: message.name, forbiddenTypeNames: [self.swiftProtobufModuleName]) - } else { - let prefix = typePrefix(forFile: message.file) - return NamingUtils.sanitize(messageName: prefix + message.name, forbiddenTypeNames: [self.swiftProtobufModuleName]) + + /// Initializes a a new namer, assuming everything will be in the same Swift module. + public convenience init() { + self.init(protoFileToModuleMappings: ProtoFileToModuleMappings(), targetModule: "") } - } - /// Calculate the full name for the given message. - public func fullName(message: Descriptor) -> String { - let relativeName = self.relativeName(message: message) - guard let containingType = message.containingType else { - return modulePrefix(file: message.file) + relativeName + /// Initializes a a new namer. All names will be generated as from the pov of the + /// given file using the provided file to module mapper. + public convenience init( + currentFile file: FileDescriptor, + protoFileToModuleMappings mappings: ProtoFileToModuleMappings + ) { + let targetModule = mappings.moduleName(forFile: file) ?? "" + self.init(protoFileToModuleMappings: mappings, targetModule: targetModule) } - return fullName(message:containingType) + "." + relativeName - } - - /// Calculate the relative name for the given enum. - public func relativeName(enum e: EnumDescriptor) -> String { - if e.containingType != nil { - return NamingUtils.sanitize(enumName: e.name, forbiddenTypeNames: [self.swiftProtobufModuleName]) - } else { - let prefix = typePrefix(forFile: e.file) - return NamingUtils.sanitize(enumName: prefix + e.name, forbiddenTypeNames: [self.swiftProtobufModuleName]) + + /// Internal initializer. + init( + protoFileToModuleMappings mappings: ProtoFileToModuleMappings, + targetModule: String + ) { + self.mappings = mappings + self.targetModule = targetModule + } + + /// Calculate the relative name for the given message. + public func relativeName(message: Descriptor) -> String { + if message.containingType != nil { + return NamingUtils.sanitize(messageName: message.name, forbiddenTypeNames: [self.swiftProtobufModuleName]) + } else { + let prefix = typePrefix(forFile: message.file) + return NamingUtils.sanitize( + messageName: prefix + message.name, + forbiddenTypeNames: [self.swiftProtobufModuleName] + ) + } } - } - /// Calculate the full name for the given enum. - public func fullName(enum e: EnumDescriptor) -> String { - let relativeName = self.relativeName(enum: e) - guard let containingType = e.containingType else { - return modulePrefix(file: e.file) + relativeName + /// Calculate the full name for the given message. + public func fullName(message: Descriptor) -> String { + let relativeName = self.relativeName(message: message) + guard let containingType = message.containingType else { + return modulePrefix(file: message.file) + relativeName + } + return fullName(message: containingType) + "." + relativeName } - return fullName(message: containingType) + "." + relativeName - } - - /// Compute the short names to use for the values of this enum. - private func computeRelativeNames(enum e: EnumDescriptor) { - let stripper = NamingUtils.PrefixStripper(prefix: e.name) - - /// Determine the initial candidate name for the name before - /// doing duplicate checks. - func candidateName(_ enumValue: EnumValueDescriptor) -> String { - let baseName = enumValue.name - if let stripped = stripper.strip(from: baseName) { - return NamingUtils.toLowerCamelCase(stripped) - } - return NamingUtils.toLowerCamelCase(baseName) + + /// Calculate the relative name for the given enum. + public func relativeName(enum e: EnumDescriptor) -> String { + if e.containingType != nil { + return NamingUtils.sanitize(enumName: e.name, forbiddenTypeNames: [self.swiftProtobufModuleName]) + } else { + let prefix = typePrefix(forFile: e.file) + return NamingUtils.sanitize(enumName: prefix + e.name, forbiddenTypeNames: [self.swiftProtobufModuleName]) + } } - // Bucketed based on candidate names to check for duplicates. - let candidates :[String:[EnumValueDescriptor]] = e.values.reduce(into: [:]) { - $0[candidateName($1), default:[]].append($1) + /// Calculate the full name for the given enum. + public func fullName(enum e: EnumDescriptor) -> String { + let relativeName = self.relativeName(enum: e) + guard let containingType = e.containingType else { + return modulePrefix(file: e.file) + relativeName + } + return fullName(message: containingType) + "." + relativeName } - for (camelCased, enumValues) in candidates { - // If there is only one, sanitize and cache it. - guard enumValues.count > 1 else { - let fullName = enumValues.first!.fullName - enumValueRelativeNameCache[fullName] = NamingUtils.sanitize(enumCaseName: camelCased) - continue - } - - // There are two possible cases: - // 1. There is the main entry and then all aliases for it that - // happen to be the same after the prefix was stripped. - // 2. There are atleast two values (there could also be aliases). - // - // For the first case, there's no need to do anything, we'll go - // with just one Swift version. For the second, append "_#" to - // the names to help make the different Swift versions clear - // which they are. - let firstValue = enumValues.first!.number - let hasMultipleValues = enumValues.contains(where: { return $0.number != firstValue }) - - guard hasMultipleValues else { - // Was the first case, all one value, just aliases that mapped - // to the same name. - let name = NamingUtils.sanitize(enumCaseName: camelCased) - for e in enumValues { - let fullName = e.fullName - enumValueRelativeNameCache[fullName] = name + /// Compute the short names to use for the values of this enum. + private func computeRelativeNames(enum e: EnumDescriptor) { + let stripper = NamingUtils.PrefixStripper(prefix: e.name) + + /// Determine the initial candidate name for the name before + /// doing duplicate checks. + func candidateName(_ enumValue: EnumValueDescriptor) -> String { + let baseName = enumValue.name + if let stripped = stripper.strip(from: baseName) { + return NamingUtils.toLowerCamelCase(stripped) + } + return NamingUtils.toLowerCamelCase(baseName) } - continue - } - - for e in enumValues { - // Can't put a negative size, so use "n" and make the number - // positive. - let suffix = e.number >= 0 ? "_\(e.number)" : "_n\(-e.number)" - let fullName = e.fullName - enumValueRelativeNameCache[fullName] = NamingUtils.sanitize(enumCaseName: camelCased + suffix) - } + + // Bucketed based on candidate names to check for duplicates. + let candidates: [String: [EnumValueDescriptor]] = e.values.reduce(into: [:]) { + $0[candidateName($1), default: []].append($1) + } + + for (camelCased, enumValues) in candidates { + // If there is only one, sanitize and cache it. + guard enumValues.count > 1 else { + let fullName = enumValues.first!.fullName + enumValueRelativeNameCache[fullName] = NamingUtils.sanitize(enumCaseName: camelCased) + continue + } + + // There are two possible cases: + // 1. There is the main entry and then all aliases for it that + // happen to be the same after the prefix was stripped. + // 2. There are atleast two values (there could also be aliases). + // + // For the first case, there's no need to do anything, we'll go + // with just one Swift version. For the second, append "_#" to + // the names to help make the different Swift versions clear + // which they are. + let firstValue = enumValues.first!.number + let hasMultipleValues = enumValues.contains(where: { return $0.number != firstValue }) + + guard hasMultipleValues else { + // Was the first case, all one value, just aliases that mapped + // to the same name. + let name = NamingUtils.sanitize(enumCaseName: camelCased) + for e in enumValues { + let fullName = e.fullName + enumValueRelativeNameCache[fullName] = name + } + continue + } + + for e in enumValues { + // Can't put a negative size, so use "n" and make the number + // positive. + let suffix = e.number >= 0 ? "_\(e.number)" : "_n\(-e.number)" + let fullName = e.fullName + enumValueRelativeNameCache[fullName] = NamingUtils.sanitize(enumCaseName: camelCased + suffix) + } + } + } + + /// Calculate the relative name for the given enum value. + public func relativeName(enumValue: EnumValueDescriptor) -> String { + if let name = enumValueRelativeNameCache[enumValue.fullName] { + return name + } + computeRelativeNames(enum: enumValue.enumType) + return enumValueRelativeNameCache[enumValue.fullName]! + } + + /// Calculate the full name for the given enum value. + public func fullName(enumValue: EnumValueDescriptor) -> String { + fullName(enum: enumValue.enumType) + "." + relativeName(enumValue: enumValue) } - } - /// Calculate the relative name for the given enum value. - public func relativeName(enumValue: EnumValueDescriptor) -> String { - if let name = enumValueRelativeNameCache[enumValue.fullName] { - return name + /// The relative name with a leading dot so it can be used where + /// the type is known. + public func dottedRelativeName(enumValue: EnumValueDescriptor) -> String { + let relativeName = self.relativeName(enumValue: enumValue) + return "." + NamingUtils.trimBackticks(relativeName) } - computeRelativeNames(enum: enumValue.enumType) - return enumValueRelativeNameCache[enumValue.fullName]! - } - - /// Calculate the full name for the given enum value. - public func fullName(enumValue: EnumValueDescriptor) -> String { - return fullName(enum: enumValue.enumType) + "." + relativeName(enumValue: enumValue) - } - - /// The relative name with a leading dot so it can be used where - /// the type is known. - public func dottedRelativeName(enumValue: EnumValueDescriptor) -> String { - let relativeName = self.relativeName(enumValue: enumValue) - return "." + NamingUtils.trimBackticks(relativeName) - } - - /// Filters the Enum's values to those that will have unique Swift - /// names. Only poorly named proto enum alias values get filtered - /// away, so the assumption is they aren't really needed from an - /// api pov. - @available(*, deprecated, message: "Please open a GitHub issue if you think functionality is missing.") - public func uniquelyNamedValues(enum e: EnumDescriptor) -> [EnumValueDescriptor] { - return e.values.filter { - // Original are kept as is. The computations for relative - // name already adds values for collisions with different - // values. - guard let aliasOf = $0.aliasOf else { return true } - let relativeName = self.relativeName(enumValue: $0) - let aliasOfRelativeName = self.relativeName(enumValue: aliasOf) - // If the relative name matches for the alias and original, drop - // the alias. - guard relativeName != aliasOfRelativeName else { return false } - // Only include this alias if it is the first one with this name. - // (handles alias with different cases in their names that get - // mangled to a single Swift name.) - let firstAlias = aliasOf.aliases.firstIndex { - let otherRelativeName = self.relativeName(enumValue: $0) - return relativeName == otherRelativeName - } - return aliasOf.aliases[firstAlias!] === $0 + + /// Filters the Enum's values to those that will have unique Swift + /// names. Only poorly named proto enum alias values get filtered + /// away, so the assumption is they aren't really needed from an + /// api pov. + @available(*, deprecated, message: "Please open a GitHub issue if you think functionality is missing.") + public func uniquelyNamedValues(enum e: EnumDescriptor) -> [EnumValueDescriptor] { + e.values.filter { + // Original are kept as is. The computations for relative + // name already adds values for collisions with different + // values. + guard let aliasOf = $0.aliasOf else { return true } + let relativeName = self.relativeName(enumValue: $0) + let aliasOfRelativeName = self.relativeName(enumValue: aliasOf) + // If the relative name matches for the alias and original, drop + // the alias. + guard relativeName != aliasOfRelativeName else { return false } + // Only include this alias if it is the first one with this name. + // (handles alias with different cases in their names that get + // mangled to a single Swift name.) + let firstAlias = aliasOf.aliases.firstIndex { + let otherRelativeName = self.relativeName(enumValue: $0) + return relativeName == otherRelativeName + } + return aliasOf.aliases[firstAlias!] === $0 + } } - } - - /// Calculate the relative name for the given oneof. - public func relativeName(oneof: OneofDescriptor) -> String { - let camelCase = NamingUtils.toUpperCamelCase(oneof.name) - return NamingUtils.sanitize(oneofName: "OneOf_\(camelCase)", forbiddenTypeNames: [self.swiftProtobufModuleName]) - } - - /// Calculate the full name for the given oneof. - public func fullName(oneof: OneofDescriptor) -> String { - return fullName(message: oneof.containingType) + "." + relativeName(oneof: oneof) - } - - /// Calculate the relative name for the given entension. - /// - /// - Precondition: `extensionField` must be FieldDescriptor for an extension. - public func relativeName(extensionField field: FieldDescriptor) -> String { - precondition(field.isExtension) - - if field.extensionScope != nil { - return NamingUtils.sanitize(messageScopedExtensionName: field.namingBase) - } else { - let swiftPrefix = typePrefix(forFile: field.file) - return swiftPrefix + "Extensions_" + field.namingBase + + /// Calculate the relative name for the given oneof. + public func relativeName(oneof: OneofDescriptor) -> String { + let camelCase = NamingUtils.toUpperCamelCase(oneof.name) + return NamingUtils.sanitize(oneofName: "OneOf_\(camelCase)", forbiddenTypeNames: [self.swiftProtobufModuleName]) } - } - /// Calculate the full name for the given extension. - /// - /// - Precondition: `extensionField` must be FieldDescriptor for an extension. - public func fullName(extensionField field: FieldDescriptor) -> String { - precondition(field.isExtension) + /// Calculate the full name for the given oneof. + public func fullName(oneof: OneofDescriptor) -> String { + fullName(message: oneof.containingType) + "." + relativeName(oneof: oneof) + } - let relativeName = self.relativeName(extensionField: field) - guard let extensionScope = field.extensionScope else { - return modulePrefix(file: field.file) + relativeName + /// Calculate the relative name for the given entension. + /// + /// - Precondition: `extensionField` must be FieldDescriptor for an extension. + public func relativeName(extensionField field: FieldDescriptor) -> String { + precondition(field.isExtension) + + if field.extensionScope != nil { + return NamingUtils.sanitize(messageScopedExtensionName: field.namingBase) + } else { + let swiftPrefix = typePrefix(forFile: field.file) + return swiftPrefix + "Extensions_" + field.namingBase + } } - let extensionScopeSwiftFullName = fullName(message: extensionScope) - let relativeNameNoBackticks = NamingUtils.trimBackticks(relativeName) - return extensionScopeSwiftFullName + ".Extensions." + relativeNameNoBackticks - } - - public typealias MessageFieldNames = (name: String, prefixed: String, has: String, clear: String) - - /// Calculate the names to use for the Swift fields on the message. - /// - /// If `prefixed` is not empty, the name prefixed with that will also be included. - /// - /// If `includeHasAndClear` is False, the has:, clear: values in the result will - /// be the empty string. - /// - /// - Precondition: `field` must be FieldDescriptor that's isn't for an extension. - public func messagePropertyNames(field: FieldDescriptor, - prefixed: String, - includeHasAndClear: Bool) -> MessageFieldNames { - precondition(!field.isExtension) - - let lowerName = NamingUtils.toLowerCamelCase(field.namingBase) - let fieldName = NamingUtils.sanitize(fieldName: lowerName) - let prefixedFieldName = - prefixed.isEmpty ? "" : NamingUtils.sanitize(fieldName: "\(prefixed)\(lowerName)", basedOn: lowerName) - - if !includeHasAndClear { - return MessageFieldNames(name: fieldName, prefixed: prefixedFieldName, has: "", clear: "") + + /// Calculate the full name for the given extension. + /// + /// - Precondition: `extensionField` must be FieldDescriptor for an extension. + public func fullName(extensionField field: FieldDescriptor) -> String { + precondition(field.isExtension) + + let relativeName = self.relativeName(extensionField: field) + guard let extensionScope = field.extensionScope else { + return modulePrefix(file: field.file) + relativeName + } + let extensionScopeSwiftFullName = fullName(message: extensionScope) + let relativeNameNoBackticks = NamingUtils.trimBackticks(relativeName) + return extensionScopeSwiftFullName + ".Extensions." + relativeNameNoBackticks } - let upperName = NamingUtils.toUpperCamelCase(field.namingBase) - let hasName = NamingUtils.sanitize(fieldName: "has\(upperName)", basedOn: lowerName) - let clearName = NamingUtils.sanitize(fieldName: "clear\(upperName)", basedOn: lowerName) - - return MessageFieldNames(name: fieldName, prefixed: prefixedFieldName, has: hasName, clear: clearName) - } - - public typealias OneofFieldNames = (name: String, prefixed: String) - - /// Calculate the name to use for the Swift field on the message. - public func messagePropertyName(oneof: OneofDescriptor, prefixed: String = "_") -> OneofFieldNames { - let lowerName = NamingUtils.toLowerCamelCase(oneof.name) - let fieldName = NamingUtils.sanitize(fieldName: lowerName) - let prefixedFieldName = NamingUtils.sanitize(fieldName: "\(prefixed)\(lowerName)", basedOn: lowerName) - return OneofFieldNames(name: fieldName, prefixed: prefixedFieldName) - } - - public typealias MessageExtensionNames = (value: String, has: String, clear: String) - - /// Calculate the names to use for the Swift Extension on the extended - /// message. - /// - /// - Precondition: `extensionField` must be FieldDescriptor for an extension. - public func messagePropertyNames(extensionField field: FieldDescriptor) -> MessageExtensionNames { - precondition(field.isExtension) - - let fieldBaseName = NamingUtils.toLowerCamelCase(field.namingBase) - - let fieldName: String - let hasName: String - let clearName: String - - if let extensionScope = field.extensionScope { - let extensionScopeSwiftFullName = fullName(message: extensionScope) - // Don't worry about any sanitize api on these names; since there is a - // Message name on the front, it should never hit a reserved word. - // - // fieldBaseName is the lowerCase name even though we put more on the - // front, this seems to help make the field name stick out a little - // compared to the message name scoping it on the front. - fieldName = NamingUtils.periodsToUnderscores(extensionScopeSwiftFullName + "_" + fieldBaseName) - let fieldNameFirstUp = NamingUtils.uppercaseFirstCharacter(fieldName) - hasName = "has" + fieldNameFirstUp - clearName = "clear" + fieldNameFirstUp - } else { - // If there was no package and no prefix, fieldBaseName could be a reserved - // word, so sanitize. These's also the slim chance the prefix plus the - // extension name resulted in a reserved word, so the sanitize is always - // needed. - let swiftPrefix = typePrefix(forFile: field.file) - fieldName = NamingUtils.sanitize(fieldName: swiftPrefix + fieldBaseName) - if swiftPrefix.isEmpty { - // No prefix, so got back to UpperCamelCasing the extension name, and then - // sanitize it like we did for the lower form. - let upperCleaned = NamingUtils.sanitize(fieldName: NamingUtils.toUpperCamelCase(field.namingBase), - basedOn: fieldBaseName) - hasName = "has" + upperCleaned - clearName = "clear" + upperCleaned - } else { - // Since there was a prefix, just add has/clear and ensure the first letter - // was capitalized. - let fieldNameFirstUp = NamingUtils.uppercaseFirstCharacter(fieldName) - hasName = "has" + fieldNameFirstUp - clearName = "clear" + fieldNameFirstUp - } + public typealias MessageFieldNames = (name: String, prefixed: String, has: String, clear: String) + + /// Calculate the names to use for the Swift fields on the message. + /// + /// If `prefixed` is not empty, the name prefixed with that will also be included. + /// + /// If `includeHasAndClear` is False, the has:, clear: values in the result will + /// be the empty string. + /// + /// - Precondition: `field` must be FieldDescriptor that's isn't for an extension. + public func messagePropertyNames( + field: FieldDescriptor, + prefixed: String, + includeHasAndClear: Bool + ) -> MessageFieldNames { + precondition(!field.isExtension) + + let lowerName = NamingUtils.toLowerCamelCase(field.namingBase) + let fieldName = NamingUtils.sanitize(fieldName: lowerName) + let prefixedFieldName = + prefixed.isEmpty ? "" : NamingUtils.sanitize(fieldName: "\(prefixed)\(lowerName)", basedOn: lowerName) + + if !includeHasAndClear { + return MessageFieldNames(name: fieldName, prefixed: prefixedFieldName, has: "", clear: "") + } + + let upperName = NamingUtils.toUpperCamelCase(field.namingBase) + let hasName = NamingUtils.sanitize(fieldName: "has\(upperName)", basedOn: lowerName) + let clearName = NamingUtils.sanitize(fieldName: "clear\(upperName)", basedOn: lowerName) + + return MessageFieldNames(name: fieldName, prefixed: prefixedFieldName, has: hasName, clear: clearName) } - return MessageExtensionNames(value: fieldName, has: hasName, clear: clearName) - } + public typealias OneofFieldNames = (name: String, prefixed: String) - /// Calculate the prefix to use for this file, it is derived from the - /// proto package or swift_prefix file option. - public func typePrefix(forFile file: FileDescriptor) -> String { - if let result = filePrefixCache[file.name] { - return result + /// Calculate the name to use for the Swift field on the message. + public func messagePropertyName(oneof: OneofDescriptor, prefixed: String = "_") -> OneofFieldNames { + let lowerName = NamingUtils.toLowerCamelCase(oneof.name) + let fieldName = NamingUtils.sanitize(fieldName: lowerName) + let prefixedFieldName = NamingUtils.sanitize(fieldName: "\(prefixed)\(lowerName)", basedOn: lowerName) + return OneofFieldNames(name: fieldName, prefixed: prefixedFieldName) } - let result = NamingUtils.typePrefix(protoPackage: file.package, - fileOptions: file.options) - filePrefixCache[file.name] = result - return result - } + public typealias MessageExtensionNames = (value: String, has: String, clear: String) + + /// Calculate the names to use for the Swift Extension on the extended + /// message. + /// + /// - Precondition: `extensionField` must be FieldDescriptor for an extension. + public func messagePropertyNames(extensionField field: FieldDescriptor) -> MessageExtensionNames { + precondition(field.isExtension) + + let fieldBaseName = NamingUtils.toLowerCamelCase(field.namingBase) + + let fieldName: String + let hasName: String + let clearName: String + + if let extensionScope = field.extensionScope { + let extensionScopeSwiftFullName = fullName(message: extensionScope) + // Don't worry about any sanitize api on these names; since there is a + // Message name on the front, it should never hit a reserved word. + // + // fieldBaseName is the lowerCase name even though we put more on the + // front, this seems to help make the field name stick out a little + // compared to the message name scoping it on the front. + fieldName = NamingUtils.periodsToUnderscores(extensionScopeSwiftFullName + "_" + fieldBaseName) + let fieldNameFirstUp = NamingUtils.uppercaseFirstCharacter(fieldName) + hasName = "has" + fieldNameFirstUp + clearName = "clear" + fieldNameFirstUp + } else { + // If there was no package and no prefix, fieldBaseName could be a reserved + // word, so sanitize. These's also the slim chance the prefix plus the + // extension name resulted in a reserved word, so the sanitize is always + // needed. + let swiftPrefix = typePrefix(forFile: field.file) + fieldName = NamingUtils.sanitize(fieldName: swiftPrefix + fieldBaseName) + if swiftPrefix.isEmpty { + // No prefix, so got back to UpperCamelCasing the extension name, and then + // sanitize it like we did for the lower form. + let upperCleaned = NamingUtils.sanitize( + fieldName: NamingUtils.toUpperCamelCase(field.namingBase), + basedOn: fieldBaseName + ) + hasName = "has" + upperCleaned + clearName = "clear" + upperCleaned + } else { + // Since there was a prefix, just add has/clear and ensure the first letter + // was capitalized. + let fieldNameFirstUp = NamingUtils.uppercaseFirstCharacter(fieldName) + hasName = "has" + fieldNameFirstUp + clearName = "clear" + fieldNameFirstUp + } + } - /// Internal helper to find the module prefix for a symbol given a file. - func modulePrefix(file: FileDescriptor) -> String { - guard let prefix = mappings.moduleName(forFile: file) else { - return String() + return MessageExtensionNames(value: fieldName, has: hasName, clear: clearName) } - if prefix == targetModule { - return String() + /// Calculate the prefix to use for this file, it is derived from the + /// proto package or swift_prefix file option. + public func typePrefix(forFile file: FileDescriptor) -> String { + if let result = filePrefixCache[file.name] { + return result + } + + let result = NamingUtils.typePrefix( + protoPackage: file.package, + fileOptions: file.options + ) + filePrefixCache[file.name] = result + return result } - return "\(prefix)." - } + /// Internal helper to find the module prefix for a symbol given a file. + func modulePrefix(file: FileDescriptor) -> String { + guard let prefix = mappings.moduleName(forFile: file) else { + return String() + } + + if prefix == targetModule { + return String() + } + + return "\(prefix)." + } } diff --git a/Sources/SwiftProtobufPluginLibrary/UnicodeScalar+Extensions.swift b/Sources/SwiftProtobufPluginLibrary/UnicodeScalar+Extensions.swift index 903fec9ed..49c46b699 100644 --- a/Sources/SwiftProtobufPluginLibrary/UnicodeScalar+Extensions.swift +++ b/Sources/SwiftProtobufPluginLibrary/UnicodeScalar+Extensions.swift @@ -18,41 +18,41 @@ extension UnicodeScalar { - /// True if the receiver is a numeric digit. - var isASCDigit: Bool { - if case "0"..."9" = self { return true } - return false - } + /// True if the receiver is a numeric digit. + var isASCDigit: Bool { + if case "0"..."9" = self { return true } + return false + } - /// True if the receiver is a lowercase character. - var isASCLowercase: Bool { - if case "a"..."z" = self { return true } - return false - } + /// True if the receiver is a lowercase character. + var isASCLowercase: Bool { + if case "a"..."z" = self { return true } + return false + } - /// True if the receiver is an uppercase character. - var isASCUppercase: Bool { - if case "A"..."Z" = self { return true } - return false - } + /// True if the receiver is an uppercase character. + var isASCUppercase: Bool { + if case "A"..."Z" = self { return true } + return false + } - /// Returns the lowercased version of the receiver, or the receiver itself if - /// it is not a cased character. - /// - /// - Precondition: The receiver is 7-bit ASCII. - /// - Returns: The lowercased version of the receiver, or `self`. - func ascLowercased() -> UnicodeScalar { - if isASCUppercase { return UnicodeScalar(value + 0x20)! } - return self - } + /// Returns the lowercased version of the receiver, or the receiver itself if + /// it is not a cased character. + /// + /// - Precondition: The receiver is 7-bit ASCII. + /// - Returns: The lowercased version of the receiver, or `self`. + func ascLowercased() -> UnicodeScalar { + if isASCUppercase { return UnicodeScalar(value + 0x20)! } + return self + } - /// Returns the uppercased version of the receiver, or the receiver itself if - /// it is not a cased character. - /// - /// - Precondition: The receiver is 7-bit ASCII. - /// - Returns: The uppercased version of the receiver, or `self`. - func ascUppercased() -> UnicodeScalar { - if isASCLowercase { return UnicodeScalar(value - 0x20)! } - return self - } + /// Returns the uppercased version of the receiver, or the receiver itself if + /// it is not a cased character. + /// + /// - Precondition: The receiver is 7-bit ASCII. + /// - Returns: The uppercased version of the receiver, or `self`. + func ascUppercased() -> UnicodeScalar { + if isASCLowercase { return UnicodeScalar(value - 0x20)! } + return self + } } diff --git a/Sources/SwiftProtobufTestHelpers/Descriptor+TestHelpers.swift b/Sources/SwiftProtobufTestHelpers/Descriptor+TestHelpers.swift index 817b853d4..e96c2bdc2 100644 --- a/Sources/SwiftProtobufTestHelpers/Descriptor+TestHelpers.swift +++ b/Sources/SwiftProtobufTestHelpers/Descriptor+TestHelpers.swift @@ -10,36 +10,36 @@ import SwiftProtobuf -public extension Google_Protobuf_FileDescriptorProto { - init(name: String, dependencies: [String] = [], publicDependencies: [Int32] = [], package: String = "") { - for idx in publicDependencies { precondition(Int(idx) <= dependencies.count) } - self.init() - self.name = name - self.dependency = dependencies - self.publicDependency = publicDependencies - self.package = package - } - init(textFormatStrings: [String]) throws { - let s = textFormatStrings.joined(separator: "\n") + "\n" - try self.init(textFormatString: s) - } +extension Google_Protobuf_FileDescriptorProto { + public init(name: String, dependencies: [String] = [], publicDependencies: [Int32] = [], package: String = "") { + for idx in publicDependencies { precondition(Int(idx) <= dependencies.count) } + self.init() + self.name = name + self.dependency = dependencies + self.publicDependency = publicDependencies + self.package = package + } + public init(textFormatStrings: [String]) throws { + let s = textFormatStrings.joined(separator: "\n") + "\n" + try self.init(textFormatString: s) + } } -public extension Google_Protobuf_FileDescriptorSet { - init(files: [Google_Protobuf_FileDescriptorProto]) { - self.init() - self.file = files - } - init(file: Google_Protobuf_FileDescriptorProto) { - self.init() - self.file = [file] - } +extension Google_Protobuf_FileDescriptorSet { + public init(files: [Google_Protobuf_FileDescriptorProto]) { + self.init() + self.file = files + } + public init(file: Google_Protobuf_FileDescriptorProto) { + self.init() + self.file = [file] + } } -public extension Google_Protobuf_EnumValueDescriptorProto { - init(name: String, number: Int32) { - self.init() - self.name = name - self.number = number - } +extension Google_Protobuf_EnumValueDescriptorProto { + public init(name: String, number: Int32) { + self.init() + self.name = name + self.number = number + } } diff --git a/Sources/protoc-gen-swift/CommandLine+Extensions.swift b/Sources/protoc-gen-swift/CommandLine+Extensions.swift index 77eb54296..3c3f31fbe 100644 --- a/Sources/protoc-gen-swift/CommandLine+Extensions.swift +++ b/Sources/protoc-gen-swift/CommandLine+Extensions.swift @@ -12,19 +12,19 @@ import Foundation extension CommandLine { - static var programName: String { - // Get the command-line arguments passed to this process in a non mutable - // form. Idea from https://github.com/swiftlang/swift/issues/66213 - let safeArgs: [String] = - UnsafeBufferPointer(start: unsafeArgv, count: Int(argc)).compactMap { - String(validatingUTF8: $0!) - } + static var programName: String { + // Get the command-line arguments passed to this process in a non mutable + // form. Idea from https://github.com/swiftlang/swift/issues/66213 + let safeArgs: [String] = + UnsafeBufferPointer(start: unsafeArgv, count: Int(argc)).compactMap { + String(validatingUTF8: $0!) + } - guard let base = safeArgs.first else { - return "protoc-gen-swift" + guard let base = safeArgs.first else { + return "protoc-gen-swift" + } + // Strip it down to just the leaf if it was a path. + let parts = splitPath(pathname: base) + return parts.base + parts.suffix } - // Strip it down to just the leaf if it was a path. - let parts = splitPath(pathname: base) - return parts.base + parts.suffix - } } diff --git a/Sources/protoc-gen-swift/Descriptor+Extensions.swift b/Sources/protoc-gen-swift/Descriptor+Extensions.swift index 4b2501d82..4e7cefbcb 100644 --- a/Sources/protoc-gen-swift/Descriptor+Extensions.swift +++ b/Sources/protoc-gen-swift/Descriptor+Extensions.swift @@ -11,522 +11,527 @@ import SwiftProtobufPluginLibrary extension FileDescriptor { - var isBundledProto: Bool { - return SwiftProtobufInfo.isBundledProto(file: self) - } - - // Returns true if the file will need to import Foundation. - // - // `bytes` fields are modeled as `Data`, that is currently the only reason - // why the generated sources need to `import Foundation`. - var needsFoundationImport: Bool { - if extensions.contains(where: { $0.type == .bytes }) { - return true + var isBundledProto: Bool { + SwiftProtobufInfo.isBundledProto(file: self) } - if messages.contains(where: { $0.needsFoundationImport }) { - return true - } + // Returns true if the file will need to import Foundation. + // + // `bytes` fields are modeled as `Data`, that is currently the only reason + // why the generated sources need to `import Foundation`. + var needsFoundationImport: Bool { + if extensions.contains(where: { $0.type == .bytes }) { + return true + } - return false - } - - // Returns a string of any import lines for the give file based on the file's - // imports. The string may include multiple lines. - // - // Protocol Buffers has the concept of "public imports", these are imports - // into a file that expose everything from within the file to the new - // context. From the docs - - // https://protobuf.dev/programming-guides/proto/#importing - // `import public` dependencies can be transitively relied upon by anyone - // importing the proto containing the import public statement. - // To properly expose the types for use, it means in each file, the public - // imports from the dependencies (recursively) have to be hoisted and - // reexported. This way someone importing a given module still sees the type - // even when moved. - // - // NOTE: There is a weakness for Swift with protobuf extensions. To make - // the protobuf extensions easier to use, a Swift extension is declared with - // field exposed as a property on the extended message. There is no way - // to reexport the Swift `extension` and/or added properties. But the raw - // types are re-exported to minimize the breaking of code if a type is moved - // between files/modules. - // - // `reexportPublicImports` will cause the `import public` types to be - // reexported to avoid breaking downstream code using a type that might have - // moved between .proto files. - // - // `asImplementationOnly` will cause all of the import directives to be - // marked as `@_implementationOnly`. It will also cause all of the `file`'s - // `publicDependencies` to instead be recursively pulled up as direct imports - // to ensure the generate file compiles, and no `import public` files are - // re-exported. - // - // Aside: This could be moved into the plugin library, but it doesn't seem - // like anyone else would need the logic. Swift GRPC support probably stick - // with the support for the module mappings. - func computeImports( - namer: SwiftProtobufNamer, - directive: GeneratorOptions.ImportDirective, - reexportPublicImports: Bool - ) -> String { - // The namer should be configured with the module this file generated for. - assert(namer.targetModule == (namer.mappings.moduleName(forFile: self) ?? "")) - // Both options can't be enabled. - assert(!reexportPublicImports || directive != .implementationOnly) - - guard namer.mappings.hasMappings else { - // No module mappings? Everything must be the same module, so no Swift - // imports will be needed. - return "" - } + if messages.contains(where: { $0.needsFoundationImport }) { + return true + } - if dependencies.isEmpty { - // No proto dependencies (imports), then no Swift imports will be needed. - return "" + return false } - let importSnippet = directive.snippet - var imports = Set() - for dependency in dependencies { - if SwiftProtobufInfo.isBundledProto(file: dependency) { - continue // No import needed for the runtime, that's always added. - } - if reexportPublicImports && publicDependencies.contains(where: { $0 === dependency }) { - // When re-exporting, the `import public` types will be imported - // instead of importing the module. - continue - } - if let depModule = namer.mappings.moduleName(forFile: dependency), - depModule != namer.targetModule { - // Different module, import it. - imports.insert("\(importSnippet) \(depModule)") - } - } + // Returns a string of any import lines for the give file based on the file's + // imports. The string may include multiple lines. + // + // Protocol Buffers has the concept of "public imports", these are imports + // into a file that expose everything from within the file to the new + // context. From the docs - + // https://protobuf.dev/programming-guides/proto/#importing + // `import public` dependencies can be transitively relied upon by anyone + // importing the proto containing the import public statement. + // To properly expose the types for use, it means in each file, the public + // imports from the dependencies (recursively) have to be hoisted and + // reexported. This way someone importing a given module still sees the type + // even when moved. + // + // NOTE: There is a weakness for Swift with protobuf extensions. To make + // the protobuf extensions easier to use, a Swift extension is declared with + // field exposed as a property on the extended message. There is no way + // to reexport the Swift `extension` and/or added properties. But the raw + // types are re-exported to minimize the breaking of code if a type is moved + // between files/modules. + // + // `reexportPublicImports` will cause the `import public` types to be + // reexported to avoid breaking downstream code using a type that might have + // moved between .proto files. + // + // `asImplementationOnly` will cause all of the import directives to be + // marked as `@_implementationOnly`. It will also cause all of the `file`'s + // `publicDependencies` to instead be recursively pulled up as direct imports + // to ensure the generate file compiles, and no `import public` files are + // re-exported. + // + // Aside: This could be moved into the plugin library, but it doesn't seem + // like anyone else would need the logic. Swift GRPC support probably stick + // with the support for the module mappings. + func computeImports( + namer: SwiftProtobufNamer, + directive: GeneratorOptions.ImportDirective, + reexportPublicImports: Bool + ) -> String { + // The namer should be configured with the module this file generated for. + assert(namer.targetModule == (namer.mappings.moduleName(forFile: self) ?? "")) + // Both options can't be enabled. + assert(!reexportPublicImports || directive != .implementationOnly) + + guard namer.mappings.hasMappings else { + // No module mappings? Everything must be the same module, so no Swift + // imports will be needed. + return "" + } - // If not re-exporting imports, then there is nothing special needed for - // `import public` files, as any transitive `import public` directives - // would have already re-exported the types, so everything this file needs - // will be covered by the above imports. - let exportingImports: [String] = reexportPublicImports - ? computeSymbolReExports( - namer: namer, - useAccessLevelOnImports: directive.isAccessLevel) - : [String]() - - var result = imports.sorted().joined(separator: "\n") - if !exportingImports.isEmpty { - if !result.isEmpty { - result.append("\n") - } - result.append("// Use of 'import public' causes re-exports:\n") - result.append(exportingImports.sorted().joined(separator: "\n")) + if dependencies.isEmpty { + // No proto dependencies (imports), then no Swift imports will be needed. + return "" + } + + let importSnippet = directive.snippet + var imports = Set() + for dependency in dependencies { + if SwiftProtobufInfo.isBundledProto(file: dependency) { + continue // No import needed for the runtime, that's always added. + } + if reexportPublicImports && publicDependencies.contains(where: { $0 === dependency }) { + // When re-exporting, the `import public` types will be imported + // instead of importing the module. + continue + } + if let depModule = namer.mappings.moduleName(forFile: dependency), + depModule != namer.targetModule + { + // Different module, import it. + imports.insert("\(importSnippet) \(depModule)") + } + } + + // If not re-exporting imports, then there is nothing special needed for + // `import public` files, as any transitive `import public` directives + // would have already re-exported the types, so everything this file needs + // will be covered by the above imports. + let exportingImports: [String] = + reexportPublicImports + ? computeSymbolReExports( + namer: namer, + useAccessLevelOnImports: directive.isAccessLevel + ) + : [String]() + + var result = imports.sorted().joined(separator: "\n") + if !exportingImports.isEmpty { + if !result.isEmpty { + result.append("\n") + } + result.append("// Use of 'import public' causes re-exports:\n") + result.append(exportingImports.sorted().joined(separator: "\n")) + } + return result } - return result - } - - // Internal helper to `computeImports(...)`. - private func computeSymbolReExports(namer: SwiftProtobufNamer, useAccessLevelOnImports: Bool) -> [String] { - var result = [String]() - - // To handle re-exporting, recursively walk all the `import public` files - // and make this module do a Swift exporting import of the specific - // symbols. That will keep any type that gets moved between .proto files - // still exposed from the same modules so as not to break developer - // authored code. - var toScan = publicDependencies - var visited = Set() - let exportedImportDirective = "@_exported\(useAccessLevelOnImports ? " public" : "") import" - while let dependency = toScan.popLast() { - let dependencyName = dependency.name - if visited.contains(dependencyName) { continue } - visited.insert(dependencyName) - - if SwiftProtobufInfo.isBundledProto(file: dependency) { - continue // Bundlined file, nothing to do. - } - guard let depModule = namer.mappings.moduleName(forFile: dependency) else { - continue // No mapping, assume same module, nothing to do. - } - if depModule == namer.targetModule { - // Same module, nothing to do (that generated file will do any re-exports). - continue - } - - toScan.append(contentsOf: dependency.publicDependencies) - - // NOTE: This re-exports/imports from the module that defines the type. - // If Xcode/SwiftPM ever were to do some sort of "layering checks" to - // ensure there is a direct dependency on the thing being imported, this - // could be updated do the re-export/import from the middle step in - // chained imports. - - for m in dependency.messages { - result.append("\(exportedImportDirective) struct \(namer.fullName(message: m))") - } - for e in dependency.enums { - result.append("\(exportedImportDirective) enum \(namer.fullName(enum: e))") - } - // There is nothing we can do for the Swift extensions declared on the - // extended Messages, best we can do is expose the raw extensions - // themselves. - for e in dependency.extensions { - result.append("\(exportedImportDirective) let \(namer.fullName(extensionField: e))") - } + + // Internal helper to `computeImports(...)`. + private func computeSymbolReExports(namer: SwiftProtobufNamer, useAccessLevelOnImports: Bool) -> [String] { + var result = [String]() + + // To handle re-exporting, recursively walk all the `import public` files + // and make this module do a Swift exporting import of the specific + // symbols. That will keep any type that gets moved between .proto files + // still exposed from the same modules so as not to break developer + // authored code. + var toScan = publicDependencies + var visited = Set() + let exportedImportDirective = "@_exported\(useAccessLevelOnImports ? " public" : "") import" + while let dependency = toScan.popLast() { + let dependencyName = dependency.name + if visited.contains(dependencyName) { continue } + visited.insert(dependencyName) + + if SwiftProtobufInfo.isBundledProto(file: dependency) { + continue // Bundlined file, nothing to do. + } + guard let depModule = namer.mappings.moduleName(forFile: dependency) else { + continue // No mapping, assume same module, nothing to do. + } + if depModule == namer.targetModule { + // Same module, nothing to do (that generated file will do any re-exports). + continue + } + + toScan.append(contentsOf: dependency.publicDependencies) + + // NOTE: This re-exports/imports from the module that defines the type. + // If Xcode/SwiftPM ever were to do some sort of "layering checks" to + // ensure there is a direct dependency on the thing being imported, this + // could be updated do the re-export/import from the middle step in + // chained imports. + + for m in dependency.messages { + result.append("\(exportedImportDirective) struct \(namer.fullName(message: m))") + } + for e in dependency.enums { + result.append("\(exportedImportDirective) enum \(namer.fullName(enum: e))") + } + // There is nothing we can do for the Swift extensions declared on the + // extended Messages, best we can do is expose the raw extensions + // themselves. + for e in dependency.extensions { + result.append("\(exportedImportDirective) let \(namer.fullName(extensionField: e))") + } + } + return result } - return result - } } extension Descriptor { - /// Returns true if the message should use the message set wireformat. - var useMessageSetWireFormat: Bool { return options.messageSetWireFormat } - - /// Returns true if the file will need to import Foundation. - /// - /// `bytes` fields are modeled as `Data`, that is currently the only reason - /// why the generated sources need to `import Foundation`. - var needsFoundationImport: Bool { - if fields.contains(where: { $0.type == .bytes }) { - return true - } - if extensions.contains(where: { $0.type == .bytes }) { - return true - } - - // Now recurse through sub-messages. - if messages.contains(where: { $0.needsFoundationImport }) { - return true - } + /// Returns true if the message should use the message set wireformat. + var useMessageSetWireFormat: Bool { options.messageSetWireFormat } - return false - } - - /// Returns True if this message recursively contains a required field. - /// This is a helper for generating isInitialized methods. - /// - /// The logic for this check comes from google/protobuf; the C++ and Java - /// generators specifically - func containsRequiredFields() -> Bool { - var alreadySeen = Set() - - func helper(_ descriptor: Descriptor) -> Bool { - if alreadySeen.contains(descriptor.fullName) { - // First required thing found causes this to return true, so one can - // assume if it is already visited and and wasn't cached, it is part - // of a recursive cycle, so return false without caching to allow - // the evaluation to continue on other fields of the message. - return false - } - alreadySeen.insert(descriptor.fullName) - - // If it can support extensions, then return true as an extension could - // have a required field. - if !descriptor.messageExtensionRanges.isEmpty { - return true - } - - for f in descriptor.fields { - if f.isRequired { - return true + /// Returns true if the file will need to import Foundation. + /// + /// `bytes` fields are modeled as `Data`, that is currently the only reason + /// why the generated sources need to `import Foundation`. + var needsFoundationImport: Bool { + if fields.contains(where: { $0.type == .bytes }) { + return true + } + if extensions.contains(where: { $0.type == .bytes }) { + return true } - if let messageType = f.messageType, helper(messageType) { - return true + + // Now recurse through sub-messages. + if messages.contains(where: { $0.needsFoundationImport }) { + return true } - } - return false + return false } - return helper(self) - } - - /// The `extensionRanges` are in the order they appear in the original .proto - /// file; this orders them and then merges any ranges that are actually - /// contiguous (i.e. - [(21,30),(10,20)] -> [(10,30)]) - /// - /// This also uses Range<> since the options that could be on - /// `extensionRanges` no longer can apply as the things have been merged. - var _normalizedExtensionRanges: [Range] { - var ordered: [Range] = self.messageExtensionRanges.sorted(by: { - return $0.start < $1.start }).map { return $0.start ..< $0.end + /// Returns True if this message recursively contains a required field. + /// This is a helper for generating isInitialized methods. + /// + /// The logic for this check comes from google/protobuf; the C++ and Java + /// generators specifically + func containsRequiredFields() -> Bool { + var alreadySeen = Set() + + func helper(_ descriptor: Descriptor) -> Bool { + if alreadySeen.contains(descriptor.fullName) { + // First required thing found causes this to return true, so one can + // assume if it is already visited and and wasn't cached, it is part + // of a recursive cycle, so return false without caching to allow + // the evaluation to continue on other fields of the message. + return false + } + alreadySeen.insert(descriptor.fullName) + + // If it can support extensions, then return true as an extension could + // have a required field. + if !descriptor.messageExtensionRanges.isEmpty { + return true + } + + for f in descriptor.fields { + if f.isRequired { + return true + } + if let messageType = f.messageType, helper(messageType) { + return true + } + } + + return false + } + + return helper(self) } - if ordered.count > 1 { - for i in (0..<(ordered.count - 1)).reversed() { - if ordered[i].upperBound == ordered[i+1].lowerBound { - ordered[i] = ordered[i].lowerBound ..< ordered[i+1].upperBound - ordered.remove(at: i + 1) + + /// The `extensionRanges` are in the order they appear in the original .proto + /// file; this orders them and then merges any ranges that are actually + /// contiguous (i.e. - [(21,30),(10,20)] -> [(10,30)]) + /// + /// This also uses Range<> since the options that could be on + /// `extensionRanges` no longer can apply as the things have been merged. + var _normalizedExtensionRanges: [Range] { + var ordered: [Range] = self.messageExtensionRanges.sorted(by: { + return $0.start < $1.start + }).map { + return $0.start..<$0.end + } + if ordered.count > 1 { + for i in (0..<(ordered.count - 1)).reversed() { + if ordered[i].upperBound == ordered[i + 1].lowerBound { + ordered[i] = ordered[i].lowerBound.. since the options that could be on - /// `extensionRanges` no longer can apply as the things have been merged. - var _ambitiousExtensionRanges: [Range] { - var merged = self._normalizedExtensionRanges - if merged.count > 1 { - var fieldNumbersReversedIterator = - self.fields.map({ Int($0.number) }).sorted(by: { $0 > $1 }).makeIterator() - var nextFieldNumber = fieldNumbersReversedIterator.next() - while nextFieldNumber != nil && merged.last!.lowerBound < nextFieldNumber! { - nextFieldNumber = fieldNumbersReversedIterator.next() - } - - for i in (0..<(merged.count - 1)).reversed() { - if nextFieldNumber == nil || merged[i].lowerBound > nextFieldNumber! { - // No fields left or range starts after the next field, merge it with - // the previous one. - merged[i] = merged[i].lowerBound ..< merged[i+1].upperBound - merged.remove(at: i + 1) - } else { - // can't merge, find the next field number below this range. - while nextFieldNumber != nil && merged[i].lowerBound < nextFieldNumber! { - nextFieldNumber = fieldNumbersReversedIterator.next() - } + + /// The `extensionRanges` from `normalizedExtensionRanges`, but takes a step + /// further in that any ranges that do _not_ have any fields inbetween them + /// are also merged together. These can then be used in context where it is + /// ok to include field numbers that have to be extension or unknown fields. + /// + /// This also uses Range<> since the options that could be on + /// `extensionRanges` no longer can apply as the things have been merged. + var _ambitiousExtensionRanges: [Range] { + var merged = self._normalizedExtensionRanges + if merged.count > 1 { + var fieldNumbersReversedIterator = + self.fields.map({ Int($0.number) }).sorted(by: { $0 > $1 }).makeIterator() + var nextFieldNumber = fieldNumbersReversedIterator.next() + while nextFieldNumber != nil && merged.last!.lowerBound < nextFieldNumber! { + nextFieldNumber = fieldNumbersReversedIterator.next() + } + + for i in (0..<(merged.count - 1)).reversed() { + if nextFieldNumber == nil || merged[i].lowerBound > nextFieldNumber! { + // No fields left or range starts after the next field, merge it with + // the previous one. + merged[i] = merged[i].lowerBound.. String { - if case (let keyField, let valueField)? = messageType?.mapKeyAndValue { - let keyType = keyField.swiftType(namer: namer) - let valueType = valueField.swiftType(namer: namer) - return "Dictionary<" + keyType + "," + valueType + ">" - } + func swiftType(namer: SwiftProtobufNamer) -> String { + if case (let keyField, let valueField)? = messageType?.mapKeyAndValue { + let keyType = keyField.swiftType(namer: namer) + let valueType = valueField.swiftType(namer: namer) + return "Dictionary<" + keyType + "," + valueType + ">" + } - let result: String - switch type { - case .double: result = "Double" - case .float: result = "Float" - case .int64: result = "Int64" - case .uint64: result = "UInt64" - case .int32: result = "Int32" - case .fixed64: result = "UInt64" - case .fixed32: result = "UInt32" - case .bool: result = "Bool" - case .string: result = "String" - case .group: result = namer.fullName(message: messageType!) - case .message: result = namer.fullName(message: messageType!) - case .bytes: result = "Data" - case .uint32: result = "UInt32" - case .enum: result = namer.fullName(enum: enumType!) - case .sfixed32: result = "Int32" - case .sfixed64: result = "Int64" - case .sint32: result = "Int32" - case .sint64: result = "Int64" - } + let result: String + switch type { + case .double: result = "Double" + case .float: result = "Float" + case .int64: result = "Int64" + case .uint64: result = "UInt64" + case .int32: result = "Int32" + case .fixed64: result = "UInt64" + case .fixed32: result = "UInt32" + case .bool: result = "Bool" + case .string: result = "String" + case .group: result = namer.fullName(message: messageType!) + case .message: result = namer.fullName(message: messageType!) + case .bytes: result = "Data" + case .uint32: result = "UInt32" + case .enum: result = namer.fullName(enum: enumType!) + case .sfixed32: result = "Int32" + case .sfixed64: result = "Int64" + case .sint32: result = "Int32" + case .sint64: result = "Int64" + } - if label == .repeated { - return "[\(result)]" - } - return result - } - - func swiftStorageType(namer: SwiftProtobufNamer) -> String { - let swiftType = self.swiftType(namer: namer) - switch label { - case .repeated: - return swiftType - case .optional, .required: - guard realContainingOneof == nil else { - return swiftType - } - if hasPresence { - return "\(swiftType)?" - } else { - return swiftType - } - } - } - - var protoGenericType: String { - precondition(!isMap) - - switch type { - case .double: return "Double" - case .float: return "Float" - case .int64: return "Int64" - case .uint64: return "UInt64" - case .int32: return "Int32" - case .fixed64: return "Fixed64" - case .fixed32: return "Fixed32" - case .bool: return "Bool" - case .string: return "String" - case .group: return "Group" - case .message: return "Message" - case .bytes: return "Bytes" - case .uint32: return "UInt32" - case .enum: return "Enum" - case .sfixed32: return "SFixed32" - case .sfixed64: return "SFixed64" - case .sint32: return "SInt32" - case .sint64: return "SInt64" + if label == .repeated { + return "[\(result)]" + } + return result } - } - func swiftDefaultValue(namer: SwiftProtobufNamer) -> String { - if isMap { - return "[:]" + func swiftStorageType(namer: SwiftProtobufNamer) -> String { + let swiftType = self.swiftType(namer: namer) + switch label { + case .repeated: + return swiftType + case .optional, .required: + guard realContainingOneof == nil else { + return swiftType + } + if hasPresence { + return "\(swiftType)?" + } else { + return swiftType + } + } } - if label == .repeated { - return "[]" + + var protoGenericType: String { + precondition(!isMap) + + switch type { + case .double: return "Double" + case .float: return "Float" + case .int64: return "Int64" + case .uint64: return "UInt64" + case .int32: return "Int32" + case .fixed64: return "Fixed64" + case .fixed32: return "Fixed32" + case .bool: return "Bool" + case .string: return "String" + case .group: return "Group" + case .message: return "Message" + case .bytes: return "Bytes" + case .uint32: return "UInt32" + case .enum: return "Enum" + case .sfixed32: return "SFixed32" + case .sfixed64: return "SFixed64" + case .sint32: return "SInt32" + case .sint64: return "SInt64" + } } - if let defaultValue = defaultValue { - switch type { - case .double: - switch defaultValue { - case "inf": return "Double.infinity" - case "-inf": return "-Double.infinity" - case "nan": return "Double.nan" - case "-nan": return "Double.nan" - default: return defaultValue + func swiftDefaultValue(namer: SwiftProtobufNamer) -> String { + if isMap { + return "[:]" } - case .float: - switch defaultValue { - case "inf": return "Float.infinity" - case "-inf": return "-Float.infinity" - case "nan": return "Float.nan" - case "-nan": return "Float.nan" - default: return defaultValue + if label == .repeated { + return "[]" } - case .string: - return stringToEscapedStringLiteral(defaultValue) - case .bytes: - return escapedToDataLiteral(defaultValue) - case .enum: - let enumValue = enumType!.value(named: defaultValue)! - return namer.dottedRelativeName(enumValue: enumValue) - default: - return defaultValue - } - } - switch type { - case .bool: return "false" - case .string: return "String()" - case .bytes: return "Data()" - case .group, .message: - return namer.fullName(message: messageType!) + "()" - case .enum: - return namer.dottedRelativeName(enumValue: enumType!.values.first!) - default: - return "0" - } - } - - /// Calculates the traits type used for maps and extensions, they - /// are used in decoding and visiting. - func traitsType(namer: SwiftProtobufNamer) -> String { - if case (let keyField, let valueField)? = messageType?.mapKeyAndValue { - let keyTraits = keyField.traitsType(namer: namer) - let valueTraits = valueField.traitsType(namer: namer) - switch valueField.type { - case .message: // Map's can't have a group as the value - return "\(namer.swiftProtobufModulePrefix)_ProtobufMessageMap<\(keyTraits),\(valueTraits)>" - case .enum: - return "\(namer.swiftProtobufModulePrefix)_ProtobufEnumMap<\(keyTraits),\(valueTraits)>" - default: - return "\(namer.swiftProtobufModulePrefix)_ProtobufMap<\(keyTraits),\(valueTraits)>" - } + if let defaultValue = defaultValue { + switch type { + case .double: + switch defaultValue { + case "inf": return "Double.infinity" + case "-inf": return "-Double.infinity" + case "nan": return "Double.nan" + case "-nan": return "Double.nan" + default: return defaultValue + } + case .float: + switch defaultValue { + case "inf": return "Float.infinity" + case "-inf": return "-Float.infinity" + case "nan": return "Float.nan" + case "-nan": return "Float.nan" + default: return defaultValue + } + case .string: + return stringToEscapedStringLiteral(defaultValue) + case .bytes: + return escapedToDataLiteral(defaultValue) + case .enum: + let enumValue = enumType!.value(named: defaultValue)! + return namer.dottedRelativeName(enumValue: enumValue) + default: + return defaultValue + } + } + + switch type { + case .bool: return "false" + case .string: return "String()" + case .bytes: return "Data()" + case .group, .message: + return namer.fullName(message: messageType!) + "()" + case .enum: + return namer.dottedRelativeName(enumValue: enumType!.values.first!) + default: + return "0" + } } - switch type { - case .double: return "\(namer.swiftProtobufModulePrefix)ProtobufDouble" - case .float: return "\(namer.swiftProtobufModulePrefix)ProtobufFloat" - case .int64: return "\(namer.swiftProtobufModulePrefix)ProtobufInt64" - case .uint64: return "\(namer.swiftProtobufModulePrefix)ProtobufUInt64" - case .int32: return "\(namer.swiftProtobufModulePrefix)ProtobufInt32" - case .fixed64: return "\(namer.swiftProtobufModulePrefix)ProtobufFixed64" - case .fixed32: return "\(namer.swiftProtobufModulePrefix)ProtobufFixed32" - case .bool: return "\(namer.swiftProtobufModulePrefix)ProtobufBool" - case .string: return "\(namer.swiftProtobufModulePrefix)ProtobufString" - case .group, .message: return namer.fullName(message: messageType!) - case .bytes: return "\(namer.swiftProtobufModulePrefix)ProtobufBytes" - case .uint32: return "\(namer.swiftProtobufModulePrefix)ProtobufUInt32" - case .enum: return namer.fullName(enum: enumType!) - case .sfixed32: return "\(namer.swiftProtobufModulePrefix)ProtobufSFixed32" - case .sfixed64: return "\(namer.swiftProtobufModulePrefix)ProtobufSFixed64" - case .sint32: return "\(namer.swiftProtobufModulePrefix)ProtobufSInt32" - case .sint64: return "\(namer.swiftProtobufModulePrefix)ProtobufSInt64" + + /// Calculates the traits type used for maps and extensions, they + /// are used in decoding and visiting. + func traitsType(namer: SwiftProtobufNamer) -> String { + if case (let keyField, let valueField)? = messageType?.mapKeyAndValue { + let keyTraits = keyField.traitsType(namer: namer) + let valueTraits = valueField.traitsType(namer: namer) + switch valueField.type { + case .message: // Map's can't have a group as the value + return "\(namer.swiftProtobufModulePrefix)_ProtobufMessageMap<\(keyTraits),\(valueTraits)>" + case .enum: + return "\(namer.swiftProtobufModulePrefix)_ProtobufEnumMap<\(keyTraits),\(valueTraits)>" + default: + return "\(namer.swiftProtobufModulePrefix)_ProtobufMap<\(keyTraits),\(valueTraits)>" + } + } + switch type { + case .double: return "\(namer.swiftProtobufModulePrefix)ProtobufDouble" + case .float: return "\(namer.swiftProtobufModulePrefix)ProtobufFloat" + case .int64: return "\(namer.swiftProtobufModulePrefix)ProtobufInt64" + case .uint64: return "\(namer.swiftProtobufModulePrefix)ProtobufUInt64" + case .int32: return "\(namer.swiftProtobufModulePrefix)ProtobufInt32" + case .fixed64: return "\(namer.swiftProtobufModulePrefix)ProtobufFixed64" + case .fixed32: return "\(namer.swiftProtobufModulePrefix)ProtobufFixed32" + case .bool: return "\(namer.swiftProtobufModulePrefix)ProtobufBool" + case .string: return "\(namer.swiftProtobufModulePrefix)ProtobufString" + case .group, .message: return namer.fullName(message: messageType!) + case .bytes: return "\(namer.swiftProtobufModulePrefix)ProtobufBytes" + case .uint32: return "\(namer.swiftProtobufModulePrefix)ProtobufUInt32" + case .enum: return namer.fullName(enum: enumType!) + case .sfixed32: return "\(namer.swiftProtobufModulePrefix)ProtobufSFixed32" + case .sfixed64: return "\(namer.swiftProtobufModulePrefix)ProtobufSFixed64" + case .sint32: return "\(namer.swiftProtobufModulePrefix)ProtobufSInt32" + case .sint64: return "\(namer.swiftProtobufModulePrefix)ProtobufSInt64" + } } - } } extension EnumDescriptor { - func value(named: String) -> EnumValueDescriptor? { - for v in values { - if v.name == named { - return v - } - } - return nil - } - - /// Helper object that computes the alias relationships of - /// `EnumValueDescriptor`s for a given `EnumDescriptor`. - final class ValueAliasInfo { - /// The `EnumValueDescriptor`s that are not aliases of another value. In - /// the same order as the values on the `EnumDescriptor`. - let mainValues : [EnumValueDescriptor] - - /// Find the alias values for the given value. - /// - /// - Parameter value: The value descriptor to look up. - /// - Returns The list of value descriptors that are aliases for this - /// value, or `nil` if there are no alias (or if this was an alias). - func aliases(_ value: EnumValueDescriptor) -> [EnumValueDescriptor]? { - assert(mainValues.first!.enumType === value.enumType) - return aliasesMap[value.index] + func value(named: String) -> EnumValueDescriptor? { + for v in values { + if v.name == named { + return v + } + } + return nil } - /// Find the original for an alias. - /// - /// - Parameter value: The value descriptor to look up. - /// - Returns The original/main value if this was an alias otherwise `nil`. - func original(of: EnumValueDescriptor) -> EnumValueDescriptor? { - assert(mainValues.first!.enumType === of.enumType) - return aliasOfMap[of.index] - } + /// Helper object that computes the alias relationships of + /// `EnumValueDescriptor`s for a given `EnumDescriptor`. + final class ValueAliasInfo { + /// The `EnumValueDescriptor`s that are not aliases of another value. In + /// the same order as the values on the `EnumDescriptor`. + let mainValues: [EnumValueDescriptor] + + /// Find the alias values for the given value. + /// + /// - Parameter value: The value descriptor to look up. + /// - Returns The list of value descriptors that are aliases for this + /// value, or `nil` if there are no alias (or if this was an alias). + func aliases(_ value: EnumValueDescriptor) -> [EnumValueDescriptor]? { + assert(mainValues.first!.enumType === value.enumType) + return aliasesMap[value.index] + } - /// Mapping from index of a "main" value to the aliases for it. - private let aliasesMap: [Int:[EnumValueDescriptor]] - - /// Mapping from value's index the main value if it was an alias. - private let aliasOfMap: [Int:EnumValueDescriptor] - - /// Initialize the mappings for the given `EnumDescriptor`. - init(enumDescriptor descriptor: EnumDescriptor) { - var mainValues = [EnumValueDescriptor]() - var aliasesMap = [Int:[EnumValueDescriptor]]() - var aliasOfMap = [Int:EnumValueDescriptor]() - - var firstValues = [Int32:EnumValueDescriptor]() - for v in descriptor.values { - if let aliasing = firstValues[v.number] { - aliasesMap[aliasing.index, default: []].append(v) - aliasOfMap[v.index] = aliasing - } else { - firstValues[v.number] = v - mainValues.append(v) + /// Find the original for an alias. + /// + /// - Parameter value: The value descriptor to look up. + /// - Returns The original/main value if this was an alias otherwise `nil`. + func original(of: EnumValueDescriptor) -> EnumValueDescriptor? { + assert(mainValues.first!.enumType === of.enumType) + return aliasOfMap[of.index] } - } - self.mainValues = mainValues - self.aliasesMap = aliasesMap - self.aliasOfMap = aliasOfMap + /// Mapping from index of a "main" value to the aliases for it. + private let aliasesMap: [Int: [EnumValueDescriptor]] + + /// Mapping from value's index the main value if it was an alias. + private let aliasOfMap: [Int: EnumValueDescriptor] + + /// Initialize the mappings for the given `EnumDescriptor`. + init(enumDescriptor descriptor: EnumDescriptor) { + var mainValues = [EnumValueDescriptor]() + var aliasesMap = [Int: [EnumValueDescriptor]]() + var aliasOfMap = [Int: EnumValueDescriptor]() + + var firstValues = [Int32: EnumValueDescriptor]() + for v in descriptor.values { + if let aliasing = firstValues[v.number] { + aliasesMap[aliasing.index, default: []].append(v) + aliasOfMap[v.index] = aliasing + } else { + firstValues[v.number] = v + mainValues.append(v) + } + } + + self.mainValues = mainValues + self.aliasesMap = aliasesMap + self.aliasOfMap = aliasOfMap + } } - } } diff --git a/Sources/protoc-gen-swift/EnumGenerator.swift b/Sources/protoc-gen-swift/EnumGenerator.swift index b716c8a6d..045f9af76 100644 --- a/Sources/protoc-gen-swift/EnumGenerator.swift +++ b/Sources/protoc-gen-swift/EnumGenerator.swift @@ -13,8 +13,8 @@ // ----------------------------------------------------------------------------- import Foundation -import SwiftProtobufPluginLibrary import SwiftProtobuf +import SwiftProtobufPluginLibrary /// The name of the case used to represent unrecognized values in proto3. /// This case has an associated value containing the raw integer value. @@ -22,223 +22,231 @@ private let unrecognizedCaseName = "UNRECOGNIZED" /// Generates a Swift enum from a protobuf enum descriptor. class EnumGenerator { - // TODO: Move these conformances back onto the `Enum` protocol when we do a major release. - private static let requiredProtocolConformancesForEnums = ["Swift.CaseIterable"].joined(separator: ", ") - - private let enumDescriptor: EnumDescriptor - private let generatorOptions: GeneratorOptions - private let namer: SwiftProtobufNamer - - /// The aliasInfo for the values. - private let aliasInfo: EnumDescriptor.ValueAliasInfo - /// The values that aren't aliases, sorted by number. - private let mainEnumValueDescriptorsSorted: [EnumValueDescriptor] - - private let swiftRelativeName: String - private let swiftFullName: String - - init(descriptor: EnumDescriptor, - generatorOptions: GeneratorOptions, - namer: SwiftProtobufNamer - ) { - self.enumDescriptor = descriptor - self.generatorOptions = generatorOptions - self.namer = namer - aliasInfo = EnumDescriptor.ValueAliasInfo(enumDescriptor: descriptor) - - mainEnumValueDescriptorsSorted = aliasInfo.mainValues.sorted(by: { - return $0.number < $1.number - }) - - swiftRelativeName = namer.relativeName(enum: descriptor) - swiftFullName = namer.fullName(enum: descriptor) - } + // TODO: Move these conformances back onto the `Enum` protocol when we do a major release. + private static let requiredProtocolConformancesForEnums = ["Swift.CaseIterable"].joined(separator: ", ") + + private let enumDescriptor: EnumDescriptor + private let generatorOptions: GeneratorOptions + private let namer: SwiftProtobufNamer + + /// The aliasInfo for the values. + private let aliasInfo: EnumDescriptor.ValueAliasInfo + /// The values that aren't aliases, sorted by number. + private let mainEnumValueDescriptorsSorted: [EnumValueDescriptor] + + private let swiftRelativeName: String + private let swiftFullName: String + + init( + descriptor: EnumDescriptor, + generatorOptions: GeneratorOptions, + namer: SwiftProtobufNamer + ) { + self.enumDescriptor = descriptor + self.generatorOptions = generatorOptions + self.namer = namer + aliasInfo = EnumDescriptor.ValueAliasInfo(enumDescriptor: descriptor) + + mainEnumValueDescriptorsSorted = aliasInfo.mainValues.sorted(by: { + $0.number < $1.number + }) + + swiftRelativeName = namer.relativeName(enum: descriptor) + swiftFullName = namer.fullName(enum: descriptor) + } - func generateMainEnum(printer p: inout CodePrinter) { - let visibility = generatorOptions.visibilitySourceSnippet + func generateMainEnum(printer p: inout CodePrinter) { + let visibility = generatorOptions.visibilitySourceSnippet - p.print( - "", - "\(enumDescriptor.protoSourceCommentsWithDeprecation(generatorOptions: generatorOptions))\(visibility)enum \(swiftRelativeName): \(namer.swiftProtobufModulePrefix)Enum, \(Self.requiredProtocolConformancesForEnums) {") - p.withIndentation { p in - p.print("\(visibility)typealias RawValue = Int") + p.print( + "", + "\(enumDescriptor.protoSourceCommentsWithDeprecation(generatorOptions: generatorOptions))\(visibility)enum \(swiftRelativeName): \(namer.swiftProtobufModulePrefix)Enum, \(Self.requiredProtocolConformancesForEnums) {" + ) + p.withIndentation { p in + p.print("\(visibility)typealias RawValue = Int") - // Cases/aliases - generateCasesOrAliases(printer: &p) + // Cases/aliases + generateCasesOrAliases(printer: &p) - // Generate the default initializer. - p.print( - "", - "\(visibility)init() {") - p.printIndented("self = \(namer.dottedRelativeName(enumValue: enumDescriptor.values.first!))") - p.print("}") + // Generate the default initializer. + p.print( + "", + "\(visibility)init() {" + ) + p.printIndented("self = \(namer.dottedRelativeName(enumValue: enumDescriptor.values.first!))") + p.print("}") - p.print() - generateInitRawValue(printer: &p) + p.print() + generateInitRawValue(printer: &p) - p.print() - generateRawValueProperty(printer: &p) + p.print() + generateRawValueProperty(printer: &p) - maybeGenerateCaseIterable(printer: &p) + maybeGenerateCaseIterable(printer: &p) + } + p.print( + "", + "}" + ) } - p.print( - "", - "}") - } - - func maybeGenerateCaseIterable(printer p: inout CodePrinter) { - guard !enumDescriptor.isClosed else { return } - - let visibility = generatorOptions.visibilitySourceSnippet - p.print( - "", - "// The compiler won't synthesize support with the \(unrecognizedCaseName) case.", - "\(visibility)static let allCases: [\(swiftFullName)] = [") - p.withIndentation { p in - for v in aliasInfo.mainValues { - let dottedName = namer.dottedRelativeName(enumValue: v) - p.print("\(dottedName),") - } - } - p.print("]") - } - - func generateRuntimeSupport(printer p: inout CodePrinter) { - p.print( - "", - "extension \(swiftFullName): \(namer.swiftProtobufModulePrefix)_ProtoNameProviding {") - p.withIndentation { p in - generateProtoNameProviding(printer: &p) - } - p.print("}") - } - - /// Generates the cases or statics (for alias) for the values. - /// - /// - Parameter p: The code printer. - private func generateCasesOrAliases(printer p: inout CodePrinter) { - let visibility = generatorOptions.visibilitySourceSnippet - for enumValueDescriptor in namer.uniquelyNamedValues(valueAliasInfo: aliasInfo) { - let comments = enumValueDescriptor.protoSourceCommentsWithDeprecation(generatorOptions: generatorOptions) - if !comments.isEmpty { - p.print() - } - let relativeName = namer.relativeName(enumValue: enumValueDescriptor) - if let aliasOf = aliasInfo.original(of: enumValueDescriptor) { - let aliasOfName = namer.relativeName(enumValue: aliasOf) - p.print("\(comments)\(visibility)static let \(relativeName) = \(aliasOfName)") - } else { - p.print("\(comments)case \(relativeName) // = \(enumValueDescriptor.number)") - } - } - if !enumDescriptor.isClosed { - p.print("case \(unrecognizedCaseName)(Int)") - } - } - - /// Generates the mapping from case numbers to their text/JSON names. - /// - /// - Parameter p: The code printer. - private func generateProtoNameProviding(printer p: inout CodePrinter) { - let visibility = generatorOptions.visibilitySourceSnippet - - p.print("\(visibility)static let _protobuf_nameMap: \(namer.swiftProtobufModulePrefix)_NameMap = [") - p.withIndentation { p in - for v in mainEnumValueDescriptorsSorted { - if let aliases = aliasInfo.aliases(v) { - let aliasNames = aliases.map({ "\"\($0.name)\"" }).joined(separator: ", ") - p.print("\(v.number): .aliased(proto: \"\(v.name)\", aliases: [\(aliasNames)]),") - } else { - p.print("\(v.number): .same(proto: \"\(v.name)\"),") + + func maybeGenerateCaseIterable(printer p: inout CodePrinter) { + guard !enumDescriptor.isClosed else { return } + + let visibility = generatorOptions.visibilitySourceSnippet + p.print( + "", + "// The compiler won't synthesize support with the \(unrecognizedCaseName) case.", + "\(visibility)static let allCases: [\(swiftFullName)] = [" + ) + p.withIndentation { p in + for v in aliasInfo.mainValues { + let dottedName = namer.dottedRelativeName(enumValue: v) + p.print("\(dottedName),") + } } - } + p.print("]") } - p.print("]") - } - - /// Generates `init?(rawValue:)` for the enum. - /// - /// - Parameter p: The code printer. - private func generateInitRawValue(printer p: inout CodePrinter) { - let visibility = generatorOptions.visibilitySourceSnippet - - p.print("\(visibility)init?(rawValue: Int) {") - p.withIndentation { p in - p.print("switch rawValue {") - for v in mainEnumValueDescriptorsSorted { - let dottedName = namer.dottedRelativeName(enumValue: v) - p.print("case \(v.number): self = \(dottedName)") - } - if !enumDescriptor.isClosed { - p.print("default: self = .\(unrecognizedCaseName)(rawValue)") - } else { - p.print("default: return nil") - } - p.print("}") + + func generateRuntimeSupport(printer p: inout CodePrinter) { + p.print( + "", + "extension \(swiftFullName): \(namer.swiftProtobufModulePrefix)_ProtoNameProviding {" + ) + p.withIndentation { p in + generateProtoNameProviding(printer: &p) + } + p.print("}") } - p.print("}") - } - - /// Generates the `rawValue` property of the enum. - /// - /// - Parameter p: The code printer. - private func generateRawValueProperty(printer p: inout CodePrinter) { - let visibility = generatorOptions.visibilitySourceSnippet - - // See https://github.com/apple/swift-protobuf/issues/904 for the full - // details on why the default has to get added even though the switch - // is complete. - - // This is a "magic" value, currently picked based on the Swift 5.1 - // compiler, it will need ensure the warning doesn't trigger on all - // versions of the compiler, meaning if the error starts to show up - // again, all one can do is lower the limit. - let maxCasesInSwitch = 500 - - let neededCases = mainEnumValueDescriptorsSorted.count + - (enumDescriptor.isClosed ? 0 : 1) - let useMultipleSwitches = neededCases > maxCasesInSwitch - - p.print("\(visibility)var rawValue: Int {") - p.withIndentation { p in - if useMultipleSwitches { - for (i, v) in mainEnumValueDescriptorsSorted.enumerated() { - if (i % maxCasesInSwitch) == 0 { - if i > 0 { - p.print( - "default: break", - "}") + + /// Generates the cases or statics (for alias) for the values. + /// + /// - Parameter p: The code printer. + private func generateCasesOrAliases(printer p: inout CodePrinter) { + let visibility = generatorOptions.visibilitySourceSnippet + for enumValueDescriptor in namer.uniquelyNamedValues(valueAliasInfo: aliasInfo) { + let comments = enumValueDescriptor.protoSourceCommentsWithDeprecation(generatorOptions: generatorOptions) + if !comments.isEmpty { + p.print() + } + let relativeName = namer.relativeName(enumValue: enumValueDescriptor) + if let aliasOf = aliasInfo.original(of: enumValueDescriptor) { + let aliasOfName = namer.relativeName(enumValue: aliasOf) + p.print("\(comments)\(visibility)static let \(relativeName) = \(aliasOfName)") + } else { + p.print("\(comments)case \(relativeName) // = \(enumValueDescriptor.number)") } - p.print("switch self {") - } - let dottedName = namer.dottedRelativeName(enumValue: v) - p.print("case \(dottedName): return \(v.number)") } if !enumDescriptor.isClosed { - p.print("case .\(unrecognizedCaseName)(let i): return i") + p.print("case \(unrecognizedCaseName)(Int)") } - p.print(""" - default: break - } - - // Can't get here, all the cases are listed in the above switches. - // See https://github.com/apple/swift-protobuf/issues/904 for more details. - fatalError() - """) - } else { - p.print("switch self {") - for v in mainEnumValueDescriptorsSorted { - let dottedName = namer.dottedRelativeName(enumValue: v) - p.print("case \(dottedName): return \(v.number)") + } + + /// Generates the mapping from case numbers to their text/JSON names. + /// + /// - Parameter p: The code printer. + private func generateProtoNameProviding(printer p: inout CodePrinter) { + let visibility = generatorOptions.visibilitySourceSnippet + + p.print("\(visibility)static let _protobuf_nameMap: \(namer.swiftProtobufModulePrefix)_NameMap = [") + p.withIndentation { p in + for v in mainEnumValueDescriptorsSorted { + if let aliases = aliasInfo.aliases(v) { + let aliasNames = aliases.map({ "\"\($0.name)\"" }).joined(separator: ", ") + p.print("\(v.number): .aliased(proto: \"\(v.name)\", aliases: [\(aliasNames)]),") + } else { + p.print("\(v.number): .same(proto: \"\(v.name)\"),") + } + } } - if !enumDescriptor.isClosed { - p.print("case .\(unrecognizedCaseName)(let i): return i") + p.print("]") + } + + /// Generates `init?(rawValue:)` for the enum. + /// + /// - Parameter p: The code printer. + private func generateInitRawValue(printer p: inout CodePrinter) { + let visibility = generatorOptions.visibilitySourceSnippet + + p.print("\(visibility)init?(rawValue: Int) {") + p.withIndentation { p in + p.print("switch rawValue {") + for v in mainEnumValueDescriptorsSorted { + let dottedName = namer.dottedRelativeName(enumValue: v) + p.print("case \(v.number): self = \(dottedName)") + } + if !enumDescriptor.isClosed { + p.print("default: self = .\(unrecognizedCaseName)(rawValue)") + } else { + p.print("default: return nil") + } + p.print("}") } p.print("}") - } + } + /// Generates the `rawValue` property of the enum. + /// + /// - Parameter p: The code printer. + private func generateRawValueProperty(printer p: inout CodePrinter) { + let visibility = generatorOptions.visibilitySourceSnippet + + // See https://github.com/apple/swift-protobuf/issues/904 for the full + // details on why the default has to get added even though the switch + // is complete. + + // This is a "magic" value, currently picked based on the Swift 5.1 + // compiler, it will need ensure the warning doesn't trigger on all + // versions of the compiler, meaning if the error starts to show up + // again, all one can do is lower the limit. + let maxCasesInSwitch = 500 + + let neededCases = mainEnumValueDescriptorsSorted.count + (enumDescriptor.isClosed ? 0 : 1) + let useMultipleSwitches = neededCases > maxCasesInSwitch + + p.print("\(visibility)var rawValue: Int {") + p.withIndentation { p in + if useMultipleSwitches { + for (i, v) in mainEnumValueDescriptorsSorted.enumerated() { + if (i % maxCasesInSwitch) == 0 { + if i > 0 { + p.print( + "default: break", + "}" + ) + } + p.print("switch self {") + } + let dottedName = namer.dottedRelativeName(enumValue: v) + p.print("case \(dottedName): return \(v.number)") + } + if !enumDescriptor.isClosed { + p.print("case .\(unrecognizedCaseName)(let i): return i") + } + p.print( + """ + default: break + } + + // Can't get here, all the cases are listed in the above switches. + // See https://github.com/apple/swift-protobuf/issues/904 for more details. + fatalError() + """ + ) + } else { + p.print("switch self {") + for v in mainEnumValueDescriptorsSorted { + let dottedName = namer.dottedRelativeName(enumValue: v) + p.print("case \(dottedName): return \(v.number)") + } + if !enumDescriptor.isClosed { + p.print("case .\(unrecognizedCaseName)(let i): return i") + } + p.print("}") + } + + } + p.print("}") } - p.print("}") - } } diff --git a/Sources/protoc-gen-swift/ExtensionSetGenerator.swift b/Sources/protoc-gen-swift/ExtensionSetGenerator.swift index 8e675be0e..8e815da03 100644 --- a/Sources/protoc-gen-swift/ExtensionSetGenerator.swift +++ b/Sources/protoc-gen-swift/ExtensionSetGenerator.swift @@ -15,8 +15,8 @@ /// // ----------------------------------------------------------------------------- import Foundation -import SwiftProtobufPluginLibrary import SwiftProtobuf +import SwiftProtobufPluginLibrary /// Provides the generation for extensions in a file. class ExtensionSetGenerator { @@ -68,20 +68,21 @@ class ExtensionSetGenerator { let swiftRelativeExtensionName = namer.relativeName(extensionField: fieldDescriptor) var fieldNamePath: String - if fieldDescriptor.containingType.useMessageSetWireFormat && - fieldDescriptor.type == .message && - fieldDescriptor.label == .optional && - fieldDescriptor.messageType === fieldDescriptor.extensionScope { + if fieldDescriptor.containingType.useMessageSetWireFormat && fieldDescriptor.type == .message + && fieldDescriptor.label == .optional && fieldDescriptor.messageType === fieldDescriptor.extensionScope + { fieldNamePath = fieldDescriptor.messageType!.fullName } else { fieldNamePath = fieldDescriptor.fullName } p.print( - "\(comments)\(visibility)\(scope)let \(swiftRelativeExtensionName) = \(namer.swiftProtobufModulePrefix)MessageExtension<\(extensionFieldType)<\(traitsType)>, \(containingTypeSwiftFullName)>(") + "\(comments)\(visibility)\(scope)let \(swiftRelativeExtensionName) = \(namer.swiftProtobufModulePrefix)MessageExtension<\(extensionFieldType)<\(traitsType)>, \(containingTypeSwiftFullName)>(" + ) p.printIndented( - "_protobuf_fieldNumber: \(fieldDescriptor.number),", - "fieldName: \"\(fieldNamePath)\"") + "_protobuf_fieldNumber: \(fieldDescriptor.number),", + "fieldName: \"\(fieldNamePath)\"" + ) p.print(")") } @@ -94,25 +95,29 @@ class ExtensionSetGenerator { // ExtensionSetGenerator provides the context to write out the properties. p.print( - "", - "\(comments)\(visibility)var \(extensionNames.value): \(apiType) {") + "", + "\(comments)\(visibility)var \(extensionNames.value): \(apiType) {" + ) p.printIndented( - "get {return getExtensionValue(ext: \(swiftFullExtensionName)) ?? \(defaultValue)}", - "set {setExtensionValue(ext: \(swiftFullExtensionName), value: newValue)}") + "get {return getExtensionValue(ext: \(swiftFullExtensionName)) ?? \(defaultValue)}", + "set {setExtensionValue(ext: \(swiftFullExtensionName), value: newValue)}" + ) p.print("}") // Repeated extension fields can use .isEmpty and clear by setting to the empty list. if fieldDescriptor.label != .repeated { p.print( "/// Returns true if extension `\(swiftFullExtensionName)`\n/// has been explicitly set.", - "\(visibility)var \(extensionNames.has): Bool {") + "\(visibility)var \(extensionNames.has): Bool {" + ) p.printIndented("return hasExtensionValue(ext: \(swiftFullExtensionName))") p.print("}") p.print( "/// Clears the value of extension `\(swiftFullExtensionName)`.", "/// Subsequent reads from it will return its default value.", - "\(visibility)mutating func \(extensionNames.clear)() {") + "\(visibility)mutating func \(extensionNames.clear)() {" + ) p.printIndented("clearExtensionValue(ext: \(swiftFullExtensionName))") p.print("}") } @@ -127,12 +132,12 @@ class ExtensionSetGenerator { // where they were declared. private var extensions: [ExtensionGenerator] = [] - var isEmpty: Bool { return extensions.isEmpty } + var isEmpty: Bool { extensions.isEmpty } init( - fileDescriptor: FileDescriptor, - generatorOptions: GeneratorOptions, - namer: SwiftProtobufNamer + fileDescriptor: FileDescriptor, + generatorOptions: GeneratorOptions, + namer: SwiftProtobufNamer ) { self.fileDescriptor = fileDescriptor self.generatorOptions = generatorOptions @@ -141,9 +146,11 @@ class ExtensionSetGenerator { func add(extensionFields: [FieldDescriptor]) { for e in extensionFields { - let extensionGenerator = ExtensionGenerator(descriptor: e, - generatorOptions: generatorOptions, - namer: namer) + let extensionGenerator = ExtensionGenerator( + descriptor: e, + generatorOptions: generatorOptions, + namer: namer + ) extensions.append(extensionGenerator) } } @@ -151,7 +158,8 @@ class ExtensionSetGenerator { func generateMessageSwiftExtensions(printer p: inout CodePrinter) { guard !extensions.isEmpty else { return } - p.print(""" + p.print( + """ // MARK: - Extension Properties @@ -159,7 +167,8 @@ class ExtensionSetGenerator { // extension fields. The names are based on the extension field name from the proto // declaration. To avoid naming collisions, the names are prefixed with the name of // the scope where the extend directive occurs. - """) + """ + ) // Reorder the list so they are grouped by the Message being extended, but // maintaining the order they were within the file within those groups. @@ -189,16 +198,18 @@ class ExtensionSetGenerator { } currentType = e.containingTypeSwiftFullName p.print( - "", - "extension \(currentType) {") + "", + "extension \(currentType) {" + ) p.indent() } e.generateMessageSwiftExtension(printer: &p) } p.outdent() p.print( - "", - "}") + "", + "}" + ) } func generateFileProtobufExtensionRegistry(printer p: inout CodePrinter) { @@ -207,66 +218,71 @@ class ExtensionSetGenerator { let pathParts = splitPath(pathname: fileDescriptor.name) let filenameAsIdentifier = NamingUtils.toUpperCamelCase(pathParts.base) let filePrefix = namer.typePrefix(forFile: fileDescriptor) - p.print(""" + p.print( + """ - // MARK: - File's ExtensionMap: \(filePrefix)\(filenameAsIdentifier)_Extensions + // MARK: - File's ExtensionMap: \(filePrefix)\(filenameAsIdentifier)_Extensions - /// A `SwiftProtobuf.SimpleExtensionMap` that includes all of the extensions defined by - /// this .proto file. It can be used any place an `SwiftProtobuf.ExtensionMap` is needed - /// in parsing, or it can be combined with other `SwiftProtobuf.SimpleExtensionMap`s to create - /// a larger `SwiftProtobuf.SimpleExtensionMap`. - \(generatorOptions.visibilitySourceSnippet)let \(filePrefix)\(filenameAsIdentifier)_Extensions: \(namer.swiftProtobufModulePrefix)SimpleExtensionMap = [ - """) + /// A `SwiftProtobuf.SimpleExtensionMap` that includes all of the extensions defined by + /// this .proto file. It can be used any place an `SwiftProtobuf.ExtensionMap` is needed + /// in parsing, or it can be combined with other `SwiftProtobuf.SimpleExtensionMap`s to create + /// a larger `SwiftProtobuf.SimpleExtensionMap`. + \(generatorOptions.visibilitySourceSnippet)let \(filePrefix)\(filenameAsIdentifier)_Extensions: \(namer.swiftProtobufModulePrefix)SimpleExtensionMap = [ + """ + ) p.withIndentation { p in - let lastIndex = extensions.count - 1 - for (i, e) in extensions.enumerated() { - p.print("\(e.swiftFullExtensionName)\(i != lastIndex ? "," : "")") - } + let lastIndex = extensions.count - 1 + for (i, e) in extensions.enumerated() { + p.print("\(e.swiftFullExtensionName)\(i != lastIndex ? "," : "")") + } } p.print("]") } func generateProtobufExtensionDeclarations(printer p: inout CodePrinter) { - guard !extensions.isEmpty else { return } - - p.print(""" - - // Extension Objects - The only reason these might be needed is when manually - // constructing a `SimpleExtensionMap`, otherwise, use the above _Extension Properties_ - // accessors for the extension fields on the messages directly. - """) - - func endScope() { - p.outdent() - p.print("}") - p.outdent() - p.print("}") - } - - let visibility = generatorOptions.visibilitySourceSnippet - var currentScope: Descriptor? = nil - var addNewline = true - for e in extensions { - if currentScope !== e.fieldDescriptor.extensionScope { - if currentScope != nil { endScope() } - currentScope = e.fieldDescriptor.extensionScope - let scopeSwiftFullName = namer.fullName(message: currentScope!) - p.print( - "", - "extension \(scopeSwiftFullName) {") - p.indent() - p.print("\(visibility)enum Extensions {") - p.indent() - addNewline = false + guard !extensions.isEmpty else { return } + + p.print( + """ + + // Extension Objects - The only reason these might be needed is when manually + // constructing a `SimpleExtensionMap`, otherwise, use the above _Extension Properties_ + // accessors for the extension fields on the messages directly. + """ + ) + + func endScope() { + p.outdent() + p.print("}") + p.outdent() + p.print("}") } - if addNewline { - p.print() - } else { - addNewline = true + let visibility = generatorOptions.visibilitySourceSnippet + var currentScope: Descriptor? = nil + var addNewline = true + for e in extensions { + if currentScope !== e.fieldDescriptor.extensionScope { + if currentScope != nil { endScope() } + currentScope = e.fieldDescriptor.extensionScope + let scopeSwiftFullName = namer.fullName(message: currentScope!) + p.print( + "", + "extension \(scopeSwiftFullName) {" + ) + p.indent() + p.print("\(visibility)enum Extensions {") + p.indent() + addNewline = false + } + + if addNewline { + p.print() + } else { + addNewline = true + } + e.generateProtobufExtensionDeclarations(printer: &p) } - e.generateProtobufExtensionDeclarations(printer: &p) - } - if currentScope != nil { endScope() } + if currentScope != nil { endScope() } } } diff --git a/Sources/protoc-gen-swift/FieldGenerator.swift b/Sources/protoc-gen-swift/FieldGenerator.swift index 1edb7fa23..9e532c3a7 100644 --- a/Sources/protoc-gen-swift/FieldGenerator.swift +++ b/Sources/protoc-gen-swift/FieldGenerator.swift @@ -14,106 +14,104 @@ // ----------------------------------------------------------------------------- import Foundation -import SwiftProtobufPluginLibrary import SwiftProtobuf - +import SwiftProtobufPluginLibrary /// Interface for field generators. protocol FieldGenerator { - var number: Int { get } + var number: Int { get } - /// Name mapping entries for the field. - var fieldMapNames: [String] { get } + /// Name mapping entries for the field. + var fieldMapNames: [String] { get } - /// Generate the interface for this field, this is includes any extra methods (has/clear). - func generateInterface(printer: inout CodePrinter) + /// Generate the interface for this field, this is includes any extra methods (has/clear). + func generateInterface(printer: inout CodePrinter) - /// Generate any additional storage needed for this field. - func generateStorage(printer: inout CodePrinter) + /// Generate any additional storage needed for this field. + func generateStorage(printer: inout CodePrinter) - /// Generate the line to copy this field during a _StorageClass clone. - func generateStorageClassClone(printer: inout CodePrinter) + /// Generate the line to copy this field during a _StorageClass clone. + func generateStorageClassClone(printer: inout CodePrinter) - /// Generate the case and decoder invoke needed for this field. - func generateDecodeFieldCase(printer: inout CodePrinter) + /// Generate the case and decoder invoke needed for this field. + func generateDecodeFieldCase(printer: inout CodePrinter) - /// True/False for if the generated traverse code will need use any locals. - /// See https://github.com/apple/swift-protobuf/issues/1034 and - /// https://github.com/apple/swift-protobuf/issues/1182 for more information. - var generateTraverseUsesLocals: Bool { get } + /// True/False for if the generated traverse code will need use any locals. + /// See https://github.com/apple/swift-protobuf/issues/1034 and + /// https://github.com/apple/swift-protobuf/issues/1182 for more information. + var generateTraverseUsesLocals: Bool { get } - /// Generate the support for traversing this field. - func generateTraverse(printer: inout CodePrinter) + /// Generate the support for traversing this field. + func generateTraverse(printer: inout CodePrinter) - /// Generate support for comparing this field's value. - /// The generated code should return false in the current scope if the field's don't match. - func generateFieldComparison(printer: inout CodePrinter) + /// Generate support for comparing this field's value. + /// The generated code should return false in the current scope if the field's don't match. + func generateFieldComparison(printer: inout CodePrinter) - /// Generate any support needed to ensure required fields are set. - /// The generated code should return false the field isn't set. - func generateRequiredFieldCheck(printer: inout CodePrinter) + /// Generate any support needed to ensure required fields are set. + /// The generated code should return false the field isn't set. + func generateRequiredFieldCheck(printer: inout CodePrinter) - /// Generate any support needed to this field's value is initialized. - /// The generated code should return false if it isn't set. - func generateIsInitializedCheck(printer: inout CodePrinter) + /// Generate any support needed to this field's value is initialized. + /// The generated code should return false if it isn't set. + func generateIsInitializedCheck(printer: inout CodePrinter) } /// Simple base class for FieldGenerators that also provides fieldMapNames. class FieldGeneratorBase { - let number: Int - let fieldDescriptor: FieldDescriptor - - var fieldMapNames: [String] { - // Protobuf Text uses the unqualified group name for the field - // name instead of the field name provided by protoc. As far - // as I can tell, no one uses the fieldname provided by protoc, - // so let's just put the field name that Protobuf Text - // actually uses here. - let protoName: String - if fieldDescriptor.internal_isGroupLike { - protoName = fieldDescriptor.messageType!.name - } else { - protoName = fieldDescriptor.name + let number: Int + let fieldDescriptor: FieldDescriptor + + var fieldMapNames: [String] { + // Protobuf Text uses the unqualified group name for the field + // name instead of the field name provided by protoc. As far + // as I can tell, no one uses the fieldname provided by protoc, + // so let's just put the field name that Protobuf Text + // actually uses here. + let protoName: String + if fieldDescriptor.internal_isGroupLike { + protoName = fieldDescriptor.messageType!.name + } else { + protoName = fieldDescriptor.name + } + + var result: String + let jsonName = fieldDescriptor.jsonName + if jsonName == protoName { + /// The proto and JSON names are identical: + result = ".same(proto: \"\(protoName)\")" + } else { + let libraryGeneratedJsonName = NamingUtils.toJsonFieldName(protoName) + if jsonName == libraryGeneratedJsonName { + /// The library will generate the same thing protoc gave, so + /// we can let the library recompute this: + result = ".standard(proto: \"\(protoName)\")" + } else { + /// The library's generation didn't match, so specify this explicitly. + result = ".unique(proto: \"\(protoName)\", json: \"\(jsonName ?? "")\")" + } + } + + // TODO: When the library can take a breaking change there should be a new + // enum for the nametable to handle the group being able to match the + // raw fieldname or the name based on the group's name. But until then + // we add two entries, to provide both options for TextFormat, but we add + // the preferred one second, so when the runtime builds up the mappings, + // it will become the default for what gets used when generating TextFormat. + if fieldDescriptor.internal_isGroupLike && protoName != fieldDescriptor.name { + let nameLowercase = protoName.lowercased() + if nameLowercase == jsonName { + return [".same(proto: \"\(nameLowercase)\")", result] + } else { + return [".unique(proto: \"\(nameLowercase)\", json: \"\(jsonName ?? "")\")", result] + } + } else { + return [result] + } } - var result: String - let jsonName = fieldDescriptor.jsonName - if jsonName == protoName { - /// The proto and JSON names are identical: - result = ".same(proto: \"\(protoName)\")" - } else { - let libraryGeneratedJsonName = NamingUtils.toJsonFieldName(protoName) - if jsonName == libraryGeneratedJsonName { - /// The library will generate the same thing protoc gave, so - /// we can let the library recompute this: - result = ".standard(proto: \"\(protoName)\")" - } else { - /// The library's generation didn't match, so specify this explicitly. - result = ".unique(proto: \"\(protoName)\", json: \"\(jsonName ?? "")\")" - } + init(descriptor: FieldDescriptor) { + number = Int(descriptor.number) + fieldDescriptor = descriptor } - - // TODO: When the library can take a breaking change there should be a new - // enum for the nametable to handle the group being able to match the - // raw fieldname or the name based on the group's name. But until then - // we add two entries, to provide both options for TextFormat, but we add - // the preferred one second, so when the runtime builds up the mappings, - // it will become the default for what gets used when generating TextFormat. - if fieldDescriptor.internal_isGroupLike && - protoName != fieldDescriptor.name { - let nameLowercase = protoName.lowercased() - if nameLowercase == jsonName { - return [".same(proto: \"\(nameLowercase)\")", result] - } else { - return [".unique(proto: \"\(nameLowercase)\", json: \"\(jsonName ?? "")\")", result] - } - } else { - return [result] - } - } - - init(descriptor: FieldDescriptor) { - number = Int(descriptor.number) - fieldDescriptor = descriptor - } } diff --git a/Sources/protoc-gen-swift/FileGenerator.swift b/Sources/protoc-gen-swift/FileGenerator.swift index 6238fd10a..edb07f1a6 100644 --- a/Sources/protoc-gen-swift/FileGenerator.swift +++ b/Sources/protoc-gen-swift/FileGenerator.swift @@ -15,9 +15,8 @@ /// // ----------------------------------------------------------------------------- import Foundation -import SwiftProtobufPluginLibrary import SwiftProtobuf - +import SwiftProtobufPluginLibrary class FileGenerator { private let fileDescriptor: FileDescriptor @@ -39,24 +38,34 @@ class FileGenerator { } } - init(fileDescriptor: FileDescriptor, - generatorOptions: GeneratorOptions) { + init( + fileDescriptor: FileDescriptor, + generatorOptions: GeneratorOptions + ) { self.fileDescriptor = fileDescriptor self.generatorOptions = generatorOptions - namer = SwiftProtobufNamer(currentFile: fileDescriptor, - protoFileToModuleMappings: generatorOptions.protoToModuleMappings) + namer = SwiftProtobufNamer( + currentFile: fileDescriptor, + protoFileToModuleMappings: generatorOptions.protoToModuleMappings + ) } /// Generate, if `errorString` gets filled in, then report error instead of using /// what written into `printer`. func generateOutputFile(printer p: inout CodePrinter, errorString: inout String?) { - guard fileDescriptor.options.swiftPrefix.isEmpty || - isValidSwiftIdentifier(fileDescriptor.options.swiftPrefix, - allowQuoted: false) else { - errorString = "\(fileDescriptor.name) has an 'swift_prefix' that isn't a valid Swift identifier (\(fileDescriptor.options.swiftPrefix))." - return + guard + fileDescriptor.options.swiftPrefix.isEmpty + || isValidSwiftIdentifier( + fileDescriptor.options.swiftPrefix, + allowQuoted: false + ) + else { + errorString = + "\(fileDescriptor.name) has an 'swift_prefix' that isn't a valid Swift identifier (\(fileDescriptor.options.swiftPrefix))." + return } - p.print(""" + p.print( + """ // DO NOT EDIT. // swift-format-ignore-file // swiftlint:disable all @@ -67,7 +76,8 @@ class FileGenerator { // For information on using the generated types, please see the documentation: // https://github.com/apple/swift-protobuf/ - """) + """ + ) // Attempt to bring over the comments at the top of the .proto file as // they likely contain copyrights/preamble/etc. @@ -86,25 +96,30 @@ class FileGenerator { commentLocation = location } if let commentLocation = commentLocation { - let comments = commentLocation.asSourceComment(commentPrefix: "///", - leadingDetachedPrefix: "//") - if !comments.isEmpty { - // If the was a leading or tailing comment it won't have a blank - // line, after it, so ensure there is one. - p.print(comments, newlines: !comments.hasSuffix("\n\n")) - } + let comments = commentLocation.asSourceComment( + commentPrefix: "///", + leadingDetachedPrefix: "//" + ) + if !comments.isEmpty { + // If the was a leading or tailing comment it won't have a blank + // line, after it, so ensure there is one. + p.print(comments, newlines: !comments.hasSuffix("\n\n")) + } } - let fileDefinesTypes = !fileDescriptor.enums.isEmpty || !fileDescriptor.messages.isEmpty || !fileDescriptor.extensions.isEmpty + let fileDefinesTypes = + !fileDescriptor.enums.isEmpty || !fileDescriptor.messages.isEmpty || !fileDescriptor.extensions.isEmpty var hasImports = false if fileDescriptor.needsFoundationImport { p.print("\(generatorOptions.importDirective.snippet) Foundation") - hasImports = true + hasImports = true } if fileDescriptor.isBundledProto { - p.print("// 'import \(namer.swiftProtobufModuleName)' suppressed, this proto file is meant to be bundled in the runtime.") + p.print( + "// 'import \(namer.swiftProtobufModuleName)' suppressed, this proto file is meant to be bundled in the runtime." + ) hasImports = true } else if fileDefinesTypes { p.print("\(generatorOptions.importDirective.snippet) \(namer.swiftProtobufModuleName)") @@ -112,15 +127,16 @@ class FileGenerator { } let neededImports = fileDescriptor.computeImports( - namer: namer, - directive: generatorOptions.importDirective, - reexportPublicImports: generatorOptions.visibility != .internal) + namer: namer, + directive: generatorOptions.importDirective, + reexportPublicImports: generatorOptions.visibility != .internal + ) if !neededImports.isEmpty { if hasImports { p.print() } p.print(neededImports) - hasImports = true + hasImports = true } // If there is nothing to generate, then just record that and be done (usually means @@ -137,9 +153,11 @@ class FileGenerator { generateVersionCheck(printer: &p) let extensionSet = - ExtensionSetGenerator(fileDescriptor: fileDescriptor, - generatorOptions: generatorOptions, - namer: namer) + ExtensionSetGenerator( + fileDescriptor: fileDescriptor, + generatorOptions: generatorOptions, + namer: namer + ) extensionSet.add(extensionFields: fileDescriptor.extensions) @@ -148,10 +166,12 @@ class FileGenerator { } let messages = fileDescriptor.messages.map { - return MessageGenerator(descriptor: $0, - generatorOptions: generatorOptions, - namer: namer, - extensionSet: extensionSet) + return MessageGenerator( + descriptor: $0, + generatorOptions: generatorOptions, + namer: namer, + extensionSet: extensionSet + ) } for e in enums { @@ -167,7 +187,8 @@ class FileGenerator { let filename = pathParts.base + pathParts.suffix p.print( "", - "// MARK: - Extension support defined in \(filename).") + "// MARK: - Extension support defined in \(filename)." + ) // Generate the Swift Extensions on the Messages that provide the api // for using the protobuf extension. @@ -189,11 +210,13 @@ class FileGenerator { if needsProtoPackage || !enums.isEmpty || !messages.isEmpty { p.print( "", - "// MARK: - Code below here is support for the SwiftProtobuf runtime.") + "// MARK: - Code below here is support for the SwiftProtobuf runtime." + ) if needsProtoPackage { p.print( "", - "fileprivate let _protobuf_package = \"\(protoPackage)\"") + "fileprivate let _protobuf_package = \"\(protoPackage)\"" + ) } for e in enums { e.generateRuntimeSupport(printer: &p) @@ -206,17 +229,20 @@ class FileGenerator { private func generateVersionCheck(printer p: inout CodePrinter) { let v = Version.compatibilityVersion - p.print(""" + p.print( + """ // If the compiler emits an error on this type, it is because this file // was generated by a version of the `protoc` Swift plug-in that is // incompatible with the version of SwiftProtobuf to which you are linking. // Please ensure that you are building against the same version of the API // that was used to generate this file. fileprivate struct _GeneratedWithProtocGenSwiftVersion: \(namer.swiftProtobufModulePrefix)ProtobufAPIVersionCheck { - """) + """ + ) p.printIndented( "struct _\(v): \(namer.swiftProtobufModulePrefix)ProtobufAPIVersion_\(v) {}", - "typealias Version = _\(v)") + "typealias Version = _\(v)" + ) p.print("}") } } diff --git a/Sources/protoc-gen-swift/FileIo.swift b/Sources/protoc-gen-swift/FileIo.swift index d6c551dee..67321bb39 100644 --- a/Sources/protoc-gen-swift/FileIo.swift +++ b/Sources/protoc-gen-swift/FileIo.swift @@ -15,12 +15,12 @@ import Foundation class Stderr { - static func print(_ s: String) { - let out = "\(CommandLine.programName): \(s)\n" - if let data = out.data(using: .utf8) { - FileHandle.standardError.write(data) + static func print(_ s: String) { + let out = "\(CommandLine.programName): \(s)\n" + if let data = out.data(using: .utf8) { + FileHandle.standardError.write(data) + } } - } } func readFileData(filename: String) throws -> Data { diff --git a/Sources/protoc-gen-swift/GenerationError.swift b/Sources/protoc-gen-swift/GenerationError.swift index 4495bcbc8..b9578b20c 100644 --- a/Sources/protoc-gen-swift/GenerationError.swift +++ b/Sources/protoc-gen-swift/GenerationError.swift @@ -9,25 +9,25 @@ // ----------------------------------------------------------------------------- enum GenerationError: Error, CustomStringConvertible { - /// Raised when parsing the parameter string and found an unknown key. - case unknownParameter(name: String) - /// Raised when a parameter was given an invalid value. - case invalidParameterValue(name: String, value: String) - /// Raised to wrap another error but provide a context message. - case wrappedError(message: String, error: any Error) - /// Raised with an specific message - case message(message: String) + /// Raised when parsing the parameter string and found an unknown key. + case unknownParameter(name: String) + /// Raised when a parameter was given an invalid value. + case invalidParameterValue(name: String, value: String) + /// Raised to wrap another error but provide a context message. + case wrappedError(message: String, error: any Error) + /// Raised with an specific message + case message(message: String) - var description: String { - switch self { - case .unknownParameter(let name): - return "Unknown generation parameter '\(name)'" - case .invalidParameterValue(let name, let value): - return "Unknown value for generation parameter '\(name)': '\(value)'" - case .wrappedError(let message, let error): - return "\(message): \(error)" - case .message(let message): - return message + var description: String { + switch self { + case .unknownParameter(let name): + return "Unknown generation parameter '\(name)'" + case .invalidParameterValue(let name, let value): + return "Unknown value for generation parameter '\(name)': '\(value)'" + case .wrappedError(let message, let error): + return "\(message): \(error)" + case .message(let message): + return message + } } - } } diff --git a/Sources/protoc-gen-swift/GeneratorOptions.swift b/Sources/protoc-gen-swift/GeneratorOptions.swift index 457be129b..3224e1381 100644 --- a/Sources/protoc-gen-swift/GeneratorOptions.swift +++ b/Sources/protoc-gen-swift/GeneratorOptions.swift @@ -11,197 +11,218 @@ import SwiftProtobufPluginLibrary class GeneratorOptions { - enum OutputNaming { - case fullPath - case pathToUnderscores - case dropPath - - init?(flag: String) { - switch flag.lowercased() { - case "fullpath", "full_path": - self = .fullPath - case "pathtounderscores", "path_to_underscores": - self = .pathToUnderscores - case "droppath", "drop_path": - self = .dropPath - default: - return nil - } + enum OutputNaming { + case fullPath + case pathToUnderscores + case dropPath + + init?(flag: String) { + switch flag.lowercased() { + case "fullpath", "full_path": + self = .fullPath + case "pathtounderscores", "path_to_underscores": + self = .pathToUnderscores + case "droppath", "drop_path": + self = .dropPath + default: + return nil + } + } } - } - enum Visibility: String { - case `internal` - case `public` - case `package` + enum Visibility: String { + case `internal` + case `public` + case `package` - init?(flag: String) { - self.init(rawValue: flag.lowercased()) + init?(flag: String) { + self.init(rawValue: flag.lowercased()) + } } - } - enum ImportDirective: Equatable { - case accessLevel(Visibility) - case plain - case implementationOnly + enum ImportDirective: Equatable { + case accessLevel(Visibility) + case plain + case implementationOnly - var isAccessLevel: Bool { - switch self { - case .accessLevel: return true - default: return false + var isAccessLevel: Bool { + switch self { + case .accessLevel: return true + default: return false + } } - } - var snippet: String { - switch self { - case let .accessLevel(visibility): - return "\(visibility) import" - case .plain: - return "import" - case .implementationOnly: - return "@_implementationOnly import" - } - } - } - - let outputNaming: OutputNaming - let protoToModuleMappings: ProtoFileToModuleMappings - let visibility: Visibility - let importDirective: ImportDirective - let experimentalStripNonfunctionalCodegen: Bool - - /// A string snippet to insert for the visibility - let visibilitySourceSnippet: String - - init(parameter: any CodeGeneratorParameter) throws { - var outputNaming: OutputNaming = .fullPath - var moduleMapPath: String? - var visibility: Visibility = .internal - var swiftProtobufModuleName: String? = nil - var implementationOnlyImports: Bool = false - var useAccessLevelOnImports = false - var experimentalStripNonfunctionalCodegen: Bool = false - - for pair in parameter.parsedPairs { - switch pair.key { - case "FileNaming": - if let naming = OutputNaming(flag: pair.value) { - outputNaming = naming - } else { - throw GenerationError.invalidParameterValue(name: pair.key, - value: pair.value) - } - case "ProtoPathModuleMappings": - if !pair.value.isEmpty { - moduleMapPath = pair.value + var snippet: String { + switch self { + case let .accessLevel(visibility): + return "\(visibility) import" + case .plain: + return "import" + case .implementationOnly: + return "@_implementationOnly import" + } } - case "Visibility": - if let value = Visibility(flag: pair.value) { - visibility = value - } else { - throw GenerationError.invalidParameterValue(name: pair.key, - value: pair.value) - } - case "SwiftProtobufModuleName": - // This option is not documented in PLUGIN.md, because it's a feature - // that would ordinarily not be required for a given adopter. - if isValidSwiftIdentifier(pair.value) { - swiftProtobufModuleName = pair.value - } else { - throw GenerationError.invalidParameterValue(name: pair.key, - value: pair.value) - } - case "ImplementationOnlyImports": - if let value = Bool(pair.value) { - implementationOnlyImports = value - } else { - throw GenerationError.invalidParameterValue(name: pair.key, - value: pair.value) - } - case "UseAccessLevelOnImports": - if let value = Bool(pair.value) { - useAccessLevelOnImports = value - } else { - throw GenerationError.invalidParameterValue(name: pair.key, - value: pair.value) + } + + let outputNaming: OutputNaming + let protoToModuleMappings: ProtoFileToModuleMappings + let visibility: Visibility + let importDirective: ImportDirective + let experimentalStripNonfunctionalCodegen: Bool + + /// A string snippet to insert for the visibility + let visibilitySourceSnippet: String + + init(parameter: any CodeGeneratorParameter) throws { + var outputNaming: OutputNaming = .fullPath + var moduleMapPath: String? + var visibility: Visibility = .internal + var swiftProtobufModuleName: String? = nil + var implementationOnlyImports: Bool = false + var useAccessLevelOnImports = false + var experimentalStripNonfunctionalCodegen: Bool = false + + for pair in parameter.parsedPairs { + switch pair.key { + case "FileNaming": + if let naming = OutputNaming(flag: pair.value) { + outputNaming = naming + } else { + throw GenerationError.invalidParameterValue( + name: pair.key, + value: pair.value + ) + } + case "ProtoPathModuleMappings": + if !pair.value.isEmpty { + moduleMapPath = pair.value + } + case "Visibility": + if let value = Visibility(flag: pair.value) { + visibility = value + } else { + throw GenerationError.invalidParameterValue( + name: pair.key, + value: pair.value + ) + } + case "SwiftProtobufModuleName": + // This option is not documented in PLUGIN.md, because it's a feature + // that would ordinarily not be required for a given adopter. + if isValidSwiftIdentifier(pair.value) { + swiftProtobufModuleName = pair.value + } else { + throw GenerationError.invalidParameterValue( + name: pair.key, + value: pair.value + ) + } + case "ImplementationOnlyImports": + if let value = Bool(pair.value) { + implementationOnlyImports = value + } else { + throw GenerationError.invalidParameterValue( + name: pair.key, + value: pair.value + ) + } + case "UseAccessLevelOnImports": + if let value = Bool(pair.value) { + useAccessLevelOnImports = value + } else { + throw GenerationError.invalidParameterValue( + name: pair.key, + value: pair.value + ) + } + case "experimental_strip_nonfunctional_codegen": + if pair.value.isEmpty { // Also support option without any value. + experimentalStripNonfunctionalCodegen = true + } else if let value = Bool(pair.value) { + experimentalStripNonfunctionalCodegen = value + } else { + throw GenerationError.invalidParameterValue( + name: pair.key, + value: pair.value + ) + } + default: + throw GenerationError.unknownParameter(name: pair.key) + } } - case "experimental_strip_nonfunctional_codegen": - if pair.value.isEmpty { // Also support option without any value. - experimentalStripNonfunctionalCodegen = true - } else if let value = Bool(pair.value) { - experimentalStripNonfunctionalCodegen = value + + if let moduleMapPath = moduleMapPath { + do { + self.protoToModuleMappings = try ProtoFileToModuleMappings( + path: moduleMapPath, + swiftProtobufModuleName: swiftProtobufModuleName + ) + } catch let e { + throw GenerationError.wrappedError( + message: "Parameter 'ProtoPathModuleMappings=\(moduleMapPath)'", + error: e + ) + } } else { - throw GenerationError.invalidParameterValue(name: pair.key, - value: pair.value) + self.protoToModuleMappings = ProtoFileToModuleMappings(swiftProtobufModuleName: swiftProtobufModuleName) } - default: - throw GenerationError.unknownParameter(name: pair.key) - } - } - - if let moduleMapPath = moduleMapPath { - do { - self.protoToModuleMappings = try ProtoFileToModuleMappings(path: moduleMapPath, swiftProtobufModuleName: swiftProtobufModuleName) - } catch let e { - throw GenerationError.wrappedError( - message: "Parameter 'ProtoPathModuleMappings=\(moduleMapPath)'", - error: e) - } - } else { - self.protoToModuleMappings = ProtoFileToModuleMappings(swiftProtobufModuleName: swiftProtobufModuleName) - } - self.outputNaming = outputNaming - self.visibility = visibility + self.outputNaming = outputNaming + self.visibility = visibility - switch visibility { - case .internal: - visibilitySourceSnippet = "" - case .public: - visibilitySourceSnippet = "public " - case .package: - visibilitySourceSnippet = "package " - } + switch visibility { + case .internal: + visibilitySourceSnippet = "" + case .public: + visibilitySourceSnippet = "public " + case .package: + visibilitySourceSnippet = "package " + } - self.experimentalStripNonfunctionalCodegen = experimentalStripNonfunctionalCodegen + self.experimentalStripNonfunctionalCodegen = experimentalStripNonfunctionalCodegen + + switch (implementationOnlyImports, useAccessLevelOnImports) { + case (false, false): self.importDirective = .plain + case (false, true): self.importDirective = .accessLevel(visibility) + case (true, false): self.importDirective = .implementationOnly + case (true, true): + throw GenerationError.message( + message: """ + When using access levels on imports the @_implementationOnly option is unnecessary. + Disable @_implementationOnly imports. + """ + ) + } - switch (implementationOnlyImports, useAccessLevelOnImports) { - case (false, false): self.importDirective = .plain - case (false, true): self.importDirective = .accessLevel(visibility) - case (true, false): self.importDirective = .implementationOnly - case (true, true): throw GenerationError.message(message: """ - When using access levels on imports the @_implementationOnly option is unnecessary. - Disable @_implementationOnly imports. - """) - } + // ------------------------------------------------------------------------ + // Now do "cross option" validations. - // ------------------------------------------------------------------------ - // Now do "cross option" validations. + if implementationOnlyImports && self.visibility != .internal { + throw GenerationError.message( + message: """ + Cannot use @_implementationOnly imports when the proto visibility is public or package. + Either change the visibility to internal, or disable @_implementationOnly imports. + """ + ) + } - if implementationOnlyImports && self.visibility != .internal { - throw GenerationError.message(message: """ - Cannot use @_implementationOnly imports when the proto visibility is public or package. - Either change the visibility to internal, or disable @_implementationOnly imports. - """) + // The majority case is that if `self.protoToModuleMappings.hasMappings` is + // true, then `self.visibility` should be either `.public` or `.package`. + // However, it is possible for someone to put top most proto files (ones + // not imported into other proto files) in a different module, and use + // internal visibility there. i.e. - + // + // module One: + // - foo.pb.swift from foo.proto generated with "public" visibility. + // module Two: + // - bar.pb.swift from bar.proto (which does `import foo.proto`) + // generated with "internal" visibility. + // + // Since this support is possible/valid, there's no good way a "bad" case + // (i.e. - if foo.pb.swift was generated with "internal" visibility). So + // no options validation here, and instead developers would have to figure + // this out via the compiler errors around missing type (when bar.pb.swift + // gets unknown reference for thing that should be in module One via + // foo.pb.swift). } - - // The majority case is that if `self.protoToModuleMappings.hasMappings` is - // true, then `self.visibility` should be either `.public` or `.package`. - // However, it is possible for someone to put top most proto files (ones - // not imported into other proto files) in a different module, and use - // internal visibility there. i.e. - - // - // module One: - // - foo.pb.swift from foo.proto generated with "public" visibility. - // module Two: - // - bar.pb.swift from bar.proto (which does `import foo.proto`) - // generated with "internal" visibility. - // - // Since this support is possible/valid, there's no good way a "bad" case - // (i.e. - if foo.pb.swift was generated with "internal" visibility). So - // no options validation here, and instead developers would have to figure - // this out via the compiler errors around missing type (when bar.pb.swift - // gets unknown reference for thing that should be in module One via - // foo.pb.swift). - } } diff --git a/Sources/protoc-gen-swift/Google_Protobuf_FileDescriptorProto+Extensions.swift b/Sources/protoc-gen-swift/Google_Protobuf_FileDescriptorProto+Extensions.swift index 1260db428..e9832bb1b 100644 --- a/Sources/protoc-gen-swift/Google_Protobuf_FileDescriptorProto+Extensions.swift +++ b/Sources/protoc-gen-swift/Google_Protobuf_FileDescriptorProto+Extensions.swift @@ -13,13 +13,13 @@ /// // ----------------------------------------------------------------------------- -import SwiftProtobufPluginLibrary import SwiftProtobuf +import SwiftProtobufPluginLibrary extension Google_Protobuf_FileDescriptorProto { - // Field numbers used to collect .proto file comments. - struct FieldNumbers { - static let syntax: Int = 12 - static let edition: Int = 14 - } + // Field numbers used to collect .proto file comments. + struct FieldNumbers { + static let syntax: Int = 12 + static let edition: Int = 14 + } } diff --git a/Sources/protoc-gen-swift/MessageFieldGenerator.swift b/Sources/protoc-gen-swift/MessageFieldGenerator.swift index 4dfe2a375..7d13c62d9 100644 --- a/Sources/protoc-gen-swift/MessageFieldGenerator.swift +++ b/Sources/protoc-gen-swift/MessageFieldGenerator.swift @@ -13,9 +13,8 @@ /// // ----------------------------------------------------------------------------- import Foundation -import SwiftProtobufPluginLibrary import SwiftProtobuf - +import SwiftProtobufPluginLibrary class MessageFieldGenerator: FieldGeneratorBase, FieldGenerator { private let generatorOptions: GeneratorOptions @@ -34,25 +33,26 @@ class MessageFieldGenerator: FieldGeneratorBase, FieldGenerator { private let traitsType: String private let comments: String - private var isMap: Bool {return fieldDescriptor.isMap} - private var isPacked: Bool { return fieldDescriptor.isPacked } + private var isMap: Bool { fieldDescriptor.isMap } + private var isPacked: Bool { fieldDescriptor.isPacked } // Note: this could still be a map (since those are repeated message fields - private var isRepeated: Bool {return fieldDescriptor.isRepeated} + private var isRepeated: Bool { fieldDescriptor.isRepeated } private var isGroupOrMessage: Bool { - switch fieldDescriptor.type { - case .group, .message: - return true - default: - return false - } + switch fieldDescriptor.type { + case .group, .message: + return true + default: + return false + } } - init(descriptor: FieldDescriptor, - generatorOptions: GeneratorOptions, - namer: SwiftProtobufNamer, - usesHeapStorage: Bool) - { + init( + descriptor: FieldDescriptor, + generatorOptions: GeneratorOptions, + namer: SwiftProtobufNamer, + usesHeapStorage: Bool + ) { precondition(descriptor.realContainingOneof == nil) self.generatorOptions = generatorOptions @@ -60,9 +60,11 @@ class MessageFieldGenerator: FieldGeneratorBase, FieldGenerator { self.namer = namer hasFieldPresence = descriptor.hasPresence - let names = namer.messagePropertyNames(field: descriptor, - prefixed: "_", - includeHasAndClear: hasFieldPresence) + let names = namer.messagePropertyNames( + field: descriptor, + prefixed: "_", + includeHasAndClear: hasFieldPresence + ) swiftName = names.name underscoreSwiftName = names.prefixed swiftHasName = names.has @@ -87,10 +89,10 @@ class MessageFieldGenerator: FieldGeneratorBase, FieldGenerator { if usesHeapStorage { p.print("var \(underscoreSwiftName): \(swiftStorageType) = \(defaultValue)") } else { - // If this field has field presence, the there is a private storage variable. - if hasFieldPresence { - p.print("fileprivate var \(underscoreSwiftName): \(swiftStorageType) = \(defaultValue)") - } + // If this field has field presence, the there is a private storage variable. + if hasFieldPresence { + p.print("fileprivate var \(underscoreSwiftName): \(swiftStorageType) = \(defaultValue)") + } } } @@ -102,15 +104,17 @@ class MessageFieldGenerator: FieldGeneratorBase, FieldGenerator { p.print("\(comments)\(visibility)var \(swiftName): \(swiftType) {") let defaultClause = hasFieldPresence ? " ?? \(swiftDefaultValue)" : "" p.printIndented( - "get {return _storage.\(underscoreSwiftName)\(defaultClause)}", - "set {_uniqueStorage().\(underscoreSwiftName) = newValue}") + "get {return _storage.\(underscoreSwiftName)\(defaultClause)}", + "set {_uniqueStorage().\(underscoreSwiftName) = newValue}" + ) p.print("}") } else { if hasFieldPresence { p.print("\(comments)\(visibility)var \(swiftName): \(swiftType) {") p.printIndented( - "get {return \(underscoreSwiftName) ?? \(swiftDefaultValue)}", - "set {\(underscoreSwiftName) = newValue}") + "get {return \(underscoreSwiftName) ?? \(swiftDefaultValue)}", + "set {\(underscoreSwiftName) = newValue}" + ) p.print("}") } else { p.print("\(comments)\(visibility)var \(swiftName): \(swiftStorageType) = \(swiftDefaultValue)") @@ -122,12 +126,14 @@ class MessageFieldGenerator: FieldGeneratorBase, FieldGenerator { let immutableStoragePrefix = usesHeapStorage ? "_storage." : "self." p.print( "/// Returns true if `\(swiftName)` has been explicitly set.", - "\(visibility)var \(swiftHasName): Bool {return \(immutableStoragePrefix)\(underscoreSwiftName) != nil}") + "\(visibility)var \(swiftHasName): Bool {return \(immutableStoragePrefix)\(underscoreSwiftName) != nil}" + ) let mutableStoragePrefix = usesHeapStorage ? "_uniqueStorage()." : "self." p.print( "/// Clears the value of `\(swiftName)`. Subsequent reads from it will return its default value.", - "\(visibility)mutating func \(swiftClearName)() {\(mutableStoragePrefix)\(underscoreSwiftName) = nil}") + "\(visibility)mutating func \(swiftClearName)() {\(mutableStoragePrefix)\(underscoreSwiftName) = nil}" + ) } func generateStorageClassClone(printer p: inout CodePrinter) { @@ -148,16 +154,18 @@ class MessageFieldGenerator: FieldGeneratorBase, FieldGenerator { p.print("if \(lhsProperty) != \(otherStoredProperty) {return false}") } - func generateRequiredFieldCheck(printer p: inout CodePrinter) { - guard fieldDescriptor.isRequired else { return } - p.print("if \(storedProperty) == nil {return false}") + func generateRequiredFieldCheck(printer p: inout CodePrinter) { + guard fieldDescriptor.isRequired else { return } + p.print("if \(storedProperty) == nil {return false}") } func generateIsInitializedCheck(printer p: inout CodePrinter) { guard isGroupOrMessage && fieldDescriptor.messageType!.containsRequiredFields() else { return } if isRepeated { // Map or Array - p.print("if !\(namer.swiftProtobufModulePrefix)Internal.areAllInitialized(\(storedProperty)) {return false}") + p.print( + "if !\(namer.swiftProtobufModulePrefix)Internal.areAllInitialized(\(storedProperty)) {return false}" + ) } else { p.print("if let v = \(storedProperty), !v.isInitialized {return false}") } @@ -179,7 +187,7 @@ class MessageFieldGenerator: FieldGeneratorBase, FieldGenerator { } var generateTraverseUsesLocals: Bool { - return !isRepeated && hasFieldPresence + !isRepeated && hasFieldPresence } func generateTraverse(printer p: inout CodePrinter) { diff --git a/Sources/protoc-gen-swift/MessageGenerator.swift b/Sources/protoc-gen-swift/MessageGenerator.swift index 3ba51924b..52437d813 100644 --- a/Sources/protoc-gen-swift/MessageGenerator.swift +++ b/Sources/protoc-gen-swift/MessageGenerator.swift @@ -15,529 +15,578 @@ // ----------------------------------------------------------------------------- import Foundation -import SwiftProtobufPluginLibrary import SwiftProtobuf +import SwiftProtobufPluginLibrary class MessageGenerator { - private let descriptor: Descriptor - private let generatorOptions: GeneratorOptions - private let namer: SwiftProtobufNamer - private let visibility: String - private let swiftFullName: String - private let swiftRelativeName: String - private let fields: [any FieldGenerator] - private let fieldsSortedByNumber: [any FieldGenerator] - private let oneofs: [OneofGenerator] - private let storage: MessageStorageClassGenerator? - private let enums: [EnumGenerator] - private let messages: [MessageGenerator] - private let isExtensible: Bool - - init( - descriptor: Descriptor, - generatorOptions: GeneratorOptions, - namer: SwiftProtobufNamer, - extensionSet: ExtensionSetGenerator - ) { - self.descriptor = descriptor - self.generatorOptions = generatorOptions - self.namer = namer - - visibility = generatorOptions.visibilitySourceSnippet - isExtensible = !descriptor.messageExtensionRanges.isEmpty - swiftRelativeName = namer.relativeName(message: descriptor) - swiftFullName = namer.fullName(message: descriptor) - - let useHeapStorage = - MessageStorageDecision.shouldUseHeapStorage(descriptor: descriptor) - - oneofs = descriptor.realOneofs.map { - return OneofGenerator(descriptor: $0, generatorOptions: generatorOptions, namer: namer, usesHeapStorage: useHeapStorage) - } + private let descriptor: Descriptor + private let generatorOptions: GeneratorOptions + private let namer: SwiftProtobufNamer + private let visibility: String + private let swiftFullName: String + private let swiftRelativeName: String + private let fields: [any FieldGenerator] + private let fieldsSortedByNumber: [any FieldGenerator] + private let oneofs: [OneofGenerator] + private let storage: MessageStorageClassGenerator? + private let enums: [EnumGenerator] + private let messages: [MessageGenerator] + private let isExtensible: Bool + + init( + descriptor: Descriptor, + generatorOptions: GeneratorOptions, + namer: SwiftProtobufNamer, + extensionSet: ExtensionSetGenerator + ) { + self.descriptor = descriptor + self.generatorOptions = generatorOptions + self.namer = namer + + visibility = generatorOptions.visibilitySourceSnippet + isExtensible = !descriptor.messageExtensionRanges.isEmpty + swiftRelativeName = namer.relativeName(message: descriptor) + swiftFullName = namer.fullName(message: descriptor) + + let useHeapStorage = + MessageStorageDecision.shouldUseHeapStorage(descriptor: descriptor) + + oneofs = descriptor.realOneofs.map { + OneofGenerator( + descriptor: $0, + generatorOptions: generatorOptions, + namer: namer, + usesHeapStorage: useHeapStorage + ) + } - let factory = MessageFieldFactory(generatorOptions: generatorOptions, - namer: namer, - useHeapStorage: useHeapStorage, - oneofGenerators: oneofs) - fields = descriptor.fields.map { - return factory.make(forFieldDescriptor: $0) - } - fieldsSortedByNumber = fields.sorted {$0.number < $1.number} + let factory = MessageFieldFactory( + generatorOptions: generatorOptions, + namer: namer, + useHeapStorage: useHeapStorage, + oneofGenerators: oneofs + ) + fields = descriptor.fields.map { + factory.make(forFieldDescriptor: $0) + } + fieldsSortedByNumber = fields.sorted { $0.number < $1.number } - extensionSet.add(extensionFields: descriptor.extensions) + extensionSet.add(extensionFields: descriptor.extensions) - enums = descriptor.enums.map { - return EnumGenerator(descriptor: $0, generatorOptions: generatorOptions, namer: namer) - } + enums = descriptor.enums.map { + EnumGenerator(descriptor: $0, generatorOptions: generatorOptions, namer: namer) + } - messages = descriptor.messages.filter { return !$0.options.mapEntry }.map { - return MessageGenerator(descriptor: $0, - generatorOptions: generatorOptions, - namer: namer, - extensionSet: extensionSet) - } + messages = descriptor.messages.filter { !$0.options.mapEntry }.map { + MessageGenerator( + descriptor: $0, + generatorOptions: generatorOptions, + namer: namer, + extensionSet: extensionSet + ) + } - if descriptor.wellKnownType == .any { - precondition(useHeapStorage) - storage = AnyMessageStorageClassGenerator(fields: fields) - } else if useHeapStorage { - storage = MessageStorageClassGenerator(fields: fields) - } else { - storage = nil - } - } - - func generateMainStruct( - printer p: inout CodePrinter, - parent: MessageGenerator?, - errorString: inout String? - ) { - // protoc does this validation; this is just here as a safety net because what is - // generated and how the runtime works assumes this. - if descriptor.useMessageSetWireFormat { - guard fields.isEmpty else { - errorString = "\(descriptor.fullName) has the option message_set_wire_format but it also has fields." - return - } - } - for e in descriptor.extensions { - guard e.containingType.useMessageSetWireFormat else { continue } - - guard e.type == .message else { - errorString = "\(e.containingType.fullName) has the option message_set_wire_format but \(e.fullName) is a non message extension field." - return - } - guard e.isOptional else { - errorString = "\(e.containingType.fullName) has the option message_set_wire_format but \(e.fullName) is not a \"optional\" extension field." - return - } + if descriptor.wellKnownType == .any { + precondition(useHeapStorage) + storage = AnyMessageStorageClassGenerator(fields: fields) + } else if useHeapStorage { + storage = MessageStorageClassGenerator(fields: fields) + } else { + storage = nil + } } - var conformances = [String]() - if isExtensible { - conformances.append("\(namer.swiftProtobufModulePrefix)ExtensibleMessage") - } + func generateMainStruct( + printer p: inout CodePrinter, + parent: MessageGenerator?, + errorString: inout String? + ) { + // protoc does this validation; this is just here as a safety net because what is + // generated and how the runtime works assumes this. + if descriptor.useMessageSetWireFormat { + guard fields.isEmpty else { + errorString = "\(descriptor.fullName) has the option message_set_wire_format but it also has fields." + return + } + } + for e in descriptor.extensions { + guard e.containingType.useMessageSetWireFormat else { continue } + + guard e.type == .message else { + errorString = + "\(e.containingType.fullName) has the option message_set_wire_format but \(e.fullName) is a non message extension field." + return + } + guard e.isOptional else { + errorString = + "\(e.containingType.fullName) has the option message_set_wire_format but \(e.fullName) is not a \"optional\" extension field." + return + } + } + + var conformances = [String]() + if isExtensible { + conformances.append("\(namer.swiftProtobufModulePrefix)ExtensibleMessage") + } - // Data isn't marked as Sendable on linux until Swift 5.9, so until then - // all messages with Data fields need to be manually marked as @unchecked. - // - // Messages that have a storage class will always need @unchecked. - let needsUnchecked = storage != nil || descriptor.fields.contains { - return $0.type == .bytes + // Data isn't marked as Sendable on linux until Swift 5.9, so until then + // all messages with Data fields need to be manually marked as @unchecked. + // + // Messages that have a storage class will always need @unchecked. + let needsUnchecked = + storage != nil + || descriptor.fields.contains { + return $0.type == .bytes + } + conformances.append(needsUnchecked ? "@unchecked Sendable" : "Sendable") + + p.print( + "", + "\(descriptor.protoSourceCommentsWithDeprecation(generatorOptions: generatorOptions))\(visibility)struct \(swiftRelativeName): \(conformances.joined(separator: ", ")) {" + ) + p.withIndentation { p in + p.print( + """ + // \(namer.swiftProtobufModuleName).Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + """ + ) + + for f in fields { + f.generateInterface(printer: &p) + } + + p.print( + "", + "\(visibility)var unknownFields = \(namer.swiftProtobufModulePrefix)UnknownStorage()" + ) + + for o in oneofs { + o.generateMainEnum(printer: &p) + } + + // Nested enums + for e in enums { + e.generateMainEnum(printer: &p) + } + + // Nested messages + for m in messages { + m.generateMainStruct(printer: &p, parent: self, errorString: &errorString) + } + + // Generate the default initializer. If we don't, Swift seems to sometimes + // generate it along with others that can take public proprerties. When it + // generates the others doesn't seem to be documented. + p.print( + "", + "\(visibility)init() {}" + ) + + // Optional extension support + if isExtensible { + p.print( + "", + "\(visibility)var _protobuf_extensionFieldValues = \(namer.swiftProtobufModulePrefix)ExtensionFieldValueSet()" + ) + } + if let storage = storage { + if !isExtensible { + p.print() + } + p.print("\(storage.storageVisibility) var _storage = _StorageClass.defaultInstance") + } else { + var subMessagePrinter = CodePrinter(p) + for f in fields { + f.generateStorage(printer: &subMessagePrinter) + } + if !subMessagePrinter.isEmpty { + if !isExtensible { + p.print() + } + p.append(subMessagePrinter) + } + } + } + p.print("}") } - conformances.append(needsUnchecked ? "@unchecked Sendable" : "Sendable") - - p.print( - "", - "\(descriptor.protoSourceCommentsWithDeprecation(generatorOptions: generatorOptions))\(visibility)struct \(swiftRelativeName): \(conformances.joined(separator: ", ")) {") - p.withIndentation { p in - p.print(""" - // \(namer.swiftProtobufModuleName).Message conformance is added in an extension below. See the - // `Message` and `Message+*Additions` files in the SwiftProtobuf library for - // methods supported on all messages. - """) - - for f in fields { - f.generateInterface(printer: &p) - } - - p.print( - "", - "\(visibility)var unknownFields = \(namer.swiftProtobufModulePrefix)UnknownStorage()") - - for o in oneofs { - o.generateMainEnum(printer: &p) - } - - // Nested enums - for e in enums { - e.generateMainEnum(printer: &p) - } - - // Nested messages - for m in messages { - m.generateMainStruct(printer: &p, parent: self, errorString: &errorString) - } - - // Generate the default initializer. If we don't, Swift seems to sometimes - // generate it along with others that can take public proprerties. When it - // generates the others doesn't seem to be documented. - p.print( - "", - "\(visibility)init() {}") - - // Optional extension support - if isExtensible { + + func generateRuntimeSupport(printer p: inout CodePrinter, file: FileGenerator, parent: MessageGenerator?) { p.print( "", - "\(visibility)var _protobuf_extensionFieldValues = \(namer.swiftProtobufModulePrefix)ExtensionFieldValueSet()") - } - if let storage = storage { - if !isExtensible { - p.print() + "extension \(swiftFullName): \(namer.swiftProtobufModulePrefix)Message, \(namer.swiftProtobufModulePrefix)_MessageImplementationBase, \(namer.swiftProtobufModulePrefix)_ProtoNameProviding {" + ) + p.withIndentation { p in + if let parent = parent { + p.print( + "\(visibility)static let protoMessageName: String = \(parent.swiftFullName).protoMessageName + \".\(descriptor.name)\"" + ) + } else if !descriptor.file.package.isEmpty { + p.print( + "\(visibility)static let protoMessageName: String = _protobuf_package + \".\(descriptor.name)\"" + ) + } else { + p.print("\(visibility)static let protoMessageName: String = \"\(descriptor.name)\"") + } + generateProtoNameProviding(printer: &p) + if let storage = storage { + p.print() + storage.generateTypeDeclaration(printer: &p) + p.print() + storage.generateUniqueStorage(printer: &p) + } + p.print() + generateIsInitialized(printer: &p) + // generateIsInitialized provides a blank line after itself. + generateDecodeMessage(printer: &p) + p.print() + generateTraverse(printer: &p) + p.print() + generateMessageEquality(printer: &p) } - p.print("\(storage.storageVisibility) var _storage = _StorageClass.defaultInstance") - } else { - var subMessagePrinter = CodePrinter(p) - for f in fields { - f.generateStorage(printer: &subMessagePrinter) + p.print("}") + + // Nested enums and messages + for e in enums { + e.generateRuntimeSupport(printer: &p) } - if !subMessagePrinter.isEmpty { - if !isExtensible { - p.print() - } - p.append(subMessagePrinter) + for m in messages { + m.generateRuntimeSupport(printer: &p, file: file, parent: self) } - } - } - p.print("}") - } - - func generateRuntimeSupport(printer p: inout CodePrinter, file: FileGenerator, parent: MessageGenerator?) { - p.print( - "", - "extension \(swiftFullName): \(namer.swiftProtobufModulePrefix)Message, \(namer.swiftProtobufModulePrefix)_MessageImplementationBase, \(namer.swiftProtobufModulePrefix)_ProtoNameProviding {") - p.withIndentation { p in - if let parent = parent { - p.print("\(visibility)static let protoMessageName: String = \(parent.swiftFullName).protoMessageName + \".\(descriptor.name)\"") - } else if !descriptor.file.package.isEmpty { - p.print("\(visibility)static let protoMessageName: String = _protobuf_package + \".\(descriptor.name)\"") - } else { - p.print("\(visibility)static let protoMessageName: String = \"\(descriptor.name)\"") - } - generateProtoNameProviding(printer: &p) - if let storage = storage { - p.print() - storage.generateTypeDeclaration(printer: &p) - p.print() - storage.generateUniqueStorage(printer: &p) - } - p.print() - generateIsInitialized(printer:&p) - // generateIsInitialized provides a blank line after itself. - generateDecodeMessage(printer: &p) - p.print() - generateTraverse(printer: &p) - p.print() - generateMessageEquality(printer: &p) } - p.print("}") - // Nested enums and messages - for e in enums { - e.generateRuntimeSupport(printer: &p) - } - for m in messages { - m.generateRuntimeSupport(printer: &p, file: file, parent: self) - } - } - - private func generateProtoNameProviding(printer p: inout CodePrinter) { - if fields.isEmpty { - p.print("\(visibility)static let _protobuf_nameMap = \(namer.swiftProtobufModulePrefix)_NameMap()") - } else { - p.print("\(visibility)static let _protobuf_nameMap: \(namer.swiftProtobufModulePrefix)_NameMap = [") - p.withIndentation { p in - for f in fields { - for n in f.fieldMapNames { - p.print("\(f.number): \(n),") - } + private func generateProtoNameProviding(printer p: inout CodePrinter) { + if fields.isEmpty { + p.print("\(visibility)static let _protobuf_nameMap = \(namer.swiftProtobufModulePrefix)_NameMap()") + } else { + p.print("\(visibility)static let _protobuf_nameMap: \(namer.swiftProtobufModulePrefix)_NameMap = [") + p.withIndentation { p in + for f in fields { + for n in f.fieldMapNames { + p.print("\(f.number): \(n),") + } + } + } + p.print("]") } - } - p.print("]") } - } + /// Generates the `decodeMessage` method for the message. + /// + /// - Parameter p: The code printer. + private func generateDecodeMessage(printer p: inout CodePrinter) { + p.print( + "\(visibility)mutating func decodeMessage(decoder: inout D) throws {" + ) + p.withIndentation { p in + if storage != nil { + p.print("_ = _uniqueStorage()") + } - /// Generates the `decodeMessage` method for the message. - /// - /// - Parameter p: The code printer. - private func generateDecodeMessage(printer p: inout CodePrinter) { - p.print("\(visibility)mutating func decodeMessage(decoder: inout D) throws {") - p.withIndentation { p in - if storage != nil { - p.print("_ = _uniqueStorage()") - } + // protoc allows message_set_wire_format without any extension ranges; no clue what that + // actually would mean (since message_set_wire_format can't have fields), but make sure + // there are extensions ranges as that is what provides the extension support in the + // rest of the generation. + if descriptor.useMessageSetWireFormat && isExtensible { - // protoc allows message_set_wire_format without any extension ranges; no clue what that - // actually would mean (since message_set_wire_format can't have fields), but make sure - // there are extensions ranges as that is what provides the extension support in the - // rest of the generation. - if descriptor.useMessageSetWireFormat && isExtensible { + // MessageSet hands off the decode to the decoder to do the custom logic into the extensions. + p.print( + "try decoder.decodeExtensionFieldsAsMessageSet(values: &_protobuf_extensionFieldValues, messageType: \(swiftFullName).self)" + ) + + } else { + + if fields.isEmpty && !isExtensible { + p.print( + "// Load everything into unknown fields", + "while try decoder.nextFieldNumber() != nil {}" + ) + } else { + generateWithLifetimeExtension(printer: &p, throws: true) { p in + p.print("while let fieldNumber = try decoder.nextFieldNumber() {") + p.withIndentation { p in + // If a message only has extensions and there are multiple extension + // ranges, get more compact source gen by still using the `switch..case` + // code. This also avoids typechecking performance issues if there are + // dozens of ranges because we aren't constructing a single large + // expression containing untyped integer literals. + let normalizedExtensionRanges = descriptor._normalizedExtensionRanges + if !fields.isEmpty || normalizedExtensionRanges.count > 3 { + p.print( + """ + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + """ + ) + for f in fieldsSortedByNumber { + f.generateDecodeFieldCase(printer: &p) + } + if isExtensible { + p.print("case \(normalizedExtensionRanges.swiftCaseExpression):") + p.printIndented( + "try { try decoder.decodeExtensionField(values: &_protobuf_extensionFieldValues, messageType: \(swiftFullName).self, fieldNumber: fieldNumber) }()" + ) + } + p.print( + "default: break", + "}" + ) + } else if isExtensible { + // Just output a simple if-statement if the message had no fields of its + // own but we still need to generate a decode statement for extensions. + p.print( + "if \(normalizedExtensionRanges.swiftBooleanExpression(variable: "fieldNumber")) {" + ) + p.printIndented( + "try decoder.decodeExtensionField(values: &_protobuf_extensionFieldValues, messageType: \(swiftFullName).self, fieldNumber: fieldNumber)" + ) + p.print("}") + } + } + p.print("}") + } + } - // MessageSet hands off the decode to the decoder to do the custom logic into the extensions. - p.print("try decoder.decodeExtensionFieldsAsMessageSet(values: &_protobuf_extensionFieldValues, messageType: \(swiftFullName).self)") + } + } + p.print("}") + } - } else { + /// Generates the `traverse` method for the message. + /// + /// - Parameter p: The code printer. + private func generateTraverse(printer p: inout CodePrinter) { + p.print("\(visibility)func traverse(visitor: inout V) throws {") + p.withIndentation { p in + generateWithLifetimeExtension(printer: &p, throws: true) { p in + if let storage = storage { + storage.generatePreTraverse(printer: &p) + } + let visitExtensionsName = + descriptor.useMessageSetWireFormat ? "visitExtensionFieldsAsMessageSet" : "visitExtensionFields" + + let usesLocals = fields.reduce(false) { $0 || $1.generateTraverseUsesLocals } + if usesLocals { + p.print( + """ + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + """ + ) + } - if fields.isEmpty && !isExtensible { - p.print( - "// Load everything into unknown fields", - "while try decoder.nextFieldNumber() != nil {}") - } else { - generateWithLifetimeExtension(printer: &p, throws: true) { p in - p.print("while let fieldNumber = try decoder.nextFieldNumber() {") - p.withIndentation { p in - // If a message only has extensions and there are multiple extension - // ranges, get more compact source gen by still using the `switch..case` - // code. This also avoids typechecking performance issues if there are - // dozens of ranges because we aren't constructing a single large - // expression containing untyped integer literals. - let normalizedExtensionRanges = descriptor._normalizedExtensionRanges - if !fields.isEmpty || normalizedExtensionRanges.count > 3 { - p.print(""" - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch fieldNumber { - """) + // Use the "ambitious" ranges because for visit because subranges with no + // intermixed fields can be merged to reduce the number of calls for + // extension visitation. + var ranges = descriptor._ambitiousExtensionRanges.makeIterator() + var nextRange = ranges.next() for f in fieldsSortedByNumber { - f.generateDecodeFieldCase(printer: &p) + while nextRange != nil && Int(nextRange!.lowerBound) < f.number { + p.print( + "try visitor.\(visitExtensionsName)(fields: _protobuf_extensionFieldValues, start: \(nextRange!.lowerBound), end: \(nextRange!.upperBound))" + ) + nextRange = ranges.next() + } + f.generateTraverse(printer: &p) } - if isExtensible { - p.print("case \(normalizedExtensionRanges.swiftCaseExpression):") - p.printIndented("try { try decoder.decodeExtensionField(values: &_protobuf_extensionFieldValues, messageType: \(swiftFullName).self, fieldNumber: fieldNumber) }()") + while nextRange != nil { + p.print( + "try visitor.\(visitExtensionsName)(fields: _protobuf_extensionFieldValues, start: \(nextRange!.lowerBound), end: \(nextRange!.upperBound))" + ) + nextRange = ranges.next() } - p.print( - "default: break", - "}") - } else if isExtensible { - // Just output a simple if-statement if the message had no fields of its - // own but we still need to generate a decode statement for extensions. - p.print("if \(normalizedExtensionRanges.swiftBooleanExpression(variable: "fieldNumber")) {") - p.printIndented("try decoder.decodeExtensionField(values: &_protobuf_extensionFieldValues, messageType: \(swiftFullName).self, fieldNumber: fieldNumber)") - p.print("}") - } } - p.print("}") - } + p.print("try unknownFields.traverse(visitor: &visitor)") } - - } + p.print("}") } - p.print("}") - } - - /// Generates the `traverse` method for the message. - /// - /// - Parameter p: The code printer. - private func generateTraverse(printer p: inout CodePrinter) { - p.print("\(visibility)func traverse(visitor: inout V) throws {") - p.withIndentation { p in - generateWithLifetimeExtension(printer: &p, throws: true) { p in - if let storage = storage { - storage.generatePreTraverse(printer: &p) - } - let visitExtensionsName = - descriptor.useMessageSetWireFormat ? "visitExtensionFieldsAsMessageSet" : "visitExtensionFields" - - let usesLocals = fields.reduce(false) { $0 || $1.generateTraverseUsesLocals } - if usesLocals { - p.print(""" - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every if/case branch local when no optimizations - // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and - // https://github.com/apple/swift-protobuf/issues/1182 - """) + private func generateMessageEquality(printer p: inout CodePrinter) { + p.print("\(visibility)static func ==(lhs: \(swiftFullName), rhs: \(swiftFullName)) -> Bool {") + p.withIndentation { p in + var compareFields = true + if let storage = storage { + p.print("if lhs._storage !== rhs._storage {") + p.indent() + p.print("let storagesAreEqual: Bool = ", newlines: false) + if storage.storageProvidesEqualTo { + p.print("lhs._storage.isEqualTo(other: rhs._storage)") + compareFields = false + } + } + if compareFields { + generateWithLifetimeExtension( + printer: &p, + alsoCapturing: "rhs", + selfQualifier: "lhs" + ) { p in + for f in fields { + f.generateFieldComparison(printer: &p) + } + if storage != nil { + p.print("return true") + } + } + } + if storage != nil { + p.print("if !storagesAreEqual {return false}") + p.outdent() + p.print("}") + } + p.print("if lhs.unknownFields != rhs.unknownFields {return false}") + if isExtensible { + p.print("if lhs._protobuf_extensionFieldValues != rhs._protobuf_extensionFieldValues {return false}") + } + p.print("return true") } + p.print("}") + } + + /// Generates the `isInitialized` property for the message, if needed. + /// + /// This may generate nothing, if the `isInitialized` property is not + /// needed. + /// + /// - Parameter printer: The code printer. + private func generateIsInitialized(printer p: inout CodePrinter) { + + var fieldCheckPrinter = CodePrinter(addNewlines: true) + + // The check is done in two passes, so a missing required field can fail + // faster without recursing through the message fields to ensure they are + // initialized. - // Use the "ambitious" ranges because for visit because subranges with no - // intermixed fields can be merged to reduce the number of calls for - // extension visitation. - var ranges = descriptor._ambitiousExtensionRanges.makeIterator() - var nextRange = ranges.next() - for f in fieldsSortedByNumber { - while nextRange != nil && Int(nextRange!.lowerBound) < f.number { - p.print("try visitor.\(visitExtensionsName)(fields: _protobuf_extensionFieldValues, start: \(nextRange!.lowerBound), end: \(nextRange!.upperBound))") - nextRange = ranges.next() - } - f.generateTraverse(printer: &p) + // Ensure required fields are set. + for f in fields { + f.generateRequiredFieldCheck(printer: &fieldCheckPrinter) } - while nextRange != nil { - p.print("try visitor.\(visitExtensionsName)(fields: _protobuf_extensionFieldValues, start: \(nextRange!.lowerBound), end: \(nextRange!.upperBound))") - nextRange = ranges.next() + + for f in fields { + f.generateIsInitializedCheck(printer: &fieldCheckPrinter) } - } - p.print("try unknownFields.traverse(visitor: &visitor)") - } - p.print("}") - } - - private func generateMessageEquality(printer p: inout CodePrinter) { - p.print("\(visibility)static func ==(lhs: \(swiftFullName), rhs: \(swiftFullName)) -> Bool {") - p.withIndentation { p in - var compareFields = true - if let storage = storage { - p.print("if lhs._storage !== rhs._storage {") - p.indent() - p.print("let storagesAreEqual: Bool = ", newlines: false) - if storage.storageProvidesEqualTo { - p.print("lhs._storage.isEqualTo(other: rhs._storage)") - compareFields = false + + let generatedChecks = !fieldCheckPrinter.isEmpty + + if !isExtensible && !generatedChecks { + // No need to generate isInitialized. + return } - } - if compareFields { - generateWithLifetimeExtension(printer: &p, - alsoCapturing: "rhs", - selfQualifier: "lhs") { p in - for f in fields { - f.generateFieldComparison(printer: &p) - } - if storage != nil { - p.print("return true") - } + + p.print("public var isInitialized: Bool {") + p.withIndentation { p in + if isExtensible { + p.print("if !_protobuf_extensionFieldValues.isInitialized {return false}") + } + if generatedChecks { + generateWithLifetimeExtension(printer: &p, returns: true) { p in + p.append(fieldCheckPrinter, indenting: true) + p.print("return true") + } + } else { + p.print("return true") + } } - } - if storage != nil { - p.print("if !storagesAreEqual {return false}") - p.outdent() - p.print("}") - } - p.print("if lhs.unknownFields != rhs.unknownFields {return false}") - if isExtensible { - p.print("if lhs._protobuf_extensionFieldValues != rhs._protobuf_extensionFieldValues {return false}") - } - p.print("return true") - } - p.print("}") - } - - /// Generates the `isInitialized` property for the message, if needed. - /// - /// This may generate nothing, if the `isInitialized` property is not - /// needed. - /// - /// - Parameter printer: The code printer. - private func generateIsInitialized(printer p: inout CodePrinter) { - - var fieldCheckPrinter = CodePrinter(addNewlines: true) - - // The check is done in two passes, so a missing required field can fail - // faster without recursing through the message fields to ensure they are - // initialized. - - // Ensure required fields are set. - for f in fields { - f.generateRequiredFieldCheck(printer: &fieldCheckPrinter) + p.print( + "}", + "" + ) } - for f in fields { - f.generateIsInitializedCheck(printer: &fieldCheckPrinter) - } + /// Executes the given closure, wrapping the code that it prints in a call + /// to `withExtendedLifetime` for the storage object if the message uses + /// one. + /// + /// - Parameter p: The code printer. + /// - Parameter canThrow: Indicates whether the code that will be printed + /// inside the block can throw; if so, the printed call to + /// `withExtendedLifetime` will be preceded by `try`. + /// - Parameter returns: Indicates whether the code that will be printed + /// inside the block returns a value; if so, the printed call to + /// `withExtendedLifetime` will be preceded by `return`. + /// - Parameter capturedVariable: The name of another variable (which is + /// assumed to be the same type as `self`) whose storage should also be + /// captured (used for equality testing, where two messages are operated + /// on simultaneously). + /// - Parameter body: A closure that takes the code printer as its sole + /// `inout` argument. + private func generateWithLifetimeExtension( + printer p: inout CodePrinter, + throws canThrow: Bool = false, + returns: Bool = false, + alsoCapturing capturedVariable: String? = nil, + selfQualifier qualifier: String? = nil, + body: (inout CodePrinter) -> Void + ) { + if storage != nil { + let prefixKeywords = "\(returns ? "return " : "")\(canThrow ? "try " : "")" + + let selfQualifier: String + if let qualifier = qualifier { + selfQualifier = "\(qualifier)." + } else { + selfQualifier = "" + } - let generatedChecks = !fieldCheckPrinter.isEmpty + if let capturedVariable = capturedVariable { + // withExtendedLifetime can only pass a single argument, + // so we have to build and deconstruct a tuple in this case: + let actualArgs = "(\(selfQualifier)_storage, \(capturedVariable)._storage)" + let formalArgs = "(_args: (_StorageClass, _StorageClass))" + p.print("\(prefixKeywords)withExtendedLifetime(\(actualArgs)) { \(formalArgs) in") + p.indent() + p.print("let _storage = _args.0") + p.print("let \(capturedVariable)_storage = _args.1") + } else { + // Single argument can be passed directly: + p.print( + "\(prefixKeywords)withExtendedLifetime(\(selfQualifier)_storage) { (_storage: _StorageClass) in" + ) + p.indent() + } + } - if !isExtensible && !generatedChecks { - // No need to generate isInitialized. - return - } + body(&p) - p.print("public var isInitialized: Bool {") - p.withIndentation { p in - if isExtensible { - p.print("if !_protobuf_extensionFieldValues.isInitialized {return false}") - } - if generatedChecks { - generateWithLifetimeExtension(printer: &p, returns: true) { p in - p.append(fieldCheckPrinter, indenting: true) - p.print("return true") + if storage != nil { + p.outdent() + p.print("}") } - } else { - p.print("return true") - } } - p.print( - "}", - "") - } - - /// Executes the given closure, wrapping the code that it prints in a call - /// to `withExtendedLifetime` for the storage object if the message uses - /// one. - /// - /// - Parameter p: The code printer. - /// - Parameter canThrow: Indicates whether the code that will be printed - /// inside the block can throw; if so, the printed call to - /// `withExtendedLifetime` will be preceded by `try`. - /// - Parameter returns: Indicates whether the code that will be printed - /// inside the block returns a value; if so, the printed call to - /// `withExtendedLifetime` will be preceded by `return`. - /// - Parameter capturedVariable: The name of another variable (which is - /// assumed to be the same type as `self`) whose storage should also be - /// captured (used for equality testing, where two messages are operated - /// on simultaneously). - /// - Parameter body: A closure that takes the code printer as its sole - /// `inout` argument. - private func generateWithLifetimeExtension( - printer p: inout CodePrinter, - throws canThrow: Bool = false, - returns: Bool = false, - alsoCapturing capturedVariable: String? = nil, - selfQualifier qualifier: String? = nil, - body: (inout CodePrinter) -> Void - ) { - if storage != nil { - let prefixKeywords = "\(returns ? "return " : "")\(canThrow ? "try " : "")" - - let selfQualifier: String - if let qualifier = qualifier { - selfQualifier = "\(qualifier)." - } else { - selfQualifier = "" - } - - if let capturedVariable = capturedVariable { - // withExtendedLifetime can only pass a single argument, - // so we have to build and deconstruct a tuple in this case: - let actualArgs = "(\(selfQualifier)_storage, \(capturedVariable)._storage)" - let formalArgs = "(_args: (_StorageClass, _StorageClass))" - p.print("\(prefixKeywords)withExtendedLifetime(\(actualArgs)) { \(formalArgs) in") - p.indent() - p.print("let _storage = _args.0") - p.print("let \(capturedVariable)_storage = _args.1") - } else { - // Single argument can be passed directly: - p.print("\(prefixKeywords)withExtendedLifetime(\(selfQualifier)_storage) { (_storage: _StorageClass) in") - p.indent() - } - } - - body(&p) +} - if storage != nil { - p.outdent() - p.print("}") +private struct MessageFieldFactory { + private let generatorOptions: GeneratorOptions + private let namer: SwiftProtobufNamer + private let useHeapStorage: Bool + private let oneofs: [OneofGenerator] + + init( + generatorOptions: GeneratorOptions, + namer: SwiftProtobufNamer, + useHeapStorage: Bool, + oneofGenerators: [OneofGenerator] + ) { + self.generatorOptions = generatorOptions + self.namer = namer + self.useHeapStorage = useHeapStorage + oneofs = oneofGenerators } - } -} -fileprivate struct MessageFieldFactory { - private let generatorOptions: GeneratorOptions - private let namer: SwiftProtobufNamer - private let useHeapStorage: Bool - private let oneofs: [OneofGenerator] - - init( - generatorOptions: GeneratorOptions, - namer: SwiftProtobufNamer, - useHeapStorage: Bool, - oneofGenerators: [OneofGenerator] - ) { - self.generatorOptions = generatorOptions - self.namer = namer - self.useHeapStorage = useHeapStorage - oneofs = oneofGenerators - } - - func make(forFieldDescriptor field: FieldDescriptor) -> any FieldGenerator { - guard field.realContainingOneof == nil else { - return oneofs[Int(field.oneofIndex!)].fieldGenerator(forFieldNumber: Int(field.number)) + func make(forFieldDescriptor field: FieldDescriptor) -> any FieldGenerator { + guard field.realContainingOneof == nil else { + return oneofs[Int(field.oneofIndex!)].fieldGenerator(forFieldNumber: Int(field.number)) + } + return MessageFieldGenerator( + descriptor: field, + generatorOptions: generatorOptions, + namer: namer, + usesHeapStorage: useHeapStorage + ) } - return MessageFieldGenerator(descriptor: field, - generatorOptions: generatorOptions, - namer: namer, - usesHeapStorage: useHeapStorage) - } } diff --git a/Sources/protoc-gen-swift/MessageStorageClassGenerator.swift b/Sources/protoc-gen-swift/MessageStorageClassGenerator.swift index cb30831fd..d58d1678e 100644 --- a/Sources/protoc-gen-swift/MessageStorageClassGenerator.swift +++ b/Sources/protoc-gen-swift/MessageStorageClassGenerator.swift @@ -14,110 +14,113 @@ // ----------------------------------------------------------------------------- import Foundation -import SwiftProtobufPluginLibrary import SwiftProtobuf +import SwiftProtobufPluginLibrary /// Generates the `_StorageClass` used for messages that employ copy-on-write /// logic for some of their fields. class MessageStorageClassGenerator { - private let fields: [any FieldGenerator] - - /// Creates a new `MessageStorageClassGenerator`. - init(fields: [any FieldGenerator]) { - self.fields = fields - } - - /// Visibility of the storage within the Message. - var storageVisibility: String { - return "fileprivate" - } - - /// If the storage wants to manually implement equality. - var storageProvidesEqualTo: Bool { return false } - - /// Generates the full code for the storage class. - /// - /// - Parameter p: The code printer. - func generateTypeDeclaration(printer p: inout CodePrinter) { - p.print("fileprivate class _StorageClass {") - p.withIndentation { p in - generateStoredProperties(printer: &p) - // Generate a default instance to be used so the heap allocation is - // delayed until mutation is needed. This is the largest savings when - // the message is used as a field in another message as it causes - // returning the default to not require that heap allocation, i.e. - - // readonly usage never causes the allocation. - p.print(""" - - #if swift(>=5.10) - // This property is used as the initial default value for new instances of the type. - // The type itself is protecting the reference to its storage via CoW semantics. - // This will force a copy to be made of this reference when the first mutation occurs; - // hence, it is safe to mark this as `nonisolated(unsafe)`. - static nonisolated(unsafe) let defaultInstance = _StorageClass() - #else - static let defaultInstance = _StorageClass() - #endif - - private init() {} - - """) - generateClone(printer: &p) + private let fields: [any FieldGenerator] + + /// Creates a new `MessageStorageClassGenerator`. + init(fields: [any FieldGenerator]) { + self.fields = fields + } + + /// Visibility of the storage within the Message. + var storageVisibility: String { + "fileprivate" + } + + /// If the storage wants to manually implement equality. + var storageProvidesEqualTo: Bool { false } + + /// Generates the full code for the storage class. + /// + /// - Parameter p: The code printer. + func generateTypeDeclaration(printer p: inout CodePrinter) { + p.print("fileprivate class _StorageClass {") + p.withIndentation { p in + generateStoredProperties(printer: &p) + // Generate a default instance to be used so the heap allocation is + // delayed until mutation is needed. This is the largest savings when + // the message is used as a field in another message as it causes + // returning the default to not require that heap allocation, i.e. - + // readonly usage never causes the allocation. + p.print( + """ + + #if swift(>=5.10) + // This property is used as the initial default value for new instances of the type. + // The type itself is protecting the reference to its storage via CoW semantics. + // This will force a copy to be made of this reference when the first mutation occurs; + // hence, it is safe to mark this as `nonisolated(unsafe)`. + static nonisolated(unsafe) let defaultInstance = _StorageClass() + #else + static let defaultInstance = _StorageClass() + #endif + + private init() {} + + """ + ) + generateClone(printer: &p) + } + p.print("}") + } + + /// Generated the uniqueStorage() implementation. + func generateUniqueStorage(printer p: inout CodePrinter) { + p.print("\(storageVisibility) mutating func _uniqueStorage() -> _StorageClass {") + p.withIndentation { p in + p.print("if !isKnownUniquelyReferenced(&_storage) {") + p.printIndented("_storage = _StorageClass(copying: _storage)") + p.print( + "}", + "return _storage" + ) + } + p.print("}") } - p.print("}") - } - - /// Generated the uniqueStorage() implementation. - func generateUniqueStorage(printer p: inout CodePrinter) { - p.print("\(storageVisibility) mutating func _uniqueStorage() -> _StorageClass {") - p.withIndentation { p in - p.print("if !isKnownUniquelyReferenced(&_storage) {") - p.printIndented("_storage = _StorageClass(copying: _storage)") - p.print( - "}", - "return _storage") + + func generatePreTraverse(printer p: inout CodePrinter) { + // Nothing } - p.print("}") - } - - func generatePreTraverse(printer p: inout CodePrinter) { - // Nothing - } - - /// Generates the stored properties for the storage class. - /// - /// - Parameter p: The code printer. - private func generateStoredProperties(printer p: inout CodePrinter) { - for f in fields { - f.generateStorage(printer: &p) + + /// Generates the stored properties for the storage class. + /// + /// - Parameter p: The code printer. + private func generateStoredProperties(printer p: inout CodePrinter) { + for f in fields { + f.generateStorage(printer: &p) + } } - } - - /// Generates the `init(copying:)` method of the storage class. - /// - /// - Parameter p: The code printer. - private func generateClone(printer p: inout CodePrinter) { - p.print("init(copying source: _StorageClass) {") - p.withIndentation { p in - for f in fields { - f.generateStorageClassClone(printer: &p) - } + + /// Generates the `init(copying:)` method of the storage class. + /// + /// - Parameter p: The code printer. + private func generateClone(printer p: inout CodePrinter) { + p.print("init(copying source: _StorageClass) {") + p.withIndentation { p in + for f in fields { + f.generateStorageClassClone(printer: &p) + } + } + p.print("}") } - p.print("}") - } } /// Custom generator for storage of an google.protobuf.Any. -class AnyMessageStorageClassGenerator : MessageStorageClassGenerator { - override var storageVisibility: String { return "internal" } - override var storageProvidesEqualTo: Bool { return true } - - override func generateTypeDeclaration(printer p: inout CodePrinter) { - // Just need an alias to the hand coded Storage. - p.print("typealias _StorageClass = AnyMessageStorage") - } - - override func generatePreTraverse(printer p: inout CodePrinter) { - p.print("try _storage.preTraverse()") - } +class AnyMessageStorageClassGenerator: MessageStorageClassGenerator { + override var storageVisibility: String { "internal" } + override var storageProvidesEqualTo: Bool { true } + + override func generateTypeDeclaration(printer p: inout CodePrinter) { + // Just need an alias to the hand coded Storage. + p.print("typealias _StorageClass = AnyMessageStorage") + } + + override func generatePreTraverse(printer p: inout CodePrinter) { + p.print("try _storage.preTraverse()") + } } diff --git a/Sources/protoc-gen-swift/MessageStorageDecision.swift b/Sources/protoc-gen-swift/MessageStorageDecision.swift index 3d50a5a15..d471975c0 100644 --- a/Sources/protoc-gen-swift/MessageStorageDecision.swift +++ b/Sources/protoc-gen-swift/MessageStorageDecision.swift @@ -20,183 +20,183 @@ import SwiftProtobufPluginLibrary /// /// As mentioned in the file comment, these numbers can be revised in the future /// to compute a real stack/heap cost if desired. -fileprivate enum FieldCost { - /// Of a repeated field. - static let repeated = 1 - /// Of a map field. - static let map = 1 - - /// Of "Plan Old Data" (ints, floating point) field that is 64 bits - static let singlePOD32 = 1 - /// Of "Plan Old Data" (ints, floating point) field that is 64 bits - static let singlePOD64 = 1 - /// Of bool field. - static let singleBool = 1 - /// Of a `string` field. - static let singleString = 1 - /// Of a `bytes` field. - static let singleBytes = 1 - - /// A single Message field where the message in question uses storage. - static let singleMessageFieldUsingStorage = 1 - - static func estimate(_ field: FieldDescriptor) -> Int { - guard !field.isRepeated else { - // Repeated fields don't count the exact types, just fixed costs. - return field.isMap ? FieldCost.map : FieldCost.repeated - } +private enum FieldCost { + /// Of a repeated field. + static let repeated = 1 + /// Of a map field. + static let map = 1 + + /// Of "Plan Old Data" (ints, floating point) field that is 64 bits + static let singlePOD32 = 1 + /// Of "Plan Old Data" (ints, floating point) field that is 64 bits + static let singlePOD64 = 1 + /// Of bool field. + static let singleBool = 1 + /// Of a `string` field. + static let singleString = 1 + /// Of a `bytes` field. + static let singleBytes = 1 + + /// A single Message field where the message in question uses storage. + static let singleMessageFieldUsingStorage = 1 + + static func estimate(_ field: FieldDescriptor) -> Int { + guard !field.isRepeated else { + // Repeated fields don't count the exact types, just fixed costs. + return field.isMap ? FieldCost.map : FieldCost.repeated + } - switch field.type { - case .bool: - return 1 - case .int32, .sint32, .uint32, .fixed32, .sfixed32, .float, .enum: - return FieldCost.singlePOD32 - case .int64, .sint64, .uint64, .fixed64, .sfixed64, .double: - return FieldCost.singlePOD64 - case .string: - return FieldCost.singleString - case .bytes: - return FieldCost.singleBytes - case .group, .message: - return analyze(descriptor: field.messageType!).costAsField + switch field.type { + case .bool: + return 1 + case .int32, .sint32, .uint32, .fixed32, .sfixed32, .float, .enum: + return FieldCost.singlePOD32 + case .int64, .sint64, .uint64, .fixed64, .sfixed64, .double: + return FieldCost.singlePOD64 + case .string: + return FieldCost.singleString + case .bytes: + return FieldCost.singleBytes + case .group, .message: + return analyze(descriptor: field.messageType!).costAsField + } } - } } /// Maximum computed cost of a Message's fields allow before it uses Storage. -fileprivate let totalFieldCostRequiringStorage = 17 +private let totalFieldCostRequiringStorage = 17 /// The result of analysis, if the message should use heap storage and the /// cost of the message when used as a field in other messages. -fileprivate struct AnalyzeResult { - let usesStorage: Bool - let costAsField: Int - - init(usesStorage: Bool, costAsField: Int) { - precondition(costAsField < totalFieldCostRequiringStorage || usesStorage) - self.usesStorage = usesStorage - self.costAsField = costAsField - } - - @inlinable - init(_ costAsField: Int) { - self.init(usesStorage: false, costAsField: costAsField) - } - - /// The message should use storage. - static let useStorage = - AnalyzeResult(usesStorage: true, costAsField: FieldCost.singleMessageFieldUsingStorage) +private struct AnalyzeResult { + let usesStorage: Bool + let costAsField: Int + + init(usesStorage: Bool, costAsField: Int) { + precondition(costAsField < totalFieldCostRequiringStorage || usesStorage) + self.usesStorage = usesStorage + self.costAsField = costAsField + } + + @inlinable + init(_ costAsField: Int) { + self.init(usesStorage: false, costAsField: costAsField) + } + + /// The message should use storage. + static let useStorage = + AnalyzeResult(usesStorage: true, costAsField: FieldCost.singleMessageFieldUsingStorage) } // This is adapted from SwiftNIO so sendable checks don't flag issues with // `analysisCache`. Another option would be something like NIO's // `LockedValueBox` or moving the entire handling to a Task. -fileprivate final class UnsafeMutableTransferBox { - var wrappedValue: Wrapped - init(_ wrappedValue: Wrapped) { - self.wrappedValue = wrappedValue - } +private final class UnsafeMutableTransferBox { + var wrappedValue: Wrapped + init(_ wrappedValue: Wrapped) { + self.wrappedValue = wrappedValue + } } extension UnsafeMutableTransferBox: @unchecked Sendable {} /// Cache for the `analyze(descriptor:)` results to avoid doing them multiple /// times. -fileprivate let analysisCache: UnsafeMutableTransferBox> = .init([ - // google.protobuf.Any can be seeded. - "google.protobuf.Any": .useStorage, +private let analysisCache: UnsafeMutableTransferBox<[String: AnalyzeResult]> = .init([ + // google.protobuf.Any can be seeded. + "google.protobuf.Any": .useStorage ]) /// Analyze the given descriptor to decide if it should use storage and what /// the cost of it will be when appearing as a single field in another message. -fileprivate func analyze(descriptor: Descriptor) -> AnalyzeResult { - if let analysis = analysisCache.wrappedValue[descriptor.fullName] { - return analysis - } - - func containsRecursiveSingularField(_ descriptor: Descriptor) -> Bool { - let initialFile = descriptor.file - - func recursionHelper(_ descriptor: Descriptor, messageStack: [Descriptor]) -> Bool { - var messageStack = messageStack - messageStack.append(descriptor) - return descriptor.fields.contains { - guard !$0.isRepeated else { return false } - // Ignore fields that aren’t messages or groups. - guard $0.type == .message || $0.type == .group else { return false } - guard let messageType = $0.messageType else { return false } - - // Proto files are a graph without cycles, to be recursive, the messages - // in the cycle must be defined in the same file. - guard messageType.file === initialFile else { return false } - - // Did things recurse? - if let first = messageStack.firstIndex(where: { $0 === messageType }) { - // Mark all those in the loop as using storage. - for msg in messageStack[first.. AnalyzeResult { + if let analysis = analysisCache.wrappedValue[descriptor.fullName] { + return analysis } - return recursionHelper(descriptor, messageStack: []) - } + func containsRecursiveSingularField(_ descriptor: Descriptor) -> Bool { + let initialFile = descriptor.file + + func recursionHelper(_ descriptor: Descriptor, messageStack: [Descriptor]) -> Bool { + var messageStack = messageStack + messageStack.append(descriptor) + return descriptor.fields.contains { + guard !$0.isRepeated else { return false } + // Ignore fields that aren’t messages or groups. + guard $0.type == .message || $0.type == .group else { return false } + guard let messageType = $0.messageType else { return false } + + // Proto files are a graph without cycles, to be recursive, the messages + // in the cycle must be defined in the same file. + guard messageType.file === initialFile else { return false } + + // Did things recurse? + if let first = messageStack.firstIndex(where: { $0 === messageType }) { + // Mark all those in the loop as using storage. + for msg in messageStack[first.. AnalyzeResult { - if containsRecursiveSingularField(descriptor) { - return .useStorage + return recursionHelper(descriptor, messageStack: []) } - var fieldsCost: Int = 0 + func helper(_ descriptor: Descriptor) -> AnalyzeResult { + if containsRecursiveSingularField(descriptor) { + return .useStorage + } - // Compute a cost for all the fields that aren't in a oneof. - for f in descriptor.fields { - guard f.oneofIndex == nil else { continue } - fieldsCost += FieldCost.estimate(f) - if fieldsCost >= totalFieldCostRequiringStorage { - return .useStorage - } - } + var fieldsCost: Int = 0 - // Add in the cost of the largest field of each oneof. - for o in descriptor.oneofs { - var oneofCost: Int = 0 - for f in o.fields { - oneofCost = max(oneofCost, FieldCost.estimate(f)) - if (fieldsCost + oneofCost) >= totalFieldCostRequiringStorage { - return .useStorage + // Compute a cost for all the fields that aren't in a oneof. + for f in descriptor.fields { + guard f.oneofIndex == nil else { continue } + fieldsCost += FieldCost.estimate(f) + if fieldsCost >= totalFieldCostRequiringStorage { + return .useStorage + } } - } - fieldsCost += oneofCost - } - assert(fieldsCost <= totalFieldCostRequiringStorage) - return AnalyzeResult(fieldsCost) - } + // Add in the cost of the largest field of each oneof. + for o in descriptor.oneofs { + var oneofCost: Int = 0 + for f in o.fields { + oneofCost = max(oneofCost, FieldCost.estimate(f)) + if (fieldsCost + oneofCost) >= totalFieldCostRequiringStorage { + return .useStorage + } + } + fieldsCost += oneofCost + } + assert(fieldsCost <= totalFieldCostRequiringStorage) + + return AnalyzeResult(fieldsCost) + } - let result = helper(descriptor) - analysisCache.wrappedValue[descriptor.fullName] = result - return result + let result = helper(descriptor) + analysisCache.wrappedValue[descriptor.fullName] = result + return result } /// Encapsulates the decision choices around when a Message should use /// heap based storage. enum MessageStorageDecision { - /// Compute if a message should use heap based storage or not. - static func shouldUseHeapStorage(descriptor: Descriptor) -> Bool { - return analyze(descriptor: descriptor).usesStorage - } + /// Compute if a message should use heap based storage or not. + static func shouldUseHeapStorage(descriptor: Descriptor) -> Bool { + analyze(descriptor: descriptor).usesStorage + } } diff --git a/Sources/protoc-gen-swift/OneofGenerator.swift b/Sources/protoc-gen-swift/OneofGenerator.swift index 71b2a95dd..f35a30b22 100644 --- a/Sources/protoc-gen-swift/OneofGenerator.swift +++ b/Sources/protoc-gen-swift/OneofGenerator.swift @@ -13,8 +13,8 @@ /// // ----------------------------------------------------------------------------- import Foundation -import SwiftProtobufPluginLibrary import SwiftProtobuf +import SwiftProtobufPluginLibrary class OneofGenerator { /// Custom FieldGenerator that caches come calculated strings, and bridges @@ -40,7 +40,7 @@ class OneofGenerator { } // Only valid on message fields. - var messageType: Descriptor? { return fieldDescriptor.messageType } + var messageType: Descriptor? { fieldDescriptor.messageType } init(descriptor: FieldDescriptor, generatorOptions: GeneratorOptions, namer: SwiftProtobufNamer) { precondition(descriptor.oneofIndex != nil) @@ -49,9 +49,11 @@ class OneofGenerator { oneof = nil group = -1 - let names = namer.messagePropertyNames(field: descriptor, - prefixed: ".", - includeHasAndClear: false) + let names = namer.messagePropertyNames( + field: descriptor, + prefixed: ".", + includeHasAndClear: false + ) swiftName = names.name dottedSwiftName = names.prefixed swiftType = descriptor.swiftType(namer: namer) @@ -98,7 +100,7 @@ class OneofGenerator { } var generateTraverseUsesLocals: Bool { - return oneof.generateTraverseUsesLocals + oneof.generateTraverseUsesLocals } func generateTraverse(printer p: inout CodePrinter) { @@ -123,7 +125,12 @@ class OneofGenerator { private let underscoreSwiftFieldName: String private let storedProperty: String - init(descriptor: OneofDescriptor, generatorOptions: GeneratorOptions, namer: SwiftProtobufNamer, usesHeapStorage: Bool) { + init( + descriptor: OneofDescriptor, + generatorOptions: GeneratorOptions, + namer: SwiftProtobufNamer, + usesHeapStorage: Bool + ) { self.oneofDescriptor = descriptor self.generatorOptions = generatorOptions self.namer = namer @@ -144,35 +151,39 @@ class OneofGenerator { } fields = descriptor.fields.map { - return MemberFieldGenerator(descriptor: $0, - generatorOptions: generatorOptions, - namer: namer) + MemberFieldGenerator( + descriptor: $0, + generatorOptions: generatorOptions, + namer: namer + ) } - fieldsSortedByNumber = fields.sorted {$0.number < $1.number} + fieldsSortedByNumber = fields.sorted { $0.number < $1.number } // Bucked these fields in continuous chunks based on the other fields // in the parent and the parent's extension ranges. Insert the `start` // from each extension range as an easy way to check for them being // mixed in between the fields. var parentNumbers = descriptor.containingType.fields.map { Int($0.number) } - parentNumbers.append(contentsOf: descriptor.containingType._normalizedExtensionRanges.map { Int($0.lowerBound) }) + parentNumbers.append( + contentsOf: descriptor.containingType._normalizedExtensionRanges.map { Int($0.lowerBound) } + ) var parentNumbersIterator = parentNumbers.sorted(by: { $0 < $1 }).makeIterator() var nextParentFieldNumber = parentNumbersIterator.next() var grouped = [[MemberFieldGenerator]]() var currentGroup = [MemberFieldGenerator]() for f in fieldsSortedByNumber { - let nextFieldNumber = f.number - if nextParentFieldNumber != nextFieldNumber { - if !currentGroup.isEmpty { - grouped.append(currentGroup) - currentGroup.removeAll() - } - while nextParentFieldNumber != nextFieldNumber { - nextParentFieldNumber = parentNumbersIterator.next() + let nextFieldNumber = f.number + if nextParentFieldNumber != nextFieldNumber { + if !currentGroup.isEmpty { + grouped.append(currentGroup) + currentGroup.removeAll() + } + while nextParentFieldNumber != nextFieldNumber { + nextParentFieldNumber = parentNumbersIterator.next() + } } - } - currentGroup.append(f) - nextParentFieldNumber = parentNumbersIterator.next() + currentGroup.append(f) + nextParentFieldNumber = parentNumbersIterator.next() } if !currentGroup.isEmpty { grouped.append(currentGroup) @@ -205,7 +216,7 @@ class OneofGenerator { // then all oneof enums with Data fields need to be manually marked as // @unchecked. let hasBytesField = oneofDescriptor.fields.contains { - return $0.type == .bytes + return $0.type == .bytes } let sendableConformance = hasBytesField ? "@unchecked Sendable" : "Sendable" @@ -213,51 +224,57 @@ class OneofGenerator { // to this enum we generated. p.print( "", - "\(comments)\(visibility)enum \(swiftRelativeName): Equatable, \(sendableConformance) {") + "\(comments)\(visibility)enum \(swiftRelativeName): Equatable, \(sendableConformance) {" + ) p.withIndentation { p in - // Oneof case for each ivar - for f in fields { - p.print("\(f.comments)case \(f.swiftName)(\(f.swiftType))") - } - - // A helper for isInitialized - let fieldsToCheck = fields.filter { - $0.isGroupOrMessage && $0.messageType!.containsRequiredFields() - } - if !fieldsToCheck.isEmpty { - p.print( - "", - "fileprivate var isInitialized: Bool {") - p.withIndentation { p in - if fieldsToCheck.count == 1 { - let f = fieldsToCheck.first! - p.print( - "guard case \(f.dottedSwiftName)(let v) = self else {return true}", - "return v.isInitialized") - } else if fieldsToCheck.count > 1 { - p.print(""" - // The use of inline closures is to circumvent an issue where the compiler - // allocates stack space for every case branch when no optimizations are - // enabled. https://github.com/apple/swift-protobuf/issues/1034 - switch self { - """) - for f in fieldsToCheck { - p.print("case \(f.dottedSwiftName): return {") - p.printIndented( - "guard case \(f.dottedSwiftName)(let v) = self else { preconditionFailure() }", - "return v.isInitialized") - p.print("}()") - } - // If there were other cases, add a default. - if fieldsToCheck.count != fields.count { - p.print("default: return true") - } - p.print("}") - } + // Oneof case for each ivar + for f in fields { + p.print("\(f.comments)case \(f.swiftName)(\(f.swiftType))") } - p.print("}") - } - p.print() + + // A helper for isInitialized + let fieldsToCheck = fields.filter { + $0.isGroupOrMessage && $0.messageType!.containsRequiredFields() + } + if !fieldsToCheck.isEmpty { + p.print( + "", + "fileprivate var isInitialized: Bool {" + ) + p.withIndentation { p in + if fieldsToCheck.count == 1 { + let f = fieldsToCheck.first! + p.print( + "guard case \(f.dottedSwiftName)(let v) = self else {return true}", + "return v.isInitialized" + ) + } else if fieldsToCheck.count > 1 { + p.print( + """ + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch self { + """ + ) + for f in fieldsToCheck { + p.print("case \(f.dottedSwiftName): return {") + p.printIndented( + "guard case \(f.dottedSwiftName)(let v) = self else { preconditionFailure() }", + "return v.isInitialized" + ) + p.print("}()") + } + // If there were other cases, add a default. + if fieldsToCheck.count != fields.count { + p.print("default: return true") + } + p.print("}") + } + } + p.print("}") + } + p.print() } p.print("}") } @@ -267,14 +284,17 @@ class OneofGenerator { p.print() if usesHeapStorage { p.print( - "\(comments)\(visibility)var \(swiftFieldName): \(swiftRelativeName)? {") + "\(comments)\(visibility)var \(swiftFieldName): \(swiftRelativeName)? {" + ) p.printIndented( - "get {return _storage.\(underscoreSwiftFieldName)}", - "set {_uniqueStorage().\(underscoreSwiftFieldName) = newValue}") + "get {return _storage.\(underscoreSwiftFieldName)}", + "set {_uniqueStorage().\(underscoreSwiftFieldName) = newValue}" + ) p.print("}") } else { p.print( - "\(comments)\(visibility)var \(swiftFieldName): \(swiftFullName)? = nil") + "\(comments)\(visibility)var \(swiftFieldName): \(swiftFullName)? = nil" + ) } } @@ -283,28 +303,34 @@ class OneofGenerator { func generateInterface(printer p: inout CodePrinter, field: MemberFieldGenerator) { // First field causes the oneof enum to get generated. if field === fields.first { - gerenateOneofEnumProperty(printer: &p) + gerenateOneofEnumProperty(printer: &p) } let getter = usesHeapStorage ? "_storage.\(underscoreSwiftFieldName)" : swiftFieldName // Within `set` below, if the oneof name was "newValue" then it has to // be qualified with `self.` to avoid the collision with the setter // parameter. - let setter = usesHeapStorage ? "_uniqueStorage().\(underscoreSwiftFieldName)" : (swiftFieldName == "newValue" ? "self.newValue" : swiftFieldName) + let setter = + usesHeapStorage + ? "_uniqueStorage().\(underscoreSwiftFieldName)" + : (swiftFieldName == "newValue" ? "self.newValue" : swiftFieldName) let visibility = generatorOptions.visibilitySourceSnippet p.print( - "", - "\(field.comments)\(visibility)var \(field.swiftName): \(field.swiftType) {") + "", + "\(field.comments)\(visibility)var \(field.swiftName): \(field.swiftType) {" + ) p.withIndentation { p in - p.print("get {") - p.printIndented( - "if case \(field.dottedSwiftName)(let v)? = \(getter) {return v}", - "return \(field.swiftDefaultValue)") - p.print( - "}", - "set {\(setter) = \(field.dottedSwiftName)(newValue)}") + p.print("get {") + p.printIndented( + "if case \(field.dottedSwiftName)(let v)? = \(getter) {return v}", + "return \(field.swiftDefaultValue)" + ) + p.print( + "}", + "set {\(setter) = \(field.dottedSwiftName)(newValue)}" + ) } p.print("}") } @@ -331,36 +357,40 @@ class OneofGenerator { func generateDecodeFieldCase(printer p: inout CodePrinter, field: MemberFieldGenerator) { p.print("case \(field.number): try {") p.withIndentation { p in - let hadValueTest: String - if field.isGroupOrMessage { - // Messages need to fetch the current value so new fields are merged into the existing - // value - p.print( - "var v: \(field.swiftType)?", - "var hadOneofValue = false", - "if let current = \(storedProperty) {") - p.printIndented( - "hadOneofValue = true", - "if case \(field.dottedSwiftName)(let m) = current {v = m}") - p.print("}") - hadValueTest = "hadOneofValue" - } else { - p.print("var v: \(field.swiftType)?") - hadValueTest = "\(storedProperty) != nil" - } - - p.print( - "try decoder.decodeSingular\(field.protoGenericType)Field(value: &v)", - "if let v = v {") - p.printIndented( - "if \(hadValueTest) {try decoder.handleConflictingOneOf()}", - "\(storedProperty) = \(field.dottedSwiftName)(v)") - p.print("}") + let hadValueTest: String + if field.isGroupOrMessage { + // Messages need to fetch the current value so new fields are merged into the existing + // value + p.print( + "var v: \(field.swiftType)?", + "var hadOneofValue = false", + "if let current = \(storedProperty) {" + ) + p.printIndented( + "hadOneofValue = true", + "if case \(field.dottedSwiftName)(let m) = current {v = m}" + ) + p.print("}") + hadValueTest = "hadOneofValue" + } else { + p.print("var v: \(field.swiftType)?") + hadValueTest = "\(storedProperty) != nil" + } + + p.print( + "try decoder.decodeSingular\(field.protoGenericType)Field(value: &v)", + "if let v = v {" + ) + p.printIndented( + "if \(hadValueTest) {try decoder.handleConflictingOneOf()}", + "\(storedProperty) = \(field.dottedSwiftName)(v)" + ) + p.print("}") } p.print("}()") } - var generateTraverseUsesLocals: Bool { return true } + var generateTraverseUsesLocals: Bool { true } func generateTraverse(printer p: inout CodePrinter, field: MemberFieldGenerator) { // First field in the group causes the output. @@ -369,15 +399,18 @@ class OneofGenerator { if group.count == 1 { p.print("try { if case \(field.dottedSwiftName)(let v)? = \(storedProperty) {") - p.printIndented("try visitor.visitSingular\(field.protoGenericType)Field(value: v, fieldNumber: \(field.number))") + p.printIndented( + "try visitor.visitSingular\(field.protoGenericType)Field(value: v, fieldNumber: \(field.number))" + ) p.print("} }()") } else { p.print("switch \(storedProperty) {") for f in group { p.print("case \(f.dottedSwiftName)?: try {") p.printIndented( - "guard case \(f.dottedSwiftName)(let v)? = \(storedProperty) else { preconditionFailure() }", - "try visitor.visitSingular\(f.protoGenericType)Field(value: v, fieldNumber: \(f.number))") + "guard case \(f.dottedSwiftName)(let v)? = \(storedProperty) else { preconditionFailure() }", + "try visitor.visitSingular\(f.protoGenericType)Field(value: v, fieldNumber: \(f.number))" + ) p.print("}()") } if fieldSortedGrouped.count == 1 { @@ -398,11 +431,11 @@ class OneofGenerator { let lhsProperty: String let otherStoredProperty: String if usesHeapStorage { - lhsProperty = "_storage.\(underscoreSwiftFieldName)" - otherStoredProperty = "rhs_storage.\(underscoreSwiftFieldName)" + lhsProperty = "_storage.\(underscoreSwiftFieldName)" + otherStoredProperty = "rhs_storage.\(underscoreSwiftFieldName)" } else { - lhsProperty = "lhs.\(swiftFieldName)" - otherStoredProperty = "rhs.\(swiftFieldName)" + lhsProperty = "lhs.\(swiftFieldName)" + otherStoredProperty = "rhs.\(swiftFieldName)" } p.print("if \(lhsProperty) != \(otherStoredProperty) {return false}") diff --git a/Sources/protoc-gen-swift/ProvidesDeprecationComment+Extensions.swift b/Sources/protoc-gen-swift/ProvidesDeprecationComment+Extensions.swift index 37c61f5a3..fc10a05ec 100644 --- a/Sources/protoc-gen-swift/ProvidesDeprecationComment+Extensions.swift +++ b/Sources/protoc-gen-swift/ProvidesDeprecationComment+Extensions.swift @@ -9,21 +9,22 @@ // ----------------------------------------------------------------------------- import Foundation - import SwiftProtobufPluginLibrary extension ProvidesSourceCodeLocation { - func protoSourceComments( - generatorOptions: GeneratorOptions, - commentPrefix: String = "///", - leadingDetachedPrefix: String? = nil - ) -> String { - if generatorOptions.experimentalStripNonfunctionalCodegen { - // Comments are inherently non-functional, and may change subtly on - // transformations. - return String() + func protoSourceComments( + generatorOptions: GeneratorOptions, + commentPrefix: String = "///", + leadingDetachedPrefix: String? = nil + ) -> String { + if generatorOptions.experimentalStripNonfunctionalCodegen { + // Comments are inherently non-functional, and may change subtly on + // transformations. + return String() + } + return protoSourceComments( + commentPrefix: commentPrefix, + leadingDetachedPrefix: leadingDetachedPrefix + ) } - return protoSourceComments(commentPrefix: commentPrefix, - leadingDetachedPrefix: leadingDetachedPrefix) - } } diff --git a/Sources/protoc-gen-swift/ProvidesSourceCodeLocation+Extensions.swift b/Sources/protoc-gen-swift/ProvidesSourceCodeLocation+Extensions.swift index e0e01c740..f9740c3f9 100644 --- a/Sources/protoc-gen-swift/ProvidesSourceCodeLocation+Extensions.swift +++ b/Sources/protoc-gen-swift/ProvidesSourceCodeLocation+Extensions.swift @@ -9,22 +9,23 @@ // ----------------------------------------------------------------------------- import Foundation - import SwiftProtobufPluginLibrary extension ProvidesDeprecationComment where Self: ProvidesSourceCodeLocation { - func protoSourceCommentsWithDeprecation( - generatorOptions: GeneratorOptions, - commentPrefix: String = "///", - leadingDetachedPrefix: String? = nil - ) -> String { - if generatorOptions.experimentalStripNonfunctionalCodegen { - // Comments are inherently non-functional, and may change subtly on - // transformations. - return deprecationComment(commentPrefix: commentPrefix) - } + func protoSourceCommentsWithDeprecation( + generatorOptions: GeneratorOptions, + commentPrefix: String = "///", + leadingDetachedPrefix: String? = nil + ) -> String { + if generatorOptions.experimentalStripNonfunctionalCodegen { + // Comments are inherently non-functional, and may change subtly on + // transformations. + return deprecationComment(commentPrefix: commentPrefix) + } - return protoSourceCommentsWithDeprecation(commentPrefix: commentPrefix, - leadingDetachedPrefix: leadingDetachedPrefix) - } + return protoSourceCommentsWithDeprecation( + commentPrefix: commentPrefix, + leadingDetachedPrefix: leadingDetachedPrefix + ) + } } diff --git a/Sources/protoc-gen-swift/Range+Extensions.swift b/Sources/protoc-gen-swift/Range+Extensions.swift index aabc61101..a3e766aec 100644 --- a/Sources/protoc-gen-swift/Range+Extensions.swift +++ b/Sources/protoc-gen-swift/Range+Extensions.swift @@ -14,56 +14,56 @@ // ----------------------------------------------------------------------------- import Foundation -import SwiftProtobufPluginLibrary import SwiftProtobuf +import SwiftProtobufPluginLibrary extension Range where Bound == Int32 { - /// A `String` containing the Swift expression that represents this range to - /// be used in a `case` statement. - var swiftCaseExpression: String { - if lowerBound == upperBound - 1 { - return "\(lowerBound)" + /// A `String` containing the Swift expression that represents this range to + /// be used in a `case` statement. + var swiftCaseExpression: String { + if lowerBound == upperBound - 1 { + return "\(lowerBound)" + } + return "\(lowerBound)..<\(upperBound)" } - return "\(lowerBound)..<\(upperBound)" - } - /// A `String` containing the Swift Boolean expression that tests the given - /// variable for containment within this range. - /// - /// - Parameter variable: The name of the variable to test in the expression. - /// - Returns: A `String` containing the Boolean expression. - func swiftBooleanExpression(variable: String) -> String { - if lowerBound == upperBound - 1 { - return "\(lowerBound) == \(variable)" + /// A `String` containing the Swift Boolean expression that tests the given + /// variable for containment within this range. + /// + /// - Parameter variable: The name of the variable to test in the expression. + /// - Returns: A `String` containing the Boolean expression. + func swiftBooleanExpression(variable: String) -> String { + if lowerBound == upperBound - 1 { + return "\(lowerBound) == \(variable)" + } + return "\(lowerBound) <= \(variable) && \(variable) < \(upperBound)" } - return "\(lowerBound) <= \(variable) && \(variable) < \(upperBound)" - } } extension Array where Element == Range { - /// A `String` containing a comma-delimited list of Swift expressions for - /// the ranges. - /// - /// This expression list is suitable as a pattern match in a `case` - /// statement. For example, `"case 5..<10, 15, 20..<30:"`. - /// - /// - Returns: A `String` containing the comma-delimited expressions. - var swiftCaseExpression: String { - return map { $0.swiftCaseExpression }.joined(separator: ", ") - } + /// A `String` containing a comma-delimited list of Swift expressions for + /// the ranges. + /// + /// This expression list is suitable as a pattern match in a `case` + /// statement. For example, `"case 5..<10, 15, 20..<30:"`. + /// + /// - Returns: A `String` containing the comma-delimited expressions. + var swiftCaseExpression: String { + map { $0.swiftCaseExpression }.joined(separator: ", ") + } - /// A `String` containing a Swift Boolean expression that tests if the given - /// variable is in any of ranges. - /// - /// - Parameter variable: The name of the variable to test in the expression. - /// - Returns: A `String` containing the Boolean expression. - func swiftBooleanExpression(variable: String) -> String { - return map { - "(\($0.swiftBooleanExpression(variable: variable)))" - }.joined(separator: " || ") - } + /// A `String` containing a Swift Boolean expression that tests if the given + /// variable is in any of ranges. + /// + /// - Parameter variable: The name of the variable to test in the expression. + /// - Returns: A `String` containing the Boolean expression. + func swiftBooleanExpression(variable: String) -> String { + map { + "(\($0.swiftBooleanExpression(variable: variable)))" + }.joined(separator: " || ") + } } diff --git a/Sources/protoc-gen-swift/StringUtils.swift b/Sources/protoc-gen-swift/StringUtils.swift index a814dddf5..d8bc114f5 100644 --- a/Sources/protoc-gen-swift/StringUtils.swift +++ b/Sources/protoc-gen-swift/StringUtils.swift @@ -11,86 +11,86 @@ import Foundation import SwiftProtobufPluginLibrary -func splitPath(pathname: String) -> (dir:String, base:String, suffix:String) { - var dir = "" - var base = "" - var suffix = "" - for c in pathname { - if c == "/" { - dir += base + suffix + String(c) - base = "" - suffix = "" - } else if c == "." { - base += suffix - suffix = String(c) - } else { - suffix += String(c) +func splitPath(pathname: String) -> (dir: String, base: String, suffix: String) { + var dir = "" + var base = "" + var suffix = "" + for c in pathname { + if c == "/" { + dir += base + suffix + String(c) + base = "" + suffix = "" + } else if c == "." { + base += suffix + suffix = String(c) + } else { + suffix += String(c) + } } - } - let validSuffix = suffix.isEmpty || suffix.first == "." - if !validSuffix { - base += suffix - suffix = "" - } - return (dir: dir, base: base, suffix: suffix) + let validSuffix = suffix.isEmpty || suffix.first == "." + if !validSuffix { + base += suffix + suffix = "" + } + return (dir: dir, base: base, suffix: suffix) } /// The protoc parser emits byte literals using an escaped C convention. /// Fortunately, it uses only a limited subset of the C escapse: /// \n\r\t\\\'\" and three-digit octal escapes but nothing else. func escapedToDataLiteral(_ s: String) -> String { - if s.isEmpty { - return "Data()" - } - var out = "Data([" - var separator = "" - var escape = false - var octal = 0 - var octalAccumulator = 0 - for c in s.utf8 { - if octal > 0 { - precondition(c >= 48 && c < 56) - octalAccumulator <<= 3 - octalAccumulator |= (Int(c) - 48) - octal -= 1 - if octal == 0 { - out += separator - out += "\(octalAccumulator)" - separator = ", " - } - } else if escape { - switch c { - case 110: - out += separator - out += "10" - separator = ", " - case 114: - out += separator - out += "13" - separator = ", " - case 116: - out += separator - out += "9" - separator = ", " - case 48..<56: - octal = 2 // 2 more digits - octalAccumulator = Int(c) - 48 - default: - out += separator - out += "\(c)" - separator = ", " - } - escape = false - } else if c == 92 { // backslash - escape = true - } else { - out += separator - out += "\(c)" - separator = ", " + if s.isEmpty { + return "Data()" + } + var out = "Data([" + var separator = "" + var escape = false + var octal = 0 + var octalAccumulator = 0 + for c in s.utf8 { + if octal > 0 { + precondition(c >= 48 && c < 56) + octalAccumulator <<= 3 + octalAccumulator |= (Int(c) - 48) + octal -= 1 + if octal == 0 { + out += separator + out += "\(octalAccumulator)" + separator = ", " + } + } else if escape { + switch c { + case 110: + out += separator + out += "10" + separator = ", " + case 114: + out += separator + out += "13" + separator = ", " + case 116: + out += separator + out += "9" + separator = ", " + case 48..<56: + octal = 2 // 2 more digits + octalAccumulator = Int(c) - 48 + default: + out += separator + out += "\(c)" + separator = ", " + } + escape = false + } else if c == 92 { // backslash + escape = true + } else { + out += separator + out += "\(c)" + separator = ", " + } } - } - out += "])" - return out + out += "])" + return out } /// Generate a Swift string literal suitable for including in @@ -98,26 +98,26 @@ func escapedToDataLiteral(_ s: String) -> String { private let hexdigits = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"] func stringToEscapedStringLiteral(_ s: String) -> String { - if s.isEmpty { - return "String()" - } - var out = "\"" - for c in s.unicodeScalars { - switch c.value { - case 0: - out += "\\0" - case 1..<32: - let n = Int(c.value) - let hex1 = hexdigits[(n >> 4) & 15] - let hex2 = hexdigits[n & 15] - out += "\\u{" + hex1 + hex2 + "}" - case 34: - out += "\\\"" - case 92: - out += "\\\\" - default: - out.append(String(c)) + if s.isEmpty { + return "String()" + } + var out = "\"" + for c in s.unicodeScalars { + switch c.value { + case 0: + out += "\\0" + case 1..<32: + let n = Int(c.value) + let hex1 = hexdigits[(n >> 4) & 15] + let hex2 = hexdigits[n & 15] + out += "\\u{" + hex1 + hex2 + "}" + case 34: + out += "\\\"" + case 92: + out += "\\\\" + default: + out.append(String(c)) + } } - } - return out + "\"" + return out + "\"" } diff --git a/Sources/protoc-gen-swift/SwiftGeneratorPlugin.swift b/Sources/protoc-gen-swift/SwiftGeneratorPlugin.swift index a0ca909f6..899bcd62c 100644 --- a/Sources/protoc-gen-swift/SwiftGeneratorPlugin.swift +++ b/Sources/protoc-gen-swift/SwiftGeneratorPlugin.swift @@ -23,84 +23,86 @@ import SwiftProtobufPluginLibrary @main struct SwiftGeneratorPlugin: CodeGenerator { - func generate( - files: [SwiftProtobufPluginLibrary.FileDescriptor], - parameter: any CodeGeneratorParameter, - protoCompilerContext: any SwiftProtobufPluginLibrary.ProtoCompilerContext, - generatorOutputs: any SwiftProtobufPluginLibrary.GeneratorOutputs - ) throws { - let options = try GeneratorOptions(parameter: parameter) - - auditProtoCVersion(context: protoCompilerContext) - var errorString: String? = nil - for fileDescriptor in files { - let fileGenerator = FileGenerator(fileDescriptor: fileDescriptor, generatorOptions: options) - var printer = CodePrinter(addNewlines: true) - fileGenerator.generateOutputFile(printer: &printer, errorString: &errorString) - if let errorString = errorString { - // If generating multiple files, scope the message with the file that triggered it. - let fullError = files.count > 1 ? "\(fileDescriptor.name): \(errorString)" : errorString - throw GenerationError.message(message: fullError) - } - try generatorOutputs.add(fileName: fileGenerator.outputFilename, contents: printer.content) + func generate( + files: [SwiftProtobufPluginLibrary.FileDescriptor], + parameter: any CodeGeneratorParameter, + protoCompilerContext: any SwiftProtobufPluginLibrary.ProtoCompilerContext, + generatorOutputs: any SwiftProtobufPluginLibrary.GeneratorOutputs + ) throws { + let options = try GeneratorOptions(parameter: parameter) + + auditProtoCVersion(context: protoCompilerContext) + var errorString: String? = nil + for fileDescriptor in files { + let fileGenerator = FileGenerator(fileDescriptor: fileDescriptor, generatorOptions: options) + var printer = CodePrinter(addNewlines: true) + fileGenerator.generateOutputFile(printer: &printer, errorString: &errorString) + if let errorString = errorString { + // If generating multiple files, scope the message with the file that triggered it. + let fullError = files.count > 1 ? "\(fileDescriptor.name): \(errorString)" : errorString + throw GenerationError.message(message: fullError) + } + try generatorOutputs.add(fileName: fileGenerator.outputFilename, contents: printer.content) + } } - } - var supportedFeatures: [SwiftProtobufPluginLibrary.Google_Protobuf_Compiler_CodeGeneratorResponse.Feature] = [ - .proto3Optional, .supportsEditions - ] + var supportedFeatures: [SwiftProtobufPluginLibrary.Google_Protobuf_Compiler_CodeGeneratorResponse.Feature] = [ + .proto3Optional, .supportsEditions, + ] - var supportedEditionRange: ClosedRange { - Google_Protobuf_Edition.proto2...Google_Protobuf_Edition.edition2023 - } - - var version: String? { return "\(SwiftProtobuf.Version.versionString)" } - var copyrightLine: String? { return "\(Version.copyright)" } - var projectURL: String? { return "https://github.com/apple/swift-protobuf" } + var supportedEditionRange: ClosedRange { + Google_Protobuf_Edition.proto2...Google_Protobuf_Edition.edition2023 + } - private func auditProtoCVersion(context: any SwiftProtobufPluginLibrary.ProtoCompilerContext) { - guard context.version != nil else { - Stderr.print("WARNING: unknown version of protoc, use 3.2.x or later to ensure JSON support is correct.") - return + var version: String? { "\(SwiftProtobuf.Version.versionString)" } + var copyrightLine: String? { "\(Version.copyright)" } + var projectURL: String? { "https://github.com/apple/swift-protobuf" } + + private func auditProtoCVersion(context: any SwiftProtobufPluginLibrary.ProtoCompilerContext) { + guard context.version != nil else { + Stderr.print("WARNING: unknown version of protoc, use 3.2.x or later to ensure JSON support is correct.") + return + } + // 3.2.x is what added the compiler_version, so there is no need to + // ensure that the version of protoc being used is newer, if the field + // is there, the JSON support should be good. } - // 3.2.x is what added the compiler_version, so there is no need to - // ensure that the version of protoc being used is newer, if the field - // is there, the JSON support should be good. - } - // Provide an expanded version of help. - func printHelp() { - print(""" - \(CommandLine.programName): Convert parsed proto definitions into Swift + // Provide an expanded version of help. + func printHelp() { + print( + """ + \(CommandLine.programName): Convert parsed proto definitions into Swift - \(Version.copyright) + \(Version.copyright) - Note: This is a plugin for protoc and should not normally be run - directly. + Note: This is a plugin for protoc and should not normally be run + directly. - If you invoke a recent version of protoc with the --swift_out= - option, then protoc will search the current PATH for protoc-gen-swift - and use it to generate Swift output. + If you invoke a recent version of protoc with the --swift_out= + option, then protoc will search the current PATH for protoc-gen-swift + and use it to generate Swift output. - In particular, if you have renamed this program, you will need to - adjust the protoc command-line option accordingly. + In particular, if you have renamed this program, you will need to + adjust the protoc command-line option accordingly. - The generated Swift output requires the SwiftProtobuf \(version!) - library be included in your project. + The generated Swift output requires the SwiftProtobuf \(version!) + library be included in your project. - If you use `swift build` to compile your project, add this to - Package.swift: + If you use `swift build` to compile your project, add this to + Package.swift: - dependencies: [ - .package(name: "SwiftProtobuf", url: "https://github.com/apple/swift-protobuf.git", from: "\(version!)"), - ] + dependencies: [ + .package(name: "SwiftProtobuf", url: "https://github.com/apple/swift-protobuf.git", from: "\(version!)"), + ] - Usage: \(CommandLine.programName) [options] [filename...] + Usage: \(CommandLine.programName) [options] [filename...] - -h|--help: Print this help message - --version: Print the program version + -h|--help: Print this help message + --version: Print the program version - """) - } + """ + ) + } } diff --git a/Sources/protoc-gen-swift/SwiftProtobufNamer+Extensions.swift b/Sources/protoc-gen-swift/SwiftProtobufNamer+Extensions.swift index db4ffeb32..92a74e856 100644 --- a/Sources/protoc-gen-swift/SwiftProtobufNamer+Extensions.swift +++ b/Sources/protoc-gen-swift/SwiftProtobufNamer+Extensions.swift @@ -16,31 +16,31 @@ import SwiftProtobufPluginLibrary extension SwiftProtobufNamer { - /// Filters the Enum's values to those that will have unique Swift - /// names. Only poorly named proto enum alias values get filtered - /// away, so the assumption is they aren't really needed from an - /// api pov. - func uniquelyNamedValues( - valueAliasInfo aliasInfo: EnumDescriptor.ValueAliasInfo - ) -> [EnumValueDescriptor] { - return aliasInfo.mainValues.first!.enumType.values.filter { - // Original are kept as is. The computations for relative - // name already adds values for collisions with different - // values. - guard let aliasOf = aliasInfo.original(of: $0) else { return true } - let relativeName = self.relativeName(enumValue: $0) - let aliasOfRelativeName = self.relativeName(enumValue: aliasOf) - // If the relative name matches for the alias and original, drop - // the alias. - guard relativeName != aliasOfRelativeName else { return false } - // Only include this alias if it is the first one with this name. - // (handles alias with different cases in their names that get - // mangled to a single Swift name.) - let firstAlias = aliasInfo.aliases(aliasOf)!.firstIndex { - let otherRelativeName = self.relativeName(enumValue: $0) - return relativeName == otherRelativeName - } - return aliasInfo.aliases(aliasOf)![firstAlias!] === $0 + /// Filters the Enum's values to those that will have unique Swift + /// names. Only poorly named proto enum alias values get filtered + /// away, so the assumption is they aren't really needed from an + /// api pov. + func uniquelyNamedValues( + valueAliasInfo aliasInfo: EnumDescriptor.ValueAliasInfo + ) -> [EnumValueDescriptor] { + aliasInfo.mainValues.first!.enumType.values.filter { + // Original are kept as is. The computations for relative + // name already adds values for collisions with different + // values. + guard let aliasOf = aliasInfo.original(of: $0) else { return true } + let relativeName = self.relativeName(enumValue: $0) + let aliasOfRelativeName = self.relativeName(enumValue: aliasOf) + // If the relative name matches for the alias and original, drop + // the alias. + guard relativeName != aliasOfRelativeName else { return false } + // Only include this alias if it is the first one with this name. + // (handles alias with different cases in their names that get + // mangled to a single Swift name.) + let firstAlias = aliasInfo.aliases(aliasOf)!.firstIndex { + let otherRelativeName = self.relativeName(enumValue: $0) + return relativeName == otherRelativeName + } + return aliasInfo.aliases(aliasOf)![firstAlias!] === $0 + } } - } } diff --git a/Tests/SwiftProtobufPluginLibraryTests/Test_Descriptor.swift b/Tests/SwiftProtobufPluginLibraryTests/Test_Descriptor.swift index 3158bd58b..7069db7df 100644 --- a/Tests/SwiftProtobufPluginLibraryTests/Test_Descriptor.swift +++ b/Tests/SwiftProtobufPluginLibraryTests/Test_Descriptor.swift @@ -8,464 +8,492 @@ // // ----------------------------------------------------------------------------- -import XCTest import SwiftProtobuf +import XCTest + @testable import SwiftProtobufPluginLibrary extension FileDescriptor { - func extensionField(named: String) -> FieldDescriptor? { - return extensions.first { return $0.name == named } - } + func extensionField(named: String) -> FieldDescriptor? { + extensions.first { $0.name == named } + } } extension Descriptor { - func field(named: String) -> FieldDescriptor? { - return fields.first { return $0.name == named } - } + func field(named: String) -> FieldDescriptor? { + fields.first { $0.name == named } + } } final class Test_Descriptor: XCTestCase { - func testParsing() throws { - let fileSet = try Google_Protobuf_FileDescriptorSet(serializedBytes: fileDescriptorSetBytes) - - let descriptorSet = DescriptorSet(proto: fileSet) - XCTAssertEqual(descriptorSet.files.count, 7) - // descriptor.proto documents the protoc will order the files based on the import - // from plugin on descriptor. - XCTAssertEqual(descriptorSet.files[0].name, "pluginlib_descriptor_test_import.proto") - XCTAssertEqual(descriptorSet.files[1].name, "pluginlib_descriptor_test.proto") - XCTAssertEqual(descriptorSet.files[2].name, "pluginlib_descriptor_test2.proto") - XCTAssertEqual(descriptorSet.files[3].name, "pluginlib_descriptor_delimited.proto") - XCTAssertEqual(descriptorSet.files[4].name, "unittest_delimited_import.proto") - XCTAssertEqual(descriptorSet.files[5].name, "unittest_delimited.proto") - XCTAssertEqual(descriptorSet.files[6].name, "swift_protobuf_module_mappings.proto") - - let importFileDescriptor = descriptorSet.files[0] + func testParsing() throws { + let fileSet = try Google_Protobuf_FileDescriptorSet(serializedBytes: fileDescriptorSetBytes) + + let descriptorSet = DescriptorSet(proto: fileSet) + XCTAssertEqual(descriptorSet.files.count, 7) + // descriptor.proto documents the protoc will order the files based on the import + // from plugin on descriptor. + XCTAssertEqual(descriptorSet.files[0].name, "pluginlib_descriptor_test_import.proto") + XCTAssertEqual(descriptorSet.files[1].name, "pluginlib_descriptor_test.proto") + XCTAssertEqual(descriptorSet.files[2].name, "pluginlib_descriptor_test2.proto") + XCTAssertEqual(descriptorSet.files[3].name, "pluginlib_descriptor_delimited.proto") + XCTAssertEqual(descriptorSet.files[4].name, "unittest_delimited_import.proto") + XCTAssertEqual(descriptorSet.files[5].name, "unittest_delimited.proto") + XCTAssertEqual(descriptorSet.files[6].name, "swift_protobuf_module_mappings.proto") + + let importFileDescriptor = descriptorSet.files[0] + + XCTAssertEqual(importFileDescriptor.messages.count, 2) + XCTAssertEqual(importFileDescriptor.messages[0].fullName, "swift_descriptor_test.import.Version") + XCTAssertNil(importFileDescriptor.messages[0].containingType) + XCTAssertEqual(importFileDescriptor.messages[0].messages.count, 0) + XCTAssertEqual(importFileDescriptor.enums.count, 0) + XCTAssertEqual(importFileDescriptor.extensions.count, 0) + + XCTAssertEqual(importFileDescriptor.messages[1].fullName, "swift_descriptor_test.import.ExtendableOne") + XCTAssertNil(importFileDescriptor.messages[1].containingType) + XCTAssertEqual(importFileDescriptor.messages[1].messages.count, 1) + + XCTAssertEqual( + importFileDescriptor.messages[1].messages[0].fullName, + "swift_descriptor_test.import.ExtendableOne.ExtendableTwo" + ) + XCTAssertEqual(importFileDescriptor.messages[1].messages[0].messages.count, 0) + + let testFileDesciptor = descriptorSet.files[1] + + XCTAssertEqual(testFileDesciptor.enums.count, 1) + XCTAssertEqual(testFileDesciptor.enums[0].fullName, "swift_descriptor_test.TopLevelEnum") + XCTAssertNil(testFileDesciptor.enums[0].containingType) + + XCTAssertEqual(testFileDesciptor.messages[0].enums.count, 1) + XCTAssertEqual(testFileDesciptor.messages[0].enums[0].fullName, "swift_descriptor_test.TopLevelMessage.SubEnum") + XCTAssertTrue(testFileDesciptor.messages[0].enums[0].containingType === testFileDesciptor.messages[0]) + + XCTAssertEqual(testFileDesciptor.messages[0].oneofs.count, 1) + XCTAssertEqual(testFileDesciptor.messages[0].oneofs[0].name, "o") + XCTAssertEqual(testFileDesciptor.messages[1].oneofs.count, 0) + + XCTAssertEqual(testFileDesciptor.extensions.count, 1) + XCTAssertEqual(testFileDesciptor.extensions[0].name, "ext_str") + XCTAssertEqual(testFileDesciptor.messages[3].extensions.count, 2) + XCTAssertEqual(testFileDesciptor.messages[3].extensions[0].name, "ext_enum") + XCTAssertEqual(testFileDesciptor.messages[3].extensions[1].name, "ext_msg") + + XCTAssertEqual(testFileDesciptor.services.count, 1) + XCTAssertEqual(testFileDesciptor.services[0].fullName, "swift_descriptor_test.SomeService") + XCTAssertEqual(testFileDesciptor.services[0].methods.count, 2) + XCTAssertEqual(testFileDesciptor.services[0].methods[0].name, "Foo") + XCTAssertEqual(testFileDesciptor.services[0].methods[1].name, "Bar") + } - XCTAssertEqual(importFileDescriptor.messages.count, 2) - XCTAssertEqual(importFileDescriptor.messages[0].fullName, "swift_descriptor_test.import.Version") - XCTAssertNil(importFileDescriptor.messages[0].containingType) - XCTAssertEqual(importFileDescriptor.messages[0].messages.count, 0) - XCTAssertEqual(importFileDescriptor.enums.count, 0) - XCTAssertEqual(importFileDescriptor.extensions.count, 0) + func testLookup() throws { + let fileSet = try Google_Protobuf_FileDescriptorSet(serializedBytes: fileDescriptorSetBytes) + + let descriptorSet = DescriptorSet(proto: fileSet) + + XCTAssertTrue( + descriptorSet.fileDescriptor(named: "pluginlib_descriptor_test_import.proto") === descriptorSet.files[0] + ) + + XCTAssertTrue( + descriptorSet.descriptor(named: "swift_descriptor_test.import.Version") + === descriptorSet.files[0].messages[0] + ) + XCTAssertTrue( + descriptorSet.descriptor(named: "swift_descriptor_test.TopLevelMessage") + === descriptorSet.files[1].messages[0] + ) + XCTAssertTrue( + descriptorSet.descriptor(named: "swift_descriptor_test.TopLevelMessage.SubMessage") + === descriptorSet.files[1].messages[0].messages[0] + ) + + XCTAssertTrue( + descriptorSet.enumDescriptor(named: "swift_descriptor_test.TopLevelEnum") + === descriptorSet.files[1].enums[0] + ) + XCTAssertTrue( + descriptorSet.enumDescriptor(named: "swift_descriptor_test.TopLevelMessage.SubEnum") + === descriptorSet.files[1].messages[0].enums[0] + ) + + XCTAssertTrue( + descriptorSet.serviceDescriptor(named: "swift_descriptor_test.SomeService") + === descriptorSet.files[1].services[0] + ) + } - XCTAssertEqual(importFileDescriptor.messages[1].fullName, "swift_descriptor_test.import.ExtendableOne") - XCTAssertNil(importFileDescriptor.messages[1].containingType) - XCTAssertEqual(importFileDescriptor.messages[1].messages.count, 1) + func testParents() throws { + let fileSet = try Google_Protobuf_FileDescriptorSet(serializedBytes: fileDescriptorSetBytes) - XCTAssertEqual(importFileDescriptor.messages[1].messages[0].fullName, "swift_descriptor_test.import.ExtendableOne.ExtendableTwo") - XCTAssertEqual(importFileDescriptor.messages[1].messages[0].messages.count, 0) - - let testFileDesciptor = descriptorSet.files[1] + let descriptorSet = DescriptorSet(proto: fileSet) - XCTAssertEqual(testFileDesciptor.enums.count, 1) - XCTAssertEqual(testFileDesciptor.enums[0].fullName, "swift_descriptor_test.TopLevelEnum") - XCTAssertNil(testFileDesciptor.enums[0].containingType) - - XCTAssertEqual(testFileDesciptor.messages[0].enums.count, 1) - XCTAssertEqual(testFileDesciptor.messages[0].enums[0].fullName, "swift_descriptor_test.TopLevelMessage.SubEnum") - XCTAssertTrue(testFileDesciptor.messages[0].enums[0].containingType === testFileDesciptor.messages[0]) - - XCTAssertEqual(testFileDesciptor.messages[0].oneofs.count, 1) - XCTAssertEqual(testFileDesciptor.messages[0].oneofs[0].name, "o") - XCTAssertEqual(testFileDesciptor.messages[1].oneofs.count, 0) - - XCTAssertEqual(testFileDesciptor.extensions.count, 1) - XCTAssertEqual(testFileDesciptor.extensions[0].name, "ext_str") - XCTAssertEqual(testFileDesciptor.messages[3].extensions.count, 2) - XCTAssertEqual(testFileDesciptor.messages[3].extensions[0].name, "ext_enum") - XCTAssertEqual(testFileDesciptor.messages[3].extensions[1].name, "ext_msg") - - XCTAssertEqual(testFileDesciptor.services.count, 1) - XCTAssertEqual(testFileDesciptor.services[0].fullName, "swift_descriptor_test.SomeService") - XCTAssertEqual(testFileDesciptor.services[0].methods.count, 2) - XCTAssertEqual(testFileDesciptor.services[0].methods[0].name, "Foo") - XCTAssertEqual(testFileDesciptor.services[0].methods[1].name, "Bar") - } - - func testLookup() throws { - let fileSet = try Google_Protobuf_FileDescriptorSet(serializedBytes: fileDescriptorSetBytes) - - let descriptorSet = DescriptorSet(proto: fileSet) - - XCTAssertTrue(descriptorSet.fileDescriptor(named: "pluginlib_descriptor_test_import.proto") === descriptorSet.files[0]) - - XCTAssertTrue(descriptorSet.descriptor(named: "swift_descriptor_test.import.Version") === descriptorSet.files[0].messages[0]) - XCTAssertTrue(descriptorSet.descriptor(named: "swift_descriptor_test.TopLevelMessage") === descriptorSet.files[1].messages[0]) - XCTAssertTrue(descriptorSet.descriptor(named: "swift_descriptor_test.TopLevelMessage.SubMessage") === descriptorSet.files[1].messages[0].messages[0]) - - XCTAssertTrue(descriptorSet.enumDescriptor(named: "swift_descriptor_test.TopLevelEnum") === descriptorSet.files[1].enums[0]) - XCTAssertTrue(descriptorSet.enumDescriptor(named: "swift_descriptor_test.TopLevelMessage.SubEnum") === descriptorSet.files[1].messages[0].enums[0]) - - XCTAssertTrue(descriptorSet.serviceDescriptor(named: "swift_descriptor_test.SomeService") === descriptorSet.files[1].services[0]) - } - - func testParents() throws { - let fileSet = try Google_Protobuf_FileDescriptorSet(serializedBytes: fileDescriptorSetBytes) - - let descriptorSet = DescriptorSet(proto: fileSet) - - let importVersion = descriptorSet.descriptor(named: "swift_descriptor_test.import.Version")! - XCTAssertTrue(importVersion.containingType == nil) - - let importExtendOne = descriptorSet.descriptor(named: "swift_descriptor_test.import.ExtendableOne")! - let importExtendTwo = descriptorSet.descriptor(named: "swift_descriptor_test.import.ExtendableOne.ExtendableTwo")! - XCTAssertTrue(importExtendTwo.containingType === importExtendOne) - - let testDescriptor = descriptorSet.descriptor(named: "swift_descriptor_test.TopLevelMessage")! - let testEnum = descriptorSet.enumDescriptor(named: "swift_descriptor_test.TopLevelMessage.SubEnum")! - XCTAssertTrue(testEnum.containingType === testDescriptor) - - let serviceDescProto = descriptorSet.serviceDescriptor(named: "swift_descriptor_test.SomeService")! - let fooMethod = serviceDescProto.methods[0] - XCTAssertTrue(fooMethod.service === serviceDescProto) - let barMethod = serviceDescProto.methods[1] - XCTAssertTrue(barMethod.service === serviceDescProto) - - let importFile = descriptorSet.files[0] - let testFile = descriptorSet.files[1] - - XCTAssertTrue(importVersion.file === importFile) - - XCTAssertTrue(importExtendOne.file === importFile) - XCTAssertTrue(importExtendTwo.file === importFile) - - XCTAssertTrue(testDescriptor.file === testFile) - XCTAssertTrue(testEnum.file === testFile) - XCTAssertTrue(serviceDescProto.file === testFile) - } - - func testFields() throws { - let fileSet = try Google_Protobuf_FileDescriptorSet(serializedBytes: fileDescriptorSetBytes) - - let descriptorSet = DescriptorSet(proto: fileSet) - - let topLevelEnum = descriptorSet.enumDescriptor(named: "swift_descriptor_test.TopLevelEnum")! - let topLevelMessage = descriptorSet.descriptor(named: "swift_descriptor_test.TopLevelMessage")! - let subEnum = topLevelMessage.enums[0] - let subMessage = topLevelMessage.messages[0] - let topLevelMessage2 = descriptorSet.descriptor(named: "swift_descriptor_test.TopLevelMessage2")! - - XCTAssertEqual(topLevelMessage.fields.count, 6) - XCTAssertEqual(topLevelMessage.fields[0].name, "field1") - XCTAssertEqual(topLevelMessage.fields[1].name, "field2") - XCTAssertEqual(topLevelMessage.fields[2].name, "field3") - XCTAssertEqual(topLevelMessage.fields[3].name, "field4") - XCTAssertEqual(topLevelMessage.fields[4].name, "field5") - XCTAssertEqual(topLevelMessage.fields[5].name, "field6") - XCTAssertTrue(topLevelMessage.fields[2].enumType === topLevelEnum) - XCTAssertTrue(topLevelMessage.fields[3].enumType === subEnum) - XCTAssertTrue(topLevelMessage.fields[4].messageType === subMessage) - XCTAssertTrue(topLevelMessage.fields[5].messageType === topLevelMessage2) - - let oneof = topLevelMessage.oneofs[0] - XCTAssertTrue(oneof.containingType === topLevelMessage) - XCTAssertEqual(oneof.fields.count, 4) - XCTAssertTrue(oneof.fields[0] === topLevelMessage.fields[2]) - XCTAssertTrue(oneof.fields[1] === topLevelMessage.fields[3]) - XCTAssertTrue(oneof.fields[2] === topLevelMessage.fields[4]) - XCTAssertTrue(oneof.fields[3] === topLevelMessage.fields[5]) - - XCTAssertEqual(topLevelMessage2.fields.count, 2) - XCTAssertEqual(topLevelMessage2.fields[0].name, "left") - XCTAssertEqual(topLevelMessage2.fields[1].name, "right") - XCTAssertTrue(topLevelMessage2.fields[0].messageType === topLevelMessage) - XCTAssertTrue(topLevelMessage2.fields[1].messageType === topLevelMessage2) - - let externalRefs = descriptorSet.descriptor(named: "swift_descriptor_test.ExternalRefs")! - let extendOne = descriptorSet.descriptor(named: "swift_descriptor_test.import.ExtendableOne")! - let testImportVersion = descriptorSet.descriptor(named: "swift_descriptor_test.import.Version")! - - XCTAssertEqual(externalRefs.fields.count, 2) - XCTAssertEqual(externalRefs.fields[0].name, "one") - XCTAssertEqual(externalRefs.fields[1].name, "ver") - XCTAssertTrue(externalRefs.fields[0].messageType === extendOne) - XCTAssertTrue(externalRefs.fields[1].messageType === testImportVersion) - - // Proto2 Presence - - let proto2ForPresence = descriptorSet.descriptor(named: "swift_descriptor_test.Proto2MessageForPresence")! - - XCTAssertEqual(proto2ForPresence.fields.count, 16) - XCTAssertEqual(proto2ForPresence.fields[0].name, "req_str_field") - XCTAssertEqual(proto2ForPresence.fields[1].name, "req_int32_field") - XCTAssertEqual(proto2ForPresence.fields[2].name, "req_enum_field") - XCTAssertEqual(proto2ForPresence.fields[3].name, "req_message_field") - XCTAssertEqual(proto2ForPresence.fields[4].name, "opt_str_field") - XCTAssertEqual(proto2ForPresence.fields[5].name, "opt_int32_field") - XCTAssertEqual(proto2ForPresence.fields[6].name, "opt_enum_field") - XCTAssertEqual(proto2ForPresence.fields[7].name, "opt_message_field") - XCTAssertEqual(proto2ForPresence.fields[8].name, "repeat_str_field") - XCTAssertEqual(proto2ForPresence.fields[9].name, "repeat_int32_field") - XCTAssertEqual(proto2ForPresence.fields[10].name, "repeat_enum_field") - XCTAssertEqual(proto2ForPresence.fields[11].name, "repeat_message_field") - XCTAssertEqual(proto2ForPresence.fields[12].name, "oneof_str_field") - XCTAssertEqual(proto2ForPresence.fields[13].name, "oneof_int32_field") - XCTAssertEqual(proto2ForPresence.fields[14].name, "oneof_enum_field") - XCTAssertEqual(proto2ForPresence.fields[15].name, "oneof_message_field") - - XCTAssertFalse(proto2ForPresence.fields[0]._hasOptionalKeyword) - XCTAssertFalse(proto2ForPresence.fields[1]._hasOptionalKeyword) - XCTAssertFalse(proto2ForPresence.fields[2]._hasOptionalKeyword) - XCTAssertFalse(proto2ForPresence.fields[3]._hasOptionalKeyword) - XCTAssertTrue(proto2ForPresence.fields[4]._hasOptionalKeyword) - XCTAssertTrue(proto2ForPresence.fields[5]._hasOptionalKeyword) - XCTAssertTrue(proto2ForPresence.fields[6]._hasOptionalKeyword) - XCTAssertTrue(proto2ForPresence.fields[7]._hasOptionalKeyword) - XCTAssertFalse(proto2ForPresence.fields[8]._hasOptionalKeyword) - XCTAssertFalse(proto2ForPresence.fields[9]._hasOptionalKeyword) - XCTAssertFalse(proto2ForPresence.fields[10]._hasOptionalKeyword) - XCTAssertFalse(proto2ForPresence.fields[11]._hasOptionalKeyword) - XCTAssertFalse(proto2ForPresence.fields[12]._hasOptionalKeyword) - XCTAssertFalse(proto2ForPresence.fields[13]._hasOptionalKeyword) - XCTAssertFalse(proto2ForPresence.fields[14]._hasOptionalKeyword) - XCTAssertFalse(proto2ForPresence.fields[15]._hasOptionalKeyword) - - XCTAssertTrue(proto2ForPresence.fields[0].hasPresence) - XCTAssertTrue(proto2ForPresence.fields[1].hasPresence) - XCTAssertTrue(proto2ForPresence.fields[2].hasPresence) - XCTAssertTrue(proto2ForPresence.fields[3].hasPresence) - XCTAssertTrue(proto2ForPresence.fields[4].hasPresence) - XCTAssertTrue(proto2ForPresence.fields[5].hasPresence) - XCTAssertTrue(proto2ForPresence.fields[6].hasPresence) - XCTAssertTrue(proto2ForPresence.fields[7].hasPresence) - XCTAssertFalse(proto2ForPresence.fields[8].hasPresence) - XCTAssertFalse(proto2ForPresence.fields[9].hasPresence) - XCTAssertFalse(proto2ForPresence.fields[10].hasPresence) - XCTAssertFalse(proto2ForPresence.fields[11].hasPresence) - XCTAssertTrue(proto2ForPresence.fields[12].hasPresence) - XCTAssertTrue(proto2ForPresence.fields[13].hasPresence) - XCTAssertTrue(proto2ForPresence.fields[14].hasPresence) - XCTAssertTrue(proto2ForPresence.fields[15].hasPresence) - - // No synthetic oneof in proto2 syntax, so the lists should be the same. - XCTAssertEqual(proto2ForPresence.oneofs.count, proto2ForPresence.realOneofs.count) - for (i, o) in proto2ForPresence.realOneofs.enumerated() { - XCTAssert(o === proto2ForPresence.oneofs[i]) - } + let importVersion = descriptorSet.descriptor(named: "swift_descriptor_test.import.Version")! + XCTAssertTrue(importVersion.containingType == nil) - // Proto3 Presence - - let proto3ForPresence = descriptorSet.descriptor(named: "swift_descriptor_test.Proto3MessageForPresence")! - XCTAssertEqual(proto3ForPresence.fields.count, 16) - XCTAssertEqual(proto3ForPresence.fields[0].name, "str_field") - XCTAssertEqual(proto3ForPresence.fields[1].name, "int32_field") - XCTAssertEqual(proto3ForPresence.fields[2].name, "enum_field") - XCTAssertEqual(proto3ForPresence.fields[3].name, "message_field") - XCTAssertEqual(proto3ForPresence.fields[4].name, "opt_str_field") - XCTAssertEqual(proto3ForPresence.fields[5].name, "opt_int32_field") - XCTAssertEqual(proto3ForPresence.fields[6].name, "opt_enum_field") - XCTAssertEqual(proto3ForPresence.fields[7].name, "opt_message_field") - XCTAssertEqual(proto3ForPresence.fields[8].name, "repeat_str_field") - XCTAssertEqual(proto3ForPresence.fields[9].name, "repeat_int32_field") - XCTAssertEqual(proto3ForPresence.fields[10].name, "repeat_enum_field") - XCTAssertEqual(proto3ForPresence.fields[11].name, "repeat_message_field") - XCTAssertEqual(proto3ForPresence.fields[12].name, "oneof_str_field") - XCTAssertEqual(proto3ForPresence.fields[13].name, "oneof_int32_field") - XCTAssertEqual(proto3ForPresence.fields[14].name, "oneof_enum_field") - XCTAssertEqual(proto3ForPresence.fields[15].name, "oneof_message_field") - - XCTAssertFalse(proto3ForPresence.fields[0]._hasOptionalKeyword) - XCTAssertFalse(proto3ForPresence.fields[1]._hasOptionalKeyword) - XCTAssertFalse(proto3ForPresence.fields[2]._hasOptionalKeyword) - XCTAssertFalse(proto3ForPresence.fields[3]._hasOptionalKeyword) - XCTAssertTrue(proto3ForPresence.fields[4]._hasOptionalKeyword) - XCTAssertTrue(proto3ForPresence.fields[5]._hasOptionalKeyword) - XCTAssertTrue(proto3ForPresence.fields[6]._hasOptionalKeyword) - XCTAssertTrue(proto3ForPresence.fields[7]._hasOptionalKeyword) - XCTAssertFalse(proto3ForPresence.fields[8]._hasOptionalKeyword) - XCTAssertFalse(proto3ForPresence.fields[9]._hasOptionalKeyword) - XCTAssertFalse(proto3ForPresence.fields[10]._hasOptionalKeyword) - XCTAssertFalse(proto3ForPresence.fields[11]._hasOptionalKeyword) - XCTAssertFalse(proto3ForPresence.fields[12]._hasOptionalKeyword) - XCTAssertFalse(proto3ForPresence.fields[13]._hasOptionalKeyword) - XCTAssertFalse(proto3ForPresence.fields[14]._hasOptionalKeyword) - XCTAssertFalse(proto3ForPresence.fields[15]._hasOptionalKeyword) - - XCTAssertFalse(proto3ForPresence.fields[0].hasPresence) - XCTAssertFalse(proto3ForPresence.fields[1].hasPresence) - XCTAssertFalse(proto3ForPresence.fields[2].hasPresence) - XCTAssertTrue(proto3ForPresence.fields[3].hasPresence) - XCTAssertTrue(proto3ForPresence.fields[4].hasPresence) - XCTAssertTrue(proto3ForPresence.fields[5].hasPresence) - XCTAssertTrue(proto3ForPresence.fields[6].hasPresence) - XCTAssertTrue(proto3ForPresence.fields[7].hasPresence) - XCTAssertFalse(proto3ForPresence.fields[8].hasPresence) - XCTAssertFalse(proto3ForPresence.fields[9].hasPresence) - XCTAssertFalse(proto3ForPresence.fields[10].hasPresence) - XCTAssertFalse(proto3ForPresence.fields[11].hasPresence) - XCTAssertTrue(proto3ForPresence.fields[12].hasPresence) - XCTAssertTrue(proto3ForPresence.fields[13].hasPresence) - XCTAssertTrue(proto3ForPresence.fields[14].hasPresence) - XCTAssertTrue(proto3ForPresence.fields[15].hasPresence) - - // Synthetic oneof in proto3 syntax for the 'optional' fields, so - // the lists should NOTE be the same, `realOneofs` one should be a - // prefix of `oneofs`. - XCTAssertTrue(proto3ForPresence.oneofs.count > proto3ForPresence.realOneofs.count) - for (i, o) in proto2ForPresence.realOneofs.enumerated() { - XCTAssert(o === proto2ForPresence.oneofs[i]) - } - } + let importExtendOne = descriptorSet.descriptor(named: "swift_descriptor_test.import.ExtendableOne")! + let importExtendTwo = descriptorSet.descriptor( + named: "swift_descriptor_test.import.ExtendableOne.ExtendableTwo" + )! + XCTAssertTrue(importExtendTwo.containingType === importExtendOne) - func testExtensions() throws { - // Extensions are a little different in how they have extensionScope and - // containingType, so they are split out to be a clear test of their behaviors. + let testDescriptor = descriptorSet.descriptor(named: "swift_descriptor_test.TopLevelMessage")! + let testEnum = descriptorSet.enumDescriptor(named: "swift_descriptor_test.TopLevelMessage.SubEnum")! + XCTAssertTrue(testEnum.containingType === testDescriptor) - let fileSet = try Google_Protobuf_FileDescriptorSet(serializedBytes: fileDescriptorSetBytes) + let serviceDescProto = descriptorSet.serviceDescriptor(named: "swift_descriptor_test.SomeService")! + let fooMethod = serviceDescProto.methods[0] + XCTAssertTrue(fooMethod.service === serviceDescProto) + let barMethod = serviceDescProto.methods[1] + XCTAssertTrue(barMethod.service === serviceDescProto) - let descriptorSet = DescriptorSet(proto: fileSet) + let importFile = descriptorSet.files[0] + let testFile = descriptorSet.files[1] - let extendOne = descriptorSet.descriptor(named: "swift_descriptor_test.import.ExtendableOne")! - let extendTwo = descriptorSet.descriptor(named: "swift_descriptor_test.import.ExtendableOne.ExtendableTwo")! + XCTAssertTrue(importVersion.file === importFile) - let descriptorTestFile = descriptorSet.files[1] + XCTAssertTrue(importExtendOne.file === importFile) + XCTAssertTrue(importExtendTwo.file === importFile) - let topLevelExt = descriptorTestFile.extensions[0] - XCTAssertNil(topLevelExt.extensionScope) - XCTAssertTrue(topLevelExt.containingType === extendOne) + XCTAssertTrue(testDescriptor.file === testFile) + XCTAssertTrue(testEnum.file === testFile) + XCTAssertTrue(serviceDescProto.file === testFile) + } - let extScoper = descriptorSet.descriptor(named: "swift_descriptor_test.ScoperForExt")! - let nestedExt1 = descriptorTestFile.messages[3].extensions[0] - let nestedExt2 = descriptorTestFile.messages[3].extensions[1] - XCTAssertTrue(nestedExt1.extensionScope === extScoper) - XCTAssertTrue(nestedExt1.containingType === extendTwo) - XCTAssertTrue(nestedExt2.extensionScope === extScoper) - XCTAssertTrue(nestedExt2.containingType === extendTwo) + func testFields() throws { + let fileSet = try Google_Protobuf_FileDescriptorSet(serializedBytes: fileDescriptorSetBytes) + + let descriptorSet = DescriptorSet(proto: fileSet) + + let topLevelEnum = descriptorSet.enumDescriptor(named: "swift_descriptor_test.TopLevelEnum")! + let topLevelMessage = descriptorSet.descriptor(named: "swift_descriptor_test.TopLevelMessage")! + let subEnum = topLevelMessage.enums[0] + let subMessage = topLevelMessage.messages[0] + let topLevelMessage2 = descriptorSet.descriptor(named: "swift_descriptor_test.TopLevelMessage2")! + + XCTAssertEqual(topLevelMessage.fields.count, 6) + XCTAssertEqual(topLevelMessage.fields[0].name, "field1") + XCTAssertEqual(topLevelMessage.fields[1].name, "field2") + XCTAssertEqual(topLevelMessage.fields[2].name, "field3") + XCTAssertEqual(topLevelMessage.fields[3].name, "field4") + XCTAssertEqual(topLevelMessage.fields[4].name, "field5") + XCTAssertEqual(topLevelMessage.fields[5].name, "field6") + XCTAssertTrue(topLevelMessage.fields[2].enumType === topLevelEnum) + XCTAssertTrue(topLevelMessage.fields[3].enumType === subEnum) + XCTAssertTrue(topLevelMessage.fields[4].messageType === subMessage) + XCTAssertTrue(topLevelMessage.fields[5].messageType === topLevelMessage2) + + let oneof = topLevelMessage.oneofs[0] + XCTAssertTrue(oneof.containingType === topLevelMessage) + XCTAssertEqual(oneof.fields.count, 4) + XCTAssertTrue(oneof.fields[0] === topLevelMessage.fields[2]) + XCTAssertTrue(oneof.fields[1] === topLevelMessage.fields[3]) + XCTAssertTrue(oneof.fields[2] === topLevelMessage.fields[4]) + XCTAssertTrue(oneof.fields[3] === topLevelMessage.fields[5]) + + XCTAssertEqual(topLevelMessage2.fields.count, 2) + XCTAssertEqual(topLevelMessage2.fields[0].name, "left") + XCTAssertEqual(topLevelMessage2.fields[1].name, "right") + XCTAssertTrue(topLevelMessage2.fields[0].messageType === topLevelMessage) + XCTAssertTrue(topLevelMessage2.fields[1].messageType === topLevelMessage2) + + let externalRefs = descriptorSet.descriptor(named: "swift_descriptor_test.ExternalRefs")! + let extendOne = descriptorSet.descriptor(named: "swift_descriptor_test.import.ExtendableOne")! + let testImportVersion = descriptorSet.descriptor(named: "swift_descriptor_test.import.Version")! + + XCTAssertEqual(externalRefs.fields.count, 2) + XCTAssertEqual(externalRefs.fields[0].name, "one") + XCTAssertEqual(externalRefs.fields[1].name, "ver") + XCTAssertTrue(externalRefs.fields[0].messageType === extendOne) + XCTAssertTrue(externalRefs.fields[1].messageType === testImportVersion) + + // Proto2 Presence + + let proto2ForPresence = descriptorSet.descriptor(named: "swift_descriptor_test.Proto2MessageForPresence")! + + XCTAssertEqual(proto2ForPresence.fields.count, 16) + XCTAssertEqual(proto2ForPresence.fields[0].name, "req_str_field") + XCTAssertEqual(proto2ForPresence.fields[1].name, "req_int32_field") + XCTAssertEqual(proto2ForPresence.fields[2].name, "req_enum_field") + XCTAssertEqual(proto2ForPresence.fields[3].name, "req_message_field") + XCTAssertEqual(proto2ForPresence.fields[4].name, "opt_str_field") + XCTAssertEqual(proto2ForPresence.fields[5].name, "opt_int32_field") + XCTAssertEqual(proto2ForPresence.fields[6].name, "opt_enum_field") + XCTAssertEqual(proto2ForPresence.fields[7].name, "opt_message_field") + XCTAssertEqual(proto2ForPresence.fields[8].name, "repeat_str_field") + XCTAssertEqual(proto2ForPresence.fields[9].name, "repeat_int32_field") + XCTAssertEqual(proto2ForPresence.fields[10].name, "repeat_enum_field") + XCTAssertEqual(proto2ForPresence.fields[11].name, "repeat_message_field") + XCTAssertEqual(proto2ForPresence.fields[12].name, "oneof_str_field") + XCTAssertEqual(proto2ForPresence.fields[13].name, "oneof_int32_field") + XCTAssertEqual(proto2ForPresence.fields[14].name, "oneof_enum_field") + XCTAssertEqual(proto2ForPresence.fields[15].name, "oneof_message_field") + + XCTAssertFalse(proto2ForPresence.fields[0]._hasOptionalKeyword) + XCTAssertFalse(proto2ForPresence.fields[1]._hasOptionalKeyword) + XCTAssertFalse(proto2ForPresence.fields[2]._hasOptionalKeyword) + XCTAssertFalse(proto2ForPresence.fields[3]._hasOptionalKeyword) + XCTAssertTrue(proto2ForPresence.fields[4]._hasOptionalKeyword) + XCTAssertTrue(proto2ForPresence.fields[5]._hasOptionalKeyword) + XCTAssertTrue(proto2ForPresence.fields[6]._hasOptionalKeyword) + XCTAssertTrue(proto2ForPresence.fields[7]._hasOptionalKeyword) + XCTAssertFalse(proto2ForPresence.fields[8]._hasOptionalKeyword) + XCTAssertFalse(proto2ForPresence.fields[9]._hasOptionalKeyword) + XCTAssertFalse(proto2ForPresence.fields[10]._hasOptionalKeyword) + XCTAssertFalse(proto2ForPresence.fields[11]._hasOptionalKeyword) + XCTAssertFalse(proto2ForPresence.fields[12]._hasOptionalKeyword) + XCTAssertFalse(proto2ForPresence.fields[13]._hasOptionalKeyword) + XCTAssertFalse(proto2ForPresence.fields[14]._hasOptionalKeyword) + XCTAssertFalse(proto2ForPresence.fields[15]._hasOptionalKeyword) + + XCTAssertTrue(proto2ForPresence.fields[0].hasPresence) + XCTAssertTrue(proto2ForPresence.fields[1].hasPresence) + XCTAssertTrue(proto2ForPresence.fields[2].hasPresence) + XCTAssertTrue(proto2ForPresence.fields[3].hasPresence) + XCTAssertTrue(proto2ForPresence.fields[4].hasPresence) + XCTAssertTrue(proto2ForPresence.fields[5].hasPresence) + XCTAssertTrue(proto2ForPresence.fields[6].hasPresence) + XCTAssertTrue(proto2ForPresence.fields[7].hasPresence) + XCTAssertFalse(proto2ForPresence.fields[8].hasPresence) + XCTAssertFalse(proto2ForPresence.fields[9].hasPresence) + XCTAssertFalse(proto2ForPresence.fields[10].hasPresence) + XCTAssertFalse(proto2ForPresence.fields[11].hasPresence) + XCTAssertTrue(proto2ForPresence.fields[12].hasPresence) + XCTAssertTrue(proto2ForPresence.fields[13].hasPresence) + XCTAssertTrue(proto2ForPresence.fields[14].hasPresence) + XCTAssertTrue(proto2ForPresence.fields[15].hasPresence) + + // No synthetic oneof in proto2 syntax, so the lists should be the same. + XCTAssertEqual(proto2ForPresence.oneofs.count, proto2ForPresence.realOneofs.count) + for (i, o) in proto2ForPresence.realOneofs.enumerated() { + XCTAssert(o === proto2ForPresence.oneofs[i]) + } + + // Proto3 Presence + + let proto3ForPresence = descriptorSet.descriptor(named: "swift_descriptor_test.Proto3MessageForPresence")! + XCTAssertEqual(proto3ForPresence.fields.count, 16) + XCTAssertEqual(proto3ForPresence.fields[0].name, "str_field") + XCTAssertEqual(proto3ForPresence.fields[1].name, "int32_field") + XCTAssertEqual(proto3ForPresence.fields[2].name, "enum_field") + XCTAssertEqual(proto3ForPresence.fields[3].name, "message_field") + XCTAssertEqual(proto3ForPresence.fields[4].name, "opt_str_field") + XCTAssertEqual(proto3ForPresence.fields[5].name, "opt_int32_field") + XCTAssertEqual(proto3ForPresence.fields[6].name, "opt_enum_field") + XCTAssertEqual(proto3ForPresence.fields[7].name, "opt_message_field") + XCTAssertEqual(proto3ForPresence.fields[8].name, "repeat_str_field") + XCTAssertEqual(proto3ForPresence.fields[9].name, "repeat_int32_field") + XCTAssertEqual(proto3ForPresence.fields[10].name, "repeat_enum_field") + XCTAssertEqual(proto3ForPresence.fields[11].name, "repeat_message_field") + XCTAssertEqual(proto3ForPresence.fields[12].name, "oneof_str_field") + XCTAssertEqual(proto3ForPresence.fields[13].name, "oneof_int32_field") + XCTAssertEqual(proto3ForPresence.fields[14].name, "oneof_enum_field") + XCTAssertEqual(proto3ForPresence.fields[15].name, "oneof_message_field") + + XCTAssertFalse(proto3ForPresence.fields[0]._hasOptionalKeyword) + XCTAssertFalse(proto3ForPresence.fields[1]._hasOptionalKeyword) + XCTAssertFalse(proto3ForPresence.fields[2]._hasOptionalKeyword) + XCTAssertFalse(proto3ForPresence.fields[3]._hasOptionalKeyword) + XCTAssertTrue(proto3ForPresence.fields[4]._hasOptionalKeyword) + XCTAssertTrue(proto3ForPresence.fields[5]._hasOptionalKeyword) + XCTAssertTrue(proto3ForPresence.fields[6]._hasOptionalKeyword) + XCTAssertTrue(proto3ForPresence.fields[7]._hasOptionalKeyword) + XCTAssertFalse(proto3ForPresence.fields[8]._hasOptionalKeyword) + XCTAssertFalse(proto3ForPresence.fields[9]._hasOptionalKeyword) + XCTAssertFalse(proto3ForPresence.fields[10]._hasOptionalKeyword) + XCTAssertFalse(proto3ForPresence.fields[11]._hasOptionalKeyword) + XCTAssertFalse(proto3ForPresence.fields[12]._hasOptionalKeyword) + XCTAssertFalse(proto3ForPresence.fields[13]._hasOptionalKeyword) + XCTAssertFalse(proto3ForPresence.fields[14]._hasOptionalKeyword) + XCTAssertFalse(proto3ForPresence.fields[15]._hasOptionalKeyword) + + XCTAssertFalse(proto3ForPresence.fields[0].hasPresence) + XCTAssertFalse(proto3ForPresence.fields[1].hasPresence) + XCTAssertFalse(proto3ForPresence.fields[2].hasPresence) + XCTAssertTrue(proto3ForPresence.fields[3].hasPresence) + XCTAssertTrue(proto3ForPresence.fields[4].hasPresence) + XCTAssertTrue(proto3ForPresence.fields[5].hasPresence) + XCTAssertTrue(proto3ForPresence.fields[6].hasPresence) + XCTAssertTrue(proto3ForPresence.fields[7].hasPresence) + XCTAssertFalse(proto3ForPresence.fields[8].hasPresence) + XCTAssertFalse(proto3ForPresence.fields[9].hasPresence) + XCTAssertFalse(proto3ForPresence.fields[10].hasPresence) + XCTAssertFalse(proto3ForPresence.fields[11].hasPresence) + XCTAssertTrue(proto3ForPresence.fields[12].hasPresence) + XCTAssertTrue(proto3ForPresence.fields[13].hasPresence) + XCTAssertTrue(proto3ForPresence.fields[14].hasPresence) + XCTAssertTrue(proto3ForPresence.fields[15].hasPresence) + + // Synthetic oneof in proto3 syntax for the 'optional' fields, so + // the lists should NOTE be the same, `realOneofs` one should be a + // prefix of `oneofs`. + XCTAssertTrue(proto3ForPresence.oneofs.count > proto3ForPresence.realOneofs.count) + for (i, o) in proto2ForPresence.realOneofs.enumerated() { + XCTAssert(o === proto2ForPresence.oneofs[i]) + } + } - XCTAssertTrue(nestedExt1.enumType === descriptorTestFile.enums[0]) - XCTAssertTrue(nestedExt2.messageType === descriptorTestFile.messages[1]) + func testExtensions() throws { + // Extensions are a little different in how they have extensionScope and + // containingType, so they are split out to be a clear test of their behaviors. - XCTAssertTrue(topLevelExt.file === descriptorTestFile) - XCTAssertTrue(nestedExt1.file === descriptorTestFile) - XCTAssertTrue(nestedExt2.file === descriptorTestFile) - } + let fileSet = try Google_Protobuf_FileDescriptorSet(serializedBytes: fileDescriptorSetBytes) - func testDelimited() throws { - let fileSet = try Google_Protobuf_FileDescriptorSet(serializedBytes: fileDescriptorSetBytes) - let descriptorSet = DescriptorSet(proto: fileSet) + let descriptorSet = DescriptorSet(proto: fileSet) - let msg = try XCTUnwrap(descriptorSet.descriptor(named: SwiftDescriptorTest_EditionsMessageForDelimited.protoMessageName)) + let extendOne = descriptorSet.descriptor(named: "swift_descriptor_test.import.ExtendableOne")! + let extendTwo = descriptorSet.descriptor(named: "swift_descriptor_test.import.ExtendableOne.ExtendableTwo")! - XCTAssertEqual(try XCTUnwrap(msg.field(named: "scalar_field")).type, .int32) - XCTAssertEqual(try XCTUnwrap(msg.field(named: "map_field")).type, .message) - XCTAssertEqual(try XCTUnwrap(msg.field(named: "message_map_field")).type, .message) - XCTAssertEqual(try XCTUnwrap(msg.field(named: "delimited_field")).type, .group) - XCTAssertEqual(try XCTUnwrap(msg.field(named: "length_prefixed_field")).type, .message) - } + let descriptorTestFile = descriptorSet.files[1] - func testIsGroupLike_GroupLikeDelimited() throws { - let fileSet = try Google_Protobuf_FileDescriptorSet(serializedBytes: fileDescriptorSetBytes) - let descriptorSet = DescriptorSet(proto: fileSet) + let topLevelExt = descriptorTestFile.extensions[0] + XCTAssertNil(topLevelExt.extensionScope) + XCTAssertTrue(topLevelExt.containingType === extendOne) - let msg = try XCTUnwrap(descriptorSet.descriptor(named: EditionsUnittest_TestDelimited.protoMessageName)) - let file = try XCTUnwrap(msg.file) + let extScoper = descriptorSet.descriptor(named: "swift_descriptor_test.ScoperForExt")! + let nestedExt1 = descriptorTestFile.messages[3].extensions[0] + let nestedExt2 = descriptorTestFile.messages[3].extensions[1] + XCTAssertTrue(nestedExt1.extensionScope === extScoper) + XCTAssertTrue(nestedExt1.containingType === extendTwo) + XCTAssertTrue(nestedExt2.extensionScope === extScoper) + XCTAssertTrue(nestedExt2.containingType === extendTwo) - XCTAssertTrue(try XCTUnwrap(msg.field(named: "grouplike")).internal_isGroupLike) - XCTAssertTrue(try XCTUnwrap(file.extensionField(named: "grouplikefilescope")).internal_isGroupLike) - } + XCTAssertTrue(nestedExt1.enumType === descriptorTestFile.enums[0]) + XCTAssertTrue(nestedExt2.messageType === descriptorTestFile.messages[1]) - func testIsGroupLike_GroupLikeNotDelimited() throws { - let fileSet = try Google_Protobuf_FileDescriptorSet(serializedBytes: fileDescriptorSetBytes) - let descriptorSet = DescriptorSet(proto: fileSet) + XCTAssertTrue(topLevelExt.file === descriptorTestFile) + XCTAssertTrue(nestedExt1.file === descriptorTestFile) + XCTAssertTrue(nestedExt2.file === descriptorTestFile) + } - let msg = try XCTUnwrap(descriptorSet.descriptor(named: EditionsUnittest_TestDelimited.protoMessageName)) - let file = try XCTUnwrap(msg.file) + func testDelimited() throws { + let fileSet = try Google_Protobuf_FileDescriptorSet(serializedBytes: fileDescriptorSetBytes) + let descriptorSet = DescriptorSet(proto: fileSet) - XCTAssertFalse(try XCTUnwrap(msg.field(named: "lengthprefixed")).internal_isGroupLike) - XCTAssertFalse(try XCTUnwrap(file.extensionField(named: "lengthprefixed")).internal_isGroupLike) - } + let msg = try XCTUnwrap( + descriptorSet.descriptor(named: SwiftDescriptorTest_EditionsMessageForDelimited.protoMessageName) + ) - func testIsGroupLike_GroupLikeMismatchedName() throws { - let fileSet = try Google_Protobuf_FileDescriptorSet(serializedBytes: fileDescriptorSetBytes) - let descriptorSet = DescriptorSet(proto: fileSet) + XCTAssertEqual(try XCTUnwrap(msg.field(named: "scalar_field")).type, .int32) + XCTAssertEqual(try XCTUnwrap(msg.field(named: "map_field")).type, .message) + XCTAssertEqual(try XCTUnwrap(msg.field(named: "message_map_field")).type, .message) + XCTAssertEqual(try XCTUnwrap(msg.field(named: "delimited_field")).type, .group) + XCTAssertEqual(try XCTUnwrap(msg.field(named: "length_prefixed_field")).type, .message) + } - let msg = try XCTUnwrap(descriptorSet.descriptor(named: EditionsUnittest_TestDelimited.protoMessageName)) - let file = try XCTUnwrap(msg.file) + func testIsGroupLike_GroupLikeDelimited() throws { + let fileSet = try Google_Protobuf_FileDescriptorSet(serializedBytes: fileDescriptorSetBytes) + let descriptorSet = DescriptorSet(proto: fileSet) - XCTAssertFalse(try XCTUnwrap(msg.field(named: "notgrouplike")).internal_isGroupLike) - XCTAssertFalse(try XCTUnwrap(file.extensionField(named: "not_group_like_scope")).internal_isGroupLike) - } + let msg = try XCTUnwrap(descriptorSet.descriptor(named: EditionsUnittest_TestDelimited.protoMessageName)) + let file = try XCTUnwrap(msg.file) - func testIsGroupLike_GroupLikeMismatchedScope() throws { - let fileSet = try Google_Protobuf_FileDescriptorSet(serializedBytes: fileDescriptorSetBytes) - let descriptorSet = DescriptorSet(proto: fileSet) + XCTAssertTrue(try XCTUnwrap(msg.field(named: "grouplike")).internal_isGroupLike) + XCTAssertTrue(try XCTUnwrap(file.extensionField(named: "grouplikefilescope")).internal_isGroupLike) + } - let msg = try XCTUnwrap(descriptorSet.descriptor(named: EditionsUnittest_TestDelimited.protoMessageName)) - let file = try XCTUnwrap(msg.file) + func testIsGroupLike_GroupLikeNotDelimited() throws { + let fileSet = try Google_Protobuf_FileDescriptorSet(serializedBytes: fileDescriptorSetBytes) + let descriptorSet = DescriptorSet(proto: fileSet) - XCTAssertFalse(try XCTUnwrap(msg.field(named: "notgrouplikescope")).internal_isGroupLike) - XCTAssertFalse(try XCTUnwrap(file.extensionField(named: "grouplike")).internal_isGroupLike) - } + let msg = try XCTUnwrap(descriptorSet.descriptor(named: EditionsUnittest_TestDelimited.protoMessageName)) + let file = try XCTUnwrap(msg.file) - func testIsGroupLike_GroupLikeMismatchedFile() throws { - let fileSet = try Google_Protobuf_FileDescriptorSet(serializedBytes: fileDescriptorSetBytes) - let descriptorSet = DescriptorSet(proto: fileSet) + XCTAssertFalse(try XCTUnwrap(msg.field(named: "lengthprefixed")).internal_isGroupLike) + XCTAssertFalse(try XCTUnwrap(file.extensionField(named: "lengthprefixed")).internal_isGroupLike) + } - let msg = try XCTUnwrap(descriptorSet.descriptor(named: EditionsUnittest_TestDelimited.protoMessageName)) - let file = try XCTUnwrap(msg.file) + func testIsGroupLike_GroupLikeMismatchedName() throws { + let fileSet = try Google_Protobuf_FileDescriptorSet(serializedBytes: fileDescriptorSetBytes) + let descriptorSet = DescriptorSet(proto: fileSet) - XCTAssertFalse(try XCTUnwrap(msg.field(named: "messageimport")).internal_isGroupLike) - XCTAssertFalse(try XCTUnwrap(file.extensionField(named: "messageimport")).internal_isGroupLike) - } + let msg = try XCTUnwrap(descriptorSet.descriptor(named: EditionsUnittest_TestDelimited.protoMessageName)) + let file = try XCTUnwrap(msg.file) - func testExtractProto_Options() throws { - let fileSet = try Google_Protobuf_FileDescriptorSet(serializedBytes: fileDescriptorSetBytes) - let descriptorSet = DescriptorSet(proto: fileSet) + XCTAssertFalse(try XCTUnwrap(msg.field(named: "notgrouplike")).internal_isGroupLike) + XCTAssertFalse(try XCTUnwrap(file.extensionField(named: "not_group_like_scope")).internal_isGroupLike) + } - let fileDescriptor = descriptorSet.fileDescriptor(named: "pluginlib_descriptor_test.proto")! + func testIsGroupLike_GroupLikeMismatchedScope() throws { + let fileSet = try Google_Protobuf_FileDescriptorSet(serializedBytes: fileDescriptorSetBytes) + let descriptorSet = DescriptorSet(proto: fileSet) - // NOTE: There should be a full tests for ExtractProto that validates all the sub descriptor - // protos. But for now, given the function's implementation, just test that the options are - // honored correctly. + let msg = try XCTUnwrap(descriptorSet.descriptor(named: EditionsUnittest_TestDelimited.protoMessageName)) + let file = try XCTUnwrap(msg.file) - // Default: - // - includeSourceCodeInfo = false - // - headerOnly = false - do { - let extract = fileDescriptor.extractProto() - XCTAssertFalse(extract.hasSourceCodeInfo, "Included SourceCodeInfo?") - XCTAssertFalse(extract.messageType.isEmpty, "Missing messages?") - XCTAssertFalse(extract.enumType.isEmpty, "Missing enums?") - XCTAssertFalse(extract.extension.isEmpty, "Missing extensions?") - XCTAssertFalse(extract.service.isEmpty, "Missing services?") + XCTAssertFalse(try XCTUnwrap(msg.field(named: "notgrouplikescope")).internal_isGroupLike) + XCTAssertFalse(try XCTUnwrap(file.extensionField(named: "grouplike")).internal_isGroupLike) } - var options = ExtractProtoOptions() - options.includeSourceCodeInfo = true - // - includeSourceCodeInfo = true - // - headerOnly = false - do { - let extract = fileDescriptor.extractProto(options: options) - XCTAssertTrue(extract.hasSourceCodeInfo, "Missing SourceCodeInfo?") - XCTAssertFalse(extract.messageType.isEmpty, "Missing messages?") - XCTAssertFalse(extract.enumType.isEmpty, "Missing enums?") - XCTAssertFalse(extract.extension.isEmpty, "Missing extensions?") - XCTAssertFalse(extract.service.isEmpty, "Missing services?") - } + func testIsGroupLike_GroupLikeMismatchedFile() throws { + let fileSet = try Google_Protobuf_FileDescriptorSet(serializedBytes: fileDescriptorSetBytes) + let descriptorSet = DescriptorSet(proto: fileSet) + + let msg = try XCTUnwrap(descriptorSet.descriptor(named: EditionsUnittest_TestDelimited.protoMessageName)) + let file = try XCTUnwrap(msg.file) - options.headerOnly = true - // - includeSourceCodeInfo = true - // - headerOnly = true - do { - let extract = fileDescriptor.extractProto(options: options) - XCTAssertTrue(extract.hasSourceCodeInfo, "Missing SourceCodeInfo?") - XCTAssertTrue(extract.messageType.isEmpty, "Incuded messages?") - XCTAssertTrue(extract.enumType.isEmpty, "Incuded enums?") - XCTAssertTrue(extract.extension.isEmpty, "Missing extensions?") - XCTAssertTrue(extract.service.isEmpty, "Missing services?") + XCTAssertFalse(try XCTUnwrap(msg.field(named: "messageimport")).internal_isGroupLike) + XCTAssertFalse(try XCTUnwrap(file.extensionField(named: "messageimport")).internal_isGroupLike) } - options.includeSourceCodeInfo = false - // - includeSourceCodeInfo = false - // - headerOnly = false - do { - let extract = fileDescriptor.extractProto(options: options) - XCTAssertFalse(extract.hasSourceCodeInfo, "Included SourceCodeInfo?") - XCTAssertTrue(extract.messageType.isEmpty, "Incuded messages?") - XCTAssertTrue(extract.enumType.isEmpty, "Incuded enums?") - XCTAssertTrue(extract.extension.isEmpty, "Missing extensions?") - XCTAssertTrue(extract.service.isEmpty, "Missing services?") + func testExtractProto_Options() throws { + let fileSet = try Google_Protobuf_FileDescriptorSet(serializedBytes: fileDescriptorSetBytes) + let descriptorSet = DescriptorSet(proto: fileSet) + + let fileDescriptor = descriptorSet.fileDescriptor(named: "pluginlib_descriptor_test.proto")! + + // NOTE: There should be a full tests for ExtractProto that validates all the sub descriptor + // protos. But for now, given the function's implementation, just test that the options are + // honored correctly. + + // Default: + // - includeSourceCodeInfo = false + // - headerOnly = false + do { + let extract = fileDescriptor.extractProto() + XCTAssertFalse(extract.hasSourceCodeInfo, "Included SourceCodeInfo?") + XCTAssertFalse(extract.messageType.isEmpty, "Missing messages?") + XCTAssertFalse(extract.enumType.isEmpty, "Missing enums?") + XCTAssertFalse(extract.extension.isEmpty, "Missing extensions?") + XCTAssertFalse(extract.service.isEmpty, "Missing services?") + } + + var options = ExtractProtoOptions() + options.includeSourceCodeInfo = true + // - includeSourceCodeInfo = true + // - headerOnly = false + do { + let extract = fileDescriptor.extractProto(options: options) + XCTAssertTrue(extract.hasSourceCodeInfo, "Missing SourceCodeInfo?") + XCTAssertFalse(extract.messageType.isEmpty, "Missing messages?") + XCTAssertFalse(extract.enumType.isEmpty, "Missing enums?") + XCTAssertFalse(extract.extension.isEmpty, "Missing extensions?") + XCTAssertFalse(extract.service.isEmpty, "Missing services?") + } + + options.headerOnly = true + // - includeSourceCodeInfo = true + // - headerOnly = true + do { + let extract = fileDescriptor.extractProto(options: options) + XCTAssertTrue(extract.hasSourceCodeInfo, "Missing SourceCodeInfo?") + XCTAssertTrue(extract.messageType.isEmpty, "Incuded messages?") + XCTAssertTrue(extract.enumType.isEmpty, "Incuded enums?") + XCTAssertTrue(extract.extension.isEmpty, "Missing extensions?") + XCTAssertTrue(extract.service.isEmpty, "Missing services?") + } + + options.includeSourceCodeInfo = false + // - includeSourceCodeInfo = false + // - headerOnly = false + do { + let extract = fileDescriptor.extractProto(options: options) + XCTAssertFalse(extract.hasSourceCodeInfo, "Included SourceCodeInfo?") + XCTAssertTrue(extract.messageType.isEmpty, "Incuded messages?") + XCTAssertTrue(extract.enumType.isEmpty, "Incuded enums?") + XCTAssertTrue(extract.extension.isEmpty, "Missing extensions?") + XCTAssertTrue(extract.service.isEmpty, "Missing services?") + } } - } } diff --git a/Tests/SwiftProtobufPluginLibraryTests/Test_Descriptor_FeatureResolution.swift b/Tests/SwiftProtobufPluginLibraryTests/Test_Descriptor_FeatureResolution.swift index 1851a14cb..d6e008eb5 100644 --- a/Tests/SwiftProtobufPluginLibraryTests/Test_Descriptor_FeatureResolution.swift +++ b/Tests/SwiftProtobufPluginLibraryTests/Test_Descriptor_FeatureResolution.swift @@ -8,676 +8,722 @@ // // ----------------------------------------------------------------------------- -import XCTest import SwiftProtobuf import SwiftProtobufPluginLibrary +import XCTest -fileprivate let testFeatureSetDefaults = - try! Google_Protobuf_FeatureSetDefaults(serializedBytes: testFeatureSetDefaultBytes, - extensions: SwiftFeatureTest_TestFeatures_Extensions) - -fileprivate struct TestContext { - let descriptorSet: DescriptorSet - var file: FileDescriptor { return descriptorSet.files.first! } - - init(_ descriptorTextFormat: String) { - let file = try! Google_Protobuf_FileDescriptorProto(textFormatString: descriptorTextFormat, - extensions: SwiftFeatureTest_TestFeatures_Extensions) - descriptorSet = DescriptorSet(protos: [file], - featureSetDefaults: testFeatureSetDefaults, - featureExtensions: [SwiftFeatureTest_Extensions_test]) - } +private let testFeatureSetDefaults = + try! Google_Protobuf_FeatureSetDefaults( + serializedBytes: testFeatureSetDefaultBytes, + extensions: SwiftFeatureTest_TestFeatures_Extensions + ) + +private struct TestContext { + let descriptorSet: DescriptorSet + var file: FileDescriptor { descriptorSet.files.first! } + + init(_ descriptorTextFormat: String) { + let file = try! Google_Protobuf_FileDescriptorProto( + textFormatString: descriptorTextFormat, + extensions: SwiftFeatureTest_TestFeatures_Extensions + ) + descriptorSet = DescriptorSet( + protos: [file], + featureSetDefaults: testFeatureSetDefaults, + featureExtensions: [SwiftFeatureTest_Extensions_test] + ) + } } final class Test_Descriptor_FeatureResolution: XCTestCase { - func testFileLevel_Defaults() throws { - let context = TestContext(""" - name: "test.proto" - edition: EDITION_2023 - """) - - let features = context.file.features - XCTAssertTrue(features.hasSwiftFeatureTest_test) - XCTAssertEqual(features.SwiftFeatureTest_test.feature1, .value1) - } - - func testFileLevel_Override() throws { - let context = TestContext(""" - name: "test.proto" - edition: EDITION_2023 - options { - features { - [swift_feature_test.test] { feature1: ENUM_FEATURE_VALUE2 } - } - } - """) - - let features = context.file.features - XCTAssertTrue(features.hasSwiftFeatureTest_test) - XCTAssertEqual(features.SwiftFeatureTest_test.feature1, .value2) // File override - } - - func testMessageLevel_Defaults() throws { - let context = TestContext(""" - name: "test.proto" - edition: EDITION_2023 - message_type { - name: "Top" - nested_type { - name: "Nested" - } - } - """) - - let topFeatures = context.file.messages.first!.features - XCTAssertTrue(topFeatures.hasSwiftFeatureTest_test) - XCTAssertEqual(topFeatures.SwiftFeatureTest_test.feature1, .value1) - XCTAssertEqual(topFeatures.SwiftFeatureTest_test.feature2, .value1) - let nestedFeatures = context.file.messages.first!.messages.first!.features - XCTAssertTrue(nestedFeatures.hasSwiftFeatureTest_test) - XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature1, .value1) - XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature2, .value1) - XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature3, .value1) - } - - func testMessageLevel_Override() throws { - let context = TestContext(""" - name: "test.proto" - edition: EDITION_2023 - options { - features { - [swift_feature_test.test] { feature1: ENUM_FEATURE_VALUE2 } - } - } - message_type { - name: "Top" - options { - features { - [swift_feature_test.test] { feature2: ENUM_FEATURE_VALUE3 } - } - } - nested_type { - name: "Nested" - options { - features { - [swift_feature_test.test] { feature3: ENUM_FEATURE_VALUE4 } - } - } - } - } - """) - - let topFeatures = context.file.messages.first!.features - XCTAssertTrue(topFeatures.hasSwiftFeatureTest_test) - XCTAssertEqual(topFeatures.SwiftFeatureTest_test.feature1, .value2) // File override - XCTAssertEqual(topFeatures.SwiftFeatureTest_test.feature2, .value3) // Top override - let nestedFeatures = context.file.messages.first!.messages.first!.features - XCTAssertTrue(nestedFeatures.hasSwiftFeatureTest_test) - XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature1, .value2) // File override - XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature2, .value3) // Top override - XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature3, .value4) // Nested override - } - - func testEnumLevel_Defaults() throws { - let context = TestContext(""" - name: "test.proto" - edition: EDITION_2023 - enum_type { - name: "Top" - value { name: "TOP_UNKNOWN", number: 0 } - value { name: "TOP_ONE", number: 1 } - } - message_type { - name: "MyMessage" - enum_type { - name: "Nested" - value { name: "NESTED_UNKNOWN", number: 0 } - value { name: "NESTED_ONE", number: 1 } - } - } - """) - - let topFeatures = context.file.enums.first!.features - XCTAssertTrue(topFeatures.hasSwiftFeatureTest_test) - XCTAssertEqual(topFeatures.SwiftFeatureTest_test.feature1, .value1) - XCTAssertEqual(topFeatures.SwiftFeatureTest_test.feature2, .value1) - let nestedFeatures = context.file.messages.first!.enums.first!.features - XCTAssertTrue(nestedFeatures.hasSwiftFeatureTest_test) - XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature1, .value1) - XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature2, .value1) - XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature3, .value1) - XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature4, .value1) - } - - func testEnumLevel_Override() throws { - let context = TestContext(""" - name: "test.proto" - edition: EDITION_2023 - options { - features { - [swift_feature_test.test] { feature1: ENUM_FEATURE_VALUE2 } - } - } - enum_type { - name: "Top" - options { - features { - [swift_feature_test.test] { feature2: ENUM_FEATURE_VALUE3 } - } - } - value { name: "TOP_UNKNOWN", number: 0 } - value { name: "TOP_ONE", number: 1 } - } - message_type { - name: "MyMessage" - options { - features { - [swift_feature_test.test] { feature3: ENUM_FEATURE_VALUE4 } - } - } - enum_type { - name: "Nested" - options { - features { - [swift_feature_test.test] { feature4: ENUM_FEATURE_VALUE5 } - } - } - value { name: "NESTED_UNKNOWN", number: 0 } - value { name: "NESTED_ONE", number: 1 } - } - } - """) - - let topFeatures = context.file.enums.first!.features - XCTAssertTrue(topFeatures.hasSwiftFeatureTest_test) - XCTAssertEqual(topFeatures.SwiftFeatureTest_test.feature1, .value2) // File override - XCTAssertEqual(topFeatures.SwiftFeatureTest_test.feature2, .value3) // "Top" Enum override - let nestedFeatures = context.file.messages.first!.enums.first!.features - XCTAssertTrue(nestedFeatures.hasSwiftFeatureTest_test) - XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature1, .value2) // File override - XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature2, .value1) // default ("Top" Enum override) - XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature3, .value4) // Message override - XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature4, .value5) // "Nested" Enum override - } - - func testEnumValueLevel_Defaults() throws { - let context = TestContext(""" - name: "test.proto" - edition: EDITION_2023 - enum_type { - name: "Top" - value { name: "TOP_UNKNOWN", number: 0 } - value { name: "TOP_ONE", number: 1 } - } - """) - - let features = context.file.enums.first!.values.first!.features - XCTAssertTrue(features.hasSwiftFeatureTest_test) - XCTAssertEqual(features.SwiftFeatureTest_test.feature1, .value1) - XCTAssertEqual(features.SwiftFeatureTest_test.feature2, .value1) - XCTAssertEqual(features.SwiftFeatureTest_test.feature3, .value1) - } - - func testEnumValueLevel_Override() throws { - let context = TestContext(""" - name: "test.proto" - edition: EDITION_2023 - options { - features { - [swift_feature_test.test] { feature1: ENUM_FEATURE_VALUE2 } - } - } - enum_type { - name: "MyEnum" - options { - features { - [swift_feature_test.test] { feature2: ENUM_FEATURE_VALUE3 } - } - } - value { - name: "TOP_UNKNOWN" - options { - features { - [swift_feature_test.test] { feature3: ENUM_FEATURE_VALUE4 } - } - } - number: 0 - } - value { name: "TOP_ONE", number: 1 } - } - """) - - let features = context.file.enums.first!.values.first!.features - XCTAssertTrue(features.hasSwiftFeatureTest_test) - XCTAssertEqual(features.SwiftFeatureTest_test.feature1, .value2) // File override - XCTAssertEqual(features.SwiftFeatureTest_test.feature2, .value3) // Enum override - XCTAssertEqual(features.SwiftFeatureTest_test.feature3, .value4) // EnumValue override - } - - func testExtensionRangeLevel_Defaults() throws { - let context = TestContext(""" - name: "test.proto" - edition: EDITION_2023 - message_type { - name: "MyMessage" - extension_range { start: 1, end: 100 } - } - """) - - let features = context.file.messages.first!.messageExtensionRanges.first!.features - XCTAssertTrue(features.hasSwiftFeatureTest_test) - XCTAssertEqual(features.SwiftFeatureTest_test.feature1, .value1) - XCTAssertEqual(features.SwiftFeatureTest_test.feature2, .value1) - XCTAssertEqual(features.SwiftFeatureTest_test.feature3, .value1) - } - - func testExtensionRangeLevel_Override() throws { - let context = TestContext(""" - name: "test.proto" - edition: EDITION_2023 - options { - features { - [swift_feature_test.test] { feature1: ENUM_FEATURE_VALUE2 } - } - } - message_type { - name: "MyMessage" - options { - features { - [swift_feature_test.test] { feature2: ENUM_FEATURE_VALUE3 } - } - } - extension_range { - start: 1 - end: 100 - options { - features { - [swift_feature_test.test] { feature3: ENUM_FEATURE_VALUE4 } - } - } - } - } - """) - - let features = context.file.messages.first!.messageExtensionRanges.first!.features - XCTAssertTrue(features.hasSwiftFeatureTest_test) - XCTAssertEqual(features.SwiftFeatureTest_test.feature1, .value2) // File override - XCTAssertEqual(features.SwiftFeatureTest_test.feature2, .value3) // Message override - XCTAssertEqual(features.SwiftFeatureTest_test.feature3, .value4) // ExtensionRange override - } - - func testExtensionLevel_Defaults() throws { - let context = TestContext(""" - name: "test.proto" - edition: EDITION_2023 - message_type { - name: "MyMessage" - extension_range { start: 1, end: 100 } - extension { - name: "nested" - json_name: "nested" - number: 2 - type: TYPE_STRING - extendee: ".MyMessage" - } - } - extension { - name: "top" - json_name: "top" - number: 1 - type: TYPE_STRING - extendee: ".MyMessage" - } - """) - - let topFeatures = context.file.extensions.first!.features - XCTAssertTrue(topFeatures.hasSwiftFeatureTest_test) - XCTAssertEqual(topFeatures.SwiftFeatureTest_test.feature1, .value1) - XCTAssertEqual(topFeatures.SwiftFeatureTest_test.feature2, .value1) - let nestedFeatures = context.file.messages.first!.extensions.first!.features - XCTAssertTrue(nestedFeatures.hasSwiftFeatureTest_test) - XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature1, .value1) - XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature2, .value1) - XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature3, .value1) - XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature4, .value1) - } - - func testExtensionLevel_Override() throws { - let context = TestContext(""" - name: "test.proto" - edition: EDITION_2023 - options { - features { - [swift_feature_test.test] { feature1: ENUM_FEATURE_VALUE2 } - } - } - message_type { - name: "MyMessage" - options { - features { - [swift_feature_test.test] { feature3: ENUM_FEATURE_VALUE4 } - } - } - extension_range { start: 1, end: 100 } - extension { - name: "nested" - json_name: "nested" - number: 2 - type: TYPE_STRING - extendee: ".MyMessage" - options { - features { - [swift_feature_test.test] { feature4: ENUM_FEATURE_VALUE5 } - } - } - } - } - extension { - name: "top" - json_name: "top" - number: 1 - type: TYPE_STRING - extendee: ".MyMessage" - options { - features { - [swift_feature_test.test] { feature2: ENUM_FEATURE_VALUE3 } - } - } - } - """) - - let topFeatures = context.file.extensions.first!.features - XCTAssertTrue(topFeatures.hasSwiftFeatureTest_test) - XCTAssertEqual(topFeatures.SwiftFeatureTest_test.feature1, .value2) // File override - XCTAssertEqual(topFeatures.SwiftFeatureTest_test.feature2, .value3) // "top" Extension override - let nestedFeatures = context.file.messages.first!.extensions.first!.features - XCTAssertTrue(nestedFeatures.hasSwiftFeatureTest_test) - XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature1, .value2) // File override - XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature2, .value1) // default ("top" Extension override) - XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature3, .value4) // Message override - XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature4, .value5) // "nested" Extension override - } - - func testMessageFieldLevel_Defaults() throws { - let context = TestContext(""" - name: "test.proto" - edition: EDITION_2023 - message_type { - name: "MyMessage" - field { - name: "field" - json_name: "field" - number: 1 - type: TYPE_STRING - } - } - """) - - let features = context.file.messages.first!.fields.first!.features - XCTAssertTrue(features.hasSwiftFeatureTest_test) - XCTAssertEqual(features.SwiftFeatureTest_test.feature1, .value1) - XCTAssertEqual(features.SwiftFeatureTest_test.feature2, .value1) - XCTAssertEqual(features.SwiftFeatureTest_test.feature3, .value1) - } - - func testMessageFieldLevel_Override() throws { - let context = TestContext(""" - name: "test.proto" - edition: EDITION_2023 - options { - features { - [swift_feature_test.test] { feature1: ENUM_FEATURE_VALUE2 } - } - } - message_type { - name: "MyMessage" - options { - features { - [swift_feature_test.test] { feature2: ENUM_FEATURE_VALUE3 } - } - } - field { - name: "field" - json_name: "field" - number: 1 - type: TYPE_STRING - options { - features { - [swift_feature_test.test] { feature3: ENUM_FEATURE_VALUE4 } - } - } - } - } - """) - - let features = context.file.messages.first!.fields.first!.features - XCTAssertTrue(features.hasSwiftFeatureTest_test) - XCTAssertEqual(features.SwiftFeatureTest_test.feature1, .value2) // File override - XCTAssertEqual(features.SwiftFeatureTest_test.feature2, .value3) // Message override - XCTAssertEqual(features.SwiftFeatureTest_test.feature3, .value4) // Field override - } - - func testMessageOneofFieldLevel_Defaults() throws { - let context = TestContext(""" - name: "test.proto" - edition: EDITION_2023 - message_type { - name: "MyMessage" - oneof_decl { name: "my_oneof" } - field { - name: "oneof_field" - json_name: "oneof_field" - number: 1 - type: TYPE_STRING - oneof_index: 0 - } - field { - name: "not_oneof_field" - json_name: "not_oneof_field" - number: 2 - type: TYPE_STRING - } - } - """) - - let oneof = context.file.messages.first!.realOneofs.first! - XCTAssertEqual(oneof.name, "my_oneof") - let oneofFeatures = oneof.features - XCTAssertTrue(oneofFeatures.hasSwiftFeatureTest_test) - XCTAssertEqual(oneofFeatures.SwiftFeatureTest_test.feature1, .value1) - XCTAssertEqual(oneofFeatures.SwiftFeatureTest_test.feature2, .value1) - XCTAssertEqual(oneofFeatures.SwiftFeatureTest_test.feature3, .value1) - let oneofField = context.file.messages.first!.fields.first! - XCTAssertEqual(oneofField.name, "oneof_field") - let oneofFieldFeatures = oneofField.features - XCTAssertTrue(oneofFieldFeatures.hasSwiftFeatureTest_test) - XCTAssertEqual(oneofFieldFeatures.SwiftFeatureTest_test.feature1, .value1) - XCTAssertEqual(oneofFieldFeatures.SwiftFeatureTest_test.feature2, .value1) - XCTAssertEqual(oneofFieldFeatures.SwiftFeatureTest_test.feature3, .value1) - XCTAssertEqual(oneofFieldFeatures.SwiftFeatureTest_test.feature4, .value1) - XCTAssertEqual(oneofFieldFeatures.SwiftFeatureTest_test.feature5, .value1) - let field = context.file.messages.first!.fields[1] - XCTAssertEqual(field.name, "not_oneof_field") - let fieldFeatures = field.features - XCTAssertTrue(fieldFeatures.hasSwiftFeatureTest_test) - XCTAssertEqual(fieldFeatures.SwiftFeatureTest_test.feature1, .value1) - XCTAssertEqual(fieldFeatures.SwiftFeatureTest_test.feature2, .value1) - XCTAssertEqual(fieldFeatures.SwiftFeatureTest_test.feature3, .value1) - XCTAssertEqual(fieldFeatures.SwiftFeatureTest_test.feature4, .value1) - XCTAssertEqual(fieldFeatures.SwiftFeatureTest_test.feature5, .value1) - } - - func testMessageOneofFieldLevel_Override() throws { - let context = TestContext(""" - name: "test.proto" - edition: EDITION_2023 - options { - features { - [swift_feature_test.test] { feature1: ENUM_FEATURE_VALUE2 } - } - } - message_type { - name: "MyMessage" - options { - features { - [swift_feature_test.test] { feature2: ENUM_FEATURE_VALUE3 } - } - } - oneof_decl { - name: "my_oneof" - options { - features { - [swift_feature_test.test] { feature3: ENUM_FEATURE_VALUE4 } - } - } - } - field { - name: "oneof_field" - json_name: "oneof_field" - number: 1 - type: TYPE_STRING - oneof_index: 0 - options { - features { - [swift_feature_test.test] { feature4: ENUM_FEATURE_VALUE5 } - } - } - } - field { - name: "not_oneof_field" - json_name: "not_oneof_field" - number: 2 - type: TYPE_STRING - options { - features { - [swift_feature_test.test] { feature5: ENUM_FEATURE_VALUE6 } - } - } - } - } - """) - - let oneof = context.file.messages.first!.realOneofs.first! - XCTAssertEqual(oneof.name, "my_oneof") - let oneofFeatures = oneof.features - XCTAssertTrue(oneofFeatures.hasSwiftFeatureTest_test) - XCTAssertEqual(oneofFeatures.SwiftFeatureTest_test.feature1, .value2) // File override - XCTAssertEqual(oneofFeatures.SwiftFeatureTest_test.feature2, .value3) // Message override - XCTAssertEqual(oneofFeatures.SwiftFeatureTest_test.feature3, .value4) // Oneof override - let oneofField = context.file.messages.first!.fields.first! - XCTAssertEqual(oneofField.name, "oneof_field") - let oneofFieldFeatures = oneofField.features - XCTAssertTrue(oneofFieldFeatures.hasSwiftFeatureTest_test) - XCTAssertEqual(oneofFieldFeatures.SwiftFeatureTest_test.feature1, .value2) // File override - XCTAssertEqual(oneofFieldFeatures.SwiftFeatureTest_test.feature2, .value3) // Message override - XCTAssertEqual(oneofFieldFeatures.SwiftFeatureTest_test.feature3, .value4) // Oneof override - XCTAssertEqual(oneofFieldFeatures.SwiftFeatureTest_test.feature4, .value5) // "oneof_field" Field override - XCTAssertEqual(oneofFieldFeatures.SwiftFeatureTest_test.feature5, .value1) // default ("not_oneof_field" Field override) - let field = context.file.messages.first!.fields[1] - XCTAssertEqual(field.name, "not_oneof_field") - let fieldFeatures = field.features - XCTAssertTrue(fieldFeatures.hasSwiftFeatureTest_test) - XCTAssertEqual(fieldFeatures.SwiftFeatureTest_test.feature1, .value2) // File override - XCTAssertEqual(fieldFeatures.SwiftFeatureTest_test.feature2, .value3) // Message override - XCTAssertEqual(fieldFeatures.SwiftFeatureTest_test.feature3, .value1) // default (Oneof override) - XCTAssertEqual(fieldFeatures.SwiftFeatureTest_test.feature4, .value1) // default ("oneof_field" Field override) - XCTAssertEqual(fieldFeatures.SwiftFeatureTest_test.feature5, .value6) // "not_oneof_field" Field override - } - - func testServiceLevel_Defaults() throws { - let context = TestContext(""" - name: "test.proto" - edition: EDITION_2023 - service { - name: "MyService" - } - """) - - let features = context.file.services.first!.features - XCTAssertTrue(features.hasSwiftFeatureTest_test) - XCTAssertEqual(features.SwiftFeatureTest_test.feature1, .value1) - XCTAssertEqual(features.SwiftFeatureTest_test.feature2, .value1) - } - - func testServiceLevel_Overrides() throws { - let context = TestContext(""" - name: "test.proto" - edition: EDITION_2023 - options { - features { - [swift_feature_test.test] { feature1: ENUM_FEATURE_VALUE3 } - } - } - service { - name: "MyService" - options { - features { - [swift_feature_test.test] { feature2: ENUM_FEATURE_VALUE4 } - } - } - } - """) - - let features = context.file.services.first!.features - XCTAssertTrue(features.hasSwiftFeatureTest_test) - XCTAssertEqual(features.SwiftFeatureTest_test.feature1, .value3) // File override - XCTAssertEqual(features.SwiftFeatureTest_test.feature2, .value4) // Service override - } - - func testMethodLevel_Defaults() throws { - let context = TestContext(""" - name: "test.proto" - edition: EDITION_2023 - message_type { name: "empty" } - service { - name: "MyService" - method { name: "doSomething" input_type: ".empty" output_type: ".empty" } - } - """) - - let features = context.file.services.first!.methods.first!.features - XCTAssertTrue(features.hasSwiftFeatureTest_test) - XCTAssertEqual(features.SwiftFeatureTest_test.feature1, .value1) - XCTAssertEqual(features.SwiftFeatureTest_test.feature2, .value1) - XCTAssertEqual(features.SwiftFeatureTest_test.feature3, .value1) - } - - func testMethodLevel_Overrides() throws { - let context = TestContext(""" - name: "test.proto" - edition: EDITION_2023 - options { - features { - [swift_feature_test.test] { feature1: ENUM_FEATURE_VALUE3 } - } - } - message_type { name: "empty" } - service { - name: "MyService" - options { - features { - [swift_feature_test.test] { feature2: ENUM_FEATURE_VALUE4 } - } - } - method { - name: "doSomething" - input_type: ".empty" - output_type: ".empty" - options { - features { - [swift_feature_test.test] { feature3: ENUM_FEATURE_VALUE5 } - } - } - } - } - """) - - let features = context.file.services.first!.methods.first!.features - XCTAssertTrue(features.hasSwiftFeatureTest_test) - XCTAssertEqual(features.SwiftFeatureTest_test.feature1, .value3) // File override - XCTAssertEqual(features.SwiftFeatureTest_test.feature2, .value4) // Service override - XCTAssertEqual(features.SwiftFeatureTest_test.feature3, .value5) // Method override - } + func testFileLevel_Defaults() throws { + let context = TestContext( + """ + name: "test.proto" + edition: EDITION_2023 + """ + ) + + let features = context.file.features + XCTAssertTrue(features.hasSwiftFeatureTest_test) + XCTAssertEqual(features.SwiftFeatureTest_test.feature1, .value1) + } + + func testFileLevel_Override() throws { + let context = TestContext( + """ + name: "test.proto" + edition: EDITION_2023 + options { + features { + [swift_feature_test.test] { feature1: ENUM_FEATURE_VALUE2 } + } + } + """ + ) + + let features = context.file.features + XCTAssertTrue(features.hasSwiftFeatureTest_test) + XCTAssertEqual(features.SwiftFeatureTest_test.feature1, .value2) // File override + } + + func testMessageLevel_Defaults() throws { + let context = TestContext( + """ + name: "test.proto" + edition: EDITION_2023 + message_type { + name: "Top" + nested_type { + name: "Nested" + } + } + """ + ) + + let topFeatures = context.file.messages.first!.features + XCTAssertTrue(topFeatures.hasSwiftFeatureTest_test) + XCTAssertEqual(topFeatures.SwiftFeatureTest_test.feature1, .value1) + XCTAssertEqual(topFeatures.SwiftFeatureTest_test.feature2, .value1) + let nestedFeatures = context.file.messages.first!.messages.first!.features + XCTAssertTrue(nestedFeatures.hasSwiftFeatureTest_test) + XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature1, .value1) + XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature2, .value1) + XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature3, .value1) + } + + func testMessageLevel_Override() throws { + let context = TestContext( + """ + name: "test.proto" + edition: EDITION_2023 + options { + features { + [swift_feature_test.test] { feature1: ENUM_FEATURE_VALUE2 } + } + } + message_type { + name: "Top" + options { + features { + [swift_feature_test.test] { feature2: ENUM_FEATURE_VALUE3 } + } + } + nested_type { + name: "Nested" + options { + features { + [swift_feature_test.test] { feature3: ENUM_FEATURE_VALUE4 } + } + } + } + } + """ + ) + + let topFeatures = context.file.messages.first!.features + XCTAssertTrue(topFeatures.hasSwiftFeatureTest_test) + XCTAssertEqual(topFeatures.SwiftFeatureTest_test.feature1, .value2) // File override + XCTAssertEqual(topFeatures.SwiftFeatureTest_test.feature2, .value3) // Top override + let nestedFeatures = context.file.messages.first!.messages.first!.features + XCTAssertTrue(nestedFeatures.hasSwiftFeatureTest_test) + XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature1, .value2) // File override + XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature2, .value3) // Top override + XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature3, .value4) // Nested override + } + + func testEnumLevel_Defaults() throws { + let context = TestContext( + """ + name: "test.proto" + edition: EDITION_2023 + enum_type { + name: "Top" + value { name: "TOP_UNKNOWN", number: 0 } + value { name: "TOP_ONE", number: 1 } + } + message_type { + name: "MyMessage" + enum_type { + name: "Nested" + value { name: "NESTED_UNKNOWN", number: 0 } + value { name: "NESTED_ONE", number: 1 } + } + } + """ + ) + + let topFeatures = context.file.enums.first!.features + XCTAssertTrue(topFeatures.hasSwiftFeatureTest_test) + XCTAssertEqual(topFeatures.SwiftFeatureTest_test.feature1, .value1) + XCTAssertEqual(topFeatures.SwiftFeatureTest_test.feature2, .value1) + let nestedFeatures = context.file.messages.first!.enums.first!.features + XCTAssertTrue(nestedFeatures.hasSwiftFeatureTest_test) + XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature1, .value1) + XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature2, .value1) + XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature3, .value1) + XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature4, .value1) + } + + func testEnumLevel_Override() throws { + let context = TestContext( + """ + name: "test.proto" + edition: EDITION_2023 + options { + features { + [swift_feature_test.test] { feature1: ENUM_FEATURE_VALUE2 } + } + } + enum_type { + name: "Top" + options { + features { + [swift_feature_test.test] { feature2: ENUM_FEATURE_VALUE3 } + } + } + value { name: "TOP_UNKNOWN", number: 0 } + value { name: "TOP_ONE", number: 1 } + } + message_type { + name: "MyMessage" + options { + features { + [swift_feature_test.test] { feature3: ENUM_FEATURE_VALUE4 } + } + } + enum_type { + name: "Nested" + options { + features { + [swift_feature_test.test] { feature4: ENUM_FEATURE_VALUE5 } + } + } + value { name: "NESTED_UNKNOWN", number: 0 } + value { name: "NESTED_ONE", number: 1 } + } + } + """ + ) + + let topFeatures = context.file.enums.first!.features + XCTAssertTrue(topFeatures.hasSwiftFeatureTest_test) + XCTAssertEqual(topFeatures.SwiftFeatureTest_test.feature1, .value2) // File override + XCTAssertEqual(topFeatures.SwiftFeatureTest_test.feature2, .value3) // "Top" Enum override + let nestedFeatures = context.file.messages.first!.enums.first!.features + XCTAssertTrue(nestedFeatures.hasSwiftFeatureTest_test) + XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature1, .value2) // File override + XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature2, .value1) // default ("Top" Enum override) + XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature3, .value4) // Message override + XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature4, .value5) // "Nested" Enum override + } + + func testEnumValueLevel_Defaults() throws { + let context = TestContext( + """ + name: "test.proto" + edition: EDITION_2023 + enum_type { + name: "Top" + value { name: "TOP_UNKNOWN", number: 0 } + value { name: "TOP_ONE", number: 1 } + } + """ + ) + + let features = context.file.enums.first!.values.first!.features + XCTAssertTrue(features.hasSwiftFeatureTest_test) + XCTAssertEqual(features.SwiftFeatureTest_test.feature1, .value1) + XCTAssertEqual(features.SwiftFeatureTest_test.feature2, .value1) + XCTAssertEqual(features.SwiftFeatureTest_test.feature3, .value1) + } + + func testEnumValueLevel_Override() throws { + let context = TestContext( + """ + name: "test.proto" + edition: EDITION_2023 + options { + features { + [swift_feature_test.test] { feature1: ENUM_FEATURE_VALUE2 } + } + } + enum_type { + name: "MyEnum" + options { + features { + [swift_feature_test.test] { feature2: ENUM_FEATURE_VALUE3 } + } + } + value { + name: "TOP_UNKNOWN" + options { + features { + [swift_feature_test.test] { feature3: ENUM_FEATURE_VALUE4 } + } + } + number: 0 + } + value { name: "TOP_ONE", number: 1 } + } + """ + ) + + let features = context.file.enums.first!.values.first!.features + XCTAssertTrue(features.hasSwiftFeatureTest_test) + XCTAssertEqual(features.SwiftFeatureTest_test.feature1, .value2) // File override + XCTAssertEqual(features.SwiftFeatureTest_test.feature2, .value3) // Enum override + XCTAssertEqual(features.SwiftFeatureTest_test.feature3, .value4) // EnumValue override + } + + func testExtensionRangeLevel_Defaults() throws { + let context = TestContext( + """ + name: "test.proto" + edition: EDITION_2023 + message_type { + name: "MyMessage" + extension_range { start: 1, end: 100 } + } + """ + ) + + let features = context.file.messages.first!.messageExtensionRanges.first!.features + XCTAssertTrue(features.hasSwiftFeatureTest_test) + XCTAssertEqual(features.SwiftFeatureTest_test.feature1, .value1) + XCTAssertEqual(features.SwiftFeatureTest_test.feature2, .value1) + XCTAssertEqual(features.SwiftFeatureTest_test.feature3, .value1) + } + + func testExtensionRangeLevel_Override() throws { + let context = TestContext( + """ + name: "test.proto" + edition: EDITION_2023 + options { + features { + [swift_feature_test.test] { feature1: ENUM_FEATURE_VALUE2 } + } + } + message_type { + name: "MyMessage" + options { + features { + [swift_feature_test.test] { feature2: ENUM_FEATURE_VALUE3 } + } + } + extension_range { + start: 1 + end: 100 + options { + features { + [swift_feature_test.test] { feature3: ENUM_FEATURE_VALUE4 } + } + } + } + } + """ + ) + + let features = context.file.messages.first!.messageExtensionRanges.first!.features + XCTAssertTrue(features.hasSwiftFeatureTest_test) + XCTAssertEqual(features.SwiftFeatureTest_test.feature1, .value2) // File override + XCTAssertEqual(features.SwiftFeatureTest_test.feature2, .value3) // Message override + XCTAssertEqual(features.SwiftFeatureTest_test.feature3, .value4) // ExtensionRange override + } + + func testExtensionLevel_Defaults() throws { + let context = TestContext( + """ + name: "test.proto" + edition: EDITION_2023 + message_type { + name: "MyMessage" + extension_range { start: 1, end: 100 } + extension { + name: "nested" + json_name: "nested" + number: 2 + type: TYPE_STRING + extendee: ".MyMessage" + } + } + extension { + name: "top" + json_name: "top" + number: 1 + type: TYPE_STRING + extendee: ".MyMessage" + } + """ + ) + + let topFeatures = context.file.extensions.first!.features + XCTAssertTrue(topFeatures.hasSwiftFeatureTest_test) + XCTAssertEqual(topFeatures.SwiftFeatureTest_test.feature1, .value1) + XCTAssertEqual(topFeatures.SwiftFeatureTest_test.feature2, .value1) + let nestedFeatures = context.file.messages.first!.extensions.first!.features + XCTAssertTrue(nestedFeatures.hasSwiftFeatureTest_test) + XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature1, .value1) + XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature2, .value1) + XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature3, .value1) + XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature4, .value1) + } + + func testExtensionLevel_Override() throws { + let context = TestContext( + """ + name: "test.proto" + edition: EDITION_2023 + options { + features { + [swift_feature_test.test] { feature1: ENUM_FEATURE_VALUE2 } + } + } + message_type { + name: "MyMessage" + options { + features { + [swift_feature_test.test] { feature3: ENUM_FEATURE_VALUE4 } + } + } + extension_range { start: 1, end: 100 } + extension { + name: "nested" + json_name: "nested" + number: 2 + type: TYPE_STRING + extendee: ".MyMessage" + options { + features { + [swift_feature_test.test] { feature4: ENUM_FEATURE_VALUE5 } + } + } + } + } + extension { + name: "top" + json_name: "top" + number: 1 + type: TYPE_STRING + extendee: ".MyMessage" + options { + features { + [swift_feature_test.test] { feature2: ENUM_FEATURE_VALUE3 } + } + } + } + """ + ) + + let topFeatures = context.file.extensions.first!.features + XCTAssertTrue(topFeatures.hasSwiftFeatureTest_test) + XCTAssertEqual(topFeatures.SwiftFeatureTest_test.feature1, .value2) // File override + XCTAssertEqual(topFeatures.SwiftFeatureTest_test.feature2, .value3) // "top" Extension override + let nestedFeatures = context.file.messages.first!.extensions.first!.features + XCTAssertTrue(nestedFeatures.hasSwiftFeatureTest_test) + XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature1, .value2) // File override + XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature2, .value1) // default ("top" Extension override) + XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature3, .value4) // Message override + XCTAssertEqual(nestedFeatures.SwiftFeatureTest_test.feature4, .value5) // "nested" Extension override + } + + func testMessageFieldLevel_Defaults() throws { + let context = TestContext( + """ + name: "test.proto" + edition: EDITION_2023 + message_type { + name: "MyMessage" + field { + name: "field" + json_name: "field" + number: 1 + type: TYPE_STRING + } + } + """ + ) + + let features = context.file.messages.first!.fields.first!.features + XCTAssertTrue(features.hasSwiftFeatureTest_test) + XCTAssertEqual(features.SwiftFeatureTest_test.feature1, .value1) + XCTAssertEqual(features.SwiftFeatureTest_test.feature2, .value1) + XCTAssertEqual(features.SwiftFeatureTest_test.feature3, .value1) + } + + func testMessageFieldLevel_Override() throws { + let context = TestContext( + """ + name: "test.proto" + edition: EDITION_2023 + options { + features { + [swift_feature_test.test] { feature1: ENUM_FEATURE_VALUE2 } + } + } + message_type { + name: "MyMessage" + options { + features { + [swift_feature_test.test] { feature2: ENUM_FEATURE_VALUE3 } + } + } + field { + name: "field" + json_name: "field" + number: 1 + type: TYPE_STRING + options { + features { + [swift_feature_test.test] { feature3: ENUM_FEATURE_VALUE4 } + } + } + } + } + """ + ) + + let features = context.file.messages.first!.fields.first!.features + XCTAssertTrue(features.hasSwiftFeatureTest_test) + XCTAssertEqual(features.SwiftFeatureTest_test.feature1, .value2) // File override + XCTAssertEqual(features.SwiftFeatureTest_test.feature2, .value3) // Message override + XCTAssertEqual(features.SwiftFeatureTest_test.feature3, .value4) // Field override + } + + func testMessageOneofFieldLevel_Defaults() throws { + let context = TestContext( + """ + name: "test.proto" + edition: EDITION_2023 + message_type { + name: "MyMessage" + oneof_decl { name: "my_oneof" } + field { + name: "oneof_field" + json_name: "oneof_field" + number: 1 + type: TYPE_STRING + oneof_index: 0 + } + field { + name: "not_oneof_field" + json_name: "not_oneof_field" + number: 2 + type: TYPE_STRING + } + } + """ + ) + + let oneof = context.file.messages.first!.realOneofs.first! + XCTAssertEqual(oneof.name, "my_oneof") + let oneofFeatures = oneof.features + XCTAssertTrue(oneofFeatures.hasSwiftFeatureTest_test) + XCTAssertEqual(oneofFeatures.SwiftFeatureTest_test.feature1, .value1) + XCTAssertEqual(oneofFeatures.SwiftFeatureTest_test.feature2, .value1) + XCTAssertEqual(oneofFeatures.SwiftFeatureTest_test.feature3, .value1) + let oneofField = context.file.messages.first!.fields.first! + XCTAssertEqual(oneofField.name, "oneof_field") + let oneofFieldFeatures = oneofField.features + XCTAssertTrue(oneofFieldFeatures.hasSwiftFeatureTest_test) + XCTAssertEqual(oneofFieldFeatures.SwiftFeatureTest_test.feature1, .value1) + XCTAssertEqual(oneofFieldFeatures.SwiftFeatureTest_test.feature2, .value1) + XCTAssertEqual(oneofFieldFeatures.SwiftFeatureTest_test.feature3, .value1) + XCTAssertEqual(oneofFieldFeatures.SwiftFeatureTest_test.feature4, .value1) + XCTAssertEqual(oneofFieldFeatures.SwiftFeatureTest_test.feature5, .value1) + let field = context.file.messages.first!.fields[1] + XCTAssertEqual(field.name, "not_oneof_field") + let fieldFeatures = field.features + XCTAssertTrue(fieldFeatures.hasSwiftFeatureTest_test) + XCTAssertEqual(fieldFeatures.SwiftFeatureTest_test.feature1, .value1) + XCTAssertEqual(fieldFeatures.SwiftFeatureTest_test.feature2, .value1) + XCTAssertEqual(fieldFeatures.SwiftFeatureTest_test.feature3, .value1) + XCTAssertEqual(fieldFeatures.SwiftFeatureTest_test.feature4, .value1) + XCTAssertEqual(fieldFeatures.SwiftFeatureTest_test.feature5, .value1) + } + + func testMessageOneofFieldLevel_Override() throws { + let context = TestContext( + """ + name: "test.proto" + edition: EDITION_2023 + options { + features { + [swift_feature_test.test] { feature1: ENUM_FEATURE_VALUE2 } + } + } + message_type { + name: "MyMessage" + options { + features { + [swift_feature_test.test] { feature2: ENUM_FEATURE_VALUE3 } + } + } + oneof_decl { + name: "my_oneof" + options { + features { + [swift_feature_test.test] { feature3: ENUM_FEATURE_VALUE4 } + } + } + } + field { + name: "oneof_field" + json_name: "oneof_field" + number: 1 + type: TYPE_STRING + oneof_index: 0 + options { + features { + [swift_feature_test.test] { feature4: ENUM_FEATURE_VALUE5 } + } + } + } + field { + name: "not_oneof_field" + json_name: "not_oneof_field" + number: 2 + type: TYPE_STRING + options { + features { + [swift_feature_test.test] { feature5: ENUM_FEATURE_VALUE6 } + } + } + } + } + """ + ) + + let oneof = context.file.messages.first!.realOneofs.first! + XCTAssertEqual(oneof.name, "my_oneof") + let oneofFeatures = oneof.features + XCTAssertTrue(oneofFeatures.hasSwiftFeatureTest_test) + XCTAssertEqual(oneofFeatures.SwiftFeatureTest_test.feature1, .value2) // File override + XCTAssertEqual(oneofFeatures.SwiftFeatureTest_test.feature2, .value3) // Message override + XCTAssertEqual(oneofFeatures.SwiftFeatureTest_test.feature3, .value4) // Oneof override + let oneofField = context.file.messages.first!.fields.first! + XCTAssertEqual(oneofField.name, "oneof_field") + let oneofFieldFeatures = oneofField.features + XCTAssertTrue(oneofFieldFeatures.hasSwiftFeatureTest_test) + XCTAssertEqual(oneofFieldFeatures.SwiftFeatureTest_test.feature1, .value2) // File override + XCTAssertEqual(oneofFieldFeatures.SwiftFeatureTest_test.feature2, .value3) // Message override + XCTAssertEqual(oneofFieldFeatures.SwiftFeatureTest_test.feature3, .value4) // Oneof override + XCTAssertEqual(oneofFieldFeatures.SwiftFeatureTest_test.feature4, .value5) // "oneof_field" Field override + XCTAssertEqual(oneofFieldFeatures.SwiftFeatureTest_test.feature5, .value1) // default ("not_oneof_field" Field override) + let field = context.file.messages.first!.fields[1] + XCTAssertEqual(field.name, "not_oneof_field") + let fieldFeatures = field.features + XCTAssertTrue(fieldFeatures.hasSwiftFeatureTest_test) + XCTAssertEqual(fieldFeatures.SwiftFeatureTest_test.feature1, .value2) // File override + XCTAssertEqual(fieldFeatures.SwiftFeatureTest_test.feature2, .value3) // Message override + XCTAssertEqual(fieldFeatures.SwiftFeatureTest_test.feature3, .value1) // default (Oneof override) + XCTAssertEqual(fieldFeatures.SwiftFeatureTest_test.feature4, .value1) // default ("oneof_field" Field override) + XCTAssertEqual(fieldFeatures.SwiftFeatureTest_test.feature5, .value6) // "not_oneof_field" Field override + } + + func testServiceLevel_Defaults() throws { + let context = TestContext( + """ + name: "test.proto" + edition: EDITION_2023 + service { + name: "MyService" + } + """ + ) + + let features = context.file.services.first!.features + XCTAssertTrue(features.hasSwiftFeatureTest_test) + XCTAssertEqual(features.SwiftFeatureTest_test.feature1, .value1) + XCTAssertEqual(features.SwiftFeatureTest_test.feature2, .value1) + } + + func testServiceLevel_Overrides() throws { + let context = TestContext( + """ + name: "test.proto" + edition: EDITION_2023 + options { + features { + [swift_feature_test.test] { feature1: ENUM_FEATURE_VALUE3 } + } + } + service { + name: "MyService" + options { + features { + [swift_feature_test.test] { feature2: ENUM_FEATURE_VALUE4 } + } + } + } + """ + ) + + let features = context.file.services.first!.features + XCTAssertTrue(features.hasSwiftFeatureTest_test) + XCTAssertEqual(features.SwiftFeatureTest_test.feature1, .value3) // File override + XCTAssertEqual(features.SwiftFeatureTest_test.feature2, .value4) // Service override + } + + func testMethodLevel_Defaults() throws { + let context = TestContext( + """ + name: "test.proto" + edition: EDITION_2023 + message_type { name: "empty" } + service { + name: "MyService" + method { name: "doSomething" input_type: ".empty" output_type: ".empty" } + } + """ + ) + + let features = context.file.services.first!.methods.first!.features + XCTAssertTrue(features.hasSwiftFeatureTest_test) + XCTAssertEqual(features.SwiftFeatureTest_test.feature1, .value1) + XCTAssertEqual(features.SwiftFeatureTest_test.feature2, .value1) + XCTAssertEqual(features.SwiftFeatureTest_test.feature3, .value1) + } + + func testMethodLevel_Overrides() throws { + let context = TestContext( + """ + name: "test.proto" + edition: EDITION_2023 + options { + features { + [swift_feature_test.test] { feature1: ENUM_FEATURE_VALUE3 } + } + } + message_type { name: "empty" } + service { + name: "MyService" + options { + features { + [swift_feature_test.test] { feature2: ENUM_FEATURE_VALUE4 } + } + } + method { + name: "doSomething" + input_type: ".empty" + output_type: ".empty" + options { + features { + [swift_feature_test.test] { feature3: ENUM_FEATURE_VALUE5 } + } + } + } + } + """ + ) + + let features = context.file.services.first!.methods.first!.features + XCTAssertTrue(features.hasSwiftFeatureTest_test) + XCTAssertEqual(features.SwiftFeatureTest_test.feature1, .value3) // File override + XCTAssertEqual(features.SwiftFeatureTest_test.feature2, .value4) // Service override + XCTAssertEqual(features.SwiftFeatureTest_test.feature3, .value5) // Method override + } } diff --git a/Tests/SwiftProtobufPluginLibraryTests/Test_FeatureResolver.swift b/Tests/SwiftProtobufPluginLibraryTests/Test_FeatureResolver.swift index aa2fc42c4..a29425aa1 100644 --- a/Tests/SwiftProtobufPluginLibraryTests/Test_FeatureResolver.swift +++ b/Tests/SwiftProtobufPluginLibraryTests/Test_FeatureResolver.swift @@ -8,230 +8,277 @@ // // ----------------------------------------------------------------------------- -import XCTest import SwiftProtobuf +import XCTest + @testable import SwiftProtobufPluginLibrary final class Test_FeatureResolver: XCTestCase { - private func simpleResolver(extensions: [any AnyMessageExtension] = []) -> FeatureResolver { - let defaults = try! Google_Protobuf_FeatureSetDefaults(textFormatString: """ - minimum_edition: EDITION_PROTO2 - maximum_edition: EDITION_2023 - defaults { edition: EDITION_2023 } - """) - return try! FeatureResolver(edition: .edition2023, - featureSetDefaults: defaults, - featureExtensions: extensions) - } - - func testInit_EditionBelowMin() { - let defaults = Google_Protobuf_FeatureSetDefaults.with { - $0.minimumEdition = .proto3 - $0.maximumEdition = .edition2023 - } - XCTAssertThrowsError(try FeatureResolver(edition: .proto2, featureSetDefaults: defaults)) { e in - XCTAssertEqual(e as! FeatureResolver.Error, - FeatureResolver.Error.unsupported(edition: .proto2, - supported: Google_Protobuf_Edition.proto3...Google_Protobuf_Edition.edition2023)) + private func simpleResolver(extensions: [any AnyMessageExtension] = []) -> FeatureResolver { + let defaults = try! Google_Protobuf_FeatureSetDefaults( + textFormatString: """ + minimum_edition: EDITION_PROTO2 + maximum_edition: EDITION_2023 + defaults { edition: EDITION_2023 } + """ + ) + return try! FeatureResolver( + edition: .edition2023, + featureSetDefaults: defaults, + featureExtensions: extensions + ) } - } - func testInit_EditionAboveMax() { - let defaults = Google_Protobuf_FeatureSetDefaults.with { - $0.minimumEdition = .proto2 - $0.maximumEdition = .proto3 + func testInit_EditionBelowMin() { + let defaults = Google_Protobuf_FeatureSetDefaults.with { + $0.minimumEdition = .proto3 + $0.maximumEdition = .edition2023 + } + XCTAssertThrowsError(try FeatureResolver(edition: .proto2, featureSetDefaults: defaults)) { e in + XCTAssertEqual( + e as! FeatureResolver.Error, + FeatureResolver.Error.unsupported( + edition: .proto2, + supported: Google_Protobuf_Edition.proto3...Google_Protobuf_Edition.edition2023 + ) + ) + } } - XCTAssertThrowsError(try FeatureResolver(edition: .edition2023, featureSetDefaults: defaults)) { e in - XCTAssertEqual(e as! FeatureResolver.Error, - FeatureResolver.Error.unsupported(edition: .edition2023, - supported: Google_Protobuf_Edition.proto2...Google_Protobuf_Edition.proto3)) - } - } - - func testInit_EditionDefaultNotFound() { - let defaults = try! Google_Protobuf_FeatureSetDefaults(textFormatString: """ - minimum_edition: EDITION_PROTO2 - maximum_edition: EDITION_2023 - defaults { edition: EDITION_2023 } - """) - XCTAssertThrowsError(try FeatureResolver(edition: .proto2, featureSetDefaults: defaults)) { e in - XCTAssertEqual(e as! FeatureResolver.Error, - FeatureResolver.Error.noDefault(edition: .proto2)) - } - } - - func testInit_EditionExactMatches() throws { - let defaults = try! Google_Protobuf_FeatureSetDefaults(textFormatString: """ - minimum_edition: EDITION_99997_TEST_ONLY - maximum_edition: EDITION_99999_TEST_ONLY - defaults { - edition: EDITION_99997_TEST_ONLY - overridable_features { field_presence: EXPLICIT} - } - defaults { - edition: EDITION_99998_TEST_ONLY - overridable_features { field_presence: IMPLICIT} - } - defaults { - edition: EDITION_99999_TEST_ONLY - overridable_features { field_presence: LEGACY_REQUIRED} - } - """) - - // Ensure the right things were matched and we got the right feature sets. - - // If lookup fails, throw out of the test method. - - // edition99997TestOnly - let resolver1: FeatureResolver = try FeatureResolver(edition: .edition99997TestOnly, - featureSetDefaults: defaults) - XCTAssertEqual(resolver1.edition, .edition99997TestOnly) - XCTAssertEqual(resolver1.defaultFeatureSet.fieldPresence, .explicit) - - // edition99998TestOnly - let resolver2 = try FeatureResolver(edition: .edition99998TestOnly, - featureSetDefaults: defaults) - XCTAssertEqual(resolver2.edition, .edition99998TestOnly) - XCTAssertEqual(resolver2.defaultFeatureSet.fieldPresence, .implicit) - - // edition99999TestOnly - let resolver3 = try FeatureResolver(edition: .edition99999TestOnly, - featureSetDefaults: defaults) - XCTAssertEqual(resolver3.edition, .edition99999TestOnly) - XCTAssertEqual(resolver3.defaultFeatureSet.fieldPresence, .legacyRequired) - } - - func testInit_EditionMatchesLower() throws { - let defaults = try! Google_Protobuf_FeatureSetDefaults(textFormatString: """ - minimum_edition: EDITION_99997_TEST_ONLY - maximum_edition: EDITION_99999_TEST_ONLY - defaults { - edition: EDITION_99997_TEST_ONLY - overridable_features { field_presence: EXPLICIT} - } - defaults { - edition: EDITION_99999_TEST_ONLY - overridable_features { field_presence: LEGACY_REQUIRED} - } - """) - - // Ensure the right things were matched and we got the right feature sets. - - // If lookup fails, throw out of the test method. - - // edition99997TestOnly - let resolver1: FeatureResolver = try FeatureResolver(edition: .edition99997TestOnly, - featureSetDefaults: defaults) - XCTAssertEqual(resolver1.edition, .edition99997TestOnly) - XCTAssertEqual(resolver1.defaultFeatureSet.fieldPresence, .explicit) - - // edition99998TestOnly - // The edition will says 99998 since that's what we requested, but the - // defaults will have what was in 99997 - let resolver2 = try FeatureResolver(edition: .edition99998TestOnly, - featureSetDefaults: defaults) - XCTAssertEqual(resolver2.edition, .edition99998TestOnly) - XCTAssertEqual(resolver2.defaultFeatureSet.fieldPresence, .explicit) - - // edition99999TestOnly - let resolver3 = try FeatureResolver(edition: .edition99999TestOnly, - featureSetDefaults: defaults) - XCTAssertEqual(resolver3.edition, .edition99999TestOnly) - XCTAssertEqual(resolver3.defaultFeatureSet.fieldPresence, .legacyRequired) - } - - func testInit_BadExtension() throws { - let defaults = try! Google_Protobuf_FeatureSetDefaults(textFormatString: """ - minimum_edition: EDITION_PROTO2 - maximum_edition: EDITION_2023 - defaults { edition: EDITION_PROTO2 } - """) - - XCTAssertThrowsError(try FeatureResolver(edition: .proto2, - featureSetDefaults: defaults, - featureExtensions: [SwiftFeatureTest_Extensions_test, - SDTExtensions_ext_str])) { e in - XCTAssertEqual(e as! FeatureResolver.Error, - FeatureResolver.Error.invalidExtension(type: "swift_descriptor_test.import.ExtendableOne")) + + func testInit_EditionAboveMax() { + let defaults = Google_Protobuf_FeatureSetDefaults.with { + $0.minimumEdition = .proto2 + $0.maximumEdition = .proto3 + } + XCTAssertThrowsError(try FeatureResolver(edition: .edition2023, featureSetDefaults: defaults)) { e in + XCTAssertEqual( + e as! FeatureResolver.Error, + FeatureResolver.Error.unsupported( + edition: .edition2023, + supported: Google_Protobuf_Edition.proto2...Google_Protobuf_Edition.proto3 + ) + ) + } } - } - - func testInit_mergingFixedOverridable() throws { - let defaults = try! Google_Protobuf_FeatureSetDefaults(textFormatString: """ - minimum_edition: EDITION_99997_TEST_ONLY - maximum_edition: EDITION_99999_TEST_ONLY - defaults { - edition: EDITION_99997_TEST_ONLY - overridable_features { field_presence: EXPLICIT } - fixed_features { enum_type: CLOSED } - } - """) - - // Test that fixed and overridable merge - - // If lookup fails, throw out of the test method. - - let resolver1: FeatureResolver = try FeatureResolver(edition: .edition99997TestOnly, - featureSetDefaults: defaults) - XCTAssertEqual(resolver1.edition, .edition99997TestOnly) - XCTAssertEqual(resolver1.defaultFeatureSet.fieldPresence, .explicit) - XCTAssertEqual(resolver1.defaultFeatureSet.enumType, .closed) - } - - func testResolve_Basics() { - let resolver = simpleResolver() - - let features1 = Google_Protobuf_FeatureSet.with { - $0.fieldPresence = .explicit - $0.messageEncoding = .lengthPrefixed + + func testInit_EditionDefaultNotFound() { + let defaults = try! Google_Protobuf_FeatureSetDefaults( + textFormatString: """ + minimum_edition: EDITION_PROTO2 + maximum_edition: EDITION_2023 + defaults { edition: EDITION_2023 } + """ + ) + XCTAssertThrowsError(try FeatureResolver(edition: .proto2, featureSetDefaults: defaults)) { e in + XCTAssertEqual( + e as! FeatureResolver.Error, + FeatureResolver.Error.noDefault(edition: .proto2) + ) + } } - let features2 = Google_Protobuf_FeatureSet.with { - $0.enumType = .open + + func testInit_EditionExactMatches() throws { + let defaults = try! Google_Protobuf_FeatureSetDefaults( + textFormatString: """ + minimum_edition: EDITION_99997_TEST_ONLY + maximum_edition: EDITION_99999_TEST_ONLY + defaults { + edition: EDITION_99997_TEST_ONLY + overridable_features { field_presence: EXPLICIT} + } + defaults { + edition: EDITION_99998_TEST_ONLY + overridable_features { field_presence: IMPLICIT} + } + defaults { + edition: EDITION_99999_TEST_ONLY + overridable_features { field_presence: LEGACY_REQUIRED} + } + """ + ) + + // Ensure the right things were matched and we got the right feature sets. + + // If lookup fails, throw out of the test method. + + // edition99997TestOnly + let resolver1: FeatureResolver = try FeatureResolver( + edition: .edition99997TestOnly, + featureSetDefaults: defaults + ) + XCTAssertEqual(resolver1.edition, .edition99997TestOnly) + XCTAssertEqual(resolver1.defaultFeatureSet.fieldPresence, .explicit) + + // edition99998TestOnly + let resolver2 = try FeatureResolver( + edition: .edition99998TestOnly, + featureSetDefaults: defaults + ) + XCTAssertEqual(resolver2.edition, .edition99998TestOnly) + XCTAssertEqual(resolver2.defaultFeatureSet.fieldPresence, .implicit) + + // edition99999TestOnly + let resolver3 = try FeatureResolver( + edition: .edition99999TestOnly, + featureSetDefaults: defaults + ) + XCTAssertEqual(resolver3.edition, .edition99999TestOnly) + XCTAssertEqual(resolver3.defaultFeatureSet.fieldPresence, .legacyRequired) } - let features3 = Google_Protobuf_FeatureSet.with { - $0.fieldPresence = .legacyRequired - $0.jsonFormat = .legacyBestEffort + + func testInit_EditionMatchesLower() throws { + let defaults = try! Google_Protobuf_FeatureSetDefaults( + textFormatString: """ + minimum_edition: EDITION_99997_TEST_ONLY + maximum_edition: EDITION_99999_TEST_ONLY + defaults { + edition: EDITION_99997_TEST_ONLY + overridable_features { field_presence: EXPLICIT} + } + defaults { + edition: EDITION_99999_TEST_ONLY + overridable_features { field_presence: LEGACY_REQUIRED} + } + """ + ) + + // Ensure the right things were matched and we got the right feature sets. + + // If lookup fails, throw out of the test method. + + // edition99997TestOnly + let resolver1: FeatureResolver = try FeatureResolver( + edition: .edition99997TestOnly, + featureSetDefaults: defaults + ) + XCTAssertEqual(resolver1.edition, .edition99997TestOnly) + XCTAssertEqual(resolver1.defaultFeatureSet.fieldPresence, .explicit) + + // edition99998TestOnly + // The edition will says 99998 since that's what we requested, but the + // defaults will have what was in 99997 + let resolver2 = try FeatureResolver( + edition: .edition99998TestOnly, + featureSetDefaults: defaults + ) + XCTAssertEqual(resolver2.edition, .edition99998TestOnly) + XCTAssertEqual(resolver2.defaultFeatureSet.fieldPresence, .explicit) + + // edition99999TestOnly + let resolver3 = try FeatureResolver( + edition: .edition99999TestOnly, + featureSetDefaults: defaults + ) + XCTAssertEqual(resolver3.edition, .edition99999TestOnly) + XCTAssertEqual(resolver3.defaultFeatureSet.fieldPresence, .legacyRequired) } - // No overlap - let merged12 = resolver.resolve(features: features1, resolvedParent: features2) - XCTAssertEqual(merged12.fieldPresence, .explicit) - XCTAssertEqual(merged12.enumType, .open) - XCTAssertEqual(merged12.messageEncoding, .lengthPrefixed) - - // Overlap, features overrides parent features - let merged13 = resolver.resolve(features: features1, resolvedParent: features3) - XCTAssertEqual(merged13.fieldPresence, .explicit) - XCTAssertEqual(merged13.jsonFormat, .legacyBestEffort) - XCTAssertEqual(merged13.messageEncoding, .lengthPrefixed) - } - - func testResolve_CustomFeature() { - let resolver = simpleResolver(extensions: [SwiftFeatureTest_Extensions_test]) - - let features1 = Google_Protobuf_FeatureSet.with { - $0.SwiftFeatureTest_test.feature1 = .value2 - $0.SwiftFeatureTest_test.feature2 = .value2 + func testInit_BadExtension() throws { + let defaults = try! Google_Protobuf_FeatureSetDefaults( + textFormatString: """ + minimum_edition: EDITION_PROTO2 + maximum_edition: EDITION_2023 + defaults { edition: EDITION_PROTO2 } + """ + ) + + XCTAssertThrowsError( + try FeatureResolver( + edition: .proto2, + featureSetDefaults: defaults, + featureExtensions: [ + SwiftFeatureTest_Extensions_test, + SDTExtensions_ext_str, + ] + ) + ) { e in + XCTAssertEqual( + e as! FeatureResolver.Error, + FeatureResolver.Error.invalidExtension(type: "swift_descriptor_test.import.ExtendableOne") + ) + } } - let features2 = Google_Protobuf_FeatureSet.with { - $0.SwiftFeatureTest_test.feature3 = .value3 + + func testInit_mergingFixedOverridable() throws { + let defaults = try! Google_Protobuf_FeatureSetDefaults( + textFormatString: """ + minimum_edition: EDITION_99997_TEST_ONLY + maximum_edition: EDITION_99999_TEST_ONLY + defaults { + edition: EDITION_99997_TEST_ONLY + overridable_features { field_presence: EXPLICIT } + fixed_features { enum_type: CLOSED } + } + """ + ) + + // Test that fixed and overridable merge + + // If lookup fails, throw out of the test method. + + let resolver1: FeatureResolver = try FeatureResolver( + edition: .edition99997TestOnly, + featureSetDefaults: defaults + ) + XCTAssertEqual(resolver1.edition, .edition99997TestOnly) + XCTAssertEqual(resolver1.defaultFeatureSet.fieldPresence, .explicit) + XCTAssertEqual(resolver1.defaultFeatureSet.enumType, .closed) } - let features3 = Google_Protobuf_FeatureSet.with { - $0.SwiftFeatureTest_test.feature1 = .value4 - $0.SwiftFeatureTest_test.feature3 = .value4 + + func testResolve_Basics() { + let resolver = simpleResolver() + + let features1 = Google_Protobuf_FeatureSet.with { + $0.fieldPresence = .explicit + $0.messageEncoding = .lengthPrefixed + } + let features2 = Google_Protobuf_FeatureSet.with { + $0.enumType = .open + } + let features3 = Google_Protobuf_FeatureSet.with { + $0.fieldPresence = .legacyRequired + $0.jsonFormat = .legacyBestEffort + } + + // No overlap + let merged12 = resolver.resolve(features: features1, resolvedParent: features2) + XCTAssertEqual(merged12.fieldPresence, .explicit) + XCTAssertEqual(merged12.enumType, .open) + XCTAssertEqual(merged12.messageEncoding, .lengthPrefixed) + + // Overlap, features overrides parent features + let merged13 = resolver.resolve(features: features1, resolvedParent: features3) + XCTAssertEqual(merged13.fieldPresence, .explicit) + XCTAssertEqual(merged13.jsonFormat, .legacyBestEffort) + XCTAssertEqual(merged13.messageEncoding, .lengthPrefixed) } - // No overlap - let merged12 = resolver.resolve(features: features1, resolvedParent: features2) - XCTAssertEqual(merged12.SwiftFeatureTest_test.feature1, .value2) - XCTAssertEqual(merged12.SwiftFeatureTest_test.feature2, .value2) - XCTAssertEqual(merged12.SwiftFeatureTest_test.feature3, .value3) - - // Overlap, features overrides parent features - let merged13 = resolver.resolve(features: features1, resolvedParent: features3) - XCTAssertEqual(merged13.SwiftFeatureTest_test.feature1, .value2) - XCTAssertEqual(merged13.SwiftFeatureTest_test.feature2, .value2) - XCTAssertEqual(merged13.SwiftFeatureTest_test.feature3, .value4) - } + func testResolve_CustomFeature() { + let resolver = simpleResolver(extensions: [SwiftFeatureTest_Extensions_test]) + + let features1 = Google_Protobuf_FeatureSet.with { + $0.SwiftFeatureTest_test.feature1 = .value2 + $0.SwiftFeatureTest_test.feature2 = .value2 + } + let features2 = Google_Protobuf_FeatureSet.with { + $0.SwiftFeatureTest_test.feature3 = .value3 + } + let features3 = Google_Protobuf_FeatureSet.with { + $0.SwiftFeatureTest_test.feature1 = .value4 + $0.SwiftFeatureTest_test.feature3 = .value4 + } + + // No overlap + let merged12 = resolver.resolve(features: features1, resolvedParent: features2) + XCTAssertEqual(merged12.SwiftFeatureTest_test.feature1, .value2) + XCTAssertEqual(merged12.SwiftFeatureTest_test.feature2, .value2) + XCTAssertEqual(merged12.SwiftFeatureTest_test.feature3, .value3) + + // Overlap, features overrides parent features + let merged13 = resolver.resolve(features: features1, resolvedParent: features3) + XCTAssertEqual(merged13.SwiftFeatureTest_test.feature1, .value2) + XCTAssertEqual(merged13.SwiftFeatureTest_test.feature2, .value2) + XCTAssertEqual(merged13.SwiftFeatureTest_test.feature3, .value4) + } } diff --git a/Tests/SwiftProtobufPluginLibraryTests/Test_NamingUtils.swift b/Tests/SwiftProtobufPluginLibraryTests/Test_NamingUtils.swift index fbd84d05d..c2adcdbb4 100644 --- a/Tests/SwiftProtobufPluginLibraryTests/Test_NamingUtils.swift +++ b/Tests/SwiftProtobufPluginLibraryTests/Test_NamingUtils.swift @@ -8,357 +8,367 @@ // // ----------------------------------------------------------------------------- -import XCTest import SwiftProtobuf +import XCTest + @testable import SwiftProtobufPluginLibrary final class Test_NamingUtils: XCTestCase { - func testTypePrefix() throws { - // package, swiftPrefix, expected - let tests: [(String, String?, String)] = [ - ( "", nil, "" ), - ( "", "", "" ), - - ( "foo", nil, "Foo_" ), - ( "FOO", nil, "FOO_" ), - ( "fooBar", nil, "FooBar_" ), - ( "FooBar", nil, "FooBar_" ), - - ( "foo.bar.baz", nil, "Foo_Bar_Baz_" ), - ( "foo_bar_baz", nil, "FooBarBaz_" ), - ( "foo.bar_baz", nil, "Foo_BarBaz_" ), - ( "foo_bar__baz", nil, "FooBarBaz_" ), - ( "foo.bar_baz_", nil, "Foo_BarBaz_" ), - ( "foo._bar_baz", nil, "Foo_BarBaz_" ), - - ( "foo.BAR_baz", nil, "Foo_BARBaz_" ), - ( "foo.bar_bAZ", nil, "Foo_BarBAZ_" ), - ( "FOO.BAR_BAZ", nil, "FOO_BARBAZ_" ), - - ( "_foo", nil, "Foo_" ), - ( "__foo", nil, "Foo_" ), - ( "_1foo", nil, "_1foo_" ), - ( "__1foo", nil, "_1foo_" ), - ( "_1foo.2bar._3baz", nil, "_1foo_2bar_3baz_" ), - ( "_1foo_2bar_3baz", nil, "_1foo2bar3baz_" ), - - ( "foo.bar.baz", "", "" ), - ( "", "ABC", "ABC" ), - - ( "foo.bar.baz", "ABC", "ABC" ), - ( "foo.bar.baz", "abc", "abc" ), - ( "foo.bar.baz", "aBc", "aBc" ), - ] - for (package, prefix, expected) in tests { - var proto = Google_Protobuf_FileOptions() - if let prefix = prefix { - proto.swiftPrefix = prefix - } - let result = NamingUtils.typePrefix(protoPackage: package, fileOptions: proto) - XCTAssertEqual(result, expected, "Package: \(package), Prefix: \(prefix ?? "nil")") + func testTypePrefix() throws { + // package, swiftPrefix, expected + let tests: [(String, String?, String)] = [ + ("", nil, ""), + ("", "", ""), + + ("foo", nil, "Foo_"), + ("FOO", nil, "FOO_"), + ("fooBar", nil, "FooBar_"), + ("FooBar", nil, "FooBar_"), + + ("foo.bar.baz", nil, "Foo_Bar_Baz_"), + ("foo_bar_baz", nil, "FooBarBaz_"), + ("foo.bar_baz", nil, "Foo_BarBaz_"), + ("foo_bar__baz", nil, "FooBarBaz_"), + ("foo.bar_baz_", nil, "Foo_BarBaz_"), + ("foo._bar_baz", nil, "Foo_BarBaz_"), + + ("foo.BAR_baz", nil, "Foo_BARBaz_"), + ("foo.bar_bAZ", nil, "Foo_BarBAZ_"), + ("FOO.BAR_BAZ", nil, "FOO_BARBAZ_"), + + ("_foo", nil, "Foo_"), + ("__foo", nil, "Foo_"), + ("_1foo", nil, "_1foo_"), + ("__1foo", nil, "_1foo_"), + ("_1foo.2bar._3baz", nil, "_1foo_2bar_3baz_"), + ("_1foo_2bar_3baz", nil, "_1foo2bar3baz_"), + + ("foo.bar.baz", "", ""), + ("", "ABC", "ABC"), + + ("foo.bar.baz", "ABC", "ABC"), + ("foo.bar.baz", "abc", "abc"), + ("foo.bar.baz", "aBc", "aBc"), + ] + for (package, prefix, expected) in tests { + var proto = Google_Protobuf_FileOptions() + if let prefix = prefix { + proto.swiftPrefix = prefix + } + let result = NamingUtils.typePrefix(protoPackage: package, fileOptions: proto) + XCTAssertEqual(result, expected, "Package: \(package), Prefix: \(prefix ?? "nil")") + } } - } - - func testPrefixStripper_strip() { - // prefix, string, expected - let tests: [(String, String, String?)] = [ - ( "", "", nil ), - - ( "FOO", "FOO", nil ), - ( "fOo", "FOO", nil ), - - ( "foo_", "FOO", nil ), - ( "_foo", "FOO", nil ), - ( "_foo_", "FOO", nil ), - - ( "foo", "FOO_", nil ), - ( "foo", "_FOO", nil ), - ( "foo", "_FOO_", nil ), - - ( "foo_", "FOObar", "bar" ), - ( "_foo", "FOObar", "bar" ), - ( "_foo_", "FOObar", "bar" ), - - ( "foo", "FOO_bar", "bar" ), - ( "foo", "_FOObar", "bar" ), - ( "foo", "_FOO_bar", "bar" ), - - ( "FOO_bar", "foo_BAR_baz", "baz" ), - ( "FooBar", "foo_bar_Baz", "Baz" ), - ( "foo_bar", "foobar_bAZ", "bAZ" ), - ( "_foo_bar", "foobar_bAZ", "bAZ" ), - ( "foo__bar_", "_foo_bar__baz", "baz" ), - - ( "FooBar", "foo_bar_1", nil ), - ( "FooBar", "foo_bar_1foo", nil ), - ( "FooBar", "foo_bar_foo1", "foo1" ), - ] - for (prefix, str, expected) in tests { - let stripper = NamingUtils.PrefixStripper(prefix: prefix) - let result = stripper.strip(from: str) - XCTAssertEqual(result, expected, "Prefix: \(prefix), Input: \(str)") + + func testPrefixStripper_strip() { + // prefix, string, expected + let tests: [(String, String, String?)] = [ + ("", "", nil), + + ("FOO", "FOO", nil), + ("fOo", "FOO", nil), + + ("foo_", "FOO", nil), + ("_foo", "FOO", nil), + ("_foo_", "FOO", nil), + + ("foo", "FOO_", nil), + ("foo", "_FOO", nil), + ("foo", "_FOO_", nil), + + ("foo_", "FOObar", "bar"), + ("_foo", "FOObar", "bar"), + ("_foo_", "FOObar", "bar"), + + ("foo", "FOO_bar", "bar"), + ("foo", "_FOObar", "bar"), + ("foo", "_FOO_bar", "bar"), + + ("FOO_bar", "foo_BAR_baz", "baz"), + ("FooBar", "foo_bar_Baz", "Baz"), + ("foo_bar", "foobar_bAZ", "bAZ"), + ("_foo_bar", "foobar_bAZ", "bAZ"), + ("foo__bar_", "_foo_bar__baz", "baz"), + + ("FooBar", "foo_bar_1", nil), + ("FooBar", "foo_bar_1foo", nil), + ("FooBar", "foo_bar_foo1", "foo1"), + ] + for (prefix, str, expected) in tests { + let stripper = NamingUtils.PrefixStripper(prefix: prefix) + let result = stripper.strip(from: str) + XCTAssertEqual(result, expected, "Prefix: \(prefix), Input: \(str)") + } } - } - - func testSanitize_messageName() { - // input, expected - let tests: [(String, String)] = [ - ( "", "" ), - - ( "Foo", "Foo" ), - ( "FooBar", "FooBar" ), - ( "foo_bar", "foo_bar" ), - - // Some of our names get the disambiguator added. - ( "SwiftProtobuf", "SwiftProtobufMessage" ), - ( "RenamedSwiftProtobuf", "RenamedSwiftProtobufMessage" ), - ( "isInitialized", "isInitializedMessage" ), - - // Some Swift keywords. - ( "associatedtype", "associatedtypeMessage" ), - ( "class", "classMessage" ), - ( "break", "breakMessage" ), - ( "do", "doMessage" ), - - // Inputs with the disambiguator. - ( "classMessage", "classMessageMessage" ), - ( "classMessageMessage", "classMessageMessageMessage" ), - - // Underscores - ( "_", "_Message" ), - ( "___", "___Message" ), - ] - for (input, expected) in tests { - XCTAssertEqual(NamingUtils.sanitize(messageName: input, forbiddenTypeNames: ["RenamedSwiftProtobuf"]), expected) + + func testSanitize_messageName() { + // input, expected + let tests: [(String, String)] = [ + ("", ""), + + ("Foo", "Foo"), + ("FooBar", "FooBar"), + ("foo_bar", "foo_bar"), + + // Some of our names get the disambiguator added. + ("SwiftProtobuf", "SwiftProtobufMessage"), + ("RenamedSwiftProtobuf", "RenamedSwiftProtobufMessage"), + ("isInitialized", "isInitializedMessage"), + + // Some Swift keywords. + ("associatedtype", "associatedtypeMessage"), + ("class", "classMessage"), + ("break", "breakMessage"), + ("do", "doMessage"), + + // Inputs with the disambiguator. + ("classMessage", "classMessageMessage"), + ("classMessageMessage", "classMessageMessageMessage"), + + // Underscores + ("_", "_Message"), + ("___", "___Message"), + ] + for (input, expected) in tests { + XCTAssertEqual( + NamingUtils.sanitize(messageName: input, forbiddenTypeNames: ["RenamedSwiftProtobuf"]), + expected + ) + } } - } - - func testSanitize_enumName() { - // input, expected - let tests: [(String, String)] = [ - ( "", "" ), - - ( "Foo", "Foo" ), - ( "FooBar", "FooBar" ), - ( "foo_bar", "foo_bar" ), - - // Some of our names get the disambiguator added. - ( "SwiftProtobuf", "SwiftProtobufEnum" ), - ( "RenamedSwiftProtobuf", "RenamedSwiftProtobufEnum" ), - ( "isInitialized", "isInitializedEnum" ), - - // Some Swift keywords. - ( "associatedtype", "associatedtypeEnum" ), - ( "class", "classEnum" ), - ( "break", "breakEnum" ), - ( "do", "doEnum" ), - - // Inputs with the disambiguator. - ( "classEnum", "classEnumEnum" ), - ( "classEnumEnum", "classEnumEnumEnum" ), - - // Underscores - ( "_", "_Enum" ), - ( "___", "___Enum" ), - ] - for (input, expected) in tests { - XCTAssertEqual(NamingUtils.sanitize(enumName: input, forbiddenTypeNames: ["RenamedSwiftProtobuf"]), expected) + + func testSanitize_enumName() { + // input, expected + let tests: [(String, String)] = [ + ("", ""), + + ("Foo", "Foo"), + ("FooBar", "FooBar"), + ("foo_bar", "foo_bar"), + + // Some of our names get the disambiguator added. + ("SwiftProtobuf", "SwiftProtobufEnum"), + ("RenamedSwiftProtobuf", "RenamedSwiftProtobufEnum"), + ("isInitialized", "isInitializedEnum"), + + // Some Swift keywords. + ("associatedtype", "associatedtypeEnum"), + ("class", "classEnum"), + ("break", "breakEnum"), + ("do", "doEnum"), + + // Inputs with the disambiguator. + ("classEnum", "classEnumEnum"), + ("classEnumEnum", "classEnumEnumEnum"), + + // Underscores + ("_", "_Enum"), + ("___", "___Enum"), + ] + for (input, expected) in tests { + XCTAssertEqual( + NamingUtils.sanitize(enumName: input, forbiddenTypeNames: ["RenamedSwiftProtobuf"]), + expected + ) + } } - } - - func testSanitize_oneofName() { - // input, expected - let tests: [(String, String)] = [ - ( "", "" ), - - ( "Foo", "Foo" ), - ( "FooBar", "FooBar" ), - ( "foo_bar", "foo_bar" ), - - // Some of our names get the disambiguator added. - ( "RenamedSwiftProtobuf", "RenamedSwiftProtobufOneof" ), - ( "isInitialized", "isInitializedOneof" ), - - // Some Swift keywords. - ( "associatedtype", "associatedtypeOneof" ), - ( "class", "classOneof" ), - ( "break", "breakOneof" ), - ( "do", "doOneof" ), - - // Inputs with the disambiguator. - ( "classOneof", "classOneofOneof" ), - ( "classOneofOneof", "classOneofOneofOneof" ), - - // Underscores - ( "_", "_Oneof" ), - ( "___", "___Oneof" ), - ] - for (input, expected) in tests { - XCTAssertEqual(NamingUtils.sanitize(oneofName: input, forbiddenTypeNames: ["RenamedSwiftProtobuf"]), expected) + + func testSanitize_oneofName() { + // input, expected + let tests: [(String, String)] = [ + ("", ""), + + ("Foo", "Foo"), + ("FooBar", "FooBar"), + ("foo_bar", "foo_bar"), + + // Some of our names get the disambiguator added. + ("RenamedSwiftProtobuf", "RenamedSwiftProtobufOneof"), + ("isInitialized", "isInitializedOneof"), + + // Some Swift keywords. + ("associatedtype", "associatedtypeOneof"), + ("class", "classOneof"), + ("break", "breakOneof"), + ("do", "doOneof"), + + // Inputs with the disambiguator. + ("classOneof", "classOneofOneof"), + ("classOneofOneof", "classOneofOneofOneof"), + + // Underscores + ("_", "_Oneof"), + ("___", "___Oneof"), + ] + for (input, expected) in tests { + XCTAssertEqual( + NamingUtils.sanitize(oneofName: input, forbiddenTypeNames: ["RenamedSwiftProtobuf"]), + expected + ) + } } - } - - func testSanitize_fieldName() { - // input, expected - let tests: [(String, String)] = [ - ( "", "" ), - - ( "Foo", "Foo" ), - ( "FooBar", "FooBar" ), - ( "foo_bar", "foo_bar" ), - - // Some of our names get the disambiguator added. - ( "debugDescription", "debugDescription_p" ), - ( "isInitialized", "isInitialized_p" ), - - // Some Swift keywords. - ( "associatedtype", "`associatedtype`" ), - ( "class", "`class`" ), - ( "break", "`break`" ), - ( "do", "`do`" ), - - // "has"/"clear" get added by us, so they get the disambiguator... - ( "hasFoo", "hasFoo_p" ), - ( "clearFoo", "clearFoo_p" ), - // ...but don't catch words... - ( "hashtag", "hashtag" ), - ( "clearable", "clearable" ), - ( "has911", "has911" ), - // ...or by themselves. - ( "has", "has" ), - ( "clear", "clear" ), - - // Underscores get more underscores. - ( "_", "___" ), - ( "___", "_____" ), - ] - - for (input, expected) in tests { - XCTAssertEqual(NamingUtils.sanitize(fieldName: input), expected) - - let inputPrefixed = "XX" + NamingUtils.uppercaseFirstCharacter(input) - let expected2 = "XX" + NamingUtils.uppercaseFirstCharacter(NamingUtils.trimBackticks(expected)) - XCTAssertEqual(NamingUtils.sanitize(fieldName: inputPrefixed, basedOn: input), expected2) + + func testSanitize_fieldName() { + // input, expected + let tests: [(String, String)] = [ + ("", ""), + + ("Foo", "Foo"), + ("FooBar", "FooBar"), + ("foo_bar", "foo_bar"), + + // Some of our names get the disambiguator added. + ("debugDescription", "debugDescription_p"), + ("isInitialized", "isInitialized_p"), + + // Some Swift keywords. + ("associatedtype", "`associatedtype`"), + ("class", "`class`"), + ("break", "`break`"), + ("do", "`do`"), + + // "has"/"clear" get added by us, so they get the disambiguator... + ("hasFoo", "hasFoo_p"), + ("clearFoo", "clearFoo_p"), + // ...but don't catch words... + ("hashtag", "hashtag"), + ("clearable", "clearable"), + ("has911", "has911"), + // ...or by themselves. + ("has", "has"), + ("clear", "clear"), + + // Underscores get more underscores. + ("_", "___"), + ("___", "_____"), + ] + + for (input, expected) in tests { + XCTAssertEqual(NamingUtils.sanitize(fieldName: input), expected) + + let inputPrefixed = "XX" + NamingUtils.uppercaseFirstCharacter(input) + let expected2 = "XX" + NamingUtils.uppercaseFirstCharacter(NamingUtils.trimBackticks(expected)) + XCTAssertEqual(NamingUtils.sanitize(fieldName: inputPrefixed, basedOn: input), expected2) + } } - } - - func testSanitize_enumCaseName() { - // input, expected - let tests: [(String, String)] = [ - ( "", "" ), - - ( "Foo", "Foo" ), - ( "FooBar", "FooBar" ), - ( "foo_bar", "foo_bar" ), - - // Some of our names get the disambiguator added. - ( "debugDescription", "debugDescription_" ), - ( "dynamicType", "dynamicType_" ), - - // Some Swift keywords work with backticks - ( "associatedtype", "`associatedtype`" ), - ( "class", "`class`" ), - ( "break", "`break`" ), - ( "do", "`do`" ), - - // Underscores get more underscores. - ( "_", "___" ), - ( "___", "_____" ), - ] - - for (input, expected) in tests { - XCTAssertEqual(NamingUtils.sanitize(enumCaseName: input), expected) + + func testSanitize_enumCaseName() { + // input, expected + let tests: [(String, String)] = [ + ("", ""), + + ("Foo", "Foo"), + ("FooBar", "FooBar"), + ("foo_bar", "foo_bar"), + + // Some of our names get the disambiguator added. + ("debugDescription", "debugDescription_"), + ("dynamicType", "dynamicType_"), + + // Some Swift keywords work with backticks + ("associatedtype", "`associatedtype`"), + ("class", "`class`"), + ("break", "`break`"), + ("do", "`do`"), + + // Underscores get more underscores. + ("_", "___"), + ("___", "_____"), + ] + + for (input, expected) in tests { + XCTAssertEqual(NamingUtils.sanitize(enumCaseName: input), expected) + } } - } - - func testSanitize_messageScopedExtensionName() { - // input, expected - let tests: [(String, String)] = [ - ( "", "" ), - - ( "Foo", "Foo" ), - ( "FooBar", "FooBar" ), - ( "foo_bar", "foo_bar" ), - - // Some of our names get the disambiguator added. - ( "debugDescription", "debugDescription_" ), - ( "dynamicType", "dynamicType_" ), - - // Some Swift keywords work with backticks - ( "associatedtype", "`associatedtype`" ), - ( "class", "`class`" ), - ( "break", "`break`" ), - ( "do", "`do`" ), - - // Underscores get more underscores. - ( "_", "___" ), - ( "___", "_____" ), - ] - - for (input, expected) in tests { - XCTAssertEqual(NamingUtils.sanitize(messageScopedExtensionName: input), expected) + + func testSanitize_messageScopedExtensionName() { + // input, expected + let tests: [(String, String)] = [ + ("", ""), + + ("Foo", "Foo"), + ("FooBar", "FooBar"), + ("foo_bar", "foo_bar"), + + // Some of our names get the disambiguator added. + ("debugDescription", "debugDescription_"), + ("dynamicType", "dynamicType_"), + + // Some Swift keywords work with backticks + ("associatedtype", "`associatedtype`"), + ("class", "`class`"), + ("break", "`break`"), + ("do", "`do`"), + + // Underscores get more underscores. + ("_", "___"), + ("___", "_____"), + ] + + for (input, expected) in tests { + XCTAssertEqual(NamingUtils.sanitize(messageScopedExtensionName: input), expected) + } } - } - - func testToCamelCase() { - // input, expectedLower, expectedUpper - let tests: [(String, String, String)] = [ - ( "", "", "" ), - - ( "foo", "foo", "Foo" ), - ( "FOO", "foo", "Foo" ), - ( "foO", "foO", "FoO" ), - - ( "foo_bar", "fooBar", "FooBar" ), - ( "foo_bar", "fooBar", "FooBar" ), - ( "foo_bAr_BaZ", "fooBArBaZ", "FooBArBaZ" ), - ( "foo_bAr_BaZ", "fooBArBaZ", "FooBArBaZ" ), - - ( "foo1bar", "foo1Bar", "Foo1Bar" ), - ( "foo2bAr3BaZ", "foo2BAr3BaZ", "Foo2BAr3BaZ" ), - - ( "foo_1bar", "foo1Bar", "Foo1Bar" ), - ( "foo_2bAr_3BaZ", "foo2BAr3BaZ", "Foo2BAr3BaZ" ), - ( "_0foo_1bar", "_0Foo1Bar", "_0Foo1Bar" ), - ( "_0foo_2bAr_3BaZ", "_0Foo2BAr3BaZ", "_0Foo2BAr3BaZ" ), - - ( "url", "url", "URL" ), - ( "http", "http", "HTTP" ), - ( "https", "https", "HTTPS" ), - ( "id", "id", "ID" ), - - ( "the_url", "theURL", "TheURL" ), - ( "use_http", "useHTTP", "UseHTTP" ), - ( "use_https", "useHTTPS", "UseHTTPS" ), - ( "request_id", "requestID", "RequestID" ), - - ( "url_number", "urlNumber", "URLNumber" ), - ( "http_needed", "httpNeeded", "HTTPNeeded" ), - ( "https_needed", "httpsNeeded", "HTTPSNeeded" ), - ( "id_number", "idNumber", "IDNumber" ), - - ( "is_url_number", "isURLNumber", "IsURLNumber" ), - ( "is_http_needed", "isHTTPNeeded", "IsHTTPNeeded" ), - ( "is_https_needed", "isHTTPSNeeded", "IsHTTPSNeeded" ), - ( "the_id_number", "theIDNumber", "TheIDNumber" ), - - ( "url_foo_http_id", "urlFooHTTPID", "URLFooHTTPID"), - - ( "göß", "göß", "Göß"), - ( "göo", "göO", "GöO"), - ( "gö_o", "göO", "GöO"), - ( "g_🎉_o", "g🎉O", "G🎉O"), - ( "g🎉o", "g🎉O", "G🎉O"), - - ( "m\u{AB}n", "m_u171N", "M_u171N"), - ( "m\u{AB}_n", "m_u171N", "M_u171N"), - ( "m_\u{AB}_n", "m_u171N", "M_u171N"), - ] - - for (input, expectedLower, expectedUppper) in tests { - XCTAssertEqual(NamingUtils.toLowerCamelCase(input), expectedLower) - XCTAssertEqual(NamingUtils.toUpperCamelCase(input), expectedUppper) + + func testToCamelCase() { + // input, expectedLower, expectedUpper + let tests: [(String, String, String)] = [ + ("", "", ""), + + ("foo", "foo", "Foo"), + ("FOO", "foo", "Foo"), + ("foO", "foO", "FoO"), + + ("foo_bar", "fooBar", "FooBar"), + ("foo_bar", "fooBar", "FooBar"), + ("foo_bAr_BaZ", "fooBArBaZ", "FooBArBaZ"), + ("foo_bAr_BaZ", "fooBArBaZ", "FooBArBaZ"), + + ("foo1bar", "foo1Bar", "Foo1Bar"), + ("foo2bAr3BaZ", "foo2BAr3BaZ", "Foo2BAr3BaZ"), + + ("foo_1bar", "foo1Bar", "Foo1Bar"), + ("foo_2bAr_3BaZ", "foo2BAr3BaZ", "Foo2BAr3BaZ"), + ("_0foo_1bar", "_0Foo1Bar", "_0Foo1Bar"), + ("_0foo_2bAr_3BaZ", "_0Foo2BAr3BaZ", "_0Foo2BAr3BaZ"), + + ("url", "url", "URL"), + ("http", "http", "HTTP"), + ("https", "https", "HTTPS"), + ("id", "id", "ID"), + + ("the_url", "theURL", "TheURL"), + ("use_http", "useHTTP", "UseHTTP"), + ("use_https", "useHTTPS", "UseHTTPS"), + ("request_id", "requestID", "RequestID"), + + ("url_number", "urlNumber", "URLNumber"), + ("http_needed", "httpNeeded", "HTTPNeeded"), + ("https_needed", "httpsNeeded", "HTTPSNeeded"), + ("id_number", "idNumber", "IDNumber"), + + ("is_url_number", "isURLNumber", "IsURLNumber"), + ("is_http_needed", "isHTTPNeeded", "IsHTTPNeeded"), + ("is_https_needed", "isHTTPSNeeded", "IsHTTPSNeeded"), + ("the_id_number", "theIDNumber", "TheIDNumber"), + + ("url_foo_http_id", "urlFooHTTPID", "URLFooHTTPID"), + + ("göß", "göß", "Göß"), + ("göo", "göO", "GöO"), + ("gö_o", "göO", "GöO"), + ("g_🎉_o", "g🎉O", "G🎉O"), + ("g🎉o", "g🎉O", "G🎉O"), + + ("m\u{AB}n", "m_u171N", "M_u171N"), + ("m\u{AB}_n", "m_u171N", "M_u171N"), + ("m_\u{AB}_n", "m_u171N", "M_u171N"), + ] + + for (input, expectedLower, expectedUppper) in tests { + XCTAssertEqual(NamingUtils.toLowerCamelCase(input), expectedLower) + XCTAssertEqual(NamingUtils.toUpperCamelCase(input), expectedUppper) + } } - } } diff --git a/Tests/SwiftProtobufPluginLibraryTests/Test_ProtoFileToModuleMappings.swift b/Tests/SwiftProtobufPluginLibraryTests/Test_ProtoFileToModuleMappings.swift index 2422a5396..87f31fae7 100644 --- a/Tests/SwiftProtobufPluginLibraryTests/Test_ProtoFileToModuleMappings.swift +++ b/Tests/SwiftProtobufPluginLibraryTests/Test_ProtoFileToModuleMappings.swift @@ -8,289 +8,314 @@ // // ----------------------------------------------------------------------------- -import XCTest import SwiftProtobuf import SwiftProtobufTestHelpers +import XCTest + @testable import SwiftProtobufPluginLibrary // Helpers to make test cases. -fileprivate typealias FileDescriptorProto = Google_Protobuf_FileDescriptorProto +private typealias FileDescriptorProto = Google_Protobuf_FileDescriptorProto final class Test_ProtoFileToModuleMappings: XCTestCase { - func test_Initialization() { - // ProtoFileToModuleMappings always includes mappings for the protos that - // ship with the library, so they will show in the counts below. - let baselineEntries = SwiftProtobufInfo.bundledProtoFiles.count - let baselineModules = 1 // Since those files are in SwiftProtobuf. - - // (config, num_expected_mappings, num_expected_modules) - let tests: [(String, Int, Int)] = [ - ("", 0, 0), - - ("mapping { module_name: \"good\", proto_file_path: \"file.proto\" }", 1, 1), - - ("mapping { module_name: \"good\", proto_file_path: [\"a\",\"b\"] }", 2, 1), - - // Two mapping {}, same module. - ("mapping { module_name: \"good\", proto_file_path: \"a\" }\n" + - "mapping { module_name: \"good\", proto_file_path: \"b\" }", 2, 1), - - // Two mapping {}, different modules. - ("mapping { module_name: \"one\", proto_file_path: \"a\" }\n" + - "mapping { module_name: \"two\", proto_file_path: \"b\" }", 2, 2), - - // Same file listed twice; odd, but ok since no conflict. - ("mapping { module_name: \"foo\", proto_file_path: [\"abc\", \"abc\"] }", 1, 1), - - // Same module/file listing; odd, but ok since no conflict. - ("mapping { module_name: \"foo\", proto_file_path: [\"mno\", \"abc\"] }\n" + - "mapping { module_name: \"foo\", proto_file_path: [\"abc\", \"xyz\"] }", 3, 1), - - ] - - for (idx, (configText, expectMappings, expectedModules)) in tests.enumerated() { - let config: SwiftProtobuf_GenSwift_ModuleMappings - do { - config = try SwiftProtobuf_GenSwift_ModuleMappings(textFormatString: configText) - } catch { - XCTFail("Index: \(idx) - Test case wasn't valid TextFormat") - continue - } - - do { - let mapper = try ProtoFileToModuleMappings(moduleMappingsProto: config) - XCTAssertEqual(mapper.mappings.count, expectMappings + baselineEntries, "Index: \(idx)") - XCTAssertEqual(Set(mapper.mappings.values).count, expectedModules + baselineModules, "Index: \(idx)") - XCTAssert(mapper.hasMappings == (expectMappings != 0), "Index: \(idx)") - } catch let error { - XCTFail("Index \(idx) - Unexpected error: \(error)") - } + func test_Initialization() { + // ProtoFileToModuleMappings always includes mappings for the protos that + // ship with the library, so they will show in the counts below. + let baselineEntries = SwiftProtobufInfo.bundledProtoFiles.count + let baselineModules = 1 // Since those files are in SwiftProtobuf. + + // (config, num_expected_mappings, num_expected_modules) + let tests: [(String, Int, Int)] = [ + ("", 0, 0), + + ("mapping { module_name: \"good\", proto_file_path: \"file.proto\" }", 1, 1), + + ("mapping { module_name: \"good\", proto_file_path: [\"a\",\"b\"] }", 2, 1), + + // Two mapping {}, same module. + ( + "mapping { module_name: \"good\", proto_file_path: \"a\" }\n" + + "mapping { module_name: \"good\", proto_file_path: \"b\" }", 2, 1 + ), + + // Two mapping {}, different modules. + ( + "mapping { module_name: \"one\", proto_file_path: \"a\" }\n" + + "mapping { module_name: \"two\", proto_file_path: \"b\" }", 2, 2 + ), + + // Same file listed twice; odd, but ok since no conflict. + ("mapping { module_name: \"foo\", proto_file_path: [\"abc\", \"abc\"] }", 1, 1), + + // Same module/file listing; odd, but ok since no conflict. + ( + "mapping { module_name: \"foo\", proto_file_path: [\"mno\", \"abc\"] }\n" + + "mapping { module_name: \"foo\", proto_file_path: [\"abc\", \"xyz\"] }", 3, 1 + ), + + ] + + for (idx, (configText, expectMappings, expectedModules)) in tests.enumerated() { + let config: SwiftProtobuf_GenSwift_ModuleMappings + do { + config = try SwiftProtobuf_GenSwift_ModuleMappings(textFormatString: configText) + } catch { + XCTFail("Index: \(idx) - Test case wasn't valid TextFormat") + continue + } + + do { + let mapper = try ProtoFileToModuleMappings(moduleMappingsProto: config) + XCTAssertEqual(mapper.mappings.count, expectMappings + baselineEntries, "Index: \(idx)") + XCTAssertEqual(Set(mapper.mappings.values).count, expectedModules + baselineModules, "Index: \(idx)") + XCTAssert(mapper.hasMappings == (expectMappings != 0), "Index: \(idx)") + } catch let error { + XCTFail("Index \(idx) - Unexpected error: \(error)") + } + } } - } - - func test_Initialization_InvalidConfigs() { - // This are valid text format, but not valid config protos. - // (input, expected_error_type) - let partialConfigs: [(String, ProtoFileToModuleMappings.LoadError)] = [ - // No module or proto files - ("mapping { }", .entryMissingModuleName(mappingIndex: 0)), - - // No proto files - ("mapping { module_name: \"foo\" }", .entryHasNoProtoPaths(mappingIndex: 0)), - - // No module - ("mapping { proto_file_path: [\"foo\"] }", .entryMissingModuleName(mappingIndex: 0)), - ("mapping { proto_file_path: [\"foo\", \"bar\"] }", .entryMissingModuleName(mappingIndex: 0)), - - // Empty module name. - ("mapping { module_name: \"\" }", .entryMissingModuleName(mappingIndex: 0)), - ("mapping { module_name: \"\", proto_file_path: [\"foo\"] }", .entryMissingModuleName(mappingIndex: 0)), - ("mapping { module_name: \"\", proto_file_path: [\"foo\", \"bar\"] }", .entryMissingModuleName(mappingIndex: 0)), - - // Throw some on a second entry just to check that also. - ("mapping { module_name: \"good\", proto_file_path: \"file.proto\" }\n" + - "mapping { }", - .entryMissingModuleName(mappingIndex: 1)), - ("mapping { module_name: \"good\", proto_file_path: \"file.proto\" }\n" + - "mapping { module_name: \"foo\" }", - .entryHasNoProtoPaths(mappingIndex: 1)), - - // Duplicates - - ("mapping { module_name: \"foo\", proto_file_path: \"abc\" }\n" + - "mapping { module_name: \"bar\", proto_file_path: \"abc\" }", - .duplicateProtoPathMapping(path: "abc", firstModule: "foo", secondModule: "bar")), - - ("mapping { module_name: \"foo\", proto_file_path: \"abc\" }\n" + - "mapping { module_name: \"bar\", proto_file_path: \"xyz\" }\n" + - "mapping { module_name: \"baz\", proto_file_path: \"abc\" }", - .duplicateProtoPathMapping(path: "abc", firstModule: "foo", secondModule: "baz")), - ] - - for (idx, (configText, expected)) in partialConfigs.enumerated() { - let config: SwiftProtobuf_GenSwift_ModuleMappings - do { - config = try SwiftProtobuf_GenSwift_ModuleMappings(textFormatString: configText) - } catch { - XCTFail("Index: \(idx) - Test case wasn't valid TextFormat") - continue - } - - do { - let _ = try ProtoFileToModuleMappings(moduleMappingsProto: config) - XCTFail("Shouldn't have gotten here, index \(idx)") - } catch let error as ProtoFileToModuleMappings.LoadError { - XCTAssertEqual(error, expected, "Index \(idx)") - } catch let error { - XCTFail("Index \(idx) - Unexpected error: \(error)") - } + + func test_Initialization_InvalidConfigs() { + // This are valid text format, but not valid config protos. + // (input, expected_error_type) + let partialConfigs: [(String, ProtoFileToModuleMappings.LoadError)] = [ + // No module or proto files + ("mapping { }", .entryMissingModuleName(mappingIndex: 0)), + + // No proto files + ("mapping { module_name: \"foo\" }", .entryHasNoProtoPaths(mappingIndex: 0)), + + // No module + ("mapping { proto_file_path: [\"foo\"] }", .entryMissingModuleName(mappingIndex: 0)), + ("mapping { proto_file_path: [\"foo\", \"bar\"] }", .entryMissingModuleName(mappingIndex: 0)), + + // Empty module name. + ("mapping { module_name: \"\" }", .entryMissingModuleName(mappingIndex: 0)), + ("mapping { module_name: \"\", proto_file_path: [\"foo\"] }", .entryMissingModuleName(mappingIndex: 0)), + ( + "mapping { module_name: \"\", proto_file_path: [\"foo\", \"bar\"] }", + .entryMissingModuleName(mappingIndex: 0) + ), + + // Throw some on a second entry just to check that also. + ( + "mapping { module_name: \"good\", proto_file_path: \"file.proto\" }\n" + "mapping { }", + .entryMissingModuleName(mappingIndex: 1) + ), + ( + "mapping { module_name: \"good\", proto_file_path: \"file.proto\" }\n" + + "mapping { module_name: \"foo\" }", + .entryHasNoProtoPaths(mappingIndex: 1) + ), + + // Duplicates + + ( + "mapping { module_name: \"foo\", proto_file_path: \"abc\" }\n" + + "mapping { module_name: \"bar\", proto_file_path: \"abc\" }", + .duplicateProtoPathMapping(path: "abc", firstModule: "foo", secondModule: "bar") + ), + + ( + "mapping { module_name: \"foo\", proto_file_path: \"abc\" }\n" + + "mapping { module_name: \"bar\", proto_file_path: \"xyz\" }\n" + + "mapping { module_name: \"baz\", proto_file_path: \"abc\" }", + .duplicateProtoPathMapping(path: "abc", firstModule: "foo", secondModule: "baz") + ), + ] + + for (idx, (configText, expected)) in partialConfigs.enumerated() { + let config: SwiftProtobuf_GenSwift_ModuleMappings + do { + config = try SwiftProtobuf_GenSwift_ModuleMappings(textFormatString: configText) + } catch { + XCTFail("Index: \(idx) - Test case wasn't valid TextFormat") + continue + } + + do { + let _ = try ProtoFileToModuleMappings(moduleMappingsProto: config) + XCTFail("Shouldn't have gotten here, index \(idx)") + } catch let error as ProtoFileToModuleMappings.LoadError { + XCTAssertEqual(error, expected, "Index \(idx)") + } catch let error { + XCTFail("Index \(idx) - Unexpected error: \(error)") + } + } } - } - - func test_moduleName_forFile() { - let configText = [ - "mapping { module_name: \"foo\", proto_file_path: \"file\" }", - "mapping { module_name: \"bar\", proto_file_path: \"dir1/file\" }", - "mapping { module_name: \"baz\", proto_file_path: [\"dir2/file\",\"file4\"] }", - "mapping { module_name: \"foo\", proto_file_path: \"file5\" }", - ].joined(separator: "\n") - - let config = try! SwiftProtobuf_GenSwift_ModuleMappings(textFormatString: configText) - let mapper = try! ProtoFileToModuleMappings(moduleMappingsProto: config) - - let tests: [(String, String?)] = [ - ( "file", "foo" ), - ( "dir1/file", "bar" ), - ( "dir2/file", "baz" ), - ( "file4", "baz" ), - ( "file5", "foo" ), - - ( "", nil ), - ( "not found", nil ), - ] - - for (name, expected) in tests { - let descSet = DescriptorSet(protos: [FileDescriptorProto(name: name)]) - XCTAssertEqual(mapper.moduleName(forFile: descSet.files.first!), expected, "Looking for \(name)") + + func test_moduleName_forFile() { + let configText = [ + "mapping { module_name: \"foo\", proto_file_path: \"file\" }", + "mapping { module_name: \"bar\", proto_file_path: \"dir1/file\" }", + "mapping { module_name: \"baz\", proto_file_path: [\"dir2/file\",\"file4\"] }", + "mapping { module_name: \"foo\", proto_file_path: \"file5\" }", + ].joined(separator: "\n") + + let config = try! SwiftProtobuf_GenSwift_ModuleMappings(textFormatString: configText) + let mapper = try! ProtoFileToModuleMappings(moduleMappingsProto: config) + + let tests: [(String, String?)] = [ + ("file", "foo"), + ("dir1/file", "bar"), + ("dir2/file", "baz"), + ("file4", "baz"), + ("file5", "foo"), + + ("", nil), + ("not found", nil), + ] + + for (name, expected) in tests { + let descSet = DescriptorSet(protos: [FileDescriptorProto(name: name)]) + XCTAssertEqual(mapper.moduleName(forFile: descSet.files.first!), expected, "Looking for \(name)") + } } - } - - func test_neededModules_forFile() { - let configText = [ - "mapping { module_name: \"foo\", proto_file_path: \"file\" }", - "mapping { module_name: \"bar\", proto_file_path: \"dir1/file\" }", - "mapping { module_name: \"baz\", proto_file_path: [\"dir2/file\",\"file4\"] }", - "mapping { module_name: \"foo\", proto_file_path: \"file5\" }", - ].joined(separator: "\n") - - let config = try! SwiftProtobuf_GenSwift_ModuleMappings(textFormatString: configText) - let mapper = try! ProtoFileToModuleMappings(moduleMappingsProto: config) - - let fileProtos = [ - FileDescriptorProto(name: "file"), - FileDescriptorProto(name: "google/protobuf/any.proto"), - FileDescriptorProto(name: "dir1/file", dependencies: ["file"]), - FileDescriptorProto(name: "dir2/file", dependencies: ["google/protobuf/any.proto"]), - FileDescriptorProto(name: "file4", dependencies: ["dir2/file", "dir1/file", "file"]), - FileDescriptorProto(name: "file5", dependencies: ["file"]), - ] - let descSet = DescriptorSet(protos: fileProtos) - - // ( filename, [deps] ) - let tests: [(String, [String]?)] = [ - ( "file", nil ), - ( "dir1/file", ["foo"] ), - ( "dir2/file", nil ), - ( "file4", ["bar", "foo"] ), - ( "file5", nil ), - ] - - for (name, expected) in tests { - let fileDesc = descSet.files.filter{ $0.name == name }.first! - let result = mapper.neededModules(forFile: fileDesc) - if let expected = expected { - XCTAssertEqual(result!, expected, "Looking for \(name)") - } else { - XCTAssertNil(result, "Looking for \(name)") - } + + func test_neededModules_forFile() { + let configText = [ + "mapping { module_name: \"foo\", proto_file_path: \"file\" }", + "mapping { module_name: \"bar\", proto_file_path: \"dir1/file\" }", + "mapping { module_name: \"baz\", proto_file_path: [\"dir2/file\",\"file4\"] }", + "mapping { module_name: \"foo\", proto_file_path: \"file5\" }", + ].joined(separator: "\n") + + let config = try! SwiftProtobuf_GenSwift_ModuleMappings(textFormatString: configText) + let mapper = try! ProtoFileToModuleMappings(moduleMappingsProto: config) + + let fileProtos = [ + FileDescriptorProto(name: "file"), + FileDescriptorProto(name: "google/protobuf/any.proto"), + FileDescriptorProto(name: "dir1/file", dependencies: ["file"]), + FileDescriptorProto(name: "dir2/file", dependencies: ["google/protobuf/any.proto"]), + FileDescriptorProto(name: "file4", dependencies: ["dir2/file", "dir1/file", "file"]), + FileDescriptorProto(name: "file5", dependencies: ["file"]), + ] + let descSet = DescriptorSet(protos: fileProtos) + + // ( filename, [deps] ) + let tests: [(String, [String]?)] = [ + ("file", nil), + ("dir1/file", ["foo"]), + ("dir2/file", nil), + ("file4", ["bar", "foo"]), + ("file5", nil), + ] + + for (name, expected) in tests { + let fileDesc = descSet.files.filter { $0.name == name }.first! + let result = mapper.neededModules(forFile: fileDesc) + if let expected = expected { + XCTAssertEqual(result!, expected, "Looking for \(name)") + } else { + XCTAssertNil(result, "Looking for \(name)") + } + } } - } - - func test_neededModules_forFile_PublicImports() { - // See the note in neededModules(forFile:) about how public import complicate things. - - // Given: - // - // + File: a.proto - // message A {} - // - // + File: imports_a_publicly.proto - // import public "a.proto"; - // - // message ImportsAPublicly { - // A a = 1; - // } - // - // + File: imports_imports_a_publicly.proto - // import public "imports_a_publicly.proto"; - // - // message ImportsImportsAPublicly { - // A a = 1; - // } - // - // + File: uses_a_transitively.proto - // import "imports_a_publicly.proto"; - // - // message UsesATransitively { - // A a = 1; - // } - // - // + File: uses_a_transitively2.proto - // import "imports_imports_a_publicly.proto"; - // - // message UsesATransitively2 { - // A a = 1; - // } - // - // With a mapping file of: - // - // mapping { - // module_name: "A" - // proto_file_path: "a.proto" - // } - // mapping { - // module_name: "ImportsAPublicly" - // proto_file_path: "imports_a_publicly.proto" - // } - // mapping { - // module_name: "ImportsImportsAPublicly" - // proto_file_path: "imports_imports_a_publicly.proto" - // } - - let configText = [ - "mapping { module_name: \"A\", proto_file_path: \"a.proto\" }", - "mapping { module_name: \"ImportsAPublicly\", proto_file_path: \"imports_a_publicly.proto\" }", - "mapping { module_name: \"ImportsImportsAPublicly\", proto_file_path: \"imports_imports_a_publicly.proto\" }", - ].joined(separator: "\n") - - let config = try! SwiftProtobuf_GenSwift_ModuleMappings(textFormatString: configText) - let mapper = try! ProtoFileToModuleMappings(moduleMappingsProto: config) - - let fileProtos = [ - FileDescriptorProto(name: "a.proto"), - FileDescriptorProto(name: "imports_a_publicly.proto", - dependencies: ["a.proto"], - publicDependencies: [0]), - FileDescriptorProto(name: "imports_imports_a_publicly.proto", - dependencies: ["imports_a_publicly.proto"], - publicDependencies: [0]), - FileDescriptorProto(name: "uses_a_transitively.proto", - dependencies: ["imports_a_publicly.proto"]), - FileDescriptorProto(name: "uses_a_transitively2.proto", - dependencies: ["imports_imports_a_publicly.proto"]), - ] - let descSet = DescriptorSet(protos: fileProtos) - - // ( filename, [deps] ) - let tests: [(String, [String]?)] = [ - ( "a.proto", nil ), - ( "imports_a_publicly.proto", ["A"] ), - ( "imports_imports_a_publicly.proto", ["A", "ImportsAPublicly"] ), - ( "uses_a_transitively.proto", ["A", "ImportsAPublicly"] ), - ( "uses_a_transitively2.proto", ["A", "ImportsAPublicly", "ImportsImportsAPublicly"] ), - ] - - for (name, expected) in tests { - let fileDesc = descSet.files.filter{ $0.name == name }.first! - let result = mapper.neededModules(forFile: fileDesc) - if let expected = expected { - XCTAssertEqual(result!, expected, "Looking for \(name)") - } else { - XCTAssertNil(result, "Looking for \(name)") - } + + func test_neededModules_forFile_PublicImports() { + // See the note in neededModules(forFile:) about how public import complicate things. + + // Given: + // + // + File: a.proto + // message A {} + // + // + File: imports_a_publicly.proto + // import public "a.proto"; + // + // message ImportsAPublicly { + // A a = 1; + // } + // + // + File: imports_imports_a_publicly.proto + // import public "imports_a_publicly.proto"; + // + // message ImportsImportsAPublicly { + // A a = 1; + // } + // + // + File: uses_a_transitively.proto + // import "imports_a_publicly.proto"; + // + // message UsesATransitively { + // A a = 1; + // } + // + // + File: uses_a_transitively2.proto + // import "imports_imports_a_publicly.proto"; + // + // message UsesATransitively2 { + // A a = 1; + // } + // + // With a mapping file of: + // + // mapping { + // module_name: "A" + // proto_file_path: "a.proto" + // } + // mapping { + // module_name: "ImportsAPublicly" + // proto_file_path: "imports_a_publicly.proto" + // } + // mapping { + // module_name: "ImportsImportsAPublicly" + // proto_file_path: "imports_imports_a_publicly.proto" + // } + + let configText = [ + "mapping { module_name: \"A\", proto_file_path: \"a.proto\" }", + "mapping { module_name: \"ImportsAPublicly\", proto_file_path: \"imports_a_publicly.proto\" }", + "mapping { module_name: \"ImportsImportsAPublicly\", proto_file_path: \"imports_imports_a_publicly.proto\" }", + ].joined(separator: "\n") + + let config = try! SwiftProtobuf_GenSwift_ModuleMappings(textFormatString: configText) + let mapper = try! ProtoFileToModuleMappings(moduleMappingsProto: config) + + let fileProtos = [ + FileDescriptorProto(name: "a.proto"), + FileDescriptorProto( + name: "imports_a_publicly.proto", + dependencies: ["a.proto"], + publicDependencies: [0] + ), + FileDescriptorProto( + name: "imports_imports_a_publicly.proto", + dependencies: ["imports_a_publicly.proto"], + publicDependencies: [0] + ), + FileDescriptorProto( + name: "uses_a_transitively.proto", + dependencies: ["imports_a_publicly.proto"] + ), + FileDescriptorProto( + name: "uses_a_transitively2.proto", + dependencies: ["imports_imports_a_publicly.proto"] + ), + ] + let descSet = DescriptorSet(protos: fileProtos) + + // ( filename, [deps] ) + let tests: [(String, [String]?)] = [ + ("a.proto", nil), + ("imports_a_publicly.proto", ["A"]), + ("imports_imports_a_publicly.proto", ["A", "ImportsAPublicly"]), + ("uses_a_transitively.proto", ["A", "ImportsAPublicly"]), + ("uses_a_transitively2.proto", ["A", "ImportsAPublicly", "ImportsImportsAPublicly"]), + ] + + for (name, expected) in tests { + let fileDesc = descSet.files.filter { $0.name == name }.first! + let result = mapper.neededModules(forFile: fileDesc) + if let expected = expected { + XCTAssertEqual(result!, expected, "Looking for \(name)") + } else { + XCTAssertNil(result, "Looking for \(name)") + } + } } - } } diff --git a/Tests/SwiftProtobufPluginLibraryTests/Test_SwiftLanguage.swift b/Tests/SwiftProtobufPluginLibraryTests/Test_SwiftLanguage.swift index 5d1527e34..5b66e810b 100644 --- a/Tests/SwiftProtobufPluginLibraryTests/Test_SwiftLanguage.swift +++ b/Tests/SwiftProtobufPluginLibraryTests/Test_SwiftLanguage.swift @@ -12,8 +12,8 @@ /// // ----------------------------------------------------------------------------- -import XCTest import SwiftProtobufPluginLibrary +import XCTest final class Test_SwiftLanguage: XCTestCase { func testIsValidSwiftIdentifier() { @@ -22,17 +22,23 @@ final class Test_SwiftLanguage: XCTestCase { "\u{1f436}\u{1f431}", ] for identifier in cases { - XCTAssertTrue(isValidSwiftIdentifier(identifier, allowQuoted: false), - "Should be valid: \(identifier)") + XCTAssertTrue( + isValidSwiftIdentifier(identifier, allowQuoted: false), + "Should be valid: \(identifier)" + ) } - let quotedCases = cases.map {return "`\($0)`"} + let quotedCases = cases.map { return "`\($0)`" } for identifier in quotedCases { - XCTAssertFalse(isValidSwiftIdentifier(identifier, allowQuoted: false), - "Should NOT be valid: \(identifier)") + XCTAssertFalse( + isValidSwiftIdentifier(identifier, allowQuoted: false), + "Should NOT be valid: \(identifier)" + ) } for identifier in cases + quotedCases { - XCTAssertTrue(isValidSwiftIdentifier(identifier, allowQuoted: true), - "Should be valid: \(identifier)") + XCTAssertTrue( + isValidSwiftIdentifier(identifier, allowQuoted: true), + "Should be valid: \(identifier)" + ) } } @@ -45,17 +51,23 @@ final class Test_SwiftLanguage: XCTestCase { "This is bad", ] for identifier in cases { - XCTAssertFalse(isValidSwiftIdentifier(identifier, allowQuoted: false), - "Should NOT be valid: \(identifier)") + XCTAssertFalse( + isValidSwiftIdentifier(identifier, allowQuoted: false), + "Should NOT be valid: \(identifier)" + ) } - let quotedCases = cases.map {return "`\($0)`"} + let quotedCases = cases.map { return "`\($0)`" } for identifier in cases + quotedCases { - XCTAssertFalse(isValidSwiftIdentifier(identifier, allowQuoted: false), - "Should NOT be valid: \(identifier)") + XCTAssertFalse( + isValidSwiftIdentifier(identifier, allowQuoted: false), + "Should NOT be valid: \(identifier)" + ) } for identifier in cases + quotedCases { - XCTAssertFalse(isValidSwiftIdentifier(identifier, allowQuoted: true), - "Should NOT be valid: \(identifier)") + XCTAssertFalse( + isValidSwiftIdentifier(identifier, allowQuoted: true), + "Should NOT be valid: \(identifier)" + ) } let badQuotes = [ @@ -67,8 +79,10 @@ final class Test_SwiftLanguage: XCTestCase { "``H9000``", ] for identifier in badQuotes { - XCTAssertFalse(isValidSwiftIdentifier(identifier, allowQuoted: true), - "Should NOT be valid: \(identifier)") + XCTAssertFalse( + isValidSwiftIdentifier(identifier, allowQuoted: true), + "Should NOT be valid: \(identifier)" + ) } } } diff --git a/Tests/SwiftProtobufPluginLibraryTests/Test_SwiftProtobufNamer.swift b/Tests/SwiftProtobufPluginLibraryTests/Test_SwiftProtobufNamer.swift index 34733bc3b..52872e85b 100644 --- a/Tests/SwiftProtobufPluginLibraryTests/Test_SwiftProtobufNamer.swift +++ b/Tests/SwiftProtobufPluginLibraryTests/Test_SwiftProtobufNamer.swift @@ -8,270 +8,278 @@ // // ----------------------------------------------------------------------------- -import XCTest import SwiftProtobuf import SwiftProtobufPluginLibrary import SwiftProtobufTestHelpers +import XCTest final class Test_SwiftProtobufNamer: XCTestCase { - func testEnumValueHandling_AliasNameMatches() throws { - let txt = [ - "name: \"test.proto\"", - "syntax: \"proto2\"", - "enum_type {", - " name: \"TestEnum\"", - " options {", - " allow_alias: true", - " }", - " value {", - " name: \"TEST_ENUM_FOO\"", - " number: 0", // Primary - " }", - " value {", - " name: \"TEST_ENUM_BAR\"", - " number: 1", - " }", - " value {", - " name: \"TESTENUM_FOO\"", - " number: 0", // Alias - " }", - " value {", - " name: \"_FOO\"", - " number: 0", // Alias - " }", - " value {", - " name: \"FOO\"", - " number: 0", // Alias - " }", - " value {", - " name: \"TEST_ENUM_ALIAS\"", - " number: 0", // Alias (unique name) - " }", - "}" - ] - - let fileProto: Google_Protobuf_FileDescriptorProto - do { - fileProto = try Google_Protobuf_FileDescriptorProto(textFormatStrings: txt) - } catch let e { - XCTFail("Error: \(e)") - return - } - - let descriptorSet = DescriptorSet(protos: [fileProto]) - let namer = - SwiftProtobufNamer(currentFile: descriptorSet.fileDescriptor(named: "test.proto")!, - protoFileToModuleMappings: ProtoFileToModuleMappings()) + func testEnumValueHandling_AliasNameMatches() throws { + let txt = [ + "name: \"test.proto\"", + "syntax: \"proto2\"", + "enum_type {", + " name: \"TestEnum\"", + " options {", + " allow_alias: true", + " }", + " value {", + " name: \"TEST_ENUM_FOO\"", + " number: 0", // Primary + " }", + " value {", + " name: \"TEST_ENUM_BAR\"", + " number: 1", + " }", + " value {", + " name: \"TESTENUM_FOO\"", + " number: 0", // Alias + " }", + " value {", + " name: \"_FOO\"", + " number: 0", // Alias + " }", + " value {", + " name: \"FOO\"", + " number: 0", // Alias + " }", + " value {", + " name: \"TEST_ENUM_ALIAS\"", + " number: 0", // Alias (unique name) + " }", + "}", + ] - let e = descriptorSet.enumDescriptor(named: "TestEnum")! - let values = e.values - XCTAssertEqual(values.count, 6) + let fileProto: Google_Protobuf_FileDescriptorProto + do { + fileProto = try Google_Protobuf_FileDescriptorProto(textFormatStrings: txt) + } catch let e { + XCTFail("Error: \(e)") + return + } - // Test relativeName(enumValue:) + let descriptorSet = DescriptorSet(protos: [fileProto]) + let namer = + SwiftProtobufNamer( + currentFile: descriptorSet.fileDescriptor(named: "test.proto")!, + protoFileToModuleMappings: ProtoFileToModuleMappings() + ) - XCTAssertEqual(namer.relativeName(enumValue: values[0]), "foo") - XCTAssertEqual(namer.relativeName(enumValue: values[1]), "bar") - XCTAssertEqual(namer.relativeName(enumValue: values[2]), "foo") - XCTAssertEqual(namer.relativeName(enumValue: values[3]), "foo") - XCTAssertEqual(namer.relativeName(enumValue: values[4]), "foo") - XCTAssertEqual(namer.relativeName(enumValue: values[5]), "alias") - } + let e = descriptorSet.enumDescriptor(named: "TestEnum")! + let values = e.values + XCTAssertEqual(values.count, 6) - func testEnumValueHandling_NameCollisions() { - let txt = [ - "name: \"test.proto\"", - "syntax: \"proto2\"", - "enum_type {", - " name: \"TestEnum\"", - " value {", - " name: \"TEST_ENUM_FOO\"", - " number: 0", // Collision - " }", - " value {", - " name: \"TEST_ENUM_BAR\"", - " number: 1", - " }", - " value {", - " name: \"TESTENUM_FOO\"", - " number: 2", // Collision - " }", - " value {", - " name: \"_FOO\"", - " number: -1", // Collision - negative value - " }", - "}" - ] + // Test relativeName(enumValue:) - let fileProto: Google_Protobuf_FileDescriptorProto - do { - fileProto = try Google_Protobuf_FileDescriptorProto(textFormatStrings: txt) - } catch let e { - XCTFail("Error: \(e)") - return + XCTAssertEqual(namer.relativeName(enumValue: values[0]), "foo") + XCTAssertEqual(namer.relativeName(enumValue: values[1]), "bar") + XCTAssertEqual(namer.relativeName(enumValue: values[2]), "foo") + XCTAssertEqual(namer.relativeName(enumValue: values[3]), "foo") + XCTAssertEqual(namer.relativeName(enumValue: values[4]), "foo") + XCTAssertEqual(namer.relativeName(enumValue: values[5]), "alias") } - let descriptorSet = DescriptorSet(protos: [fileProto]) - let namer = - SwiftProtobufNamer(currentFile: descriptorSet.fileDescriptor(named: "test.proto")!, - protoFileToModuleMappings: ProtoFileToModuleMappings()) + func testEnumValueHandling_NameCollisions() { + let txt = [ + "name: \"test.proto\"", + "syntax: \"proto2\"", + "enum_type {", + " name: \"TestEnum\"", + " value {", + " name: \"TEST_ENUM_FOO\"", + " number: 0", // Collision + " }", + " value {", + " name: \"TEST_ENUM_BAR\"", + " number: 1", + " }", + " value {", + " name: \"TESTENUM_FOO\"", + " number: 2", // Collision + " }", + " value {", + " name: \"_FOO\"", + " number: -1", // Collision - negative value + " }", + "}", + ] - let e = descriptorSet.enumDescriptor(named: "TestEnum")! - let values = e.values - XCTAssertEqual(values.count, 4) + let fileProto: Google_Protobuf_FileDescriptorProto + do { + fileProto = try Google_Protobuf_FileDescriptorProto(textFormatStrings: txt) + } catch let e { + XCTFail("Error: \(e)") + return + } - // Test relativeName(enumValue:) + let descriptorSet = DescriptorSet(protos: [fileProto]) + let namer = + SwiftProtobufNamer( + currentFile: descriptorSet.fileDescriptor(named: "test.proto")!, + protoFileToModuleMappings: ProtoFileToModuleMappings() + ) - XCTAssertEqual(namer.relativeName(enumValue: values[0]), "foo_0") - XCTAssertEqual(namer.relativeName(enumValue: values[1]), "bar") - XCTAssertEqual(namer.relativeName(enumValue: values[2]), "foo_2") - XCTAssertEqual(namer.relativeName(enumValue: values[3]), "foo_n1") - } + let e = descriptorSet.enumDescriptor(named: "TestEnum")! + let values = e.values + XCTAssertEqual(values.count, 4) - func testEnumValueHandling_NameCollisionsAndAliasMatches() { - let txt = [ - "name: \"test.proto\"", - "syntax: \"proto2\"", - "enum_type {", - " name: \"TestEnum\"", - " options {", - " allow_alias: true", - " }", - " value {", - " name: \"TEST_ENUM_FOO\"", - " number: 0", // Collision/Primary0 - " }", - " value {", - " name: \"TEST_ENUM_BAR\"", - " number: 1", - " }", - " value {", - " name: \"TESTENUM_FOO\"", - " number: 0", // Alias 0 - " }", - " value {", - " name: \"_FOO\"", - " number: 2", // Collision/Primary2 - " }", - " value {", - " name: \"FOO\"", - " number: 2", // Alias 2 - " }", - " value {", - " name: \"TEST_ENUM_ALIAS\"", - " number: 0", // Alias 0 - Unique name - " }", - " value {", - " name: \"mumble\"", - " number: 1", // Alias 1 - Collision with next alias - " }", - " value {", - " name: \"MUMBLE\"", - " number: 0", // Alias 0 - Collision with previous alias - " }", - "}" - ] + // Test relativeName(enumValue:) - let fileProto: Google_Protobuf_FileDescriptorProto - do { - fileProto = try Google_Protobuf_FileDescriptorProto(textFormatStrings: txt) - } catch let e { - XCTFail("Error: \(e)") - return + XCTAssertEqual(namer.relativeName(enumValue: values[0]), "foo_0") + XCTAssertEqual(namer.relativeName(enumValue: values[1]), "bar") + XCTAssertEqual(namer.relativeName(enumValue: values[2]), "foo_2") + XCTAssertEqual(namer.relativeName(enumValue: values[3]), "foo_n1") } - let descriptorSet = DescriptorSet(protos: [fileProto]) - let namer = - SwiftProtobufNamer(currentFile: descriptorSet.fileDescriptor(named: "test.proto")!, - protoFileToModuleMappings: ProtoFileToModuleMappings()) + func testEnumValueHandling_NameCollisionsAndAliasMatches() { + let txt = [ + "name: \"test.proto\"", + "syntax: \"proto2\"", + "enum_type {", + " name: \"TestEnum\"", + " options {", + " allow_alias: true", + " }", + " value {", + " name: \"TEST_ENUM_FOO\"", + " number: 0", // Collision/Primary0 + " }", + " value {", + " name: \"TEST_ENUM_BAR\"", + " number: 1", + " }", + " value {", + " name: \"TESTENUM_FOO\"", + " number: 0", // Alias 0 + " }", + " value {", + " name: \"_FOO\"", + " number: 2", // Collision/Primary2 + " }", + " value {", + " name: \"FOO\"", + " number: 2", // Alias 2 + " }", + " value {", + " name: \"TEST_ENUM_ALIAS\"", + " number: 0", // Alias 0 - Unique name + " }", + " value {", + " name: \"mumble\"", + " number: 1", // Alias 1 - Collision with next alias + " }", + " value {", + " name: \"MUMBLE\"", + " number: 0", // Alias 0 - Collision with previous alias + " }", + "}", + ] - let e = descriptorSet.enumDescriptor(named: "TestEnum")! - let values = e.values - XCTAssertEqual(values.count, 8) + let fileProto: Google_Protobuf_FileDescriptorProto + do { + fileProto = try Google_Protobuf_FileDescriptorProto(textFormatStrings: txt) + } catch let e { + XCTFail("Error: \(e)") + return + } - // Test relativeName(enumValue:) + let descriptorSet = DescriptorSet(protos: [fileProto]) + let namer = + SwiftProtobufNamer( + currentFile: descriptorSet.fileDescriptor(named: "test.proto")!, + protoFileToModuleMappings: ProtoFileToModuleMappings() + ) - XCTAssertEqual(namer.relativeName(enumValue: values[0]), "foo_0") - XCTAssertEqual(namer.relativeName(enumValue: values[1]), "bar") - XCTAssertEqual(namer.relativeName(enumValue: values[2]), "foo_0") - XCTAssertEqual(namer.relativeName(enumValue: values[3]), "foo_2") - XCTAssertEqual(namer.relativeName(enumValue: values[4]), "foo_2") - XCTAssertEqual(namer.relativeName(enumValue: values[5]), "alias") - XCTAssertEqual(namer.relativeName(enumValue: values[6]), "mumble_1") - XCTAssertEqual(namer.relativeName(enumValue: values[7]), "mumble_0") - } + let e = descriptorSet.enumDescriptor(named: "TestEnum")! + let values = e.values + XCTAssertEqual(values.count, 8) - func testEnumValueHandling_UniqueAliasNameCollisions() { - // Tests were the aliases collided in naming, but not with - // the original. + // Test relativeName(enumValue:) - let txt = [ - "name: \"test.proto\"", - "syntax: \"proto2\"", - "enum_type {", - " name: \"AliasedEnum\"", - " options {", - " allow_alias: true", - " }", - " value {", - " name: \"ALIAS_FOO\"", - " number: 0", - " }", - " value {", - " name: \"ALIAS_BAR\"", - " number: 1", - " }", - " value {", - " name: \"ALIAS_BAZ\"", - " number: 2", - " }", - " value {", - " name: \"QUX\"", - " number: 2", // short name merged with the next because they have the same value. - " }", - " value {", - " name: \"qux\"", - " number: 2", - " }", - " value {", - " name: \"bAz\"", - " number: 2", - " }", - "}" - ] - - let fileProto: Google_Protobuf_FileDescriptorProto - do { - fileProto = try Google_Protobuf_FileDescriptorProto(textFormatStrings: txt) - } catch let e { - XCTFail("Error: \(e)") - return + XCTAssertEqual(namer.relativeName(enumValue: values[0]), "foo_0") + XCTAssertEqual(namer.relativeName(enumValue: values[1]), "bar") + XCTAssertEqual(namer.relativeName(enumValue: values[2]), "foo_0") + XCTAssertEqual(namer.relativeName(enumValue: values[3]), "foo_2") + XCTAssertEqual(namer.relativeName(enumValue: values[4]), "foo_2") + XCTAssertEqual(namer.relativeName(enumValue: values[5]), "alias") + XCTAssertEqual(namer.relativeName(enumValue: values[6]), "mumble_1") + XCTAssertEqual(namer.relativeName(enumValue: values[7]), "mumble_0") } - let descriptorSet = DescriptorSet(protos: [fileProto]) - let namer = - SwiftProtobufNamer(currentFile: descriptorSet.fileDescriptor(named: "test.proto")!, - protoFileToModuleMappings: ProtoFileToModuleMappings()) + func testEnumValueHandling_UniqueAliasNameCollisions() { + // Tests were the aliases collided in naming, but not with + // the original. + + let txt = [ + "name: \"test.proto\"", + "syntax: \"proto2\"", + "enum_type {", + " name: \"AliasedEnum\"", + " options {", + " allow_alias: true", + " }", + " value {", + " name: \"ALIAS_FOO\"", + " number: 0", + " }", + " value {", + " name: \"ALIAS_BAR\"", + " number: 1", + " }", + " value {", + " name: \"ALIAS_BAZ\"", + " number: 2", + " }", + " value {", + " name: \"QUX\"", + " number: 2", // short name merged with the next because they have the same value. + " }", + " value {", + " name: \"qux\"", + " number: 2", + " }", + " value {", + " name: \"bAz\"", + " number: 2", + " }", + "}", + ] + + let fileProto: Google_Protobuf_FileDescriptorProto + do { + fileProto = try Google_Protobuf_FileDescriptorProto(textFormatStrings: txt) + } catch let e { + XCTFail("Error: \(e)") + return + } - let e = descriptorSet.enumDescriptor(named: "AliasedEnum")! - let values = e.values - XCTAssertEqual(values.count, 6) + let descriptorSet = DescriptorSet(protos: [fileProto]) + let namer = + SwiftProtobufNamer( + currentFile: descriptorSet.fileDescriptor(named: "test.proto")!, + protoFileToModuleMappings: ProtoFileToModuleMappings() + ) - XCTAssertEqual(values[0].name, "ALIAS_FOO") - XCTAssertEqual(values[1].name, "ALIAS_BAR") - XCTAssertEqual(values[2].name, "ALIAS_BAZ") - XCTAssertEqual(values[3].name, "QUX") - XCTAssertEqual(values[4].name, "qux") - XCTAssertEqual(values[5].name, "bAz") + let e = descriptorSet.enumDescriptor(named: "AliasedEnum")! + let values = e.values + XCTAssertEqual(values.count, 6) - // Test relativeName(enumValue:) + XCTAssertEqual(values[0].name, "ALIAS_FOO") + XCTAssertEqual(values[1].name, "ALIAS_BAR") + XCTAssertEqual(values[2].name, "ALIAS_BAZ") + XCTAssertEqual(values[3].name, "QUX") + XCTAssertEqual(values[4].name, "qux") + XCTAssertEqual(values[5].name, "bAz") - XCTAssertEqual(namer.relativeName(enumValue: values[0]), "aliasFoo") - XCTAssertEqual(namer.relativeName(enumValue: values[1]), "aliasBar") - XCTAssertEqual(namer.relativeName(enumValue: values[2]), "aliasBaz") - XCTAssertEqual(namer.relativeName(enumValue: values[3]), "qux") - XCTAssertEqual(namer.relativeName(enumValue: values[4]), "qux") - XCTAssertEqual(namer.relativeName(enumValue: values[5]), "bAz") - } + // Test relativeName(enumValue:) + + XCTAssertEqual(namer.relativeName(enumValue: values[0]), "aliasFoo") + XCTAssertEqual(namer.relativeName(enumValue: values[1]), "aliasBar") + XCTAssertEqual(namer.relativeName(enumValue: values[2]), "aliasBaz") + XCTAssertEqual(namer.relativeName(enumValue: values[3]), "qux") + XCTAssertEqual(namer.relativeName(enumValue: values[4]), "qux") + XCTAssertEqual(namer.relativeName(enumValue: values[5]), "bAz") + } } diff --git a/Tests/SwiftProtobufTests/Data+TestHelpers.swift b/Tests/SwiftProtobufTests/Data+TestHelpers.swift index 59820a333..92de75bb0 100644 --- a/Tests/SwiftProtobufTests/Data+TestHelpers.swift +++ b/Tests/SwiftProtobufTests/Data+TestHelpers.swift @@ -9,6 +9,7 @@ // ----------------------------------------------------------------------------- import Foundation + @testable import SwiftProtobuf /// Helpers for building up wire encoding in tests. @@ -43,4 +44,3 @@ extension Data { } } - diff --git a/Tests/SwiftProtobufTests/TestHelpers.swift b/Tests/SwiftProtobufTests/TestHelpers.swift index 30ae1d61a..30f57aec4 100644 --- a/Tests/SwiftProtobufTests/TestHelpers.swift +++ b/Tests/SwiftProtobufTests/TestHelpers.swift @@ -12,8 +12,9 @@ /// // ----------------------------------------------------------------------------- -import XCTest import Foundation +import XCTest + @testable import SwiftProtobuf typealias XCTestFileArgType = StaticString @@ -25,20 +26,35 @@ protocol PBTestHelpers { extension PBTestHelpers where MessageTestType: SwiftProtobuf.Message & Equatable { private func string(from data: [UInt8]) -> String { - return "[" + data.map { String($0) }.joined(separator: ", ") + "]" + "[" + data.map { String($0) }.joined(separator: ", ") + "]" } - func assertEncode(_ expected: [UInt8], file: XCTestFileArgType = #file, line: UInt = #line, configure: (inout MessageTestType) -> Void) { + func assertEncode( + _ expected: [UInt8], + file: XCTestFileArgType = #file, + line: UInt = #line, + configure: (inout MessageTestType) -> Void + ) { let empty = MessageTestType() var configured = empty configure(&configured) XCTAssert(configured != empty, "Object should not be equal to empty object", file: file, line: line) do { let encoded: [UInt8] = try configured.serializedBytes() - XCTAssert(expected == encoded, "Did not encode correctly: got \(string(from: encoded))", file: file, line: line) + XCTAssert( + expected == encoded, + "Did not encode correctly: got \(string(from: encoded))", + file: file, + line: line + ) do { let decoded = try MessageTestType(serializedBytes: encoded) - XCTAssert(decoded == configured, "Encode/decode cycle should generate equal object: \(decoded) != \(configured)", file: file, line: line) + XCTAssert( + decoded == configured, + "Encode/decode cycle should generate equal object: \(decoded) != \(configured)", + file: file, + line: line + ) } catch { XCTFail("Failed to decode protobuf: \(string(from: encoded))", file: file, line: line) } @@ -47,7 +63,12 @@ extension PBTestHelpers where MessageTestType: SwiftProtobuf.Message & Equatable } } - func baseAssertDecodeSucceeds(_ bytes: [UInt8], file: XCTestFileArgType = #file, line: UInt = #line, check: (MessageTestType) -> Bool) { + func baseAssertDecodeSucceeds( + _ bytes: [UInt8], + file: XCTestFileArgType = #file, + line: UInt = #line, + check: (MessageTestType) -> Bool + ) { do { let decoded = try MessageTestType(serializedBytes: bytes) XCTAssert(check(decoded), "Condition failed for \(decoded)", file: file, line: line) @@ -69,13 +90,23 @@ extension PBTestHelpers where MessageTestType: SwiftProtobuf.Message & Equatable } } - func assertDecodeSucceeds(_ bytes: [UInt8], file: XCTestFileArgType = #file, line: UInt = #line, check: (MessageTestType) -> Bool) { + func assertDecodeSucceeds( + _ bytes: [UInt8], + file: XCTestFileArgType = #file, + line: UInt = #line, + check: (MessageTestType) -> Bool + ) { baseAssertDecodeSucceeds(bytes, file: file, line: line, check: check) } // Helper to check that decode succeeds by the data ended up in unknown fields. // Supports an optional `check` to do additional validation. - func assertDecodesAsUnknownFields(_ bytes: [UInt8], file: XCTestFileArgType = #file, line: UInt = #line, check: ((MessageTestType) -> Bool)? = nil) { + func assertDecodesAsUnknownFields( + _ bytes: [UInt8], + file: XCTestFileArgType = #file, + line: UInt = #line, + check: ((MessageTestType) -> Bool)? = nil + ) { assertDecodeSucceeds(bytes, file: file, line: line) { if $0.unknownFields.data != Data(bytes) { return false @@ -87,7 +118,13 @@ extension PBTestHelpers where MessageTestType: SwiftProtobuf.Message & Equatable } } - func assertMergesAsUnknownFields(_ bytes: [UInt8], inTo message: MessageTestType, file: XCTestFileArgType = #file, line: UInt = #line, check: ((MessageTestType) -> Bool)? = nil) { + func assertMergesAsUnknownFields( + _ bytes: [UInt8], + inTo message: MessageTestType, + file: XCTestFileArgType = #file, + line: UInt = #line, + check: ((MessageTestType) -> Bool)? = nil + ) { var msgCopy = message do { try msgCopy.merge(serializedBytes: bytes) @@ -100,14 +137,26 @@ extension PBTestHelpers where MessageTestType: SwiftProtobuf.Message & Equatable } } - func assertDecodeSucceeds(inputBytes bytes: [UInt8], recodedBytes: [UInt8], file: XCTestFileArgType = #file, line: UInt = #line, check: (MessageTestType) -> Bool) { + func assertDecodeSucceeds( + inputBytes bytes: [UInt8], + recodedBytes: [UInt8], + file: XCTestFileArgType = #file, + line: UInt = #line, + check: (MessageTestType) -> Bool + ) { do { let decoded = try MessageTestType(serializedBytes: bytes) XCTAssert(check(decoded), "Condition failed for \(decoded)", file: file, line: line) do { let encoded: [UInt8] = try decoded.serializedBytes() - XCTAssertEqual(recodedBytes, encoded, "Didn't recode as expected: \(string(from: encoded)) expected: \(recodedBytes)", file: file, line: line) + XCTAssertEqual( + recodedBytes, + encoded, + "Didn't recode as expected: \(string(from: encoded)) expected: \(recodedBytes)", + file: file, + line: line + ) do { let redecoded = try MessageTestType(serializedBytes: encoded) XCTAssert(check(redecoded), "Condition failed for redecoded \(redecoded)", file: file, line: line) @@ -123,7 +172,6 @@ extension PBTestHelpers where MessageTestType: SwiftProtobuf.Message & Equatable } } - func assertDecodeFails(_ bytes: [UInt8], file: XCTestFileArgType = #file, line: UInt = #line) { do { let _ = try MessageTestType(serializedBytes: bytes) @@ -134,19 +182,40 @@ extension PBTestHelpers where MessageTestType: SwiftProtobuf.Message & Equatable } - func assertJSONEncode(_ expected: String, extensions: any ExtensionMap = SimpleExtensionMap(), encodingOptions: JSONEncodingOptions = .init(), file: XCTestFileArgType = #file, line: UInt = #line, configure: (inout MessageTestType) -> Void) { + func assertJSONEncode( + _ expected: String, + extensions: any ExtensionMap = SimpleExtensionMap(), + encodingOptions: JSONEncodingOptions = .init(), + file: XCTestFileArgType = #file, + line: UInt = #line, + configure: (inout MessageTestType) -> Void + ) { let empty = MessageTestType() var configured = empty configure(&configured) XCTAssert(configured != empty, "Object should not be equal to empty object", file: file, line: line) do { let encoded = try configured.jsonString(options: encodingOptions) - XCTAssert(expected == encoded, "Did not encode correctly: got \(encoded) but expected \(expected)", file: file, line: line) + XCTAssert( + expected == encoded, + "Did not encode correctly: got \(encoded) but expected \(expected)", + file: file, + line: line + ) do { let decoded = try MessageTestType(jsonString: encoded, extensions: extensions) - XCTAssert(decoded == configured, "Encode/decode cycle should generate equal object: \(decoded) != \(configured)", file: file, line: line) + XCTAssert( + decoded == configured, + "Encode/decode cycle should generate equal object: \(decoded) != \(configured)", + file: file, + line: line + ) } catch { - XCTFail("Encode/decode cycle should not throw error decoding: \(encoded), but it threw \(error)", file: file, line: line) + XCTFail( + "Encode/decode cycle should not throw error decoding: \(encoded), but it threw \(error)", + file: file, + line: line + ) } } catch let e { XCTFail("Failed to serialize JSON: \(e)\n \(configured)", file: file, line: line) @@ -157,12 +226,26 @@ extension PBTestHelpers where MessageTestType: SwiftProtobuf.Message & Equatable let encodedOptString = String(bytes: encodedData, encoding: String.Encoding.utf8) XCTAssertNotNil(encodedOptString) let encodedString = encodedOptString! - XCTAssert(expected == encodedString, "Did not encode correctly: got \(encodedString)", file: file, line: line) + XCTAssert( + expected == encodedString, + "Did not encode correctly: got \(encodedString)", + file: file, + line: line + ) do { let decoded = try MessageTestType(jsonUTF8Bytes: encodedData, extensions: extensions) - XCTAssert(decoded == configured, "Encode/decode cycle should generate equal object: \(decoded) != \(configured)", file: file, line: line) + XCTAssert( + decoded == configured, + "Encode/decode cycle should generate equal object: \(decoded) != \(configured)", + file: file, + line: line + ) } catch { - XCTFail("Encode/decode cycle should not throw error decoding: \(encodedString), but it threw \(error)", file: file, line: line) + XCTFail( + "Encode/decode cycle should not throw error decoding: \(encodedString), but it threw \(error)", + file: file, + line: line + ) } } catch let e { XCTFail("Failed to serialize JSON: \(e)\n \(configured)", file: file, line: line) @@ -173,7 +256,13 @@ extension PBTestHelpers where MessageTestType: SwiftProtobuf.Message & Equatable /// This uses the provided block to initialize the object, then: /// * Encodes the object and checks that the result is the expected result /// * Decodes it again and verifies that the round-trip gives an equal object - func assertTextFormatEncode(_ expected: String, extensions: (any ExtensionMap)? = nil, file: XCTestFileArgType = #file, line: UInt = #line, configure: (inout MessageTestType) -> Void) { + func assertTextFormatEncode( + _ expected: String, + extensions: (any ExtensionMap)? = nil, + file: XCTestFileArgType = #file, + line: UInt = #line, + configure: (inout MessageTestType) -> Void + ) { let empty = MessageTestType() var configured = empty configure(&configured) @@ -183,9 +272,18 @@ extension PBTestHelpers where MessageTestType: SwiftProtobuf.Message & Equatable XCTAssertEqual(expected, encoded, "Did not encode correctly", file: file, line: line) do { let decoded = try MessageTestType(textFormatString: encoded, extensions: extensions) - XCTAssert(decoded == configured, "Encode/decode cycle should generate equal object: \(decoded) != \(configured)", file: file, line: line) + XCTAssert( + decoded == configured, + "Encode/decode cycle should generate equal object: \(decoded) != \(configured)", + file: file, + line: line + ) } catch { - XCTFail("Encode/decode cycle should not throw error but got \(error) while decoding \(encoded)", file: file, line: line) + XCTFail( + "Encode/decode cycle should not throw error but got \(error) while decoding \(encoded)", + file: file, + line: line + ) } } @@ -204,11 +302,22 @@ extension PBTestHelpers where MessageTestType: SwiftProtobuf.Message & Equatable let encoded = try MessageTestType.jsonString(from: configured) XCTAssert(expected == encoded, "Did not encode correctly: got \(encoded)", file: file, line: line) do { - let decoded = try MessageTestType.array(fromJSONString: encoded, - extensions: extensions) - XCTAssert(decoded == configured, "Encode/decode cycle should generate equal object: \(decoded) != \(configured)", file: file, line: line) + let decoded = try MessageTestType.array( + fromJSONString: encoded, + extensions: extensions + ) + XCTAssert( + decoded == configured, + "Encode/decode cycle should generate equal object: \(decoded) != \(configured)", + file: file, + line: line + ) } catch { - XCTFail("Encode/decode cycle should not throw error decoding: \(encoded), but it threw \(error)", file: file, line: line) + XCTFail( + "Encode/decode cycle should not throw error decoding: \(encoded), but it threw \(error)", + file: file, + line: line + ) } } catch let e { XCTFail("Failed to serialize JSON: \(e)\n \(configured)", file: file, line: line) @@ -224,14 +333,23 @@ extension PBTestHelpers where MessageTestType: SwiftProtobuf.Message & Equatable check: (MessageTestType) -> Bool ) { do { - let decoded: MessageTestType = try MessageTestType(jsonString: json, extensions: extensions, options: options) + let decoded: MessageTestType = try MessageTestType( + jsonString: json, + extensions: extensions, + options: options + ) XCTAssert(check(decoded), "Condition failed for \(decoded)", file: file, line: line) do { let encoded = try decoded.jsonString() do { let redecoded = try MessageTestType(jsonString: encoded, extensions: extensions, options: options) - XCTAssert(check(redecoded), "Condition failed for redecoded \(redecoded) from \(encoded)", file: file, line: line) + XCTAssert( + check(redecoded), + "Condition failed for redecoded \(redecoded) from \(encoded)", + file: file, + line: line + ) XCTAssertEqual(decoded, redecoded, file: file, line: line) } catch { XCTFail("Swift should have recoded/redecoded without error: \(encoded)", file: file, line: line) @@ -246,18 +364,35 @@ extension PBTestHelpers where MessageTestType: SwiftProtobuf.Message & Equatable do { let jsonData = json.data(using: String.Encoding.utf8)! - let decoded: MessageTestType = try MessageTestType(jsonUTF8Bytes: jsonData, extensions: extensions, options: options) + let decoded: MessageTestType = try MessageTestType( + jsonUTF8Bytes: jsonData, + extensions: extensions, + options: options + ) XCTAssert(check(decoded), "Condition failed for \(decoded) from binary \(json)", file: file, line: line) do { let encoded: [UInt8] = try decoded.jsonUTF8Bytes() let encodedString = String(bytes: encoded, encoding: String.Encoding.utf8)! do { - let redecoded = try MessageTestType(jsonUTF8Bytes: encoded, extensions: extensions, options: options) - XCTAssert(check(redecoded), "Condition failed for redecoded \(redecoded) from binary \(encodedString)", file: file, line: line) + let redecoded = try MessageTestType( + jsonUTF8Bytes: encoded, + extensions: extensions, + options: options + ) + XCTAssert( + check(redecoded), + "Condition failed for redecoded \(redecoded) from binary \(encodedString)", + file: file, + line: line + ) XCTAssertEqual(decoded, redecoded, file: file, line: line) } catch { - XCTFail("Swift should have recoded/redecoded without error: \(encodedString)", file: file, line: line) + XCTFail( + "Swift should have recoded/redecoded without error: \(encodedString)", + file: file, + line: line + ) } } catch let e { XCTFail("Swift should have recoded without error but got \(e)\n \(decoded)", file: file, line: line) @@ -268,7 +403,13 @@ extension PBTestHelpers where MessageTestType: SwiftProtobuf.Message & Equatable } } - func assertTextFormatDecodeSucceeds(_ text: String, options: TextFormatDecodingOptions = TextFormatDecodingOptions(), file: XCTestFileArgType = #file, line: UInt = #line, check: (MessageTestType) throws -> Bool) { + func assertTextFormatDecodeSucceeds( + _ text: String, + options: TextFormatDecodingOptions = TextFormatDecodingOptions(), + file: XCTestFileArgType = #file, + line: UInt = #line, + check: (MessageTestType) throws -> Bool + ) { do { let decoded: MessageTestType = try MessageTestType(textFormatString: text, options: options) do { @@ -370,7 +511,12 @@ extension PBTestHelpers where MessageTestType: SwiftProtobuf.Message & Equatable } } - func assertDebugDescription(_ expected: String, file: XCTestFileArgType = #file, line: UInt = #line, configure: (inout MessageTestType) -> ()) { + func assertDebugDescription( + _ expected: String, + file: XCTestFileArgType = #file, + line: UInt = #line, + configure: (inout MessageTestType) -> Void + ) { // `assertDebugDescription` is a no-op in release as `debugDescription` is unavailable. #if DEBUG var m = MessageTestType() @@ -382,7 +528,13 @@ extension PBTestHelpers where MessageTestType: SwiftProtobuf.Message & Equatable } extension XCTestCase { - func assertDebugDescription(_ expected: String, _ m: any SwiftProtobuf.Message, fmt: String? = nil, file: XCTestFileArgType = #file, line: UInt = #line) { + func assertDebugDescription( + _ expected: String, + _ m: any SwiftProtobuf.Message, + fmt: String? = nil, + file: XCTestFileArgType = #file, + line: UInt = #line + ) { // `assertDebugDescription` is a no-op in release as `debugDescription` is unavailable. #if DEBUG let actual = m.debugDescription @@ -392,14 +544,20 @@ extension XCTestCase { /// Like ``assertDebugDescription``, but only checks the the ``debugDescription`` ends with /// ``expectedSuffix``, mainly useful where you want to be agnotics to some preable like /// the module name. - func assertDebugDescriptionSuffix(_ expectedSuffix: String, _ m: any SwiftProtobuf.Message, fmt: String? = nil, file: XCTestFileArgType = #file, line: UInt = #line) { + func assertDebugDescriptionSuffix( + _ expectedSuffix: String, + _ m: any SwiftProtobuf.Message, + fmt: String? = nil, + file: XCTestFileArgType = #file, + line: UInt = #line + ) { // `assertDebugDescriptionSuffix` is a no-op in release as `debugDescription` is unavailable. -#if DEBUG + #if DEBUG let actual = m.debugDescription XCTAssertTrue(actual.hasSuffix(expectedSuffix), fmt ?? "debugDescription did not match", file: file, line: line) -#endif + #endif } - + func isSwiftProtobufErrorEqual(_ actual: SwiftProtobufError, _ expected: SwiftProtobufError) -> Bool { (actual.code == expected.code) && (actual.message == expected.message) } @@ -409,67 +567,67 @@ extension XCTestCase { /// that will cause a failure if anything gets called. This way specific tests can /// just hook the methods they intend to validate. protocol PBTestVisitor: Visitor { - // Adds nothing. + // Adds nothing. } extension PBTestVisitor { - mutating func visitUnknown(bytes: Data) throws { - XCTFail("Unexpected unknowns: \(bytes)") - } - - mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws { - XCTFail("Unexpected bool: \(fieldNumber) = \(value)") - } - - mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws { - XCTFail("Unexpected bytes: \(fieldNumber) = \(value)") - } - - mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws { - XCTFail("Unexpected Int64: \(fieldNumber) = \(value)") - } - - mutating func visitSingularEnumField(value: E, fieldNumber: Int) throws { - XCTFail("Unexpected Enum: \(fieldNumber) = \(value)") - } - - mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws { - XCTFail("Unexpected Int64: \(fieldNumber) = \(value)") - } - - mutating func visitSingularMessageField(value: M, fieldNumber: Int) throws { - XCTFail("Unexpected Message: \(fieldNumber) = \(value)") - } - - mutating func visitSingularStringField(value: String, fieldNumber: Int) throws { - XCTFail("Unexpected String: \(fieldNumber) = \(value)") - } - - mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws { - XCTFail("Unexpected UInt64: \(fieldNumber) = \(value)") - } - - mutating func visitMapField( - fieldType: _ProtobufMap.Type, - value: _ProtobufMap.BaseType, - fieldNumber: Int - ) throws { - XCTFail("Unexpected map<*, *>: \(fieldNumber) = \(value)") - } - - mutating func visitMapField( - fieldType: _ProtobufEnumMap.Type, - value: _ProtobufEnumMap.BaseType, - fieldNumber: Int - ) throws where ValueType.RawValue == Int { - XCTFail("Unexpected map<*, Enum>: \(fieldNumber) = \(value)") - } - - mutating func visitMapField( - fieldType: _ProtobufMessageMap.Type, - value: _ProtobufMessageMap.BaseType, - fieldNumber: Int - ) throws { - XCTFail("Unexpected map<*, Message>: \(fieldNumber) = \(value)") - } + mutating func visitUnknown(bytes: Data) throws { + XCTFail("Unexpected unknowns: \(bytes)") + } + + mutating func visitSingularBoolField(value: Bool, fieldNumber: Int) throws { + XCTFail("Unexpected bool: \(fieldNumber) = \(value)") + } + + mutating func visitSingularBytesField(value: Data, fieldNumber: Int) throws { + XCTFail("Unexpected bytes: \(fieldNumber) = \(value)") + } + + mutating func visitSingularDoubleField(value: Double, fieldNumber: Int) throws { + XCTFail("Unexpected Int64: \(fieldNumber) = \(value)") + } + + mutating func visitSingularEnumField(value: E, fieldNumber: Int) throws { + XCTFail("Unexpected Enum: \(fieldNumber) = \(value)") + } + + mutating func visitSingularInt64Field(value: Int64, fieldNumber: Int) throws { + XCTFail("Unexpected Int64: \(fieldNumber) = \(value)") + } + + mutating func visitSingularMessageField(value: M, fieldNumber: Int) throws { + XCTFail("Unexpected Message: \(fieldNumber) = \(value)") + } + + mutating func visitSingularStringField(value: String, fieldNumber: Int) throws { + XCTFail("Unexpected String: \(fieldNumber) = \(value)") + } + + mutating func visitSingularUInt64Field(value: UInt64, fieldNumber: Int) throws { + XCTFail("Unexpected UInt64: \(fieldNumber) = \(value)") + } + + mutating func visitMapField( + fieldType: _ProtobufMap.Type, + value: _ProtobufMap.BaseType, + fieldNumber: Int + ) throws { + XCTFail("Unexpected map<*, *>: \(fieldNumber) = \(value)") + } + + mutating func visitMapField( + fieldType: _ProtobufEnumMap.Type, + value: _ProtobufEnumMap.BaseType, + fieldNumber: Int + ) throws where ValueType.RawValue == Int { + XCTFail("Unexpected map<*, Enum>: \(fieldNumber) = \(value)") + } + + mutating func visitMapField( + fieldType: _ProtobufMessageMap.Type, + value: _ProtobufMessageMap.BaseType, + fieldNumber: Int + ) throws { + XCTFail("Unexpected map<*, Message>: \(fieldNumber) = \(value)") + } } diff --git a/Tests/SwiftProtobufTests/Test_AllTypes.swift b/Tests/SwiftProtobufTests/Test_AllTypes.swift index 9410dd87b..fd889d97e 100644 --- a/Tests/SwiftProtobufTests/Test_AllTypes.swift +++ b/Tests/SwiftProtobufTests/Test_AllTypes.swift @@ -24,7 +24,12 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { // Custom decodeSucceeds that also does a round-trip through the Empty // message to make sure unknown fields are consistently preserved by proto2. - func assertDecodeSucceeds(_ bytes: [UInt8], file: XCTestFileArgType = #file, line: UInt = #line, check: (MessageTestType) -> Bool) { + func assertDecodeSucceeds( + _ bytes: [UInt8], + file: XCTestFileArgType = #file, + line: UInt = #line, + check: (MessageTestType) -> Bool + ) { baseAssertDecodeSucceeds(bytes, file: file, line: line, check: check) do { // Make sure unknown fields are preserved by empty message decode/encode @@ -44,15 +49,15 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { // Unknown field // func testEncoding_unknown() { - assertDecodeFails([208, 41]) // Field 666, wiretype 0 - assertDecodeSucceeds([208, 41, 0]) {$0 != MessageTestType()} // Ditto, with varint body + assertDecodeFails([208, 41]) // Field 666, wiretype 0 + assertDecodeSucceeds([208, 41, 0]) { $0 != MessageTestType() } // Ditto, with varint body // This test validation when putting things into unknown fields. In // this case, ensuring the a varint value isn't invalid. assertDecodeFails([ - (7 << 3) + 0, // Field 7 as a varint (it should be a fixed32) - // And overly encoded varint for the value (extracted from some fuzz testing) - 239, 191, 189, 239, 191, 189, 239, 191, 189, 239, 191, 189, 49, + (7 << 3) + 0, // Field 7 as a varint (it should be a fixed32) + // And overly encoded varint for the value (extracted from some fuzz testing) + 239, 191, 189, 239, 191, 189, 239, 191, 189, 239, 191, 189, 49, ]) } @@ -60,16 +65,22 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { // Singular types // func testEncoding_optionalInt32() { - assertEncode([8, 1]) {(o: inout MessageTestType) in o.optionalInt32 = 1} - assertEncode([8, 255, 255, 255, 255, 7]) {(o: inout MessageTestType) in o.optionalInt32 = Int32.max} - assertEncode([8, 128, 128, 128, 128, 248, 255, 255, 255, 255, 1]) {(o: inout MessageTestType) in o.optionalInt32 = Int32.min} - assertDecodeSucceeds([8, 1]) {$0.optionalInt32 == 1} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_int32: 1\n") {(o: inout MessageTestType) in o.optionalInt32 = 1} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_int32: -2147483648\noptional_uint32: 4294967295\n") {(o: inout MessageTestType) in + assertEncode([8, 1]) { (o: inout MessageTestType) in o.optionalInt32 = 1 } + assertEncode([8, 255, 255, 255, 255, 7]) { (o: inout MessageTestType) in o.optionalInt32 = Int32.max } + assertEncode([8, 128, 128, 128, 128, 248, 255, 255, 255, 255, 1]) { (o: inout MessageTestType) in + o.optionalInt32 = Int32.min + } + assertDecodeSucceeds([8, 1]) { $0.optionalInt32 == 1 } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_int32: 1\n") { + (o: inout MessageTestType) in o.optionalInt32 = 1 + } + assertDebugDescription( + "SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_int32: -2147483648\noptional_uint32: 4294967295\n" + ) { (o: inout MessageTestType) in o.optionalInt32 = Int32.min o.optionalUint32 = UInt32.max } - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\n") {(o: inout MessageTestType) in + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\n") { (o: inout MessageTestType) in o.optionalInt32 = 1 o.clearOptionalInt32() } @@ -85,17 +96,17 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } // We should recognize a valid field after an unknown field: - assertDecodeSucceeds([208, 41, 0, 8, 1]) {$0.optionalInt32 == 1} + assertDecodeSucceeds([208, 41, 0, 8, 1]) { $0.optionalInt32 == 1 } assertDecodeFails([8]) - assertDecodeFails([9, 57]) // Cannot use wire type 1 - assertDecodeFails([10, 58]) // Cannot use wire type 2 - assertDecodeFails([11, 59]) // Cannot use wire type 3 - assertDecodeFails([12, 60]) // Cannot use wire type 4 - assertDecodeFails([13, 61]) // Cannot use wire type 5 - assertDecodeFails([14, 62]) // Cannot use wire type 6 - assertDecodeFails([15, 63]) // Cannot use wire type 7 + assertDecodeFails([9, 57]) // Cannot use wire type 1 + assertDecodeFails([10, 58]) // Cannot use wire type 2 + assertDecodeFails([11, 59]) // Cannot use wire type 3 + assertDecodeFails([12, 60]) // Cannot use wire type 4 + assertDecodeFails([13, 61]) // Cannot use wire type 5 + assertDecodeFails([14, 62]) // Cannot use wire type 6 + assertDecodeFails([15, 63]) // Cannot use wire type 7 assertDecodeFails([8, 188]) assertDecodeFails([8]) @@ -126,11 +137,17 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_optionalInt64() { - assertEncode([16, 1]) {(o: inout MessageTestType) in o.optionalInt64 = 1} - assertEncode([16, 255, 255, 255, 255, 255, 255, 255, 255, 127]) {(o: inout MessageTestType) in o.optionalInt64 = Int64.max} - assertEncode([16, 128, 128, 128, 128, 128, 128, 128, 128, 128, 1]) {(o: inout MessageTestType) in o.optionalInt64 = Int64.min} - assertDecodeSucceeds([16, 184, 156, 195, 145, 203, 1]) {$0.optionalInt64 == 54529150520} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_int64: 1\n") {(o: inout MessageTestType) in o.optionalInt64 = 1} + assertEncode([16, 1]) { (o: inout MessageTestType) in o.optionalInt64 = 1 } + assertEncode([16, 255, 255, 255, 255, 255, 255, 255, 255, 127]) { (o: inout MessageTestType) in + o.optionalInt64 = Int64.max + } + assertEncode([16, 128, 128, 128, 128, 128, 128, 128, 128, 128, 1]) { (o: inout MessageTestType) in + o.optionalInt64 = Int64.min + } + assertDecodeSucceeds([16, 184, 156, 195, 145, 203, 1]) { $0.optionalInt64 == 54_529_150_520 } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_int64: 1\n") { + (o: inout MessageTestType) in o.optionalInt64 = 1 + } assertDecodeFails([16]) assertDecodeFails([16, 184, 156, 195, 145, 203]) assertDecodeFails([17, 81]) @@ -168,10 +185,12 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_optionalUint32() { - assertEncode([24, 255, 255, 255, 255, 15]) {(o: inout MessageTestType) in o.optionalUint32 = UInt32.max} - assertEncode([24, 0]) {(o: inout MessageTestType) in o.optionalUint32 = UInt32.min} - assertDecodeSucceeds([24, 149, 88]) {$0.optionalUint32 == 11285} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_uint32: 1\n") {(o: inout MessageTestType) in o.optionalUint32 = 1} + assertEncode([24, 255, 255, 255, 255, 15]) { (o: inout MessageTestType) in o.optionalUint32 = UInt32.max } + assertEncode([24, 0]) { (o: inout MessageTestType) in o.optionalUint32 = UInt32.min } + assertDecodeSucceeds([24, 149, 88]) { $0.optionalUint32 == 11285 } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_uint32: 1\n") { + (o: inout MessageTestType) in o.optionalUint32 = 1 + } assertDecodeFails([24]) assertDecodeFails([24, 149]) assertDecodeFails([25, 105]) @@ -209,10 +228,14 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_optionalUint64() { - assertEncode([32, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1]) {(o: inout MessageTestType) in o.optionalUint64 = UInt64.max} - assertEncode([32, 0]) {(o: inout MessageTestType) in o.optionalUint64 = UInt64.min} - assertDecodeSucceeds([32, 149, 7]) {$0.optionalUint64 == 917} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_uint64: 1\n") {(o: inout MessageTestType) in o.optionalUint64 = 1} + assertEncode([32, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1]) { (o: inout MessageTestType) in + o.optionalUint64 = UInt64.max + } + assertEncode([32, 0]) { (o: inout MessageTestType) in o.optionalUint64 = UInt64.min } + assertDecodeSucceeds([32, 149, 7]) { $0.optionalUint64 == 917 } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_uint64: 1\n") { + (o: inout MessageTestType) in o.optionalUint64 = 1 + } assertDecodeFails([32]) assertDecodeFails([32, 149]) assertDecodeFails([32, 149, 190, 193, 230, 186, 233, 166, 219]) @@ -220,7 +243,7 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { assertDecodeFails([33, 0]) assertDecodeFails([33, 8, 0]) assertDecodeFails([34]) - assertDecodesAsUnknownFields([34, 0]) // Wrong wire type (length delimited), valid as an unknown field + assertDecodesAsUnknownFields([34, 0]) // Wrong wire type (length delimited), valid as an unknown field assertDecodeFails([34, 8, 0]) assertDecodeFails([35]) assertDecodeFails([35, 0]) @@ -265,18 +288,24 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_optionalSint32() { - assertEncode([40, 254, 255, 255, 255, 15]) {(o: inout MessageTestType) in o.optionalSint32 = Int32.max} - assertEncode([40, 255, 255, 255, 255, 15]) {(o: inout MessageTestType) in o.optionalSint32 = Int32.min} - assertDecodeSucceeds([40, 0x81, 0x82, 0x80, 0x00]) {$0.optionalSint32 == -129} - assertDecodeSucceeds([40, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00]) {$0.optionalSint32 == 0} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_sint32: 1\n") {(o: inout MessageTestType) in o.optionalSint32 = 1} + assertEncode([40, 254, 255, 255, 255, 15]) { (o: inout MessageTestType) in o.optionalSint32 = Int32.max } + assertEncode([40, 255, 255, 255, 255, 15]) { (o: inout MessageTestType) in o.optionalSint32 = Int32.min } + assertDecodeSucceeds([40, 0x81, 0x82, 0x80, 0x00]) { $0.optionalSint32 == -129 } + assertDecodeSucceeds([40, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00]) { + $0.optionalSint32 == 0 + } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_sint32: 1\n") { + (o: inout MessageTestType) in o.optionalSint32 = 1 + } // Truncate on overflow - assertDecodeSucceeds([40, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f]) {$0.optionalSint32 == -2147483648} - assertDecodeSucceeds([40, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x7f]) {$0.optionalSint32 == 2147483647} + assertDecodeSucceeds([40, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f]) { $0.optionalSint32 == -2_147_483_648 } + assertDecodeSucceeds([40, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x7f]) { $0.optionalSint32 == 2_147_483_647 } assertDecodeFails([40]) - assertDecodeFails([40, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00]) + assertDecodeFails([ + 40, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + ]) assertDecodeFails([41]) assertDecodeFails([41, 0]) assertDecodeFails([42]) @@ -319,10 +348,16 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_optionalSint64() { - assertEncode([48, 254, 255, 255, 255, 255, 255, 255, 255, 255, 1]) {(o: inout MessageTestType) in o.optionalSint64 = Int64.max} - assertEncode([48, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1]) {(o: inout MessageTestType) in o.optionalSint64 = Int64.min} - assertDecodeSucceeds([48, 139, 94]) {$0.optionalSint64 == -6022} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_sint64: 1\n") {(o: inout MessageTestType) in o.optionalSint64 = 1} + assertEncode([48, 254, 255, 255, 255, 255, 255, 255, 255, 255, 1]) { (o: inout MessageTestType) in + o.optionalSint64 = Int64.max + } + assertEncode([48, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1]) { (o: inout MessageTestType) in + o.optionalSint64 = Int64.min + } + assertDecodeSucceeds([48, 139, 94]) { $0.optionalSint64 == -6022 } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_sint64: 1\n") { + (o: inout MessageTestType) in o.optionalSint64 = 1 + } assertDecodeFails([48]) assertDecodeFails([48, 139]) assertDecodeFails([49]) @@ -367,10 +402,12 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_optionalFixed32() { - assertEncode([61, 255, 255, 255, 255]) {(o: inout MessageTestType) in o.optionalFixed32 = UInt32.max} - assertEncode([61, 0, 0, 0, 0]) {(o: inout MessageTestType) in o.optionalFixed32 = UInt32.min} - assertDecodeSucceeds([61, 8, 12, 108, 1]) {$0.optionalFixed32 == 23858184} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_fixed32: 1\n") {(o: inout MessageTestType) in o.optionalFixed32 = 1} + assertEncode([61, 255, 255, 255, 255]) { (o: inout MessageTestType) in o.optionalFixed32 = UInt32.max } + assertEncode([61, 0, 0, 0, 0]) { (o: inout MessageTestType) in o.optionalFixed32 = UInt32.min } + assertDecodeSucceeds([61, 8, 12, 108, 1]) { $0.optionalFixed32 == 23_858_184 } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_fixed32: 1\n") { + (o: inout MessageTestType) in o.optionalFixed32 = 1 + } assertDecodeFails([61]) assertDecodeFails([61, 255]) assertDecodeFails([61, 255, 255]) @@ -424,10 +461,16 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_optionalFixed64() { - assertEncode([65, 255, 255, 255, 255, 255, 255, 255, 255]) {(o: inout MessageTestType) in o.optionalFixed64 = UInt64.max} - assertEncode([65, 0, 0, 0, 0, 0, 0, 0, 0]) {(o: inout MessageTestType) in o.optionalFixed64 = UInt64.min} - assertDecodeSucceeds([65, 255, 255, 255, 255, 255, 255, 255, 255]) {$0.optionalFixed64 == 18446744073709551615} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_fixed64: 1\n") {(o: inout MessageTestType) in o.optionalFixed64 = 1} + assertEncode([65, 255, 255, 255, 255, 255, 255, 255, 255]) { (o: inout MessageTestType) in + o.optionalFixed64 = UInt64.max + } + assertEncode([65, 0, 0, 0, 0, 0, 0, 0, 0]) { (o: inout MessageTestType) in o.optionalFixed64 = UInt64.min } + assertDecodeSucceeds([65, 255, 255, 255, 255, 255, 255, 255, 255]) { + $0.optionalFixed64 == 18_446_744_073_709_551_615 + } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_fixed64: 1\n") { + (o: inout MessageTestType) in o.optionalFixed64 = 1 + } assertDecodeFails([65]) assertDecodeFails([65, 255]) assertDecodeFails([65, 255, 255]) @@ -485,11 +528,13 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_optionalSfixed32() { - assertEncode([77, 255, 255, 255, 127]) {(o: inout MessageTestType) in o.optionalSfixed32 = Int32.max} - assertEncode([77, 0, 0, 0, 128]) {(o: inout MessageTestType) in o.optionalSfixed32 = Int32.min} - assertDecodeSucceeds([77, 0, 0, 0, 0]) {$0.optionalSfixed32 == 0} - assertDecodeSucceeds([77, 255, 255, 255, 255]) {$0.optionalSfixed32 == -1} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_sfixed32: 1\n") {(o: inout MessageTestType) in o.optionalSfixed32 = 1} + assertEncode([77, 255, 255, 255, 127]) { (o: inout MessageTestType) in o.optionalSfixed32 = Int32.max } + assertEncode([77, 0, 0, 0, 128]) { (o: inout MessageTestType) in o.optionalSfixed32 = Int32.min } + assertDecodeSucceeds([77, 0, 0, 0, 0]) { $0.optionalSfixed32 == 0 } + assertDecodeSucceeds([77, 255, 255, 255, 255]) { $0.optionalSfixed32 == -1 } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_sfixed32: 1\n") { + (o: inout MessageTestType) in o.optionalSfixed32 = 1 + } assertDecodeFails([77]) assertDecodeFails([77]) assertDecodeFails([77, 0]) @@ -544,10 +589,14 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_optionalSfixed64() { - assertEncode([81, 255, 255, 255, 255, 255, 255, 255, 127]) {(o: inout MessageTestType) in o.optionalSfixed64 = Int64.max} - assertEncode([81, 0, 0, 0, 0, 0, 0, 0, 128]) {(o: inout MessageTestType) in o.optionalSfixed64 = Int64.min} - assertDecodeSucceeds([81, 0, 0, 0, 0, 0, 0, 0, 128]) {$0.optionalSfixed64 == -9223372036854775808} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_sfixed64: 1\n") {(o: inout MessageTestType) in o.optionalSfixed64 = 1} + assertEncode([81, 255, 255, 255, 255, 255, 255, 255, 127]) { (o: inout MessageTestType) in + o.optionalSfixed64 = Int64.max + } + assertEncode([81, 0, 0, 0, 0, 0, 0, 0, 128]) { (o: inout MessageTestType) in o.optionalSfixed64 = Int64.min } + assertDecodeSucceeds([81, 0, 0, 0, 0, 0, 0, 0, 128]) { $0.optionalSfixed64 == -9_223_372_036_854_775_808 } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_sfixed64: 1\n") { + (o: inout MessageTestType) in o.optionalSfixed64 = 1 + } assertDecodeFails([81]) assertDecodeFails([81, 0]) assertDecodeFails([81, 0, 0]) @@ -603,9 +652,9 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_optionalFloat() { - assertEncode([93, 0, 0, 0, 0]) {(o: inout MessageTestType) in o.optionalFloat = 0.0} - assertEncode([93, 0, 0, 0, 63]) {(o: inout MessageTestType) in o.optionalFloat = 0.5} - assertEncode([93, 0, 0, 0, 64]) {(o: inout MessageTestType) in o.optionalFloat = 2.0} + assertEncode([93, 0, 0, 0, 0]) { (o: inout MessageTestType) in o.optionalFloat = 0.0 } + assertEncode([93, 0, 0, 0, 63]) { (o: inout MessageTestType) in o.optionalFloat = 0.5 } + assertEncode([93, 0, 0, 0, 64]) { (o: inout MessageTestType) in o.optionalFloat = 2.0 } assertDecodeSucceeds([93, 0, 0, 0, 0]) { if $0.hasOptionalFloat { return $0.optionalFloat == 0 @@ -615,25 +664,26 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } } assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_float: 1.0\n") { - (o: inout MessageTestType) in o.optionalFloat = 1.0} + (o: inout MessageTestType) in o.optionalFloat = 1.0 + } assertDecodeFails([93, 0, 0, 0]) assertDecodeFails([93, 0, 0]) assertDecodeFails([93, 0]) assertDecodeFails([93]) - assertDecodeFails([88]) // Float cannot use wire type 0 + assertDecodeFails([88]) // Float cannot use wire type 0 assertDecodesAsUnknownFields([88, 0]) // Wrong wire type (varint), valid as an unknown field - assertDecodeFails([89]) // Float cannot use wire type 1 - assertDecodeFails([89, 0, 0, 0, 0]) // Float cannot use wire type 1 - assertDecodeFails([90]) // Float cannot use wire type 2 + assertDecodeFails([89]) // Float cannot use wire type 1 + assertDecodeFails([89, 0, 0, 0, 0]) // Float cannot use wire type 1 + assertDecodeFails([90]) // Float cannot use wire type 2 assertDecodesAsUnknownFields([90, 0]) // Wrong wire type (length delimited), valid as an unknown field - assertDecodeFails([91]) // Float cannot use wire type 3 - assertDecodeFails([91, 0, 0, 0, 0]) // Float cannot use wire type 3 - assertDecodeFails([92]) // Float cannot use wire type 4 - assertDecodeFails([92, 0, 0, 0, 0]) // Float cannot use wire type 4 - assertDecodeFails([94]) // Float cannot use wire type 6 - assertDecodeFails([94, 0, 0, 0, 0]) // Float cannot use wire type 6 - assertDecodeFails([95]) // Float cannot use wire type 7 - assertDecodeFails([95, 0, 0, 0, 0]) // Float cannot use wire type 7 + assertDecodeFails([91]) // Float cannot use wire type 3 + assertDecodeFails([91, 0, 0, 0, 0]) // Float cannot use wire type 3 + assertDecodeFails([92]) // Float cannot use wire type 4 + assertDecodeFails([92, 0, 0, 0, 0]) // Float cannot use wire type 4 + assertDecodeFails([94]) // Float cannot use wire type 6 + assertDecodeFails([94, 0, 0, 0, 0]) // Float cannot use wire type 6 + assertDecodeFails([95]) // Float cannot use wire type 7 + assertDecodeFails([95, 0, 0, 0, 0]) // Float cannot use wire type 7 let empty = MessageTestType() var a = empty @@ -662,12 +712,13 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_optionalDouble() { - assertEncode([97, 0, 0, 0, 0, 0, 0, 0, 0]) {(o: inout MessageTestType) in o.optionalDouble = 0.0} - assertEncode([97, 0, 0, 0, 0, 0, 0, 224, 63]) {(o: inout MessageTestType) in o.optionalDouble = 0.5} - assertEncode([97, 0, 0, 0, 0, 0, 0, 0, 64]) {(o: inout MessageTestType) in o.optionalDouble = 2.0} - assertDecodeSucceeds([97, 0, 0, 0, 0, 0, 0, 224, 63]) {$0.optionalDouble == 0.5} + assertEncode([97, 0, 0, 0, 0, 0, 0, 0, 0]) { (o: inout MessageTestType) in o.optionalDouble = 0.0 } + assertEncode([97, 0, 0, 0, 0, 0, 0, 224, 63]) { (o: inout MessageTestType) in o.optionalDouble = 0.5 } + assertEncode([97, 0, 0, 0, 0, 0, 0, 0, 64]) { (o: inout MessageTestType) in o.optionalDouble = 2.0 } + assertDecodeSucceeds([97, 0, 0, 0, 0, 0, 0, 224, 63]) { $0.optionalDouble == 0.5 } assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_double: 1.0\n") { - (o: inout MessageTestType) in o.optionalDouble = 1.0} + (o: inout MessageTestType) in o.optionalDouble = 1.0 + } assertDecodeFails([97, 0, 0, 0, 0, 0, 0, 224]) assertDecodeFails([97]) assertDecodeFails([96]) @@ -719,8 +770,8 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_optionalBool() { - assertEncode([104, 0]) {(o: inout MessageTestType) in o.optionalBool = false} - assertEncode([104, 1]) {(o: inout MessageTestType) in o.optionalBool = true} + assertEncode([104, 0]) { (o: inout MessageTestType) in o.optionalBool = false } + assertEncode([104, 1]) { (o: inout MessageTestType) in o.optionalBool = true } assertDecodeSucceeds([104, 1]) { if $0.hasOptionalBool { return $0.optionalBool == true @@ -729,8 +780,12 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { return false } } - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_bool: true\n") {(o: inout MessageTestType) in o.optionalBool = true} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_bool: false\n") {(o: inout MessageTestType) in o.optionalBool = false} + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_bool: true\n") { + (o: inout MessageTestType) in o.optionalBool = true + } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_bool: false\n") { + (o: inout MessageTestType) in o.optionalBool = false + } assertDecodeFails([104]) assertDecodeFails([104, 255]) assertDecodeFails([105]) @@ -775,11 +830,12 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_optionalString() { - assertEncode([114, 0]) {(o: inout MessageTestType) in o.optionalString = ""} - assertEncode([114, 1, 65]) {(o: inout MessageTestType) in o.optionalString = "A"} - assertEncode([114, 4, 0xf0, 0x9f, 0x98, 0x84]) {(o: inout MessageTestType) in o.optionalString = "😄"} - assertEncode([114, 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) {(o: inout MessageTestType) in - o.optionalString = "\u{00}\u{01}\u{02}\u{03}\u{04}\u{05}\u{06}\u{07}\u{08}\u{09}\u{0a}"} + assertEncode([114, 0]) { (o: inout MessageTestType) in o.optionalString = "" } + assertEncode([114, 1, 65]) { (o: inout MessageTestType) in o.optionalString = "A" } + assertEncode([114, 4, 0xf0, 0x9f, 0x98, 0x84]) { (o: inout MessageTestType) in o.optionalString = "😄" } + assertEncode([114, 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) { (o: inout MessageTestType) in + o.optionalString = "\u{00}\u{01}\u{02}\u{03}\u{04}\u{05}\u{06}\u{07}\u{08}\u{09}\u{0a}" + } assertDecodeSucceeds([114, 5, 72, 101, 108, 108, 111]) { if $0.hasOptionalString { return $0.optionalString == "Hello" @@ -789,20 +845,26 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } } assertDecodeSucceeds([114, 4, 97, 0, 98, 99]) { - return $0.optionalString == "a\0bc" + $0.optionalString == "a\0bc" } assertDecodeSucceeds([114, 16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]) { - return $0.optionalString == "\u{00}\u{01}\u{02}\u{03}\u{04}\u{05}\u{06}\u{07}\u{08}\u{09}\u{0a}\u{0b}\u{0c}\u{0d}\u{0e}\u{0f}" + $0.optionalString + == "\u{00}\u{01}\u{02}\u{03}\u{04}\u{05}\u{06}\u{07}\u{08}\u{09}\u{0a}\u{0b}\u{0c}\u{0d}\u{0e}\u{0f}" } assertDecodeSucceeds([114, 16, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]) { - return $0.optionalString == "\u{10}\u{11}\u{12}\u{13}\u{14}\u{15}\u{16}\u{17}\u{18}\u{19}\u{1a}\u{1b}\u{1c}\u{1d}\u{1e}\u{1f}" + $0.optionalString + == "\u{10}\u{11}\u{12}\u{13}\u{14}\u{15}\u{16}\u{17}\u{18}\u{19}\u{1a}\u{1b}\u{1c}\u{1d}\u{1e}\u{1f}" + } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_string: \"abc\"\n") { + (o: inout MessageTestType) in o.optionalString = "abc" + } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_string: \"\\b\\t\"\n") { + (o: inout MessageTestType) in o.optionalString = "\u{08}\u{09}" } - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_string: \"abc\"\n") {(o: inout MessageTestType) in o.optionalString = "abc"} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_string: \"\\b\\t\"\n") {(o: inout MessageTestType) in o.optionalString = "\u{08}\u{09}"} assertDecodeFails([114]) assertDecodeFails([114, 1]) assertDecodeFails([114, 2, 65]) - assertDecodeFails([114, 1, 193]) // Invalid UTF-8 + assertDecodeFails([114, 1, 193]) // Invalid UTF-8 assertDecodeFails([112]) assertDecodesAsUnknownFields([112, 0]) // Wrong wire type (varint), valid as an unknown field assertDecodeFails([113]) @@ -819,12 +881,14 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { assertDecodeFails([119, 0]) // Ensure strings over 2GB fail to decode according to spec. - XCTAssertThrowsError(try MessageTestType(serializedBytes: [ - 114, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, - // Don't need all the bytes, want some to let the length issue trigger. - 0x01, 0x02, 0x03, - ])) { - XCTAssertEqual($0 as! BinaryDecodingError, .malformedProtobuf) + XCTAssertThrowsError( + try MessageTestType(serializedBytes: [ + 114, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, + // Don't need all the bytes, want some to let the length issue trigger. + 0x01, 0x02, 0x03, + ]) + ) { + XCTAssertEqual($0 as! BinaryDecodingError, .malformedProtobuf) } let empty = MessageTestType() @@ -854,7 +918,7 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_optionalGroup() { - assertEncode([131, 1, 136, 1, 159, 141, 6, 132, 1]) {(o: inout MessageTestType) in + assertEncode([131, 1, 136, 1, 159, 141, 6, 132, 1]) { (o: inout MessageTestType) in var g = MessageTestType.OptionalGroup() g.a = 99999 o.optionalGroup = g @@ -865,36 +929,37 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { // Extra field 1 (varint of zero) within group assertDecodeSucceeds([131, 1, 8, 0, 136, 1, 159, 141, 6, 132, 1]) { $0.optionalGroup.a == 99999 - && $0.optionalGroup.unknownFields.data == Data([8, 0]) + && $0.optionalGroup.unknownFields.data == Data([8, 0]) } // Empty group assertDecodeSucceeds([131, 1, 132, 1]) { $0.optionalGroup == MessageTestType.OptionalGroup() } assertDebugDescription( - "SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\nOptionalGroup {\n a: 1\n}\n") {(o: inout MessageTestType) in + "SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\nOptionalGroup {\n a: 1\n}\n" + ) { (o: inout MessageTestType) in var g = MessageTestType.OptionalGroup() g.a = 1 o.optionalGroup = g } - assertDecodeFails([131, 1, 136, 1, 159, 141, 6]) // End group missing. - assertDecodeFails([131, 1, 136, 1, 159, 141, 6, 132, 2]) // Wrong end group. + assertDecodeFails([131, 1, 136, 1, 159, 141, 6]) // End group missing. + assertDecodeFails([131, 1, 136, 1, 159, 141, 6, 132, 2]) // Wrong end group. - assertDecodeFails([128, 1]) // Bad wire type + assertDecodeFails([128, 1]) // Bad wire type assertDecodesAsUnknownFields([128, 1, 0]) // Wrong wire type (varint), valid as an unknown field assertDecodesAsUnknownFields([128, 1, 132, 1]) // Wrong wire type (varint), valid as an unknown field - assertDecodeFails([129, 1]) // Bad wire type - assertDecodeFails([129, 1, 0]) // Bad wire type - assertDecodeFails([130, 1]) // Bad wire type + assertDecodeFails([129, 1]) // Bad wire type + assertDecodeFails([129, 1, 0]) // Bad wire type + assertDecodeFails([130, 1]) // Bad wire type assertDecodesAsUnknownFields([130, 1, 0]) // Wrong wire type (length delimited), valid as an unknown field - assertDecodeFails([131, 1]) // Lone start marker should fail - assertDecodeFails([132, 1]) // Lone stop marker should fail - assertDecodeFails([133, 1]) // Bad wire type - assertDecodeFails([133, 1, 0]) // Bad wire type - assertDecodeFails([134, 1]) // Bad wire type - assertDecodeFails([134, 1, 0]) // Bad wire type - assertDecodeFails([135, 1]) // Bad wire type - assertDecodeFails([135, 1, 0]) // Bad wire type + assertDecodeFails([131, 1]) // Lone start marker should fail + assertDecodeFails([132, 1]) // Lone stop marker should fail + assertDecodeFails([133, 1]) // Bad wire type + assertDecodeFails([133, 1, 0]) // Bad wire type + assertDecodeFails([134, 1]) // Bad wire type + assertDecodeFails([134, 1, 0]) // Bad wire type + assertDecodeFails([135, 1]) // Bad wire type + assertDecodeFails([135, 1, 0]) // Bad wire type // Ensure storage is uniqued for clear. let c = MessageTestType.with { @@ -911,9 +976,9 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_optionalBytes() { - assertEncode([122, 0]) {(o: inout MessageTestType) in o.optionalBytes = Data()} - assertEncode([122, 1, 1]) {(o: inout MessageTestType) in o.optionalBytes = Data([1])} - assertEncode([122, 2, 1, 2]) {(o: inout MessageTestType) in o.optionalBytes = Data([1, 2])} + assertEncode([122, 0]) { (o: inout MessageTestType) in o.optionalBytes = Data() } + assertEncode([122, 1, 1]) { (o: inout MessageTestType) in o.optionalBytes = Data([1]) } + assertEncode([122, 2, 1, 2]) { (o: inout MessageTestType) in o.optionalBytes = Data([1, 2]) } assertDecodeSucceeds([122, 4, 0, 1, 2, 255]) { if $0.hasOptionalBytes { return $0.optionalBytes == Data([0, 1, 2, 255]) @@ -922,7 +987,9 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { return false } } - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_bytes: \"\\001\\002\\003\"\n") {(o: inout MessageTestType) in o.optionalBytes = Data([1, 2, 3])} + assertDebugDescription( + "SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_bytes: \"\\001\\002\\003\"\n" + ) { (o: inout MessageTestType) in o.optionalBytes = Data([1, 2, 3]) } assertDecodeFails([122]) assertDecodeFails([122, 1]) assertDecodeFails([122, 2, 0]) @@ -943,12 +1010,14 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { assertDecodeFails([127, 0]) // Ensure bytes over 2GB fail to decode according to spec. - XCTAssertThrowsError(try MessageTestType(serializedBytes: [ - 122, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, - // Don't need all the bytes, want some to let the length issue trigger. - 0x01, 0x02, 0x03, - ])) { - XCTAssertEqual($0 as! BinaryDecodingError, .malformedProtobuf) + XCTAssertThrowsError( + try MessageTestType(serializedBytes: [ + 122, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, + // Don't need all the bytes, want some to let the length issue trigger. + 0x01, 0x02, 0x03, + ]) + ) { + XCTAssertEqual($0 as! BinaryDecodingError, .malformedProtobuf) } let empty = MessageTestType() @@ -978,14 +1047,16 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_optionalNestedMessage() { - assertEncode([146, 1, 2, 8, 1]) {(o: inout MessageTestType) in + assertEncode([146, 1, 2, 8, 1]) { (o: inout MessageTestType) in var nested = MessageTestType.NestedMessage() nested.bb = 1 o.optionalNestedMessage = nested } - assertDecodeSucceeds([146, 1, 4, 8, 1, 8, 3]) {$0.optionalNestedMessage.bb == 3} - assertDecodeSucceeds([146, 1, 2, 8, 1, 146, 1, 2, 8, 4]) {$0.optionalNestedMessage.bb == 4} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_nested_message {\n bb: 1\n}\n") {(o: inout MessageTestType) in + assertDecodeSucceeds([146, 1, 4, 8, 1, 8, 3]) { $0.optionalNestedMessage.bb == 3 } + assertDecodeSucceeds([146, 1, 2, 8, 1, 146, 1, 2, 8, 4]) { $0.optionalNestedMessage.bb == 4 } + assertDebugDescription( + "SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_nested_message {\n bb: 1\n}\n" + ) { (o: inout MessageTestType) in var nested = MessageTestType.NestedMessage() nested.bb = 1 o.optionalNestedMessage = nested @@ -995,11 +1066,13 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { assertDecodeFails([146, 1, 1, 128]) // Ensure message field over 2GB fail to decode according to spec. - XCTAssertThrowsError(try MessageTestType(serializedBytes: [ - 146, 1, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, - // Don't need all the bytes, want some to let the length issue trigger. - 0x01, 0x02, 0x03, - ])) { + XCTAssertThrowsError( + try MessageTestType(serializedBytes: [ + 146, 1, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, + // Don't need all the bytes, want some to let the length issue trigger. + 0x01, 0x02, 0x03, + ]) + ) { XCTAssertEqual($0 as! BinaryDecodingError, .malformedProtobuf) } @@ -1019,11 +1092,13 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { // Known message field followed by unknown field func testEncoding_optionalNestedMessage_unknown1() throws { - let bytes: [UInt8] = [146, 1, 2, 8, 1, // nested message with bb=1 - 208, 41, 0] // Unknown field 666 with varint 0 + let bytes: [UInt8] = [ + 146, 1, 2, 8, 1, // nested message with bb=1 + 208, 41, 0, + ] // Unknown field 666 with varint 0 do { let m = try MessageTestType(serializedBytes: bytes) - XCTAssertEqual(m.optionalNestedMessage, MessageTestType.NestedMessage.with{$0.bb = 1}) + XCTAssertEqual(m.optionalNestedMessage, MessageTestType.NestedMessage.with { $0.bb = 1 }) do { let recoded: [UInt8] = try m.serializedBytes() XCTAssertEqual(recoded, bytes) @@ -1037,11 +1112,13 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { // Unknown field followed by known message field func testEncoding_optionalNestedMessage_unknown2() throws { - let bytes: [UInt8] = [208, 41, 0, // Unknown 666 with varint 0 - 146, 1, 2, 8, 1] // Nested msg with bb=1 + let bytes: [UInt8] = [ + 208, 41, 0, // Unknown 666 with varint 0 + 146, 1, 2, 8, 1, + ] // Nested msg with bb=1 do { let m = try MessageTestType(serializedBytes: bytes) - XCTAssertEqual(m.optionalNestedMessage, MessageTestType.NestedMessage.with{$0.bb = 1}) + XCTAssertEqual(m.optionalNestedMessage, MessageTestType.NestedMessage.with { $0.bb = 1 }) do { let recoded: [UInt8] = try m.serializedBytes() // Unknown field gets reserialized at end @@ -1059,11 +1136,13 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { func testEncoding_optionalNestedMessage_unknown3() throws { // Inner field has bb=1 (8, 1) and unknown 666 with varint 99 // Outer message has unknown 666 with varint 0 - let bytes: [UInt8] = [146, 1, 5, 8, 1, 208, 41, 99, - 208, 41, 0] + let bytes: [UInt8] = [ + 146, 1, 5, 8, 1, 208, 41, 99, + 208, 41, 0, + ] do { let m = try MessageTestType(serializedBytes: bytes) - XCTAssertNotEqual(m.optionalNestedMessage, MessageTestType.NestedMessage.with{$0.bb = 1}) + XCTAssertNotEqual(m.optionalNestedMessage, MessageTestType.NestedMessage.with { $0.bb = 1 }) XCTAssertEqual(m.optionalNestedMessage.bb, 1) do { let recoded: [UInt8] = try m.serializedBytes() @@ -1083,7 +1162,7 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { let bytes: [UInt8] = [208, 41, 0, 146, 1, 5, 208, 41, 99, 8, 1] do { let m = try MessageTestType(serializedBytes: bytes) - XCTAssertNotEqual(m.optionalNestedMessage, MessageTestType.NestedMessage.with{$0.bb = 1}) + XCTAssertNotEqual(m.optionalNestedMessage, MessageTestType.NestedMessage.with { $0.bb = 1 }) XCTAssertEqual(m.optionalNestedMessage.bb, 1) do { let recoded: [UInt8] = try m.serializedBytes() @@ -1099,36 +1178,38 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_optionalForeignMessage() { - assertEncode([154, 1, 2, 8, 1]) {(o: inout MessageTestType) in + assertEncode([154, 1, 2, 8, 1]) { (o: inout MessageTestType) in var foreign = SwiftProtoTesting_ForeignMessage() foreign.c = 1 o.optionalForeignMessage = foreign } - assertDecodeSucceeds([154, 1, 4, 8, 1, 8, 3]) {$0.optionalForeignMessage.c == 3} - assertDecodeSucceeds([154, 1, 2, 8, 1, 154, 1, 2, 8, 4]) {$0.optionalForeignMessage.c == 4} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_foreign_message {\n c: 1\n}\n") {(o: inout MessageTestType) in + assertDecodeSucceeds([154, 1, 4, 8, 1, 8, 3]) { $0.optionalForeignMessage.c == 3 } + assertDecodeSucceeds([154, 1, 2, 8, 1, 154, 1, 2, 8, 4]) { $0.optionalForeignMessage.c == 4 } + assertDebugDescription( + "SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_foreign_message {\n c: 1\n}\n" + ) { (o: inout MessageTestType) in var foreign = SwiftProtoTesting_ForeignMessage() foreign.c = 1 o.optionalForeignMessage = foreign } assertDecodesAsUnknownFields([152, 1, 0]) // Wrong wire type (varint), valid as an unknown field - assertDecodeFails([153, 1]) // Wire type 1 + assertDecodeFails([153, 1]) // Wire type 1 assertDecodeFails([153, 1, 0]) assertDecodesAsUnknownFields([153, 1, 0, 0, 0, 0, 0, 0, 0, 0]) // Wrong wire type (fixed64), valid as an unknown field - assertDecodeFails([155, 1]) // Wire type 3 + assertDecodeFails([155, 1]) // Wire type 3 assertDecodeFails([155, 1, 0]) - assertDecodesAsUnknownFields([155, 1, 156, 1]) // Wrong wire type (start group, end group), valid as an unknown field - assertDecodeFails([156, 1]) // Wire type 4 + assertDecodesAsUnknownFields([155, 1, 156, 1]) // Wrong wire type (start group, end group), valid as an unknown field + assertDecodeFails([156, 1]) // Wire type 4 assertDecodeFails([156, 1, 0]) - assertDecodeFails([157, 1]) // Wire type 5 + assertDecodeFails([157, 1]) // Wire type 5 assertDecodeFails([157, 1, 0]) assertDecodesAsUnknownFields([157, 1, 0, 0, 0, 0]) // Wrong wire type (fixed32), valid as an unknown field - assertDecodeFails([158, 1]) // Wire type 6 + assertDecodeFails([158, 1]) // Wire type 6 assertDecodeFails([158, 1, 0]) - assertDecodeFails([159, 1]) // Wire type 7 + assertDecodeFails([159, 1]) // Wire type 7 assertDecodeFails([159, 1, 0]) - assertDecodeFails([154, 1, 4, 8, 1]) // Truncated + assertDecodeFails([154, 1, 4, 8, 1]) // Truncated // Ensure storage is uniqued for clear. let c = MessageTestType.with { @@ -1145,13 +1226,13 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_optionalImportMessage() { - assertEncode([162, 1, 2, 8, 1]) {(o: inout MessageTestType) in + assertEncode([162, 1, 2, 8, 1]) { (o: inout MessageTestType) in var imp = SwiftProtoTesting_Import_ImportMessage() imp.d = 1 o.optionalImportMessage = imp } - assertDecodeSucceeds([162, 1, 4, 8, 1, 8, 3]) {$0.optionalImportMessage.d == 3} - assertDecodeSucceeds([162, 1, 2, 8, 1, 162, 1, 2, 8, 4]) {$0.optionalImportMessage.d == 4} + assertDecodeSucceeds([162, 1, 4, 8, 1, 8, 3]) { $0.optionalImportMessage.d == 3 } + assertDecodeSucceeds([162, 1, 2, 8, 1, 162, 1, 2, 8, 4]) { $0.optionalImportMessage.d == 4 } // Ensure storage is uniqued for clear. let c = MessageTestType.with { @@ -1168,7 +1249,7 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_optionalNestedEnum() throws { - assertEncode([168, 1, 2]) {(o: inout MessageTestType) in + assertEncode([168, 1, 2]) { (o: inout MessageTestType) in o.optionalNestedEnum = .bar } assertDecodeSucceeds([168, 1, 2]) { @@ -1181,8 +1262,9 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } assertDecodeFails([168, 1]) assertDecodeSucceeds([168, 1, 128, 1]) { !$0.hasOptionalNestedEnum } - assertDecodeSucceeds([168, 1, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1]) {$0.optionalNestedEnum == .neg} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_nested_enum: BAR\n") {(o: inout MessageTestType) in + assertDecodeSucceeds([168, 1, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1]) { $0.optionalNestedEnum == .neg } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_nested_enum: BAR\n") { + (o: inout MessageTestType) in o.optionalNestedEnum = .bar } @@ -1207,10 +1289,12 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_optionalForeignEnum() { - assertEncode([176, 1, 5]) {(o: inout MessageTestType) in + assertEncode([176, 1, 5]) { (o: inout MessageTestType) in o.optionalForeignEnum = .foreignBar } - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_foreign_enum: FOREIGN_BAR\n") {(o: inout MessageTestType) in + assertDebugDescription( + "SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_foreign_enum: FOREIGN_BAR\n" + ) { (o: inout MessageTestType) in o.optionalForeignEnum = .foreignBar } @@ -1229,10 +1313,11 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_optionalImportEnum() { - assertEncode([184, 1, 8]) {(o: inout MessageTestType) in + assertEncode([184, 1, 8]) { (o: inout MessageTestType) in o.optionalImportEnum = .importBar } - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_import_enum: IMPORT_BAR\n") {(o: inout MessageTestType) in + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_import_enum: IMPORT_BAR\n") + { (o: inout MessageTestType) in o.optionalImportEnum = .importBar } @@ -1251,7 +1336,7 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_optionalStringPiece() { - assertEncode([194, 1, 6, 97, 98, 99, 100, 101, 102]) {(o: inout MessageTestType) in + assertEncode([194, 1, 6, 97, 98, 99, 100, 101, 102]) { (o: inout MessageTestType) in o.optionalStringPiece = "abcdef" } @@ -1270,7 +1355,7 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_optionalCord() { - assertEncode([202, 1, 6, 102, 101, 100, 99, 98, 97]) {(o: inout MessageTestType) in + assertEncode([202, 1, 6, 102, 101, 100, 99, 98, 97]) { (o: inout MessageTestType) in o.optionalCord = "fedcba" } @@ -1289,12 +1374,14 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_optionalPublicImportMessage() { - assertEncode([210, 1, 2, 8, 12]) {(o: inout MessageTestType) in + assertEncode([210, 1, 2, 8, 12]) { (o: inout MessageTestType) in var sub = SwiftProtoTesting_Import_PublicImportMessage() sub.e = 12 o.optionalPublicImportMessage = sub } - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_public_import_message {\n e: 9999\n}\n") {(o: inout MessageTestType) in + assertDebugDescription( + "SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_public_import_message {\n e: 9999\n}\n" + ) { (o: inout MessageTestType) in var sub = SwiftProtoTesting_Import_PublicImportMessage() sub.e = 9999 o.optionalPublicImportMessage = sub @@ -1302,7 +1389,7 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_optionalLazyMessage() { - assertEncode([218, 1, 2, 8, 7]) {(o: inout MessageTestType) in + assertEncode([218, 1, 2, 8, 7]) { (o: inout MessageTestType) in var m = MessageTestType.NestedMessage() m.bb = 7 o.optionalLazyMessage = m @@ -1313,36 +1400,46 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { // Repeated types // func testEncoding_repeatedInt32() { - assertEncode([248, 1, 255, 255, 255, 255, 7, 248, 1, 128, 128, 128, 128, 248, 255, 255, 255, 255, 1]) {(o: inout MessageTestType) in o.repeatedInt32 = [Int32.max, Int32.min]} - assertDecodeSucceeds([248, 1, 8, 248, 1, 247, 255, 255, 255, 15]) {$0.repeatedInt32 == [8, -9]} + assertEncode([248, 1, 255, 255, 255, 255, 7, 248, 1, 128, 128, 128, 128, 248, 255, 255, 255, 255, 1]) { + (o: inout MessageTestType) in o.repeatedInt32 = [Int32.max, Int32.min] + } + assertDecodeSucceeds([248, 1, 8, 248, 1, 247, 255, 255, 255, 15]) { $0.repeatedInt32 == [8, -9] } assertDecodeFails([248, 1, 8, 248, 1, 247, 255, 255, 255, 255, 255, 255, 255, 255]) assertDecodeFails([248, 1, 8, 248, 1]) assertDecodeFails([248, 1]) assertDecodeFails([249, 1, 73]) // 250, 1 should actually work because that's packed - assertDecodeSucceeds([250, 1, 4, 8, 9, 10, 11]) {$0.repeatedInt32 == [8, 9, 10, 11]} + assertDecodeSucceeds([250, 1, 4, 8, 9, 10, 11]) { $0.repeatedInt32 == [8, 9, 10, 11] } assertDecodeFails([251, 1, 75]) assertDecodeFails([252, 1, 76]) assertDecodeFails([253, 1, 77]) assertDecodeFails([254, 1, 78]) assertDecodeFails([255, 1, 79]) - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\nrepeated_int32: 1\n") {(o: inout MessageTestType) in + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\nrepeated_int32: 1\n") { + (o: inout MessageTestType) in o.repeatedInt32 = [1] } - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\n") {(o: inout MessageTestType) in + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\n") { (o: inout MessageTestType) in o.repeatedInt32 = [] } - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\nrepeated_int32: 1\nrepeated_int32: 2\n") {(o: inout MessageTestType) in + assertDebugDescription( + "SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\nrepeated_int32: 1\nrepeated_int32: 2\n" + ) { (o: inout MessageTestType) in o.repeatedInt32 = [1, 2] } } - func testEncoding_repeatedInt64() { - assertEncode([128, 2, 255, 255, 255, 255, 255, 255, 255, 255, 127, 128, 2, 128, 128, 128, 128, 128, 128, 128, 128, 128, 1]) {(o: inout MessageTestType) in o.repeatedInt64 = [Int64.max, Int64.min]} - assertDecodeSucceeds([128, 2, 255, 255, 153, 166, 234, 175, 227, 1, 128, 2, 185, 156, 196, 237, 158, 222, 230, 255, 255, 1]) {$0.repeatedInt64 == [999999999999999, -111111111111111]} - assertDecodeSucceeds([130, 2, 1, 1]) {$0.repeatedInt64 == [1]} // Accepts packed coding - assertDecodeFails([128, 2, 255, 255, 153, 166, 234, 175, 227, 1, 128, 2, 185, 156, 196, 237, 158, 222, 230, 255, 255]) + assertEncode([ + 128, 2, 255, 255, 255, 255, 255, 255, 255, 255, 127, 128, 2, 128, 128, 128, 128, 128, 128, 128, 128, 128, 1, + ]) { (o: inout MessageTestType) in o.repeatedInt64 = [Int64.max, Int64.min] } + assertDecodeSucceeds([ + 128, 2, 255, 255, 153, 166, 234, 175, 227, 1, 128, 2, 185, 156, 196, 237, 158, 222, 230, 255, 255, 1, + ]) { $0.repeatedInt64 == [999_999_999_999_999, -111_111_111_111_111] } + assertDecodeSucceeds([130, 2, 1, 1]) { $0.repeatedInt64 == [1] } // Accepts packed coding + assertDecodeFails([ + 128, 2, 255, 255, 153, 166, 234, 175, 227, 1, 128, 2, 185, 156, 196, 237, 158, 222, 230, 255, 255, + ]) assertDecodeFails([128, 2, 1, 128, 2]) assertDecodeFails([128, 2, 128]) assertDecodeFails([128, 2]) @@ -1355,19 +1452,26 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_repeatedUint32() { - assertEncode([136, 2, 255, 255, 255, 255, 15, 136, 2, 0]) {(o: inout MessageTestType) in o.repeatedUint32 = [UInt32.max, UInt32.min]} - assertDecodeSucceeds([136, 2, 210, 9, 136, 2, 213, 27]) {(o:MessageTestType) in - o.repeatedUint32 == [1234, 3541]} - assertDecodeSucceeds([136, 2, 255, 255, 255, 255, 15, 136, 2, 213, 27]) {(o:MessageTestType) in - o.repeatedUint32 == [4294967295, 3541]} - assertDecodeSucceeds([138, 2, 2, 1, 2]) {(o:MessageTestType) in - o.repeatedUint32 == [1, 2]} + assertEncode([136, 2, 255, 255, 255, 255, 15, 136, 2, 0]) { (o: inout MessageTestType) in + o.repeatedUint32 = [UInt32.max, UInt32.min] + } + assertDecodeSucceeds([136, 2, 210, 9, 136, 2, 213, 27]) { (o: MessageTestType) in + o.repeatedUint32 == [1234, 3541] + } + assertDecodeSucceeds([136, 2, 255, 255, 255, 255, 15, 136, 2, 213, 27]) { (o: MessageTestType) in + o.repeatedUint32 == [4_294_967_295, 3541] + } + assertDecodeSucceeds([138, 2, 2, 1, 2]) { (o: MessageTestType) in + o.repeatedUint32 == [1, 2] + } // Truncate on 32-bit overflow - assertDecodeSucceeds([136, 2, 255, 255, 255, 255, 31]) {(o:MessageTestType) in - o.repeatedUint32 == [4294967295]} - assertDecodeSucceeds([136, 2, 255, 255, 255, 255, 255, 255, 255, 1]) {(o:MessageTestType) in - o.repeatedUint32 == [4294967295]} + assertDecodeSucceeds([136, 2, 255, 255, 255, 255, 31]) { (o: MessageTestType) in + o.repeatedUint32 == [4_294_967_295] + } + assertDecodeSucceeds([136, 2, 255, 255, 255, 255, 255, 255, 255, 1]) { (o: MessageTestType) in + o.repeatedUint32 == [4_294_967_295] + } assertDecodeFails([136, 2]) assertDecodeFails([136, 2, 210]) @@ -1381,9 +1485,11 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_repeatedUint64() { - assertEncode([144, 2, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1, 144, 2, 0]) {(o: inout MessageTestType) in o.repeatedUint64 = [UInt64.max, UInt64.min]} - assertDecodeSucceeds([144, 2, 149, 8]) {$0.repeatedUint64 == [1045 ]} - assertDecodeSucceeds([146, 2, 2, 0, 1]) {$0.repeatedUint64 == [0, 1]} + assertEncode([144, 2, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1, 144, 2, 0]) { + (o: inout MessageTestType) in o.repeatedUint64 = [UInt64.max, UInt64.min] + } + assertDecodeSucceeds([144, 2, 149, 8]) { $0.repeatedUint64 == [1045] } + assertDecodeSucceeds([146, 2, 2, 0, 1]) { $0.repeatedUint64 == [0, 1] } assertDecodeFails([144]) assertDecodeFails([144, 2]) assertDecodeFails([144, 2, 149]) @@ -1403,13 +1509,18 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_repeatedSint32() { - assertEncode([152, 2, 254, 255, 255, 255, 15, 152, 2, 255, 255, 255, 255, 15]) {(o: inout MessageTestType) in o.repeatedSint32 = [Int32.max, Int32.min]} - assertDecodeSucceeds([152, 2, 170, 180, 222, 117, 152, 2, 225, 162, 243, 173, 1]) {$0.repeatedSint32 == [123456789, -182347953]} - assertDecodeSucceeds([154, 2, 1, 0]) {$0.repeatedSint32 == [0]} - assertDecodeSucceeds([154, 2, 1, 1, 152, 2, 2]) {$0.repeatedSint32 == [-1, 1]} + assertEncode([152, 2, 254, 255, 255, 255, 15, 152, 2, 255, 255, 255, 255, 15]) { (o: inout MessageTestType) in + o.repeatedSint32 = [Int32.max, Int32.min] + } + assertDecodeSucceeds([152, 2, 170, 180, 222, 117, 152, 2, 225, 162, 243, 173, 1]) { + $0.repeatedSint32 == [123_456_789, -182_347_953] + } + assertDecodeSucceeds([154, 2, 1, 0]) { $0.repeatedSint32 == [0] } + assertDecodeSucceeds([154, 2, 1, 1, 152, 2, 2]) { $0.repeatedSint32 == [-1, 1] } // 32-bit overflow truncates - assertDecodeSucceeds([152, 2, 170, 180, 222, 117, 152, 2, 225, 162, 243, 173, 255, 255, 1]) {$0.repeatedSint32 == [123456789, -2061396145]} - + assertDecodeSucceeds([152, 2, 170, 180, 222, 117, 152, 2, 225, 162, 243, 173, 255, 255, 1]) { + $0.repeatedSint32 == [123_456_789, -2_061_396_145] + } assertDecodeFails([152, 2, 170, 180, 222, 117, 152]) assertDecodeFails([152, 2, 170, 180, 222, 117, 152, 2]) @@ -1430,9 +1541,14 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_repeatedSint64() { - assertEncode([160, 2, 254, 255, 255, 255, 255, 255, 255, 255, 255, 1, 160, 2, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1]) {(o: inout MessageTestType) in o.repeatedSint64 = [Int64.max, Int64.min]} - assertDecodeSucceeds([160, 2, 170, 180, 222, 117, 160, 2, 225, 162, 243, 173, 255, 89]) {$0.repeatedSint64 == [123456789,-1546102139057]} - assertDecodeSucceeds([162, 2, 1, 1]) {$0.repeatedSint64 == [-1]} + assertEncode([ + 160, 2, 254, 255, 255, 255, 255, 255, 255, 255, 255, 1, 160, 2, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 1, + ]) { (o: inout MessageTestType) in o.repeatedSint64 = [Int64.max, Int64.min] } + assertDecodeSucceeds([160, 2, 170, 180, 222, 117, 160, 2, 225, 162, 243, 173, 255, 89]) { + $0.repeatedSint64 == [123_456_789, -1_546_102_139_057] + } + assertDecodeSucceeds([162, 2, 1, 1]) { $0.repeatedSint64 == [-1] } assertDecodeFails([160, 2, 170, 180, 222, 117, 160]) assertDecodeFails([160, 2, 170, 180, 222, 117, 160, 2]) assertDecodeFails([160, 2, 170, 180, 222, 117, 160, 2, 225]) @@ -1452,9 +1568,15 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_repeatedFixed32() { - assertEncode([173, 2, 255, 255, 255, 255, 173, 2, 0, 0, 0, 0]) {(o: inout MessageTestType) in o.repeatedFixed32 = [UInt32.max, UInt32.min]} - assertDecodeSucceeds([173, 2, 255, 255, 255, 127, 173, 2, 127, 127, 127, 127]) {$0.repeatedFixed32 == [2147483647, 2139062143]} - assertDecodeSucceeds([170, 2, 4, 1, 0, 0, 0, 173, 2, 255, 255, 255, 127]) {$0.repeatedFixed32 == [1, 2147483647]} + assertEncode([173, 2, 255, 255, 255, 255, 173, 2, 0, 0, 0, 0]) { (o: inout MessageTestType) in + o.repeatedFixed32 = [UInt32.max, UInt32.min] + } + assertDecodeSucceeds([173, 2, 255, 255, 255, 127, 173, 2, 127, 127, 127, 127]) { + $0.repeatedFixed32 == [2_147_483_647, 2_139_062_143] + } + assertDecodeSucceeds([170, 2, 4, 1, 0, 0, 0, 173, 2, 255, 255, 255, 127]) { + $0.repeatedFixed32 == [1, 2_147_483_647] + } assertDecodeFails([173]) assertDecodeFails([173, 2]) assertDecodeFails([173, 2, 255]) @@ -1486,10 +1608,17 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_repeatedFixed64() { - assertEncode([177, 2, 255, 255, 255, 255, 255, 255, 255, 255, 177, 2, 0, 0, 0, 0, 0, 0, 0, 0]) {(o: inout MessageTestType) in o.repeatedFixed64 = [UInt64.max, UInt64.min]} - assertDecodeSucceeds([177, 2, 255, 255, 255, 127, 0, 0, 0, 0, 177, 2, 255, 255, 255, 255, 0, 0, 0, 0, 177, 2, 255, 255, 255, 255, 255, 255, 255, 255]) {$0.repeatedFixed64 == [2147483647, 4294967295, 18446744073709551615]} - assertDecodeSucceeds([178, 2, 8, 1, 0, 0, 0, 0, 0, 0, 0]) {$0.repeatedFixed64 == [1]} - assertDecodeSucceeds([177, 2, 2, 0, 0, 0, 0, 0, 0, 0, 178, 2, 8, 1, 0, 0, 0, 0, 0, 0, 0]) {$0.repeatedFixed64 == [2, 1]} + assertEncode([177, 2, 255, 255, 255, 255, 255, 255, 255, 255, 177, 2, 0, 0, 0, 0, 0, 0, 0, 0]) { + (o: inout MessageTestType) in o.repeatedFixed64 = [UInt64.max, UInt64.min] + } + assertDecodeSucceeds([ + 177, 2, 255, 255, 255, 127, 0, 0, 0, 0, 177, 2, 255, 255, 255, 255, 0, 0, 0, 0, 177, 2, 255, 255, 255, 255, + 255, 255, 255, 255, + ]) { $0.repeatedFixed64 == [2_147_483_647, 4_294_967_295, 18_446_744_073_709_551_615] } + assertDecodeSucceeds([178, 2, 8, 1, 0, 0, 0, 0, 0, 0, 0]) { $0.repeatedFixed64 == [1] } + assertDecodeSucceeds([177, 2, 2, 0, 0, 0, 0, 0, 0, 0, 178, 2, 8, 1, 0, 0, 0, 0, 0, 0, 0]) { + $0.repeatedFixed64 == [2, 1] + } assertDecodeFails([177]) assertDecodeFails([177, 2]) assertDecodeFails([177, 2, 255]) @@ -1518,9 +1647,11 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_repeatedSfixed32() { - assertEncode([189, 2, 255, 255, 255, 127, 189, 2, 0, 0, 0, 128]) {(o: inout MessageTestType) in o.repeatedSfixed32 = [Int32.max, Int32.min]} - assertDecodeSucceeds([189, 2, 0, 0, 0, 0]) {$0.repeatedSfixed32 == [0]} - assertDecodeSucceeds([186, 2, 4, 1, 0, 0, 0, 189, 2, 3, 0, 0, 0]) {$0.repeatedSfixed32 == [1, 3]} + assertEncode([189, 2, 255, 255, 255, 127, 189, 2, 0, 0, 0, 128]) { (o: inout MessageTestType) in + o.repeatedSfixed32 = [Int32.max, Int32.min] + } + assertDecodeSucceeds([189, 2, 0, 0, 0, 0]) { $0.repeatedSfixed32 == [0] } + assertDecodeSucceeds([186, 2, 4, 1, 0, 0, 0, 189, 2, 3, 0, 0, 0]) { $0.repeatedSfixed32 == [1, 3] } assertDecodeFails([189]) assertDecodeFails([189, 2]) assertDecodeFails([189, 2, 0]) @@ -1547,9 +1678,16 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_repeatedSfixed64() { - assertEncode([193, 2, 255, 255, 255, 255, 255, 255, 255, 127, 193, 2, 0, 0, 0, 0, 0, 0, 0, 128]) {(o: inout MessageTestType) in o.repeatedSfixed64 = [Int64.max, Int64.min]} - assertDecodeSucceeds([193, 2, 0, 0, 0, 0, 0, 0, 0, 128, 193, 2, 255, 255, 255, 255, 255, 255, 255, 255, 193, 2, 1, 0, 0, 0, 0, 0, 0, 0, 193, 2, 255, 255, 255, 255, 255, 255, 255, 127]) {$0.repeatedSfixed64 == [-9223372036854775808, -1, 1, 9223372036854775807]} - assertDecodeSucceeds([194, 2, 8, 0, 0, 0, 0, 0, 0, 0, 0, 193, 2, 1, 0, 0, 0, 0, 0, 0, 0]) {$0.repeatedSfixed64 == [0, 1]} + assertEncode([193, 2, 255, 255, 255, 255, 255, 255, 255, 127, 193, 2, 0, 0, 0, 0, 0, 0, 0, 128]) { + (o: inout MessageTestType) in o.repeatedSfixed64 = [Int64.max, Int64.min] + } + assertDecodeSucceeds([ + 193, 2, 0, 0, 0, 0, 0, 0, 0, 128, 193, 2, 255, 255, 255, 255, 255, 255, 255, 255, 193, 2, 1, 0, 0, 0, 0, 0, + 0, 0, 193, 2, 255, 255, 255, 255, 255, 255, 255, 127, + ]) { $0.repeatedSfixed64 == [-9_223_372_036_854_775_808, -1, 1, 9_223_372_036_854_775_807] } + assertDecodeSucceeds([194, 2, 8, 0, 0, 0, 0, 0, 0, 0, 0, 193, 2, 1, 0, 0, 0, 0, 0, 0, 0]) { + $0.repeatedSfixed64 == [0, 1] + } assertDecodeFails([193]) assertDecodeFails([193, 2]) assertDecodeFails([193, 2, 0]) @@ -1580,29 +1718,37 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_repeatedFloat() { - assertEncode([205, 2, 0, 0, 0, 63, 205, 2, 0, 0, 0, 0]) {(o: inout MessageTestType) in o.repeatedFloat = [0.5, 0.0]} - assertDecodeSucceeds([205, 2, 0, 0, 0, 63, 205, 2, 0, 0, 0, 63]) {$0.repeatedFloat == [0.5, 0.5]} - assertDecodeSucceeds([202, 2, 8, 0, 0, 0, 63, 0, 0, 0, 63]) {$0.repeatedFloat == [0.5, 0.5]} + assertEncode([205, 2, 0, 0, 0, 63, 205, 2, 0, 0, 0, 0]) { (o: inout MessageTestType) in + o.repeatedFloat = [0.5, 0.0] + } + assertDecodeSucceeds([205, 2, 0, 0, 0, 63, 205, 2, 0, 0, 0, 63]) { $0.repeatedFloat == [0.5, 0.5] } + assertDecodeSucceeds([202, 2, 8, 0, 0, 0, 63, 0, 0, 0, 63]) { $0.repeatedFloat == [0.5, 0.5] } assertDecodeFails([205, 2, 0, 0, 0, 63, 205, 2, 0, 0, 128]) assertDecodeFails([205, 2, 0, 0, 0, 63, 205, 2]) - assertDecodeFails([200, 2]) // Bad byte sequence - assertDecodeFails([200, 2, 0, 0, 0, 0]) // Bad byte sequence - assertDecodeFails([201, 2]) // Bad byte sequence - assertDecodeFails([201, 2, 0, 0, 0, 0]) // Bad byte sequence - assertDecodeFails([203, 2]) // Bad byte sequence - assertDecodeFails([203, 2, 0, 0, 0, 0]) // Bad byte sequence - assertDecodeFails([204, 2]) // Bad byte sequence - assertDecodeFails([204, 2, 0, 0, 0, 0]) // Bad byte sequence - assertDecodeFails([206, 2]) // Bad byte sequence - assertDecodeFails([206, 2, 0, 0, 0, 0]) // Bad byte sequence - assertDecodeFails([207, 2]) // Bad byte sequence - assertDecodeFails([207, 2, 0, 0, 0, 0]) // Bad byte sequence + assertDecodeFails([200, 2]) // Bad byte sequence + assertDecodeFails([200, 2, 0, 0, 0, 0]) // Bad byte sequence + assertDecodeFails([201, 2]) // Bad byte sequence + assertDecodeFails([201, 2, 0, 0, 0, 0]) // Bad byte sequence + assertDecodeFails([203, 2]) // Bad byte sequence + assertDecodeFails([203, 2, 0, 0, 0, 0]) // Bad byte sequence + assertDecodeFails([204, 2]) // Bad byte sequence + assertDecodeFails([204, 2, 0, 0, 0, 0]) // Bad byte sequence + assertDecodeFails([206, 2]) // Bad byte sequence + assertDecodeFails([206, 2, 0, 0, 0, 0]) // Bad byte sequence + assertDecodeFails([207, 2]) // Bad byte sequence + assertDecodeFails([207, 2, 0, 0, 0, 0]) // Bad byte sequence } func testEncoding_repeatedDouble() { - assertEncode([209, 2, 0, 0, 0, 0, 0, 0, 224, 63, 209, 2, 0, 0, 0, 0, 0, 0, 0, 0]) {(o: inout MessageTestType) in o.repeatedDouble = [0.5, 0.0]} - assertDecodeSucceeds([209, 2, 0, 0, 0, 0, 0, 0, 224, 63, 209, 2, 0, 0, 0, 0, 0, 0, 208, 63]) {$0.repeatedDouble == [0.5, 0.25]} - assertDecodeSucceeds([210, 2, 16, 0, 0, 0, 0, 0, 0, 224, 63, 0, 0, 0, 0, 0, 0, 208, 63]) {$0.repeatedDouble == [0.5, 0.25]} + assertEncode([209, 2, 0, 0, 0, 0, 0, 0, 224, 63, 209, 2, 0, 0, 0, 0, 0, 0, 0, 0]) { + (o: inout MessageTestType) in o.repeatedDouble = [0.5, 0.0] + } + assertDecodeSucceeds([209, 2, 0, 0, 0, 0, 0, 0, 224, 63, 209, 2, 0, 0, 0, 0, 0, 0, 208, 63]) { + $0.repeatedDouble == [0.5, 0.25] + } + assertDecodeSucceeds([210, 2, 16, 0, 0, 0, 0, 0, 0, 224, 63, 0, 0, 0, 0, 0, 0, 208, 63]) { + $0.repeatedDouble == [0.5, 0.25] + } assertDecodeFails([209, 2]) assertDecodeFails([209, 2, 0]) assertDecodeFails([209, 2, 0, 0]) @@ -1629,9 +1775,13 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_repeatedBool() { - assertEncode([216, 2, 1, 216, 2, 0, 216, 2, 1]) {(o: inout MessageTestType) in o.repeatedBool = [true, false, true]} - assertDecodeSucceeds([216, 2, 1, 216, 2, 0, 216, 2, 0, 216, 2, 1]) {$0.repeatedBool == [true, false, false, true]} - assertDecodeSucceeds([218, 2, 3, 1, 0, 1, 216, 2, 0]) {$0.repeatedBool == [true, false, true, false]} + assertEncode([216, 2, 1, 216, 2, 0, 216, 2, 1]) { (o: inout MessageTestType) in + o.repeatedBool = [true, false, true] + } + assertDecodeSucceeds([216, 2, 1, 216, 2, 0, 216, 2, 0, 216, 2, 1]) { + $0.repeatedBool == [true, false, false, true] + } + assertDecodeSucceeds([218, 2, 3, 1, 0, 1, 216, 2, 0]) { $0.repeatedBool == [true, false, true, false] } assertDecodeFails([216]) assertDecodeFails([216, 2]) assertDecodeFails([216, 2, 255]) @@ -1651,13 +1801,15 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_repeatedString() { - assertEncode([226, 2, 1, 65, 226, 2, 1, 66]) {(o: inout MessageTestType) in o.repeatedString = ["A", "B"]} - assertDecodeSucceeds([226, 2, 5, 72, 101, 108, 108, 111, 226, 2, 5, 119, 111, 114, 108, 100, 226, 2, 0]) {$0.repeatedString == ["Hello", "world", ""]} + assertEncode([226, 2, 1, 65, 226, 2, 1, 66]) { (o: inout MessageTestType) in o.repeatedString = ["A", "B"] } + assertDecodeSucceeds([226, 2, 5, 72, 101, 108, 108, 111, 226, 2, 5, 119, 111, 114, 108, 100, 226, 2, 0]) { + $0.repeatedString == ["Hello", "world", ""] + } assertDecodeFails([226]) assertDecodeFails([226, 2]) assertDecodeFails([226, 2, 1]) assertDecodeFails([226, 2, 2, 65]) - assertDecodeFails([226, 2, 1, 193]) // Invalid UTF-8 + assertDecodeFails([226, 2, 1, 193]) // Invalid UTF-8 assertDecodeFails([224, 2]) assertDecodesAsUnknownFields([224, 2, 0]) // Wrong wire type (varint), valid as an unknown field assertDecodeFails([225, 2]) @@ -1675,10 +1827,12 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_repeatedBytes() { - assertEncode([234, 2, 1, 1, 234, 2, 0, 234, 2, 1, 2]) {(o: inout MessageTestType) in o.repeatedBytes = [Data([1]), Data(), Data([2])]} + assertEncode([234, 2, 1, 1, 234, 2, 0, 234, 2, 1, 2]) { (o: inout MessageTestType) in + o.repeatedBytes = [Data([1]), Data(), Data([2])] + } assertDecodeSucceeds([234, 2, 4, 0, 1, 2, 255, 234, 2, 0]) { let ref = [Data([0, 1, 2, 255]), Data()] - for (a,b) in zip($0.repeatedBytes, ref) { + for (a, b) in zip($0.repeatedBytes, ref) { if a != b { return false } } return true @@ -1702,16 +1856,16 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_repeatedGroup() { - assertEncode([243, 2, 248, 2, 1, 244, 2, 243, 2, 244, 2]) {(o: inout MessageTestType) in + assertEncode([243, 2, 248, 2, 1, 244, 2, 243, 2, 244, 2]) { (o: inout MessageTestType) in var g1 = MessageTestType.RepeatedGroup() g1.a = 1 let g2 = MessageTestType.RepeatedGroup() // g2 has nothing set. o.repeatedGroup = [g1, g2] } - assertDecodeFails([243, 2, 248, 2, 1]) // End group missing. - assertDecodeFails([243, 2, 248, 2, 1, 244, 3]) // Wrong end group. - assertDecodeFails([240, 2]) // Wire type 0 + assertDecodeFails([243, 2, 248, 2, 1]) // End group missing. + assertDecodeFails([243, 2, 248, 2, 1, 244, 3]) // Wrong end group. + assertDecodeFails([240, 2]) // Wire type 0 assertDecodesAsUnknownFields([240, 2, 0]) // Wrong wire type (varint), valid as an unknown field assertDecodesAsUnknownFields([240, 2, 244, 2]) // Wrong wire type (varint), valid as an unknown field // JSON current disable, pending what to do with groups @@ -1722,14 +1876,18 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { // g2.a = 2 // o.repeatedGroup = [g1, g2] // } - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\nRepeatedGroup {\n a: 1\n}\nRepeatedGroup {\n a: 2\n}\n") {(o: inout MessageTestType) in + assertDebugDescription( + "SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\nRepeatedGroup {\n a: 1\n}\nRepeatedGroup {\n a: 2\n}\n" + ) { (o: inout MessageTestType) in var g1 = MessageTestType.RepeatedGroup() g1.a = 1 var g2 = MessageTestType.RepeatedGroup() g2.a = 2 o.repeatedGroup = [g1, g2] } - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\nrepeated_int32: 1\nrepeated_int32: 2\nrepeated_int32: 3\nRepeatedGroup {\n a: 1\n}\nRepeatedGroup {\n a: 2\n}\n") {(o: inout MessageTestType) in + assertDebugDescription( + "SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\nrepeated_int32: 1\nrepeated_int32: 2\nrepeated_int32: 3\nRepeatedGroup {\n a: 1\n}\nRepeatedGroup {\n a: 2\n}\n" + ) { (o: inout MessageTestType) in o.repeatedInt32 = [1, 2, 3] var g1 = MessageTestType.RepeatedGroup() g1.a = 1 @@ -1740,7 +1898,7 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_repeatedNestedMessage() { - assertEncode([130, 3, 2, 8, 1, 130, 3, 2, 8, 2]) {(o: inout MessageTestType) in + assertEncode([130, 3, 2, 8, 1, 130, 3, 2, 8, 2]) { (o: inout MessageTestType) in var m1 = MessageTestType.NestedMessage() m1.bb = 1 var m2 = MessageTestType.NestedMessage() @@ -1751,15 +1909,19 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { assertDecodesAsUnknownFields([128, 3, 0]) // Wrong wire type (varint), valid as an unknown field // Ensure message field over 2GB fail to decode according to spec. - XCTAssertThrowsError(try MessageTestType(serializedBytes: [ - 130, 3, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, - // Don't need all the bytes, want some to let the length issue trigger. - 0x01, 0x02, 0x03, - ])) { - XCTAssertEqual($0 as! BinaryDecodingError, .malformedProtobuf) + XCTAssertThrowsError( + try MessageTestType(serializedBytes: [ + 130, 3, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, + // Don't need all the bytes, want some to let the length issue trigger. + 0x01, 0x02, 0x03, + ]) + ) { + XCTAssertEqual($0 as! BinaryDecodingError, .malformedProtobuf) } - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\nrepeated_nested_message {\n bb: 1\n}\nrepeated_nested_message {\n bb: 2\n}\n") {(o: inout MessageTestType) in + assertDebugDescription( + "SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\nrepeated_nested_message {\n bb: 1\n}\nrepeated_nested_message {\n bb: 2\n}\n" + ) { (o: inout MessageTestType) in var m1 = MessageTestType.NestedMessage() m1.bb = 1 var m2 = MessageTestType.NestedMessage() @@ -1770,25 +1932,25 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { func testEncoding_repeatedNestedMessage_unknown() { let bytes: [UInt8] = [ - 208, 41, 0, // Unknown 666 with varint 0 + 208, 41, 0, // Unknown 666 with varint 0 130, 3, 8, // Inner message with 8 bytes - 208, 41, 8, // Unknown 666 with varint 8 - 8, 1, // bb = 1 - 208, 41, 9, // Unknown 666 with varint 9 - 208, 41, 1, // Unknown 666 with varint 1 - 130, 3, 8, // inner message with 8 bytes - 208, 41, 10, // Unknown 666 with varint 10 - 8, 2, // bb = 2 - 208, 41, 11, // Unknown 666 with varint 11 - 208, 41, 2 // Unknown 666 with varint 2 + 208, 41, 8, // Unknown 666 with varint 8 + 8, 1, // bb = 1 + 208, 41, 9, // Unknown 666 with varint 9 + 208, 41, 1, // Unknown 666 with varint 1 + 130, 3, 8, // inner message with 8 bytes + 208, 41, 10, // Unknown 666 with varint 10 + 8, 2, // bb = 2 + 208, 41, 11, // Unknown 666 with varint 11 + 208, 41, 2, // Unknown 666 with varint 2 ] do { let m = try MessageTestType(serializedBytes: bytes) XCTAssertEqual(m.repeatedNestedMessage.count, 2) - XCTAssertNotEqual(m.repeatedNestedMessage[0], MessageTestType.NestedMessage.with{$0.bb = 1}) + XCTAssertNotEqual(m.repeatedNestedMessage[0], MessageTestType.NestedMessage.with { $0.bb = 1 }) XCTAssertEqual(m.repeatedNestedMessage[0].bb, 1) - XCTAssertNotEqual(m.repeatedNestedMessage[1], MessageTestType.NestedMessage.with{$0.bb = 2}) + XCTAssertNotEqual(m.repeatedNestedMessage[1], MessageTestType.NestedMessage.with { $0.bb = 2 }) XCTAssertEqual(m.repeatedNestedMessage[1].bb, 2) do { // Same contents, but reordered @@ -1797,7 +1959,7 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { 130, 3, 8, 8, 2, 208, 41, 10, 208, 41, 11, 208, 41, 0, 208, 41, 1, - 208, 41, 2 + 208, 41, 2, ] let recoded: [UInt8] = try m.serializedBytes() XCTAssertEqual(recoded, expectedBytes) @@ -1810,10 +1972,12 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_repeatedNestedEnum() throws { - assertEncode([152, 3, 2, 152, 3, 3]) {(o: inout MessageTestType) in + assertEncode([152, 3, 2, 152, 3, 3]) { (o: inout MessageTestType) in o.repeatedNestedEnum = [.bar, .baz] } - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\nrepeated_nested_enum: BAR\nrepeated_nested_enum: BAZ\n") {(o: inout MessageTestType) in + assertDebugDescription( + "SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\nrepeated_nested_enum: BAR\nrepeated_nested_enum: BAZ\n" + ) { (o: inout MessageTestType) in o.repeatedNestedEnum = [.bar, .baz] } assertDecodeSucceeds([152, 3, 2, 152, 3, 128, 1]) { @@ -1858,7 +2022,8 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { let empty = MessageTestType() XCTAssertEqual(empty.defaultInt32, 41) // Setting explicitly does serialize (even if default) - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\ndefault_int32: 41\n") {(o: inout MessageTestType) in + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\ndefault_int32: 41\n") { + (o: inout MessageTestType) in o.defaultInt32 = 41 } @@ -1886,9 +2051,10 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { let s: [UInt8] = try t.serializedBytes() XCTAssertEqual([], s) - assertDecodeSucceeds([]) {$0.defaultInt32 == 41} - assertDecodeSucceeds([232, 3, 4]) {$0.defaultInt32 == 4} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\ndefault_int32: 4\n") {(o: inout MessageTestType) in + assertDecodeSucceeds([]) { $0.defaultInt32 == 41 } + assertDecodeSucceeds([232, 3, 4]) { $0.defaultInt32 == 4 } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\ndefault_int32: 4\n") { + (o: inout MessageTestType) in o.defaultInt32 = 4 } } @@ -1917,9 +2083,9 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { b.optionalInt32 = 1 XCTAssertNotEqual(a, b) - assertDecodeSucceeds([]) {$0.defaultInt64 == 42} - assertDecodeSucceeds([240, 3, 42]) {$0.defaultInt64 == 42} - assertDecodeSucceeds([240, 3, 8]) {$0.defaultInt64 == 8} + assertDecodeSucceeds([]) { $0.defaultInt64 == 42 } + assertDecodeSucceeds([240, 3, 42]) { $0.defaultInt64 == 42 } + assertDecodeSucceeds([240, 3, 8]) { $0.defaultInt64 == 8 } } func testEncoding_defaultUint32() throws { @@ -1940,9 +2106,9 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { b.optionalInt32 = 1 XCTAssertNotEqual(a, b) - assertDecodeSucceeds([]) {$0.defaultUint32 == 43} - assertDecodeSucceeds([248, 3, 43]) {$0.defaultUint32 == 43} - assertDecodeSucceeds([248, 3, 9]) {$0.defaultUint32 == 9} + assertDecodeSucceeds([]) { $0.defaultUint32 == 43 } + assertDecodeSucceeds([248, 3, 43]) { $0.defaultUint32 == 43 } + assertDecodeSucceeds([248, 3, 9]) { $0.defaultUint32 == 9 } } func testEncoding_defaultUint64() throws { @@ -1963,9 +2129,9 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { b.optionalInt32 = 1 XCTAssertNotEqual(a, b) - assertDecodeSucceeds([]) {$0.defaultUint64 == 44} - assertDecodeSucceeds([128, 4, 44]) {$0.defaultUint64 == 44} - assertDecodeSucceeds([128, 4, 9]) {$0.defaultUint64 == 9} + assertDecodeSucceeds([]) { $0.defaultUint64 == 44 } + assertDecodeSucceeds([128, 4, 44]) { $0.defaultUint64 == 44 } + assertDecodeSucceeds([128, 4, 9]) { $0.defaultUint64 == 9 } } func testEncoding_defaultSint32() throws { @@ -1986,9 +2152,9 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { b.optionalInt32 = 1 XCTAssertNotEqual(a, b) - assertDecodeSucceeds([]) {$0.defaultSint32 == -45} - assertDecodeSucceeds([136, 4, 89]) {$0.defaultSint32 == -45} - assertDecodeSucceeds([136, 4, 0]) {$0.defaultSint32 == 0} + assertDecodeSucceeds([]) { $0.defaultSint32 == -45 } + assertDecodeSucceeds([136, 4, 89]) { $0.defaultSint32 == -45 } + assertDecodeSucceeds([136, 4, 0]) { $0.defaultSint32 == 0 } assertDecodeFails([136, 4]) } @@ -2010,9 +2176,9 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { b.optionalInt32 = 1 XCTAssertNotEqual(a, b) - assertDecodeSucceeds([]) {$0.defaultSint64 == 46} - assertDecodeSucceeds([144, 4, 92]) {$0.defaultSint64 == 46} - assertDecodeSucceeds([144, 4, 0]) {$0.defaultSint64 == 0} + assertDecodeSucceeds([]) { $0.defaultSint64 == 46 } + assertDecodeSucceeds([144, 4, 92]) { $0.defaultSint64 == 46 } + assertDecodeSucceeds([144, 4, 0]) { $0.defaultSint64 == 0 } } func testEncoding_defaultFixed32() throws { @@ -2033,9 +2199,9 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { b.optionalInt32 = 1 XCTAssertNotEqual(a, b) - assertDecodeSucceeds([]) {$0.defaultFixed32 == 47} - assertDecodeSucceeds([157, 4, 47, 0, 0, 0]) {$0.defaultFixed32 == 47} - assertDecodeSucceeds([157, 4, 0, 0, 0, 0]) {$0.defaultFixed32 == 0} + assertDecodeSucceeds([]) { $0.defaultFixed32 == 47 } + assertDecodeSucceeds([157, 4, 47, 0, 0, 0]) { $0.defaultFixed32 == 47 } + assertDecodeSucceeds([157, 4, 0, 0, 0, 0]) { $0.defaultFixed32 == 0 } } func testEncoding_defaultFixed64() throws { @@ -2056,9 +2222,9 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { b.optionalInt32 = 1 XCTAssertNotEqual(a, b) - assertDecodeSucceeds([]) {$0.defaultFixed64 == 48} - assertDecodeSucceeds([161, 4, 48, 0, 0, 0, 0, 0, 0, 0]) {$0.defaultFixed64 == 48} - assertDecodeSucceeds([161, 4, 0, 0, 0, 0, 0, 0, 0, 0]) {$0.defaultFixed64 == 0} + assertDecodeSucceeds([]) { $0.defaultFixed64 == 48 } + assertDecodeSucceeds([161, 4, 48, 0, 0, 0, 0, 0, 0, 0]) { $0.defaultFixed64 == 48 } + assertDecodeSucceeds([161, 4, 0, 0, 0, 0, 0, 0, 0, 0]) { $0.defaultFixed64 == 0 } } func testEncoding_defaultSfixed32() throws { @@ -2079,9 +2245,9 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { b.optionalInt32 = 1 XCTAssertNotEqual(a, b) - assertDecodeSucceeds([]) {$0.defaultSfixed32 == 49} - assertDecodeSucceeds([173, 4, 49, 0, 0, 0]) {$0.defaultSfixed32 == 49} - assertDecodeSucceeds([173, 4, 0, 0, 0, 0]) {$0.defaultSfixed32 == 0} + assertDecodeSucceeds([]) { $0.defaultSfixed32 == 49 } + assertDecodeSucceeds([173, 4, 49, 0, 0, 0]) { $0.defaultSfixed32 == 49 } + assertDecodeSucceeds([173, 4, 0, 0, 0, 0]) { $0.defaultSfixed32 == 0 } } func testEncoding_defaultSfixed64() throws { @@ -2102,9 +2268,9 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { b.optionalInt32 = 1 XCTAssertNotEqual(a, b) - assertDecodeSucceeds([]) {$0.defaultSfixed64 == -50} - assertDecodeSucceeds([177, 4, 206, 255, 255, 255, 255, 255, 255, 255]) {$0.defaultSfixed64 == -50} - assertDecodeSucceeds([177, 4, 0, 0, 0, 0, 0, 0, 0, 0]) {$0.defaultSfixed64 == 0} + assertDecodeSucceeds([]) { $0.defaultSfixed64 == -50 } + assertDecodeSucceeds([177, 4, 206, 255, 255, 255, 255, 255, 255, 255]) { $0.defaultSfixed64 == -50 } + assertDecodeSucceeds([177, 4, 0, 0, 0, 0, 0, 0, 0, 0]) { $0.defaultSfixed64 == 0 } } func testEncoding_defaultFloat() throws { @@ -2125,9 +2291,9 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { b.optionalInt32 = 1 XCTAssertNotEqual(a, b) - assertDecodeSucceeds([]) {$0.defaultFloat == 51.5} - assertDecodeSucceeds([189, 4, 0, 0, 0, 0]) {$0.defaultFloat == 0} - assertDecodeSucceeds([189, 4, 0, 0, 78, 66]) {$0.defaultFloat == 51.5} + assertDecodeSucceeds([]) { $0.defaultFloat == 51.5 } + assertDecodeSucceeds([189, 4, 0, 0, 0, 0]) { $0.defaultFloat == 0 } + assertDecodeSucceeds([189, 4, 0, 0, 78, 66]) { $0.defaultFloat == 51.5 } } func testEncoding_defaultDouble() throws { @@ -2147,9 +2313,9 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { a.optionalInt32 = 1 XCTAssertNotEqual(a, b) - assertDecodeSucceeds([]) {$0.defaultDouble == 52e3} - assertDecodeSucceeds([193, 4, 0, 0, 0, 0, 0, 0, 0, 0]) {$0.defaultDouble == 0} - assertDecodeSucceeds([193, 4, 0, 0, 0, 0, 0, 100, 233, 64]) {$0.defaultDouble == 52e3} + assertDecodeSucceeds([]) { $0.defaultDouble == 52e3 } + assertDecodeSucceeds([193, 4, 0, 0, 0, 0, 0, 0, 0, 0]) { $0.defaultDouble == 0 } + assertDecodeSucceeds([193, 4, 0, 0, 0, 0, 0, 100, 233, 64]) { $0.defaultDouble == 52e3 } } func testEncoding_defaultBool() throws { @@ -2169,12 +2335,12 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { b.optionalInt32 = 1 XCTAssertNotEqual(a, b) - assertEncode([200, 4, 0]) {(o: inout MessageTestType) in o.defaultBool = false} - assertJSONEncode("{\"defaultBool\":false}") {(o: inout MessageTestType) in o.defaultBool = false} + assertEncode([200, 4, 0]) { (o: inout MessageTestType) in o.defaultBool = false } + assertJSONEncode("{\"defaultBool\":false}") { (o: inout MessageTestType) in o.defaultBool = false } - assertDecodeSucceeds([]) {$0.defaultBool == true} - assertDecodeSucceeds([200, 4, 0]) {$0.defaultBool == false} - assertDecodeSucceeds([200, 4, 1]) {$0.defaultBool == true} + assertDecodeSucceeds([]) { $0.defaultBool == true } + assertDecodeSucceeds([200, 4, 0]) { $0.defaultBool == false } + assertDecodeSucceeds([200, 4, 1]) { $0.defaultBool == true } } @@ -2195,8 +2361,8 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { b.optionalInt32 = 1 XCTAssertNotEqual(a, b) - assertDecodeSucceeds([]) {$0.defaultString == "hello"} - assertDecodeSucceeds([210, 4, 1, 97]) {$0.defaultString == "a"} + assertDecodeSucceeds([]) { $0.defaultString == "hello" } + assertDecodeSucceeds([210, 4, 1, 97]) { $0.defaultString == "a" } } func testEncoding_defaultBytes() throws { @@ -2216,8 +2382,8 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { a.optionalInt32 = 1 XCTAssertNotEqual(a, b) - assertDecodeSucceeds([]) {$0.defaultBytes == Data([119, 111, 114, 108, 100])} - assertDecodeSucceeds([218, 4, 1, 1]) {$0.defaultBytes == Data([1])} + assertDecodeSucceeds([]) { $0.defaultBytes == Data([119, 111, 114, 108, 100]) } + assertDecodeSucceeds([218, 4, 1, 1]) { $0.defaultBytes == Data([1]) } } func testEncoding_defaultNestedEnum() throws { @@ -2237,8 +2403,8 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { a.optionalInt32 = 1 XCTAssertNotEqual(a, b) - assertDecodeSucceeds([]) {$0.defaultNestedEnum == .bar} - assertDecodeSucceeds([136, 5, 3]) {$0.defaultNestedEnum == .baz} + assertDecodeSucceeds([]) { $0.defaultNestedEnum == .bar } + assertDecodeSucceeds([136, 5, 3]) { $0.defaultNestedEnum == .baz } } func testEncoding_defaultForeignEnum() throws { @@ -2258,16 +2424,16 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { a.optionalInt32 = 1 XCTAssertNotEqual(a, b) - assertDecodeSucceeds([]) {$0.defaultForeignEnum == .foreignBar} - assertDecodeSucceeds([144, 5, 6]) {$0.defaultForeignEnum == SwiftProtoTesting_ForeignEnum.foreignBaz} + assertDecodeSucceeds([]) { $0.defaultForeignEnum == .foreignBar } + assertDecodeSucceeds([144, 5, 6]) { $0.defaultForeignEnum == SwiftProtoTesting_ForeignEnum.foreignBaz } } func testEncoding_defaultImportEnum() throws { let empty = MessageTestType() XCTAssertEqual(empty.defaultImportEnum, SwiftProtoTesting_Import_ImportEnum.importBar) - assertEncode([152, 5, 9]) {(o: inout MessageTestType) in o.defaultImportEnum = .importBaz} - assertDecodeSucceeds([]) {$0.defaultImportEnum == .importBar} - assertDecodeSucceeds([152, 5, 9]) {$0.defaultImportEnum == .importBaz} + assertEncode([152, 5, 9]) { (o: inout MessageTestType) in o.defaultImportEnum = .importBaz } + assertDecodeSucceeds([]) { $0.defaultImportEnum == .importBar } + assertDecodeSucceeds([152, 5, 9]) { $0.defaultImportEnum == .importBaz } } func testEncoding_defaultStringPiece() throws { @@ -2291,16 +2457,16 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_oneofUint32() throws { - assertEncode([248, 6, 0]) {(o: inout MessageTestType) in o.oneofUint32 = 0} - assertDecodeSucceeds([248, 6, 255, 255, 255, 255, 15]) {$0.oneofUint32 == UInt32.max} - assertDecodeSucceeds([138, 7, 1, 97, 248, 6, 1]) {(o: MessageTestType) in + assertEncode([248, 6, 0]) { (o: inout MessageTestType) in o.oneofUint32 = 0 } + assertDecodeSucceeds([248, 6, 255, 255, 255, 255, 15]) { $0.oneofUint32 == UInt32.max } + assertDecodeSucceeds([138, 7, 1, 97, 248, 6, 1]) { (o: MessageTestType) in if case .oneofUint32? = o.oneofField, o.oneofUint32 == UInt32(1) { return true } return false } - assertDecodeFails([248, 6, 128]) // Bad varint + assertDecodeFails([248, 6, 128]) // Bad varint // Bad wire types: assertDecodeFails([249, 6]) assertDecodeFails([249, 6, 0]) @@ -2310,7 +2476,7 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } let initialMsg = MessageTestType.with { $0.oneofString = "initial" } assertMergesAsUnknownFields([250, 6, 0], inTo: initialMsg) { - $0.oneofString == "initial" // Shouldn't have gotten cleared. + $0.oneofString == "initial" // Shouldn't have gotten cleared. } assertDecodeFails([251, 6]) assertDecodeFails([251, 6, 0]) @@ -2332,18 +2498,18 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_oneofNestedMessage() { - assertEncode([130, 7, 2, 8, 1]) {(o: inout MessageTestType) in + assertEncode([130, 7, 2, 8, 1]) { (o: inout MessageTestType) in var nested = MessageTestType.NestedMessage() nested.bb = 1 o.oneofNestedMessage = nested } - assertDecodeSucceeds([130, 7, 0]) {(o: MessageTestType) in + assertDecodeSucceeds([130, 7, 0]) { (o: MessageTestType) in if case .oneofNestedMessage(let m)? = o.oneofField { return !m.hasBb } return false } - assertDecodeSucceeds([248, 6, 0, 130, 7, 2, 8, 1]) {(o: MessageTestType) in + assertDecodeSucceeds([248, 6, 0, 130, 7, 2, 8, 1]) { (o: MessageTestType) in if case .oneofUint32? = o.oneofField { return false } @@ -2354,14 +2520,14 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } } func testEncoding_oneofNestedMessage1() { - assertDecodeSucceeds([130, 7, 2, 8, 1, 248, 6, 0]) {(o: MessageTestType) in + assertDecodeSucceeds([130, 7, 2, 8, 1, 248, 6, 0]) { (o: MessageTestType) in if case .oneofUint32? = o.oneofField, o.oneofUint32 == 0 { return true } return false } // Unkonwn field within nested message should not break decoding - assertDecodeSucceeds([130, 7, 5, 128, 127, 0, 8, 1, 248, 6, 0]) {(o: MessageTestType) in + assertDecodeSucceeds([130, 7, 5, 128, 127, 0, 8, 1, 248, 6, 0]) { (o: MessageTestType) in if case .oneofUint32? = o.oneofField, o.oneofUint32 == 0 { return true } @@ -2374,7 +2540,10 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { nested1.bb = 1 var m = MessageTestType() m.oneofNestedMessage = nested1 - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noneof_nested_message {\n bb: 1\n}\n", m) + assertDebugDescription( + "SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noneof_nested_message {\n bb: 1\n}\n", + m + ) var nested2 = MessageTestType.NestedMessage() nested2.bb = 2 var m2 = MessageTestType() @@ -2389,7 +2558,7 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } let initialMsg = MessageTestType.with { $0.oneofString = "initial" } assertMergesAsUnknownFields([128, 7, 0], inTo: initialMsg) { - $0.oneofString == "initial" // Shouldn't have gotten cleared. + $0.oneofString == "initial" // Shouldn't have gotten cleared. } assertDecodeFails([129, 7]) assertDecodeFails([129, 7, 0]) @@ -2406,50 +2575,50 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_oneofString() { - assertEncode([138, 7, 1, 97]) {(o: inout MessageTestType) in o.oneofString = "a"} - assertDecodeSucceeds([138, 7, 1, 97]) {$0.oneofString == "a"} - assertDecodeSucceeds([138, 7, 0]) {$0.oneofString == ""} - assertDecodeSucceeds([146, 7, 0, 138, 7, 1, 97]) {(o:MessageTestType) in + assertEncode([138, 7, 1, 97]) { (o: inout MessageTestType) in o.oneofString = "a" } + assertDecodeSucceeds([138, 7, 1, 97]) { $0.oneofString == "a" } + assertDecodeSucceeds([138, 7, 0]) { $0.oneofString == "" } + assertDecodeSucceeds([146, 7, 0, 138, 7, 1, 97]) { (o: MessageTestType) in if case .oneofString? = o.oneofField, o.oneofString == "a" { return true } return false } - assertDecodeFails([138, 7, 1]) // Truncated body - assertDecodeFails([138, 7, 1, 192]) // Malformed UTF-8 + assertDecodeFails([138, 7, 1]) // Truncated body + assertDecodeFails([138, 7, 1, 192]) // Malformed UTF-8 // Bad wire types: assertDecodesAsUnknownFields([136, 7, 0]) { // Wrong wire type (varint), valid as an unknown field $0.oneofField == nil // oneof doesn't get set. } let initialMsg = MessageTestType.with { $0.oneofUint32 = 123 } assertMergesAsUnknownFields([136, 7, 0], inTo: initialMsg) { - $0.oneofUint32 == 123 // Shouldn't have gotten cleared. + $0.oneofUint32 == 123 // Shouldn't have gotten cleared. } assertDecodesAsUnknownFields([136, 7, 1]) { // Wrong wire type (varint), valid as an unknown field $0.oneofField == nil // oneof doesn't get set. } assertMergesAsUnknownFields([136, 7, 1], inTo: initialMsg) { - $0.oneofUint32 == 123 // Shouldn't have gotten cleared. + $0.oneofUint32 == 123 // Shouldn't have gotten cleared. } assertDecodesAsUnknownFields([137, 7, 1, 1, 1, 1, 1, 1, 1, 1]) { // Wrong wire type (fixed64), valid as an unknown field $0.oneofField == nil // oneof doesn't get set. } assertMergesAsUnknownFields([137, 7, 1, 1, 1, 1, 1, 1, 1, 1], inTo: initialMsg) { - $0.oneofUint32 == 123 // Shouldn't have gotten cleared. + $0.oneofUint32 == 123 // Shouldn't have gotten cleared. } - assertDecodeFails([139, 7]) // Wire type 3 - assertDecodeFails([140, 7]) // Wire type 4 + assertDecodeFails([139, 7]) // Wire type 3 + assertDecodeFails([140, 7]) // Wire type 4 assertDecodeFails([141, 7, 0]) // Wire type 5 assertDecodesAsUnknownFields([141, 7, 0, 0, 0, 0]) { // Wrong wire type (fixed32), valid as an unknown field $0.oneofField == nil // oneof doesn't get set. } assertMergesAsUnknownFields([141, 7, 0, 0, 0, 0], inTo: initialMsg) { - $0.oneofUint32 == 123 // Shouldn't have gotten cleared. + $0.oneofUint32 == 123 // Shouldn't have gotten cleared. } - assertDecodeFails([142, 7]) // Wire type 6 - assertDecodeFails([142, 7, 0]) // Wire type 6 - assertDecodeFails([143, 7]) // Wire type 7 - assertDecodeFails([143, 7, 0]) // Wire type 7 + assertDecodeFails([142, 7]) // Wire type 6 + assertDecodeFails([142, 7, 0]) // Wire type 6 + assertDecodeFails([143, 7]) // Wire type 7 + assertDecodeFails([143, 7, 0]) // Wire type 7 var m = MessageTestType() m.oneofString = "abc" @@ -2460,10 +2629,10 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testEncoding_oneofBytes() { - assertEncode([146, 7, 1, 1]) {(o: inout MessageTestType) in o.oneofBytes = Data([1])} + assertEncode([146, 7, 1, 1]) { (o: inout MessageTestType) in o.oneofBytes = Data([1]) } } func testEncoding_oneofBytes2() { - assertDecodeSucceeds([146, 7, 1, 1]) {(o: MessageTestType) in + assertDecodeSucceeds([146, 7, 1, 1]) { (o: MessageTestType) in let expectedB = Data([1]) if case .oneofBytes(let b)? = o.oneofField { let s = o.oneofString @@ -2473,7 +2642,7 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } } func testEncoding_oneofBytes3() { - assertDecodeSucceeds([146, 7, 0]) {(o: MessageTestType) in + assertDecodeSucceeds([146, 7, 0]) { (o: MessageTestType) in let expectedB = Data() if case .oneofBytes(let b)? = o.oneofField { let s = o.oneofString @@ -2483,7 +2652,7 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } } func testEncoding_oneofBytes4() { - assertDecodeSucceeds([138, 7, 1, 97, 146, 7, 0]) {(o: MessageTestType) in + assertDecodeSucceeds([138, 7, 1, 97, 146, 7, 0]) { (o: MessageTestType) in let expectedB = Data() if case .oneofBytes(let b)? = o.oneofField { let s = o.oneofString @@ -2507,7 +2676,7 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } let initialMsg = MessageTestType.with { $0.oneofString = "initial" } assertMergesAsUnknownFields([144, 7, 0], inTo: initialMsg) { - $0.oneofString == "initial" // Shouldn't have gotten cleared. + $0.oneofString == "initial" // Shouldn't have gotten cleared. } assertDecodeFails([145, 7]) assertDecodeFails([145, 7, 0]) @@ -2527,7 +2696,10 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { var m = MessageTestType() m.oneofBytes = Data([1, 2, 3]) - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noneof_bytes: \"\\001\\002\\003\"\n", m) + assertDebugDescription( + "SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noneof_bytes: \"\\001\\002\\003\"\n", + m + ) var m2 = MessageTestType() m2.oneofBytes = Data([4, 5, 6]) XCTAssertNotEqual(m.hashValue, m2.hashValue) @@ -2539,7 +2711,10 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { m.optionalInt32 = 7 assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_int32: 7\n", m) m.repeatedString = ["a", "b"] - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_int32: 7\nrepeated_string: \"a\"\nrepeated_string: \"b\"\n", m) + assertDebugDescription( + "SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_int32: 7\nrepeated_string: \"a\"\nrepeated_string: \"b\"\n", + m + ) } func testDebugDescription2() { @@ -2562,11 +2737,16 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { g.a = 7 g.b = "b" m.fooGroup = g - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestOneof:\nFooGroup {\n a: 7\n b: \"b\"\n}\n", m) + assertDebugDescription( + "SwiftProtobufTests.SwiftProtoTesting_TestOneof:\nFooGroup {\n a: 7\n b: \"b\"\n}\n", + m + ) } func testDebugDescription4() { - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_int32: 88\nrepeated_int32: 1\nrepeated_int32: 2\nrepeated_int32: 3\nRepeatedGroup {\n a: 1\n}\nRepeatedGroup {\n a: 2\n}\n") {(o: inout MessageTestType) in + assertDebugDescription( + "SwiftProtobufTests.SwiftProtoTesting_TestAllTypes:\noptional_int32: 88\nrepeated_int32: 1\nrepeated_int32: 2\nrepeated_int32: 3\nRepeatedGroup {\n a: 1\n}\nRepeatedGroup {\n a: 2\n}\n" + ) { (o: inout MessageTestType) in o.optionalInt32 = 88 o.repeatedInt32 = [1, 2, 3] var g1 = MessageTestType.RepeatedGroup() @@ -2583,14 +2763,14 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } func testWithFactoryHelperRethrows() { - struct TestWithFactoryHelperRethrows_Error : Error {} + struct TestWithFactoryHelperRethrows_Error: Error {} - let pNoThrow: (inout SwiftProtoTesting_ForeignMessage) -> () = { $0.c = 1 } + let pNoThrow: (inout SwiftProtoTesting_ForeignMessage) -> Void = { $0.c = 1 } let m1 = SwiftProtoTesting_ForeignMessage.with(pNoThrow) XCTAssertEqual(1, m1.c) var populatorRan = false - let pThrow: (inout SwiftProtoTesting_ForeignMessage) throws -> () = { + let pThrow: (inout SwiftProtoTesting_ForeignMessage) throws -> Void = { $0.c = 2 populatorRan = true throw TestWithFactoryHelperRethrows_Error() @@ -2603,14 +2783,14 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { func testUnknownFields_Success() throws { let testInputs: [([UInt8], String)] = [ - ([192, 12, 1], "200: 1"), // varint of 1. - ([193, 12, 20, 0, 0, 0, 0, 0, 0, 0], "200: 0x0000000000000014"), // fixed64 of 20 - ([194, 12, 3, 65, 66, 67], "200: \"ABC\""), // length delimited. - ([195, 12, 8, 1, 196, 12], "200 {\n 1: 1\n}"), // StartGroup, Field 1: varint of 1, EndGroup. - ([197, 12, 30, 0, 0, 0], "200: 0x0000001E"), // fixed32. - - ([192, 12, 129, 1], "200: 129"), // varint of 129 (two bytes on wire). - ([195, 12, 11, 8, 1, 12, 196, 12], "200 {\n 1 {\n 1: 1\n }\n}"), // StartGroup, Field 1: StartGroup, Field 1: varint of 1, EndGroup, EndGroup. + ([192, 12, 1], "200: 1"), // varint of 1. + ([193, 12, 20, 0, 0, 0, 0, 0, 0, 0], "200: 0x0000000000000014"), // fixed64 of 20 + ([194, 12, 3, 65, 66, 67], "200: \"ABC\""), // length delimited. + ([195, 12, 8, 1, 196, 12], "200 {\n 1: 1\n}"), // StartGroup, Field 1: varint of 1, EndGroup. + ([197, 12, 30, 0, 0, 0], "200: 0x0000001E"), // fixed32. + + ([192, 12, 129, 1], "200: 129"), // varint of 129 (two bytes on wire). + ([195, 12, 11, 8, 1, 12, 196, 12], "200 {\n 1 {\n 1: 1\n }\n}"), // StartGroup, Field 1: StartGroup, Field 1: varint of 1, EndGroup, EndGroup. ] // Fields at the top level of the message. @@ -2669,20 +2849,20 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { func testUnknownFields_Failures() throws { let testInputs: [[UInt8]] = [ - [192, 12], // varint - [192, 12, 129], // varint (should be two bytes) - [193, 12], // fixed64 - [193, 12, 20, 0, 0, 0, 0, 0, 0], // fixed64 - [194, 12], // length delimited. - [194, 12, 3, 65, 66], // length delimited. - [195, 12], // StartGroup. - [195, 12, 8, 1], // StartGroup, Field 1: varint of 1. - [197, 12], // fixed32. - [197, 12, 30, 0, 0], // fixed32. - - [195, 12, 11], // StartGroup, Field 1: StartGroup. - [195, 12, 11, 8, 1, 12], // StartGroup, Field 1: StartGroup, Field 1: varint of 1, EndGroup. - [195, 12, 11, 8, 1, 196, 12], // StartGroup, Field 1: StartGroup, Field 1: varint of 1, EndGroup (but wrong group). + [192, 12], // varint + [192, 12, 129], // varint (should be two bytes) + [193, 12], // fixed64 + [193, 12, 20, 0, 0, 0, 0, 0, 0], // fixed64 + [194, 12], // length delimited. + [194, 12, 3, 65, 66], // length delimited. + [195, 12], // StartGroup. + [195, 12, 8, 1], // StartGroup, Field 1: varint of 1. + [197, 12], // fixed32. + [197, 12, 30, 0, 0], // fixed32. + + [195, 12, 11], // StartGroup, Field 1: StartGroup. + [195, 12, 11, 8, 1, 12], // StartGroup, Field 1: StartGroup, Field 1: varint of 1, EndGroup. + [195, 12, 11, 8, 1, 196, 12], // StartGroup, Field 1: StartGroup, Field 1: varint of 1, EndGroup (but wrong group). ] // Fields at the top level of the message. @@ -2717,6 +2897,6 @@ final class Test_AllTypes: XCTestCase, PBTestHelpers { } catch { // Nothing should error! } - } + } } } diff --git a/Tests/SwiftProtobufTests/Test_AllTypes_Proto3.swift b/Tests/SwiftProtobufTests/Test_AllTypes_Proto3.swift index 7a1807089..ebaceb7a3 100644 --- a/Tests/SwiftProtobufTests/Test_AllTypes_Proto3.swift +++ b/Tests/SwiftProtobufTests/Test_AllTypes_Proto3.swift @@ -23,7 +23,12 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { // Custom decodeSucceeds that also does a round-trip through the Empty // message to make sure unknown fields are consistently preserved by proto2. - func assertDecodeSucceeds(_ bytes: [UInt8], file: XCTestFileArgType = #file, line: UInt = #line, check: (MessageTestType) -> Bool) { + func assertDecodeSucceeds( + _ bytes: [UInt8], + file: XCTestFileArgType = #file, + line: UInt = #line, + check: (MessageTestType) -> Bool + ) { baseAssertDecodeSucceeds(bytes, file: file, line: line, check: check) do { // Make sure unknown fields are preserved by empty message decode/encode @@ -43,35 +48,42 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { // Singular types // func testEncoding_optionalInt32() { - assertEncode([8, 1]) {(o: inout MessageTestType) in o.optionalInt32 = 1} - assertEncode([8, 255, 255, 255, 255, 7]) {(o: inout MessageTestType) in o.optionalInt32 = Int32.max} - assertEncode([8, 128, 128, 128, 128, 248, 255, 255, 255, 255, 1]) {(o: inout MessageTestType) in o.optionalInt32 = Int32.min} - assertDecodeSucceeds([8, 1]) {(o: MessageTestType) in + assertEncode([8, 1]) { (o: inout MessageTestType) in o.optionalInt32 = 1 } + assertEncode([8, 255, 255, 255, 255, 7]) { (o: inout MessageTestType) in o.optionalInt32 = Int32.max } + assertEncode([8, 128, 128, 128, 128, 248, 255, 255, 255, 255, 1]) { (o: inout MessageTestType) in + o.optionalInt32 = Int32.min + } + assertDecodeSucceeds([8, 1]) { (o: MessageTestType) in let t: Int32 = o.optionalInt32 return t == 1 } - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_int32: 1\n") {(o: inout MessageTestType) in o.optionalInt32 = 1} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_int32: -2147483648\noptional_uint32: 4294967295\n") {(o: inout MessageTestType) in + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_int32: 1\n") { + (o: inout MessageTestType) in o.optionalInt32 = 1 + } + assertDebugDescription( + "SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_int32: -2147483648\noptional_uint32: 4294967295\n" + ) { (o: inout MessageTestType) in o.optionalInt32 = Int32.min o.optionalUint32 = UInt32.max } - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\n") {(o: inout MessageTestType) in + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\n") { + (o: inout MessageTestType) in o.optionalInt32 = 1 o.optionalInt32 = 0 } // Technically, this overflows Int32, but we truncate and accept it. - assertDecodeSucceeds([8, 255, 255, 255, 255, 255, 255, 1]) {$0.optionalInt32 == -1} + assertDecodeSucceeds([8, 255, 255, 255, 255, 255, 255, 1]) { $0.optionalInt32 == -1 } assertDecodeFails([8]) - assertDecodeFails([9, 57]) // Cannot use wire type 1 - assertDecodeFails([10, 58]) // Cannot use wire type 2 - assertDecodeFails([11, 59]) // Cannot use wire type 3 - assertDecodeFails([12, 60]) // Cannot use wire type 4 - assertDecodeFails([13, 61]) // Cannot use wire type 5 - assertDecodeFails([14, 62]) // Cannot use wire type 6 - assertDecodeFails([15, 63]) // Cannot use wire type 7 + assertDecodeFails([9, 57]) // Cannot use wire type 1 + assertDecodeFails([10, 58]) // Cannot use wire type 2 + assertDecodeFails([11, 59]) // Cannot use wire type 3 + assertDecodeFails([12, 60]) // Cannot use wire type 4 + assertDecodeFails([13, 61]) // Cannot use wire type 5 + assertDecodeFails([14, 62]) // Cannot use wire type 6 + assertDecodeFails([15, 63]) // Cannot use wire type 7 assertDecodeFails([8, 188]) assertDecodeFails([8]) @@ -87,14 +99,20 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } func testEncoding_optionalInt64() { - assertEncode([16, 1]) {(o: inout MessageTestType) in o.optionalInt64 = 1} - assertEncode([16, 255, 255, 255, 255, 255, 255, 255, 255, 127]) {(o: inout MessageTestType) in o.optionalInt64 = Int64.max} - assertEncode([16, 128, 128, 128, 128, 128, 128, 128, 128, 128, 1]) {(o: inout MessageTestType) in o.optionalInt64 = Int64.min} - assertDecodeSucceeds([16, 184, 156, 195, 145, 203, 1]) {(o: MessageTestType) in - let t: Int64 = o.optionalInt64 // Verify in-memory type - return t == 54529150520 - } - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_int64: 1\n") {(o: inout MessageTestType) in o.optionalInt64 = 1} + assertEncode([16, 1]) { (o: inout MessageTestType) in o.optionalInt64 = 1 } + assertEncode([16, 255, 255, 255, 255, 255, 255, 255, 255, 127]) { (o: inout MessageTestType) in + o.optionalInt64 = Int64.max + } + assertEncode([16, 128, 128, 128, 128, 128, 128, 128, 128, 128, 1]) { (o: inout MessageTestType) in + o.optionalInt64 = Int64.min + } + assertDecodeSucceeds([16, 184, 156, 195, 145, 203, 1]) { (o: MessageTestType) in + let t: Int64 = o.optionalInt64 // Verify in-memory type + return t == 54_529_150_520 + } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_int64: 1\n") { + (o: inout MessageTestType) in o.optionalInt64 = 1 + } assertDecodeFails([16]) assertDecodeFails([16, 184, 156, 195, 145, 203]) assertDecodeFails([17, 81]) @@ -117,12 +135,14 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } func testEncoding_optionalUint32() { - assertEncode([24, 255, 255, 255, 255, 15]) {(o: inout MessageTestType) in o.optionalUint32 = UInt32.max} - assertDecodeSucceeds([24, 149, 88]) {(o: MessageTestType) in + assertEncode([24, 255, 255, 255, 255, 15]) { (o: inout MessageTestType) in o.optionalUint32 = UInt32.max } + assertDecodeSucceeds([24, 149, 88]) { (o: MessageTestType) in let t: UInt32 = o.optionalUint32 return t == 11285 } - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_uint32: 1\n") {(o: inout MessageTestType) in o.optionalUint32 = 1} + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_uint32: 1\n") { + (o: inout MessageTestType) in o.optionalUint32 = 1 + } assertDecodeFails([24]) assertDecodeFails([24, 149]) assertDecodeFails([25, 105]) @@ -146,12 +166,16 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } func testEncoding_optionalUint64() throws { - assertEncode([32, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1]) {(o: inout MessageTestType) in o.optionalUint64 = UInt64.max} - assertDecodeSucceeds([32, 149, 7]) {(o: MessageTestType) in + assertEncode([32, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1]) { (o: inout MessageTestType) in + o.optionalUint64 = UInt64.max + } + assertDecodeSucceeds([32, 149, 7]) { (o: MessageTestType) in let t: UInt64 = o.optionalUint64 return t == 917 } - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_uint64: 1\n") {(o: inout MessageTestType) in o.optionalUint64 = 1} + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_uint64: 1\n") { + (o: inout MessageTestType) in o.optionalUint64 = 1 + } assertDecodeFails([32]) assertDecodeFails([32, 149]) assertDecodeFails([32, 149, 190, 193, 230, 186, 233, 166, 219]) @@ -159,7 +183,7 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { assertDecodeFails([33, 0]) assertDecodeFails([33, 8, 0]) assertDecodeFails([34]) - assertDecodesAsUnknownFields([34, 0]) // Wrong wire type (length delimited), valid as an unknown field + assertDecodesAsUnknownFields([34, 0]) // Wrong wire type (length delimited), valid as an unknown field assertDecodeFails([34, 8, 0]) assertDecodeFails([35]) assertDecodeFails([35, 0]) @@ -190,21 +214,27 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } func testEncoding_optionalSint32() { - assertEncode([40, 254, 255, 255, 255, 15]) {(o: inout MessageTestType) in o.optionalSint32 = Int32.max} - assertEncode([40, 255, 255, 255, 255, 15]) {(o: inout MessageTestType) in o.optionalSint32 = Int32.min} - assertDecodeSucceeds([40, 0x81, 0x82, 0x80, 0x00]) {(o: MessageTestType) in - let t: Int32 = o.optionalSint32 // Verify in-memory type + assertEncode([40, 254, 255, 255, 255, 15]) { (o: inout MessageTestType) in o.optionalSint32 = Int32.max } + assertEncode([40, 255, 255, 255, 255, 15]) { (o: inout MessageTestType) in o.optionalSint32 = Int32.min } + assertDecodeSucceeds([40, 0x81, 0x82, 0x80, 0x00]) { (o: MessageTestType) in + let t: Int32 = o.optionalSint32 // Verify in-memory type return t == -129 } - assertDecodeSucceeds([40, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00]) {$0.optionalSint32 == 0} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_sint32: 1\n") {(o: inout MessageTestType) in o.optionalSint32 = 1} + assertDecodeSucceeds([40, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00]) { + $0.optionalSint32 == 0 + } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_sint32: 1\n") { + (o: inout MessageTestType) in o.optionalSint32 = 1 + } // Truncate on overflow - assertDecodeSucceeds([40, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f]) {$0.optionalSint32 == -2147483648} - assertDecodeSucceeds([40, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x7f]) {$0.optionalSint32 == 2147483647} + assertDecodeSucceeds([40, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f]) { $0.optionalSint32 == -2_147_483_648 } + assertDecodeSucceeds([40, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x7f]) { $0.optionalSint32 == 2_147_483_647 } assertDecodeFails([40]) - assertDecodeFails([40, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00]) + assertDecodeFails([ + 40, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + ]) assertDecodeFails([41]) assertDecodeFails([41, 0]) assertDecodeFails([42]) @@ -232,13 +262,19 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } func testEncoding_optionalSint64() { - assertEncode([48, 254, 255, 255, 255, 255, 255, 255, 255, 255, 1]) {(o: inout MessageTestType) in o.optionalSint64 = Int64.max} - assertEncode([48, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1]) {(o: inout MessageTestType) in o.optionalSint64 = Int64.min} - assertDecodeSucceeds([48, 139, 94]) {(o: MessageTestType) in + assertEncode([48, 254, 255, 255, 255, 255, 255, 255, 255, 255, 1]) { (o: inout MessageTestType) in + o.optionalSint64 = Int64.max + } + assertEncode([48, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1]) { (o: inout MessageTestType) in + o.optionalSint64 = Int64.min + } + assertDecodeSucceeds([48, 139, 94]) { (o: MessageTestType) in let t: Int64 = o.optionalSint64 return t == -6022 } - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_sint64: 1\n") {(o: inout MessageTestType) in o.optionalSint64 = 1} + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_sint64: 1\n") { + (o: inout MessageTestType) in o.optionalSint64 = 1 + } assertDecodeFails([48]) assertDecodeFails([48, 139]) assertDecodeFails([49]) @@ -268,12 +304,14 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } func testEncoding_optionalFixed32() throws { - assertEncode([61, 255, 255, 255, 255]) {(o: inout MessageTestType) in o.optionalFixed32 = UInt32.max} - assertDecodeSucceeds([61, 8, 12, 108, 1]) {(o: MessageTestType) in + assertEncode([61, 255, 255, 255, 255]) { (o: inout MessageTestType) in o.optionalFixed32 = UInt32.max } + assertDecodeSucceeds([61, 8, 12, 108, 1]) { (o: MessageTestType) in let t: UInt32 = o.optionalFixed32 - return t == 23858184 + return t == 23_858_184 + } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_fixed32: 1\n") { + (o: inout MessageTestType) in o.optionalFixed32 = 1 } - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_fixed32: 1\n") {(o: inout MessageTestType) in o.optionalFixed32 = 1} assertDecodeFails([61]) assertDecodeFails([61, 255]) assertDecodeFails([61, 255, 255]) @@ -313,12 +351,16 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } func testEncoding_optionalFixed64() throws { - assertEncode([65, 255, 255, 255, 255, 255, 255, 255, 255]) {(o: inout MessageTestType) in o.optionalFixed64 = UInt64.max} - assertDecodeSucceeds([65, 255, 255, 255, 255, 255, 255, 255, 255]) {(o: MessageTestType) in - let t: UInt64 = o.optionalFixed64 // Verify in-memory type - return t == 18446744073709551615 + assertEncode([65, 255, 255, 255, 255, 255, 255, 255, 255]) { (o: inout MessageTestType) in + o.optionalFixed64 = UInt64.max + } + assertDecodeSucceeds([65, 255, 255, 255, 255, 255, 255, 255, 255]) { (o: MessageTestType) in + let t: UInt64 = o.optionalFixed64 // Verify in-memory type + return t == 18_446_744_073_709_551_615 + } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_fixed64: 1\n") { + (o: inout MessageTestType) in o.optionalFixed64 = 1 } - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_fixed64: 1\n") {(o: inout MessageTestType) in o.optionalFixed64 = 1} assertDecodeFails([65]) assertDecodeFails([65, 255]) assertDecodeFails([65, 255, 255]) @@ -362,14 +404,16 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } func testEncoding_optionalSfixed32() throws { - assertEncode([77, 255, 255, 255, 127]) {(o: inout MessageTestType) in o.optionalSfixed32 = Int32.max} - assertEncode([77, 0, 0, 0, 128]) {(o: inout MessageTestType) in o.optionalSfixed32 = Int32.min} - assertDecodeSucceeds([77, 0, 0, 0, 0]) {(o: MessageTestType) in - let t: Int32 = o.optionalSfixed32 // Verify in-memory type + assertEncode([77, 255, 255, 255, 127]) { (o: inout MessageTestType) in o.optionalSfixed32 = Int32.max } + assertEncode([77, 0, 0, 0, 128]) { (o: inout MessageTestType) in o.optionalSfixed32 = Int32.min } + assertDecodeSucceeds([77, 0, 0, 0, 0]) { (o: MessageTestType) in + let t: Int32 = o.optionalSfixed32 // Verify in-memory type return t == 0 } - assertDecodeSucceeds([77, 255, 255, 255, 255]) {$0.optionalSfixed32 == -1} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_sfixed32: 1\n") {(o: inout MessageTestType) in o.optionalSfixed32 = 1} + assertDecodeSucceeds([77, 255, 255, 255, 255]) { $0.optionalSfixed32 == -1 } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_sfixed32: 1\n") { + (o: inout MessageTestType) in o.optionalSfixed32 = 1 + } assertDecodeFails([77]) assertDecodeFails([77]) assertDecodeFails([77, 0]) @@ -409,13 +453,17 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } func testEncoding_optionalSfixed64() throws { - assertEncode([81, 255, 255, 255, 255, 255, 255, 255, 127]) {(o: inout MessageTestType) in o.optionalSfixed64 = Int64.max} - assertEncode([81, 0, 0, 0, 0, 0, 0, 0, 128]) {(o: inout MessageTestType) in o.optionalSfixed64 = Int64.min} - assertDecodeSucceeds([81, 0, 0, 0, 0, 0, 0, 0, 128]) {(o: MessageTestType) in - let t: Int64 = o.optionalSfixed64 // Verify in-memory type - return t == -9223372036854775808 + assertEncode([81, 255, 255, 255, 255, 255, 255, 255, 127]) { (o: inout MessageTestType) in + o.optionalSfixed64 = Int64.max + } + assertEncode([81, 0, 0, 0, 0, 0, 0, 0, 128]) { (o: inout MessageTestType) in o.optionalSfixed64 = Int64.min } + assertDecodeSucceeds([81, 0, 0, 0, 0, 0, 0, 0, 128]) { (o: MessageTestType) in + let t: Int64 = o.optionalSfixed64 // Verify in-memory type + return t == -9_223_372_036_854_775_808 + } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_sfixed64: 1\n") { + (o: inout MessageTestType) in o.optionalSfixed64 = 1 } - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_sfixed64: 1\n") {(o: inout MessageTestType) in o.optionalSfixed64 = 1} assertDecodeFails([81]) assertDecodeFails([81, 0]) assertDecodeFails([81, 0, 0]) @@ -456,32 +504,33 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } func testEncoding_optionalFloat() throws { - assertEncode([93, 0, 0, 0, 63]) {(o: inout MessageTestType) in o.optionalFloat = 0.5} - assertEncode([93, 0, 0, 0, 64]) {(o: inout MessageTestType) in o.optionalFloat = 2.0} - assertDecodeSucceeds([93, 0, 0, 0, 0]) {(o: MessageTestType) in + assertEncode([93, 0, 0, 0, 63]) { (o: inout MessageTestType) in o.optionalFloat = 0.5 } + assertEncode([93, 0, 0, 0, 64]) { (o: inout MessageTestType) in o.optionalFloat = 2.0 } + assertDecodeSucceeds([93, 0, 0, 0, 0]) { (o: MessageTestType) in let t: Float = o.optionalFloat return t == 0 } assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_float: 1.0\n") { - (o: inout MessageTestType) in o.optionalFloat = 1.0} + (o: inout MessageTestType) in o.optionalFloat = 1.0 + } assertDecodeFails([93, 0, 0, 0]) assertDecodeFails([93, 0, 0]) assertDecodeFails([93, 0]) assertDecodeFails([93]) - assertDecodeFails([88]) // Float cannot use wire type 0 + assertDecodeFails([88]) // Float cannot use wire type 0 assertDecodesAsUnknownFields([88, 0]) // Wrong wire type (varint), valid as an unknown field - assertDecodeFails([89]) // Float cannot use wire type 1 - assertDecodeFails([89, 0, 0, 0, 0]) // Float cannot use wire type 1 - assertDecodeFails([90]) // Float cannot use wire type 2 + assertDecodeFails([89]) // Float cannot use wire type 1 + assertDecodeFails([89, 0, 0, 0, 0]) // Float cannot use wire type 1 + assertDecodeFails([90]) // Float cannot use wire type 2 assertDecodesAsUnknownFields([90, 0]) // Wrong wire type (length delimited), valid as an unknown field - assertDecodeFails([91]) // Float cannot use wire type 3 - assertDecodeFails([91, 0, 0, 0, 0]) // Float cannot use wire type 3 - assertDecodeFails([92]) // Float cannot use wire type 4 - assertDecodeFails([92, 0, 0, 0, 0]) // Float cannot use wire type 4 - assertDecodeFails([94]) // Float cannot use wire type 6 - assertDecodeFails([94, 0, 0, 0, 0]) // Float cannot use wire type 6 - assertDecodeFails([95]) // Float cannot use wire type 7 - assertDecodeFails([95, 0, 0, 0, 0]) // Float cannot use wire type 7 + assertDecodeFails([91]) // Float cannot use wire type 3 + assertDecodeFails([91, 0, 0, 0, 0]) // Float cannot use wire type 3 + assertDecodeFails([92]) // Float cannot use wire type 4 + assertDecodeFails([92, 0, 0, 0, 0]) // Float cannot use wire type 4 + assertDecodeFails([94]) // Float cannot use wire type 6 + assertDecodeFails([94, 0, 0, 0, 0]) // Float cannot use wire type 6 + assertDecodeFails([95]) // Float cannot use wire type 7 + assertDecodeFails([95, 0, 0, 0, 0]) // Float cannot use wire type 7 let empty = MessageTestType() var a = empty @@ -496,14 +545,15 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } func testEncoding_optionalDouble() throws { - assertEncode([97, 0, 0, 0, 0, 0, 0, 224, 63]) {(o: inout MessageTestType) in o.optionalDouble = 0.5} - assertEncode([97, 0, 0, 0, 0, 0, 0, 0, 64]) {(o: inout MessageTestType) in o.optionalDouble = 2.0} - assertDecodeSucceeds([97, 0, 0, 0, 0, 0, 0, 224, 63]) {(o: MessageTestType) in + assertEncode([97, 0, 0, 0, 0, 0, 0, 224, 63]) { (o: inout MessageTestType) in o.optionalDouble = 0.5 } + assertEncode([97, 0, 0, 0, 0, 0, 0, 0, 64]) { (o: inout MessageTestType) in o.optionalDouble = 2.0 } + assertDecodeSucceeds([97, 0, 0, 0, 0, 0, 0, 224, 63]) { (o: MessageTestType) in let t: Double = o.optionalDouble return t == 0.5 } assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_double: 1.0\n") { - (o: inout MessageTestType) in o.optionalDouble = 1.0} + (o: inout MessageTestType) in o.optionalDouble = 1.0 + } assertDecodeFails([97, 0, 0, 0, 0, 0, 0, 224]) assertDecodeFails([97]) assertDecodeFails([96]) @@ -541,13 +591,17 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } func testEncoding_optionalBool() throws { - assertEncode([104, 1]) {(o: inout MessageTestType) in o.optionalBool = true} - assertDecodeSucceeds([104, 1]) {(o: MessageTestType) in - let t: Bool = o.optionalBool // Verify non-optional + assertEncode([104, 1]) { (o: inout MessageTestType) in o.optionalBool = true } + assertDecodeSucceeds([104, 1]) { (o: MessageTestType) in + let t: Bool = o.optionalBool // Verify non-optional return t == true } - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_bool: true\n") {(o: inout MessageTestType) in o.optionalBool = true} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\n") {(o: inout MessageTestType) in o.optionalBool = false} + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_bool: true\n") { + (o: inout MessageTestType) in o.optionalBool = true + } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\n") { + (o: inout MessageTestType) in o.optionalBool = false + } assertDecodeFails([104]) assertDecodeFails([104, 255]) assertDecodeFails([105]) @@ -578,18 +632,21 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } func testEncoding_optionalString() throws { - assertEncode([114, 1, 65]) {(o: inout MessageTestType) in o.optionalString = "A"} - assertEncode([114, 4, 0xf0, 0x9f, 0x98, 0x84]) {(o: inout MessageTestType) in o.optionalString = "😄"} - assertDecodeSucceeds([114, 5, 72, 101, 108, 108, 111]) {(o: MessageTestType) in - let t: String = o.optionalString // Verify non-optional + assertEncode([114, 1, 65]) { (o: inout MessageTestType) in o.optionalString = "A" } + assertEncode([114, 4, 0xf0, 0x9f, 0x98, 0x84]) { (o: inout MessageTestType) in o.optionalString = "😄" } + assertDecodeSucceeds([114, 5, 72, 101, 108, 108, 111]) { (o: MessageTestType) in + let t: String = o.optionalString // Verify non-optional return t == "Hello" } - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_string: \"abc\"\n") {(o: inout MessageTestType) in o.optionalString = "abc"} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_string: \"\\b\\t\"\n") {(o: inout MessageTestType) in o.optionalString = "\u{08}\u{09}"} + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_string: \"abc\"\n") + { (o: inout MessageTestType) in o.optionalString = "abc" } + assertDebugDescription( + "SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_string: \"\\b\\t\"\n" + ) { (o: inout MessageTestType) in o.optionalString = "\u{08}\u{09}" } assertDecodeFails([114]) assertDecodeFails([114, 1]) assertDecodeFails([114, 2, 65]) - assertDecodeFails([114, 1, 193]) // Invalid UTF-8 + assertDecodeFails([114, 1, 193]) // Invalid UTF-8 assertDecodeFails([112]) assertDecodesAsUnknownFields([112, 0]) // Wrong wire type (varint), valid as an unknown field assertDecodeFails([113]) @@ -618,13 +675,15 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } func testEncoding_optionalBytes() { - assertEncode([122, 1, 1]) {(o: inout MessageTestType) in o.optionalBytes = Data([1])} - assertEncode([122, 2, 1, 2]) {(o: inout MessageTestType) in o.optionalBytes = Data([1, 2])} - assertDecodeSucceeds([122, 4, 0, 1, 2, 255]) {(o: MessageTestType) in - let t = o.optionalBytes // Verify non-optional + assertEncode([122, 1, 1]) { (o: inout MessageTestType) in o.optionalBytes = Data([1]) } + assertEncode([122, 2, 1, 2]) { (o: inout MessageTestType) in o.optionalBytes = Data([1, 2]) } + assertDecodeSucceeds([122, 4, 0, 1, 2, 255]) { (o: MessageTestType) in + let t = o.optionalBytes // Verify non-optional return t == Data([0, 1, 2, 255]) } - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_bytes: \"\\001\\002\\003\"\n") {(o: inout MessageTestType) in o.optionalBytes = Data([1, 2, 3])} + assertDebugDescription( + "SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_bytes: \"\\001\\002\\003\"\n" + ) { (o: inout MessageTestType) in o.optionalBytes = Data([1, 2, 3]) } assertDecodeFails([122]) assertDecodeFails([122, 1]) assertDecodeFails([122, 2, 0]) @@ -657,12 +716,14 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } func testEncoding_optionalNestedMessage() { - assertEncode([146, 1, 2, 8, 1]) {(o: inout MessageTestType) in + assertEncode([146, 1, 2, 8, 1]) { (o: inout MessageTestType) in o.optionalNestedMessage.bb = 1 } - assertDecodeSucceeds([146, 1, 4, 8, 1, 8, 3]) {$0.optionalNestedMessage.bb == 3} - assertDecodeSucceeds([146, 1, 2, 8, 1, 146, 1, 2, 8, 4]) {$0.optionalNestedMessage.bb == 4} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_nested_message {\n bb: 1\n}\n") {(o: inout MessageTestType) in + assertDecodeSucceeds([146, 1, 4, 8, 1, 8, 3]) { $0.optionalNestedMessage.bb == 3 } + assertDecodeSucceeds([146, 1, 2, 8, 1, 146, 1, 2, 8, 4]) { $0.optionalNestedMessage.bb == 4 } + assertDebugDescription( + "SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_nested_message {\n bb: 1\n}\n" + ) { (o: inout MessageTestType) in var nested = MessageTestType.NestedMessage() nested.bb = 1 o.optionalNestedMessage = nested @@ -685,34 +746,36 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } func testEncoding_optionalForeignMessage() { - assertEncode([154, 1, 2, 8, 1]) {(o: inout MessageTestType) in + assertEncode([154, 1, 2, 8, 1]) { (o: inout MessageTestType) in o.optionalForeignMessage.c = 1 } - assertDecodeSucceeds([154, 1, 4, 8, 1, 8, 3]) {$0.optionalForeignMessage.c == 3} - assertDecodeSucceeds([154, 1, 2, 8, 1, 154, 1, 2, 8, 4]) {$0.optionalForeignMessage.c == 4} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_foreign_message {\n c: 1\n}\n") {(o: inout MessageTestType) in + assertDecodeSucceeds([154, 1, 4, 8, 1, 8, 3]) { $0.optionalForeignMessage.c == 3 } + assertDecodeSucceeds([154, 1, 2, 8, 1, 154, 1, 2, 8, 4]) { $0.optionalForeignMessage.c == 4 } + assertDebugDescription( + "SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_foreign_message {\n c: 1\n}\n" + ) { (o: inout MessageTestType) in var foreign = SwiftProtoTesting_Proto3_ForeignMessage() foreign.c = 1 o.optionalForeignMessage = foreign } assertDecodesAsUnknownFields([152, 1, 0]) // Wrong wire type (varint), valid as an unknown field - assertDecodeFails([153, 1]) // Wire type 1 + assertDecodeFails([153, 1]) // Wire type 1 assertDecodeFails([153, 1, 0]) assertDecodesAsUnknownFields([153, 1, 0, 0, 0, 0, 0, 0, 0, 0]) // Wrong wire type (fixed64), valid as an unknown field - assertDecodeFails([155, 1]) // Wire type 3 + assertDecodeFails([155, 1]) // Wire type 3 assertDecodeFails([155, 1, 0]) - assertDecodesAsUnknownFields([155, 1, 156, 1]) // Wrong wire type (start group, end group), valid as an unknown field - assertDecodeFails([156, 1]) // Wire type 4 + assertDecodesAsUnknownFields([155, 1, 156, 1]) // Wrong wire type (start group, end group), valid as an unknown field + assertDecodeFails([156, 1]) // Wire type 4 assertDecodeFails([156, 1, 0]) - assertDecodeFails([157, 1]) // Wire type 5 + assertDecodeFails([157, 1]) // Wire type 5 assertDecodeFails([157, 1, 0]) assertDecodesAsUnknownFields([157, 1, 0, 0, 0, 0]) // Wrong wire type (fixed32), valid as an unknown field - assertDecodeFails([158, 1]) // Wire type 6 + assertDecodeFails([158, 1]) // Wire type 6 assertDecodeFails([158, 1, 0]) - assertDecodeFails([159, 1]) // Wire type 7 + assertDecodeFails([159, 1]) // Wire type 7 assertDecodeFails([159, 1, 0]) - assertDecodeFails([154, 1, 4, 8, 1]) // Truncated + assertDecodeFails([154, 1, 4, 8, 1]) // Truncated // Ensure storage is uniqued for clear. let c = MessageTestType.with { @@ -729,11 +792,11 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } func testEncoding_optionalImportMessage() { - assertEncode([162, 1, 2, 8, 1]) {(o: inout MessageTestType) in + assertEncode([162, 1, 2, 8, 1]) { (o: inout MessageTestType) in o.optionalImportMessage.d = 1 } - assertDecodeSucceeds([162, 1, 4, 8, 1, 8, 3]) {$0.optionalImportMessage.d == 3} - assertDecodeSucceeds([162, 1, 2, 8, 1, 162, 1, 2, 8, 4]) {$0.optionalImportMessage.d == 4} + assertDecodeSucceeds([162, 1, 4, 8, 1, 8, 3]) { $0.optionalImportMessage.d == 3 } + assertDecodeSucceeds([162, 1, 2, 8, 1, 162, 1, 2, 8, 4]) { $0.optionalImportMessage.d == 4 } // Ensure storage is uniqued for clear. let c = MessageTestType.with { @@ -750,23 +813,28 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } func testEncoding_optionalNestedEnum() { - assertEncode([168, 1, 2]) {(o: inout MessageTestType) in + assertEncode([168, 1, 2]) { (o: inout MessageTestType) in o.optionalNestedEnum = .bar } - assertDecodeSucceeds([168, 1, 2]) {$0.optionalNestedEnum == .bar} + assertDecodeSucceeds([168, 1, 2]) { $0.optionalNestedEnum == .bar } assertDecodeFails([168, 1]) - assertDecodeSucceeds([168, 1, 128, 1]) {$0.optionalNestedEnum == .UNRECOGNIZED(128)} - assertDecodeSucceeds([168, 1, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1]) {$0.optionalNestedEnum == .UNRECOGNIZED(-1)} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_nested_enum: BAR\n") {(o: inout MessageTestType) in + assertDecodeSucceeds([168, 1, 128, 1]) { $0.optionalNestedEnum == .UNRECOGNIZED(128) } + assertDecodeSucceeds([168, 1, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1]) { + $0.optionalNestedEnum == .UNRECOGNIZED(-1) + } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_nested_enum: BAR\n") + { (o: inout MessageTestType) in o.optionalNestedEnum = .bar } } func testEncoding_optionalForeignEnum() { - assertEncode([176, 1, 5]) {(o: inout MessageTestType) in + assertEncode([176, 1, 5]) { (o: inout MessageTestType) in o.optionalForeignEnum = .foreignBar } - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_foreign_enum: FOREIGN_BAR\n") {(o: inout MessageTestType) in + assertDebugDescription( + "SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_foreign_enum: FOREIGN_BAR\n" + ) { (o: inout MessageTestType) in o.optionalForeignEnum = .foreignBar } } @@ -775,36 +843,46 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { // Repeated types // func testEncoding_repeatedInt32() { - assertEncode([250, 1, 15, 255, 255, 255, 255, 7, 128, 128, 128, 128, 248, 255, 255, 255, 255, 1]) {(o: inout MessageTestType) in o.repeatedInt32 = [Int32.max, Int32.min]} - assertDecodeSucceeds([248, 1, 8, 248, 1, 247, 255, 255, 255, 15]) {$0.repeatedInt32 == [8, -9]} + assertEncode([250, 1, 15, 255, 255, 255, 255, 7, 128, 128, 128, 128, 248, 255, 255, 255, 255, 1]) { + (o: inout MessageTestType) in o.repeatedInt32 = [Int32.max, Int32.min] + } + assertDecodeSucceeds([248, 1, 8, 248, 1, 247, 255, 255, 255, 15]) { $0.repeatedInt32 == [8, -9] } assertDecodeFails([248, 1, 8, 248, 1, 247, 255, 255, 255, 255, 255, 255, 255, 255]) assertDecodeFails([248, 1, 8, 248, 1]) assertDecodeFails([248, 1]) assertDecodeFails([249, 1, 73]) // 250, 1 should actually work because that's packed - assertDecodeSucceeds([250, 1, 4, 8, 9, 10, 11]) {$0.repeatedInt32 == [8, 9, 10, 11]} + assertDecodeSucceeds([250, 1, 4, 8, 9, 10, 11]) { $0.repeatedInt32 == [8, 9, 10, 11] } assertDecodeFails([251, 1, 75]) assertDecodeFails([252, 1, 76]) assertDecodeFails([253, 1, 77]) assertDecodeFails([254, 1, 78]) assertDecodeFails([255, 1, 79]) - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\nrepeated_int32: [1]\n") {(o: inout MessageTestType) in + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\nrepeated_int32: [1]\n") { + (o: inout MessageTestType) in o.repeatedInt32 = [1] } - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\n") {(o: inout MessageTestType) in + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\n") { + (o: inout MessageTestType) in o.repeatedInt32 = [] } - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\nrepeated_int32: [1, 2]\n") {(o: inout MessageTestType) in + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\nrepeated_int32: [1, 2]\n") { + (o: inout MessageTestType) in o.repeatedInt32 = [1, 2] } } - func testEncoding_repeatedInt64() { - assertEncode([130, 2, 19, 255, 255, 255, 255, 255, 255, 255, 255, 127, 128, 128, 128, 128, 128, 128, 128, 128, 128, 1]) {(o: inout MessageTestType) in o.repeatedInt64 = [Int64.max, Int64.min]} - assertDecodeSucceeds([128, 2, 255, 255, 153, 166, 234, 175, 227, 1, 128, 2, 185, 156, 196, 237, 158, 222, 230, 255, 255, 1]) {$0.repeatedInt64 == [999999999999999, -111111111111111]} - assertDecodeSucceeds([130, 2, 1, 1]) {$0.repeatedInt64 == [1]} // Accepts packed coding - assertDecodeFails([128, 2, 255, 255, 153, 166, 234, 175, 227, 1, 128, 2, 185, 156, 196, 237, 158, 222, 230, 255, 255]) + assertEncode([ + 130, 2, 19, 255, 255, 255, 255, 255, 255, 255, 255, 127, 128, 128, 128, 128, 128, 128, 128, 128, 128, 1, + ]) { (o: inout MessageTestType) in o.repeatedInt64 = [Int64.max, Int64.min] } + assertDecodeSucceeds([ + 128, 2, 255, 255, 153, 166, 234, 175, 227, 1, 128, 2, 185, 156, 196, 237, 158, 222, 230, 255, 255, 1, + ]) { $0.repeatedInt64 == [999_999_999_999_999, -111_111_111_111_111] } + assertDecodeSucceeds([130, 2, 1, 1]) { $0.repeatedInt64 == [1] } // Accepts packed coding + assertDecodeFails([ + 128, 2, 255, 255, 153, 166, 234, 175, 227, 1, 128, 2, 185, 156, 196, 237, 158, 222, 230, 255, 255, + ]) assertDecodeFails([128, 2, 1, 128, 2]) assertDecodeFails([128, 2, 128]) assertDecodeFails([128, 2]) @@ -817,19 +895,26 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } func testEncoding_repeatedUint32() { - assertEncode([138, 2, 6, 255, 255, 255, 255, 15, 0]) {(o: inout MessageTestType) in o.repeatedUint32 = [UInt32.max, UInt32.min]} - assertDecodeSucceeds([136, 2, 210, 9, 136, 2, 213, 27]) {(o:MessageTestType) in - o.repeatedUint32 == [1234, 3541]} - assertDecodeSucceeds([136, 2, 255, 255, 255, 255, 15, 136, 2, 213, 27]) {(o:MessageTestType) in - o.repeatedUint32 == [4294967295, 3541]} - assertDecodeSucceeds([138, 2, 2, 1, 2]) {(o:MessageTestType) in - o.repeatedUint32 == [1, 2]} + assertEncode([138, 2, 6, 255, 255, 255, 255, 15, 0]) { (o: inout MessageTestType) in + o.repeatedUint32 = [UInt32.max, UInt32.min] + } + assertDecodeSucceeds([136, 2, 210, 9, 136, 2, 213, 27]) { (o: MessageTestType) in + o.repeatedUint32 == [1234, 3541] + } + assertDecodeSucceeds([136, 2, 255, 255, 255, 255, 15, 136, 2, 213, 27]) { (o: MessageTestType) in + o.repeatedUint32 == [4_294_967_295, 3541] + } + assertDecodeSucceeds([138, 2, 2, 1, 2]) { (o: MessageTestType) in + o.repeatedUint32 == [1, 2] + } // Truncate on 32-bit overflow - assertDecodeSucceeds([136, 2, 255, 255, 255, 255, 31]) {(o:MessageTestType) in - o.repeatedUint32 == [4294967295]} - assertDecodeSucceeds([136, 2, 255, 255, 255, 255, 255, 255, 255, 1]) {(o:MessageTestType) in - o.repeatedUint32 == [4294967295]} + assertDecodeSucceeds([136, 2, 255, 255, 255, 255, 31]) { (o: MessageTestType) in + o.repeatedUint32 == [4_294_967_295] + } + assertDecodeSucceeds([136, 2, 255, 255, 255, 255, 255, 255, 255, 1]) { (o: MessageTestType) in + o.repeatedUint32 == [4_294_967_295] + } assertDecodeFails([136, 2]) assertDecodeFails([136, 2, 210]) @@ -843,9 +928,11 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } func testEncoding_repeatedUint64() { - assertEncode([146, 2, 11, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1, 0]) {(o: inout MessageTestType) in o.repeatedUint64 = [UInt64.max, UInt64.min]} - assertDecodeSucceeds([144, 2, 149, 8]) {$0.repeatedUint64 == [1045 ]} - assertDecodeSucceeds([146, 2, 2, 0, 1]) {$0.repeatedUint64 == [0, 1]} + assertEncode([146, 2, 11, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1, 0]) { (o: inout MessageTestType) in + o.repeatedUint64 = [UInt64.max, UInt64.min] + } + assertDecodeSucceeds([144, 2, 149, 8]) { $0.repeatedUint64 == [1045] } + assertDecodeSucceeds([146, 2, 2, 0, 1]) { $0.repeatedUint64 == [0, 1] } assertDecodeFails([144]) assertDecodeFails([144, 2]) assertDecodeFails([144, 2, 149]) @@ -865,13 +952,18 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } func testEncoding_repeatedSint32() { - assertEncode([154, 2, 10, 254, 255, 255, 255, 15, 255, 255, 255, 255, 15]) {(o: inout MessageTestType) in o.repeatedSint32 = [Int32.max, Int32.min]} - assertDecodeSucceeds([152, 2, 170, 180, 222, 117, 152, 2, 225, 162, 243, 173, 1]) {$0.repeatedSint32 == [123456789, -182347953]} - assertDecodeSucceeds([154, 2, 1, 0]) {$0.repeatedSint32 == [0]} - assertDecodeSucceeds([154, 2, 1, 1, 152, 2, 2]) {$0.repeatedSint32 == [-1, 1]} + assertEncode([154, 2, 10, 254, 255, 255, 255, 15, 255, 255, 255, 255, 15]) { (o: inout MessageTestType) in + o.repeatedSint32 = [Int32.max, Int32.min] + } + assertDecodeSucceeds([152, 2, 170, 180, 222, 117, 152, 2, 225, 162, 243, 173, 1]) { + $0.repeatedSint32 == [123_456_789, -182_347_953] + } + assertDecodeSucceeds([154, 2, 1, 0]) { $0.repeatedSint32 == [0] } + assertDecodeSucceeds([154, 2, 1, 1, 152, 2, 2]) { $0.repeatedSint32 == [-1, 1] } // 32-bit overflow truncates - assertDecodeSucceeds([152, 2, 170, 180, 222, 117, 152, 2, 225, 162, 243, 173, 255, 255, 1]) {$0.repeatedSint32 == [123456789, -2061396145]} - + assertDecodeSucceeds([152, 2, 170, 180, 222, 117, 152, 2, 225, 162, 243, 173, 255, 255, 1]) { + $0.repeatedSint32 == [123_456_789, -2_061_396_145] + } assertDecodeFails([152, 2, 170, 180, 222, 117, 152]) assertDecodeFails([152, 2, 170, 180, 222, 117, 152, 2]) @@ -892,9 +984,13 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } func testEncoding_repeatedSint64() { - assertEncode([162, 2, 20, 254, 255, 255, 255, 255, 255, 255, 255, 255, 1, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1]) {(o: inout MessageTestType) in o.repeatedSint64 = [Int64.max, Int64.min]} - assertDecodeSucceeds([160, 2, 170, 180, 222, 117, 160, 2, 225, 162, 243, 173, 255, 89]) {$0.repeatedSint64 == [123456789,-1546102139057]} - assertDecodeSucceeds([162, 2, 1, 1]) {$0.repeatedSint64 == [-1]} + assertEncode([ + 162, 2, 20, 254, 255, 255, 255, 255, 255, 255, 255, 255, 1, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1, + ]) { (o: inout MessageTestType) in o.repeatedSint64 = [Int64.max, Int64.min] } + assertDecodeSucceeds([160, 2, 170, 180, 222, 117, 160, 2, 225, 162, 243, 173, 255, 89]) { + $0.repeatedSint64 == [123_456_789, -1_546_102_139_057] + } + assertDecodeSucceeds([162, 2, 1, 1]) { $0.repeatedSint64 == [-1] } assertDecodeFails([160, 2, 170, 180, 222, 117, 160]) assertDecodeFails([160, 2, 170, 180, 222, 117, 160, 2]) assertDecodeFails([160, 2, 170, 180, 222, 117, 160, 2, 225]) @@ -914,9 +1010,15 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } func testEncoding_repeatedFixed32() { - assertEncode([170, 2, 8, 255, 255, 255, 255, 0, 0, 0, 0]) {(o: inout MessageTestType) in o.repeatedFixed32 = [UInt32.max, UInt32.min]} - assertDecodeSucceeds([173, 2, 255, 255, 255, 127, 173, 2, 127, 127, 127, 127]) {$0.repeatedFixed32 == [2147483647, 2139062143]} - assertDecodeSucceeds([170, 2, 4, 1, 0, 0, 0, 173, 2, 255, 255, 255, 127]) {$0.repeatedFixed32 == [1, 2147483647]} + assertEncode([170, 2, 8, 255, 255, 255, 255, 0, 0, 0, 0]) { (o: inout MessageTestType) in + o.repeatedFixed32 = [UInt32.max, UInt32.min] + } + assertDecodeSucceeds([173, 2, 255, 255, 255, 127, 173, 2, 127, 127, 127, 127]) { + $0.repeatedFixed32 == [2_147_483_647, 2_139_062_143] + } + assertDecodeSucceeds([170, 2, 4, 1, 0, 0, 0, 173, 2, 255, 255, 255, 127]) { + $0.repeatedFixed32 == [1, 2_147_483_647] + } assertDecodeFails([173]) assertDecodeFails([173, 2]) assertDecodeFails([173, 2, 255]) @@ -948,10 +1050,17 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } func testEncoding_repeatedFixed64() { - assertEncode([178, 2, 16, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0]) {(o: inout MessageTestType) in o.repeatedFixed64 = [UInt64.max, UInt64.min]} - assertDecodeSucceeds([177, 2, 255, 255, 255, 127, 0, 0, 0, 0, 177, 2, 255, 255, 255, 255, 0, 0, 0, 0, 177, 2, 255, 255, 255, 255, 255, 255, 255, 255]) {$0.repeatedFixed64 == [2147483647, 4294967295, 18446744073709551615]} - assertDecodeSucceeds([178, 2, 8, 1, 0, 0, 0, 0, 0, 0, 0]) {$0.repeatedFixed64 == [1]} - assertDecodeSucceeds([177, 2, 2, 0, 0, 0, 0, 0, 0, 0, 178, 2, 8, 1, 0, 0, 0, 0, 0, 0, 0]) {$0.repeatedFixed64 == [2, 1]} + assertEncode([178, 2, 16, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0]) { + (o: inout MessageTestType) in o.repeatedFixed64 = [UInt64.max, UInt64.min] + } + assertDecodeSucceeds([ + 177, 2, 255, 255, 255, 127, 0, 0, 0, 0, 177, 2, 255, 255, 255, 255, 0, 0, 0, 0, 177, 2, 255, 255, 255, 255, + 255, 255, 255, 255, + ]) { $0.repeatedFixed64 == [2_147_483_647, 4_294_967_295, 18_446_744_073_709_551_615] } + assertDecodeSucceeds([178, 2, 8, 1, 0, 0, 0, 0, 0, 0, 0]) { $0.repeatedFixed64 == [1] } + assertDecodeSucceeds([177, 2, 2, 0, 0, 0, 0, 0, 0, 0, 178, 2, 8, 1, 0, 0, 0, 0, 0, 0, 0]) { + $0.repeatedFixed64 == [2, 1] + } assertDecodeFails([177]) assertDecodeFails([177, 2]) assertDecodeFails([177, 2, 255]) @@ -980,9 +1089,11 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } func testEncoding_repeatedSfixed32() { - assertEncode([186, 2, 8, 255, 255, 255, 127, 0, 0, 0, 128]) {(o: inout MessageTestType) in o.repeatedSfixed32 = [Int32.max, Int32.min]} - assertDecodeSucceeds([189, 2, 0, 0, 0, 0]) {$0.repeatedSfixed32 == [0]} - assertDecodeSucceeds([186, 2, 4, 1, 0, 0, 0, 189, 2, 3, 0, 0, 0]) {$0.repeatedSfixed32 == [1, 3]} + assertEncode([186, 2, 8, 255, 255, 255, 127, 0, 0, 0, 128]) { (o: inout MessageTestType) in + o.repeatedSfixed32 = [Int32.max, Int32.min] + } + assertDecodeSucceeds([189, 2, 0, 0, 0, 0]) { $0.repeatedSfixed32 == [0] } + assertDecodeSucceeds([186, 2, 4, 1, 0, 0, 0, 189, 2, 3, 0, 0, 0]) { $0.repeatedSfixed32 == [1, 3] } assertDecodeFails([189]) assertDecodeFails([189, 2]) assertDecodeFails([189, 2, 0]) @@ -1009,9 +1120,16 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } func testEncoding_repeatedSfixed64() { - assertEncode([194, 2, 16, 255, 255, 255, 255, 255, 255, 255, 127, 0, 0, 0, 0, 0, 0, 0, 128]) {(o: inout MessageTestType) in o.repeatedSfixed64 = [Int64.max, Int64.min]} - assertDecodeSucceeds([193, 2, 0, 0, 0, 0, 0, 0, 0, 128, 193, 2, 255, 255, 255, 255, 255, 255, 255, 255, 193, 2, 1, 0, 0, 0, 0, 0, 0, 0, 193, 2, 255, 255, 255, 255, 255, 255, 255, 127]) {$0.repeatedSfixed64 == [-9223372036854775808, -1, 1, 9223372036854775807]} - assertDecodeSucceeds([194, 2, 8, 0, 0, 0, 0, 0, 0, 0, 0, 193, 2, 1, 0, 0, 0, 0, 0, 0, 0]) {$0.repeatedSfixed64 == [0, 1]} + assertEncode([194, 2, 16, 255, 255, 255, 255, 255, 255, 255, 127, 0, 0, 0, 0, 0, 0, 0, 128]) { + (o: inout MessageTestType) in o.repeatedSfixed64 = [Int64.max, Int64.min] + } + assertDecodeSucceeds([ + 193, 2, 0, 0, 0, 0, 0, 0, 0, 128, 193, 2, 255, 255, 255, 255, 255, 255, 255, 255, 193, 2, 1, 0, 0, 0, 0, 0, + 0, 0, 193, 2, 255, 255, 255, 255, 255, 255, 255, 127, + ]) { $0.repeatedSfixed64 == [-9_223_372_036_854_775_808, -1, 1, 9_223_372_036_854_775_807] } + assertDecodeSucceeds([194, 2, 8, 0, 0, 0, 0, 0, 0, 0, 0, 193, 2, 1, 0, 0, 0, 0, 0, 0, 0]) { + $0.repeatedSfixed64 == [0, 1] + } assertDecodeFails([193]) assertDecodeFails([193, 2]) assertDecodeFails([193, 2, 0]) @@ -1042,29 +1160,36 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } func testEncoding_repeatedFloat() { - assertEncode([202, 2, 8, 0, 0, 0, 63, 0, 0, 0, 0]) {(o: inout MessageTestType) in o.repeatedFloat = [0.5, 0.0]} - assertDecodeSucceeds([205, 2, 0, 0, 0, 63, 205, 2, 0, 0, 0, 63]) {$0.repeatedFloat == [0.5, 0.5]} - assertDecodeSucceeds([202, 2, 8, 0, 0, 0, 63, 0, 0, 0, 63]) {$0.repeatedFloat == [0.5, 0.5]} + assertEncode([202, 2, 8, 0, 0, 0, 63, 0, 0, 0, 0]) { (o: inout MessageTestType) in o.repeatedFloat = [0.5, 0.0] + } + assertDecodeSucceeds([205, 2, 0, 0, 0, 63, 205, 2, 0, 0, 0, 63]) { $0.repeatedFloat == [0.5, 0.5] } + assertDecodeSucceeds([202, 2, 8, 0, 0, 0, 63, 0, 0, 0, 63]) { $0.repeatedFloat == [0.5, 0.5] } assertDecodeFails([205, 2, 0, 0, 0, 63, 205, 2, 0, 0, 128]) assertDecodeFails([205, 2, 0, 0, 0, 63, 205, 2]) - assertDecodeFails([200, 2]) // Bad byte sequence - assertDecodeFails([200, 2, 0, 0, 0, 0]) // Bad byte sequence - assertDecodeFails([201, 2]) // Bad byte sequence - assertDecodeFails([201, 2, 0, 0, 0, 0]) // Bad byte sequence - assertDecodeFails([203, 2]) // Bad byte sequence - assertDecodeFails([203, 2, 0, 0, 0, 0]) // Bad byte sequence - assertDecodeFails([204, 2]) // Bad byte sequence - assertDecodeFails([204, 2, 0, 0, 0, 0]) // Bad byte sequence - assertDecodeFails([206, 2]) // Bad byte sequence - assertDecodeFails([206, 2, 0, 0, 0, 0]) // Bad byte sequence - assertDecodeFails([207, 2]) // Bad byte sequence - assertDecodeFails([207, 2, 0, 0, 0, 0]) // Bad byte sequence + assertDecodeFails([200, 2]) // Bad byte sequence + assertDecodeFails([200, 2, 0, 0, 0, 0]) // Bad byte sequence + assertDecodeFails([201, 2]) // Bad byte sequence + assertDecodeFails([201, 2, 0, 0, 0, 0]) // Bad byte sequence + assertDecodeFails([203, 2]) // Bad byte sequence + assertDecodeFails([203, 2, 0, 0, 0, 0]) // Bad byte sequence + assertDecodeFails([204, 2]) // Bad byte sequence + assertDecodeFails([204, 2, 0, 0, 0, 0]) // Bad byte sequence + assertDecodeFails([206, 2]) // Bad byte sequence + assertDecodeFails([206, 2, 0, 0, 0, 0]) // Bad byte sequence + assertDecodeFails([207, 2]) // Bad byte sequence + assertDecodeFails([207, 2, 0, 0, 0, 0]) // Bad byte sequence } func testEncoding_repeatedDouble() { - assertEncode([210, 2, 16, 0, 0, 0, 0, 0, 0, 224, 63, 0, 0, 0, 0, 0, 0, 0, 0]) {(o: inout MessageTestType) in o.repeatedDouble = [0.5, 0.0]} - assertDecodeSucceeds([209, 2, 0, 0, 0, 0, 0, 0, 224, 63, 209, 2, 0, 0, 0, 0, 0, 0, 208, 63]) {$0.repeatedDouble == [0.5, 0.25]} - assertDecodeSucceeds([210, 2, 16, 0, 0, 0, 0, 0, 0, 224, 63, 0, 0, 0, 0, 0, 0, 208, 63]) {$0.repeatedDouble == [0.5, 0.25]} + assertEncode([210, 2, 16, 0, 0, 0, 0, 0, 0, 224, 63, 0, 0, 0, 0, 0, 0, 0, 0]) { (o: inout MessageTestType) in + o.repeatedDouble = [0.5, 0.0] + } + assertDecodeSucceeds([209, 2, 0, 0, 0, 0, 0, 0, 224, 63, 209, 2, 0, 0, 0, 0, 0, 0, 208, 63]) { + $0.repeatedDouble == [0.5, 0.25] + } + assertDecodeSucceeds([210, 2, 16, 0, 0, 0, 0, 0, 0, 224, 63, 0, 0, 0, 0, 0, 0, 208, 63]) { + $0.repeatedDouble == [0.5, 0.25] + } assertDecodeFails([209, 2]) assertDecodeFails([209, 2, 0]) assertDecodeFails([209, 2, 0, 0]) @@ -1091,9 +1216,11 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } func testEncoding_repeatedBool() { - assertEncode([218, 2, 3, 1, 0, 1]) {(o: inout MessageTestType) in o.repeatedBool = [true, false, true]} - assertDecodeSucceeds([216, 2, 1, 216, 2, 0, 216, 2, 0, 216, 2, 1]) {$0.repeatedBool == [true, false, false, true]} - assertDecodeSucceeds([218, 2, 3, 1, 0, 1, 216, 2, 0]) {$0.repeatedBool == [true, false, true, false]} + assertEncode([218, 2, 3, 1, 0, 1]) { (o: inout MessageTestType) in o.repeatedBool = [true, false, true] } + assertDecodeSucceeds([216, 2, 1, 216, 2, 0, 216, 2, 0, 216, 2, 1]) { + $0.repeatedBool == [true, false, false, true] + } + assertDecodeSucceeds([218, 2, 3, 1, 0, 1, 216, 2, 0]) { $0.repeatedBool == [true, false, true, false] } assertDecodeFails([216]) assertDecodeFails([216, 2]) assertDecodeFails([216, 2, 255]) @@ -1113,13 +1240,15 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } func testEncoding_repeatedString() { - assertEncode([226, 2, 1, 65, 226, 2, 1, 66]) {(o: inout MessageTestType) in o.repeatedString = ["A", "B"]} - assertDecodeSucceeds([226, 2, 5, 72, 101, 108, 108, 111, 226, 2, 5, 119, 111, 114, 108, 100, 226, 2, 0]) {$0.repeatedString == ["Hello", "world", ""]} + assertEncode([226, 2, 1, 65, 226, 2, 1, 66]) { (o: inout MessageTestType) in o.repeatedString = ["A", "B"] } + assertDecodeSucceeds([226, 2, 5, 72, 101, 108, 108, 111, 226, 2, 5, 119, 111, 114, 108, 100, 226, 2, 0]) { + $0.repeatedString == ["Hello", "world", ""] + } assertDecodeFails([226]) assertDecodeFails([226, 2]) assertDecodeFails([226, 2, 1]) assertDecodeFails([226, 2, 2, 65]) - assertDecodeFails([226, 2, 1, 193]) // Invalid UTF-8 + assertDecodeFails([226, 2, 1, 193]) // Invalid UTF-8 assertDecodeFails([224, 2]) assertDecodesAsUnknownFields([224, 2, 0]) // Wrong wire type (varint), valid as an unknown field assertDecodeFails([225, 2]) @@ -1137,10 +1266,12 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } func testEncoding_repeatedBytes() { - assertEncode([234, 2, 1, 1, 234, 2, 0, 234, 2, 1, 2]) {(o: inout MessageTestType) in o.repeatedBytes = [Data([1]), Data(), Data([2])]} + assertEncode([234, 2, 1, 1, 234, 2, 0, 234, 2, 1, 2]) { (o: inout MessageTestType) in + o.repeatedBytes = [Data([1]), Data(), Data([2])] + } assertDecodeSucceeds([234, 2, 4, 0, 1, 2, 255, 234, 2, 0]) { let ref: [[UInt8]] = [[0, 1, 2, 255], []] - for (a,b) in zip($0.repeatedBytes, ref) { + for (a, b) in zip($0.repeatedBytes, ref) { if a != Data(b) { return false } } return true @@ -1164,7 +1295,7 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } func testEncoding_repeatedNestedMessage() { - assertEncode([130, 3, 2, 8, 1, 130, 3, 2, 8, 2]) {(o: inout MessageTestType) in + assertEncode([130, 3, 2, 8, 1, 130, 3, 2, 8, 2]) { (o: inout MessageTestType) in var m1 = MessageTestType.NestedMessage() m1.bb = 1 var m2 = MessageTestType.NestedMessage() @@ -1173,7 +1304,9 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } assertDecodeFails([128, 3]) assertDecodesAsUnknownFields([128, 3, 0]) // Wrong wire type (varint), valid as an unknown field - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\nrepeated_nested_message {\n bb: 1\n}\nrepeated_nested_message {\n bb: 2\n}\n") {(o: inout MessageTestType) in + assertDebugDescription( + "SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\nrepeated_nested_message {\n bb: 1\n}\nrepeated_nested_message {\n bb: 2\n}\n" + ) { (o: inout MessageTestType) in var m1 = MessageTestType.NestedMessage() m1.bb = 1 var m2 = MessageTestType.NestedMessage() @@ -1183,25 +1316,27 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } func testEncoding_repeatedNestedEnum() { - assertEncode([154, 3, 2, 2, 3]) {(o: inout MessageTestType) in + assertEncode([154, 3, 2, 2, 3]) { (o: inout MessageTestType) in o.repeatedNestedEnum = [.bar, .baz] } - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\nrepeated_nested_enum: [BAR, BAZ]\n") {(o: inout MessageTestType) in + assertDebugDescription( + "SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\nrepeated_nested_enum: [BAR, BAZ]\n" + ) { (o: inout MessageTestType) in o.repeatedNestedEnum = [.bar, .baz] } } func testEncoding_oneofUint32() { - assertEncode([248, 6, 0]) {(o: inout MessageTestType) in o.oneofUint32 = 0} - assertDecodeSucceeds([248, 6, 255, 255, 255, 255, 15]) {$0.oneofUint32 == UInt32.max} - assertDecodeSucceeds([138, 7, 1, 97, 248, 6, 1]) {(o: MessageTestType) in + assertEncode([248, 6, 0]) { (o: inout MessageTestType) in o.oneofUint32 = 0 } + assertDecodeSucceeds([248, 6, 255, 255, 255, 255, 15]) { $0.oneofUint32 == UInt32.max } + assertDecodeSucceeds([138, 7, 1, 97, 248, 6, 1]) { (o: MessageTestType) in if case .oneofUint32? = o.oneofField, o.oneofUint32 == UInt32(1) { - return true + return true } return false } - assertDecodeFails([248, 6, 128]) // Bad varint + assertDecodeFails([248, 6, 128]) // Bad varint // Bad wire types: assertDecodeFails([249, 6]) assertDecodeFails([249, 6, 0]) @@ -1229,17 +1364,17 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } func testEncoding_oneofNestedMessage() { - assertEncode([130, 7, 2, 8, 1]) {(o: inout MessageTestType) in + assertEncode([130, 7, 2, 8, 1]) { (o: inout MessageTestType) in o.oneofNestedMessage = MessageTestType.NestedMessage() o.oneofNestedMessage.bb = 1 } - assertDecodeSucceeds([130, 7, 0]) {(o: MessageTestType) in + assertDecodeSucceeds([130, 7, 0]) { (o: MessageTestType) in if case .oneofNestedMessage(let m)? = o.oneofField { return m.bb == 0 } return false } - assertDecodeSucceeds([248, 6, 0, 130, 7, 2, 8, 1]) {(o: MessageTestType) in + assertDecodeSucceeds([248, 6, 0, 130, 7, 2, 8, 1]) { (o: MessageTestType) in if case .oneofUint32? = o.oneofField { return false } @@ -1250,14 +1385,14 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } } func testEncoding_oneofNestedMessage1() { - assertDecodeSucceeds([130, 7, 2, 8, 1, 248, 6, 0]) {(o: MessageTestType) in + assertDecodeSucceeds([130, 7, 2, 8, 1, 248, 6, 0]) { (o: MessageTestType) in if case .oneofUint32? = o.oneofField, o.oneofUint32 == UInt32(0) { return true } return false } // Unkonwn field within nested message should not break decoding - assertDecodeSucceeds([130, 7, 5, 128, 127, 0, 8, 1, 248, 6, 0]) {(o: MessageTestType) in + assertDecodeSucceeds([130, 7, 5, 128, 127, 0, 8, 1, 248, 6, 0]) { (o: MessageTestType) in if case .oneofUint32? = o.oneofField, o.oneofUint32 == 0 { return true } @@ -1269,7 +1404,10 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { var m = MessageTestType() m.oneofNestedMessage = MessageTestType.NestedMessage() m.oneofNestedMessage.bb = 1 - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noneof_nested_message {\n bb: 1\n}\n", m) + assertDebugDescription( + "SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noneof_nested_message {\n bb: 1\n}\n", + m + ) var m2 = MessageTestType() m2.oneofNestedMessage = MessageTestType.NestedMessage() m2.oneofNestedMessage.bb = 2 @@ -1278,7 +1416,7 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { func testEncoding_oneofNestedMessage9() { assertDecodeFails([128, 7]) - assertDecodesAsUnknownFields([128, 7, 0]) { // Wrong wire type (varint), valid as an unknown field + assertDecodesAsUnknownFields([128, 7, 0]) { // Wrong wire type (varint), valid as an unknown field $0.oneofField == nil // oneof doesn't get set. } assertDecodeFails([129, 7]) @@ -1296,17 +1434,17 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } func testEncoding_oneofString() { - assertEncode([138, 7, 1, 97]) {(o: inout MessageTestType) in o.oneofString = "a"} - assertDecodeSucceeds([138, 7, 1, 97]) {$0.oneofString == "a"} - assertDecodeSucceeds([138, 7, 0]) {$0.oneofString == ""} - assertDecodeSucceeds([146, 7, 0, 138, 7, 1, 97]) {(o:MessageTestType) in + assertEncode([138, 7, 1, 97]) { (o: inout MessageTestType) in o.oneofString = "a" } + assertDecodeSucceeds([138, 7, 1, 97]) { $0.oneofString == "a" } + assertDecodeSucceeds([138, 7, 0]) { $0.oneofString == "" } + assertDecodeSucceeds([146, 7, 0, 138, 7, 1, 97]) { (o: MessageTestType) in if case .oneofString? = o.oneofField, o.oneofString == "a" { - return true + return true } return false } - assertDecodeFails([138, 7, 1]) // Truncated body - assertDecodeFails([138, 7, 1, 192]) // Malformed UTF-8 + assertDecodeFails([138, 7, 1]) // Truncated body + assertDecodeFails([138, 7, 1, 192]) // Malformed UTF-8 // Bad wire types: assertDecodesAsUnknownFields([136, 7, 0]) { // Wrong wire type (varint), valid as an unknown field $0.oneofField == nil // oneof doesn't get set. @@ -1317,16 +1455,16 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { assertDecodesAsUnknownFields([137, 7, 1, 1, 1, 1, 1, 1, 1, 1]) { // Wrong wire type (fixed64), valid as an unknown field $0.oneofField == nil // oneof doesn't get set. } - assertDecodeFails([139, 7]) // Wire type 3 - assertDecodeFails([140, 7]) // Wire type 4 + assertDecodeFails([139, 7]) // Wire type 3 + assertDecodeFails([140, 7]) // Wire type 4 assertDecodeFails([141, 7, 0]) // Wire type 5 assertDecodesAsUnknownFields([141, 7, 0, 0, 0, 0]) { // Wrong wire type (fixed32), valid as an unknown field $0.oneofField == nil // oneof doesn't get set. } - assertDecodeFails([142, 7]) // Wire type 6 - assertDecodeFails([142, 7, 0]) // Wire type 6 - assertDecodeFails([143, 7]) // Wire type 7 - assertDecodeFails([143, 7, 0]) // Wire type 7 + assertDecodeFails([142, 7]) // Wire type 6 + assertDecodeFails([142, 7, 0]) // Wire type 6 + assertDecodeFails([143, 7]) // Wire type 7 + assertDecodeFails([143, 7, 0]) // Wire type 7 var m = MessageTestType() m.oneofString = "abc" @@ -1337,10 +1475,10 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } func testEncoding_oneofBytes() { - assertEncode([146, 7, 1, 1]) {(o: inout MessageTestType) in o.oneofBytes = Data([1])} + assertEncode([146, 7, 1, 1]) { (o: inout MessageTestType) in o.oneofBytes = Data([1]) } } func testEncoding_oneofBytes2() { - assertDecodeSucceeds([146, 7, 1, 1]) {(o: MessageTestType) in + assertDecodeSucceeds([146, 7, 1, 1]) { (o: MessageTestType) in let expectedB = Data([1]) if case .oneofBytes(let b)? = o.oneofField { let s = o.oneofString @@ -1350,7 +1488,7 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } } func testEncoding_oneofBytes3() { - assertDecodeSucceeds([146, 7, 0]) {(o: MessageTestType) in + assertDecodeSucceeds([146, 7, 0]) { (o: MessageTestType) in let expectedB = Data() if case .oneofBytes(let b)? = o.oneofField { let s = o.oneofString @@ -1360,7 +1498,7 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { } } func testEncoding_oneofBytes4() { - assertDecodeSucceeds([138, 7, 1, 97, 146, 7, 0]) {(o: MessageTestType) in + assertDecodeSucceeds([138, 7, 1, 97, 146, 7, 0]) { (o: MessageTestType) in let expectedB = Data() if case .oneofBytes(let b)? = o.oneofField { let s = o.oneofString @@ -1400,7 +1538,10 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { var m = MessageTestType() m.oneofBytes = Data([1, 2, 3]) - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noneof_bytes: \"\\001\\002\\003\"\n", m) + assertDebugDescription( + "SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noneof_bytes: \"\\001\\002\\003\"\n", + m + ) var m2 = MessageTestType() m2.oneofBytes = Data([4, 5, 6]) XCTAssertNotEqual(m.hashValue, m2.hashValue) @@ -1412,7 +1553,10 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { m.optionalInt32 = 7 assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_int32: 7\n", m) m.repeatedString = ["a", "b"] - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_int32: 7\nrepeated_string: \"a\"\nrepeated_string: \"b\"\n", m) + assertDebugDescription( + "SwiftProtobufTests.SwiftProtoTesting_Proto3_TestAllTypes:\noptional_int32: 7\nrepeated_string: \"a\"\nrepeated_string: \"b\"\n", + m + ) } func testDebugDescription2() { @@ -1435,7 +1579,9 @@ final class Test_AllTypes_Proto3: XCTestCase, PBTestHelpers { g.a = 7 g.b = "b" m.fooGroup = g - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestOneof:\nFooGroup {\n a: 7\n b: \"b\"\n}\n", m) + assertDebugDescription( + "SwiftProtobufTests.SwiftProtoTesting_TestOneof:\nFooGroup {\n a: 7\n b: \"b\"\n}\n", + m + ) } } - diff --git a/Tests/SwiftProtobufTests/Test_AllTypes_Proto3_Optional.swift b/Tests/SwiftProtobufTests/Test_AllTypes_Proto3_Optional.swift index e21a1172d..ae80a6e65 100644 --- a/Tests/SwiftProtobufTests/Test_AllTypes_Proto3_Optional.swift +++ b/Tests/SwiftProtobufTests/Test_AllTypes_Proto3_Optional.swift @@ -23,7 +23,12 @@ final class Test_AllTypes_Proto3_Optional: XCTestCase, PBTestHelpers { // Custom decodeSucceeds that also does a round-trip through the Empty // message to make sure unknown fields are consistently preserved by proto2. - func assertDecodeSucceeds(_ bytes: [UInt8], file: XCTestFileArgType = #file, line: UInt = #line, check: (MessageTestType) -> Bool) { + func assertDecodeSucceeds( + _ bytes: [UInt8], + file: XCTestFileArgType = #file, + line: UInt = #line, + check: (MessageTestType) -> Bool + ) { baseAssertDecodeSucceeds(bytes, file: file, line: line, check: check) do { // Make sure unknown fields are preserved by empty message decode/encode @@ -45,10 +50,13 @@ final class Test_AllTypes_Proto3_Optional: XCTestCase, PBTestHelpers { // Setting the values to zero values to ensure when encoded the values are captured. func testEncoding_optionalInt32() { - assertEncode([8, 0]) {(o: inout MessageTestType) in o.optionalInt32 = 0} - assertDecodeSucceeds([8, 0]) {$0.hasOptionalInt32 && $0.optionalInt32 == 0} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\noptional_int32: 0\n") {(o: inout MessageTestType) in o.optionalInt32 = 0} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\n") {(o: inout MessageTestType) in + assertEncode([8, 0]) { (o: inout MessageTestType) in o.optionalInt32 = 0 } + assertDecodeSucceeds([8, 0]) { $0.hasOptionalInt32 && $0.optionalInt32 == 0 } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\noptional_int32: 0\n") { + (o: inout MessageTestType) in o.optionalInt32 = 0 + } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\n") { + (o: inout MessageTestType) in o.optionalInt32 = 0 o.clearOptionalInt32() } @@ -67,12 +75,15 @@ final class Test_AllTypes_Proto3_Optional: XCTestCase, PBTestHelpers { } func testEncoding_optionalInt64() { - assertEncode([16, 0]) {(o: inout MessageTestType) in o.optionalInt64 = 0} - assertDecodeSucceeds([16, 0]) {$0.hasOptionalInt64 && $0.optionalInt64 == 0} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\noptional_int64: 0\n") {(o: inout MessageTestType) in o.optionalInt64 = 0} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\n") {(o: inout MessageTestType) in - o.optionalInt64 = 0 - o.clearOptionalInt64() + assertEncode([16, 0]) { (o: inout MessageTestType) in o.optionalInt64 = 0 } + assertDecodeSucceeds([16, 0]) { $0.hasOptionalInt64 && $0.optionalInt64 == 0 } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\noptional_int64: 0\n") { + (o: inout MessageTestType) in o.optionalInt64 = 0 + } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\n") { + (o: inout MessageTestType) in + o.optionalInt64 = 0 + o.clearOptionalInt64() } let empty = MessageTestType() @@ -102,12 +113,15 @@ final class Test_AllTypes_Proto3_Optional: XCTestCase, PBTestHelpers { } func testEncoding_optionalUint32() { - assertEncode([24, 0]) {(o: inout MessageTestType) in o.optionalUint32 = 0} - assertDecodeSucceeds([24, 0]) {$0.hasOptionalUint32 && $0.optionalUint32 == 0} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\noptional_uint32: 0\n") {(o: inout MessageTestType) in o.optionalUint32 = 0} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\n") {(o: inout MessageTestType) in - o.optionalUint32 = 0 - o.clearOptionalUint32() + assertEncode([24, 0]) { (o: inout MessageTestType) in o.optionalUint32 = 0 } + assertDecodeSucceeds([24, 0]) { $0.hasOptionalUint32 && $0.optionalUint32 == 0 } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\noptional_uint32: 0\n") { + (o: inout MessageTestType) in o.optionalUint32 = 0 + } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\n") { + (o: inout MessageTestType) in + o.optionalUint32 = 0 + o.clearOptionalUint32() } let empty = MessageTestType() @@ -137,12 +151,15 @@ final class Test_AllTypes_Proto3_Optional: XCTestCase, PBTestHelpers { } func testEncoding_optionalUint64() { - assertEncode([32, 0]) {(o: inout MessageTestType) in o.optionalUint64 = 0} - assertDecodeSucceeds([32, 0]) {$0.hasOptionalUint64 && $0.optionalUint64 == 0} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\noptional_uint64: 0\n") {(o: inout MessageTestType) in o.optionalUint64 = 0} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\n") {(o: inout MessageTestType) in - o.optionalUint64 = 0 - o.clearOptionalUint64() + assertEncode([32, 0]) { (o: inout MessageTestType) in o.optionalUint64 = 0 } + assertDecodeSucceeds([32, 0]) { $0.hasOptionalUint64 && $0.optionalUint64 == 0 } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\noptional_uint64: 0\n") { + (o: inout MessageTestType) in o.optionalUint64 = 0 + } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\n") { + (o: inout MessageTestType) in + o.optionalUint64 = 0 + o.clearOptionalUint64() } let empty = MessageTestType() @@ -172,12 +189,15 @@ final class Test_AllTypes_Proto3_Optional: XCTestCase, PBTestHelpers { } func testEncoding_optionalSint32() { - assertEncode([40, 0]) {(o: inout MessageTestType) in o.optionalSint32 = 0} - assertDecodeSucceeds([40, 0]) {$0.hasOptionalSint32 && $0.optionalSint32 == 0} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\noptional_sint32: 0\n") {(o: inout MessageTestType) in o.optionalSint32 = 0} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\n") {(o: inout MessageTestType) in - o.optionalSint32 = 0 - o.clearOptionalSint32() + assertEncode([40, 0]) { (o: inout MessageTestType) in o.optionalSint32 = 0 } + assertDecodeSucceeds([40, 0]) { $0.hasOptionalSint32 && $0.optionalSint32 == 0 } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\noptional_sint32: 0\n") { + (o: inout MessageTestType) in o.optionalSint32 = 0 + } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\n") { + (o: inout MessageTestType) in + o.optionalSint32 = 0 + o.clearOptionalSint32() } let empty = MessageTestType() @@ -207,12 +227,15 @@ final class Test_AllTypes_Proto3_Optional: XCTestCase, PBTestHelpers { } func testEncoding_optionalSint64() { - assertEncode([48, 0]) {(o: inout MessageTestType) in o.optionalSint64 = 0} - assertDecodeSucceeds([48, 0]) {$0.hasOptionalSint64 && $0.optionalSint64 == 0} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\noptional_sint64: 0\n") {(o: inout MessageTestType) in o.optionalSint64 = 0} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\n") {(o: inout MessageTestType) in - o.optionalSint64 = 0 - o.clearOptionalSint64() + assertEncode([48, 0]) { (o: inout MessageTestType) in o.optionalSint64 = 0 } + assertDecodeSucceeds([48, 0]) { $0.hasOptionalSint64 && $0.optionalSint64 == 0 } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\noptional_sint64: 0\n") { + (o: inout MessageTestType) in o.optionalSint64 = 0 + } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\n") { + (o: inout MessageTestType) in + o.optionalSint64 = 0 + o.clearOptionalSint64() } let empty = MessageTestType() @@ -242,12 +265,15 @@ final class Test_AllTypes_Proto3_Optional: XCTestCase, PBTestHelpers { } func testEncoding_optionalFixed32() { - assertEncode([61, 0, 0, 0, 0]) {(o: inout MessageTestType) in o.optionalFixed32 = 0} - assertDecodeSucceeds([61, 0, 0, 0, 0]) {$0.hasOptionalFixed32 && $0.optionalFixed32 == 0} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\noptional_fixed32: 0\n") {(o: inout MessageTestType) in o.optionalFixed32 = 0} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\n") {(o: inout MessageTestType) in - o.optionalFixed32 = 0 - o.clearOptionalFixed32() + assertEncode([61, 0, 0, 0, 0]) { (o: inout MessageTestType) in o.optionalFixed32 = 0 } + assertDecodeSucceeds([61, 0, 0, 0, 0]) { $0.hasOptionalFixed32 && $0.optionalFixed32 == 0 } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\noptional_fixed32: 0\n") { + (o: inout MessageTestType) in o.optionalFixed32 = 0 + } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\n") { + (o: inout MessageTestType) in + o.optionalFixed32 = 0 + o.clearOptionalFixed32() } let empty = MessageTestType() @@ -277,12 +303,15 @@ final class Test_AllTypes_Proto3_Optional: XCTestCase, PBTestHelpers { } func testEncoding_optionalFixed64() { - assertEncode([65, 0, 0, 0, 0, 0, 0, 0, 0]) {(o: inout MessageTestType) in o.optionalFixed64 = UInt64.min} - assertDecodeSucceeds([65, 0, 0, 0, 0, 0, 0, 0, 0]) {$0.hasOptionalFixed64 && $0.optionalFixed64 == 0} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\noptional_fixed64: 0\n") {(o: inout MessageTestType) in o.optionalFixed64 = 0} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\n") {(o: inout MessageTestType) in - o.optionalFixed64 = 0 - o.clearOptionalFixed64() + assertEncode([65, 0, 0, 0, 0, 0, 0, 0, 0]) { (o: inout MessageTestType) in o.optionalFixed64 = UInt64.min } + assertDecodeSucceeds([65, 0, 0, 0, 0, 0, 0, 0, 0]) { $0.hasOptionalFixed64 && $0.optionalFixed64 == 0 } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\noptional_fixed64: 0\n") { + (o: inout MessageTestType) in o.optionalFixed64 = 0 + } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\n") { + (o: inout MessageTestType) in + o.optionalFixed64 = 0 + o.clearOptionalFixed64() } let empty = MessageTestType() @@ -312,12 +341,15 @@ final class Test_AllTypes_Proto3_Optional: XCTestCase, PBTestHelpers { } func testEncoding_optionalSfixed32() { - assertEncode([77, 0, 0, 0, 0]) {(o: inout MessageTestType) in o.optionalSfixed32 = 0} - assertDecodeSucceeds([77, 0, 0, 0, 0]) {$0.hasOptionalSfixed32 && $0.optionalSfixed32 == 0} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\noptional_sfixed32: 0\n") {(o: inout MessageTestType) in o.optionalSfixed32 = 0} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\n") {(o: inout MessageTestType) in - o.optionalSfixed32 = 0 - o.clearOptionalSfixed32() + assertEncode([77, 0, 0, 0, 0]) { (o: inout MessageTestType) in o.optionalSfixed32 = 0 } + assertDecodeSucceeds([77, 0, 0, 0, 0]) { $0.hasOptionalSfixed32 && $0.optionalSfixed32 == 0 } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\noptional_sfixed32: 0\n") { + (o: inout MessageTestType) in o.optionalSfixed32 = 0 + } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\n") { + (o: inout MessageTestType) in + o.optionalSfixed32 = 0 + o.clearOptionalSfixed32() } let empty = MessageTestType() @@ -347,12 +379,15 @@ final class Test_AllTypes_Proto3_Optional: XCTestCase, PBTestHelpers { } func testEncoding_optionalSfixed64() { - assertEncode([81, 0, 0, 0, 0, 0, 0, 0, 0]) {(o: inout MessageTestType) in o.optionalSfixed64 = 0} - assertDecodeSucceeds([81, 0, 0, 0, 0, 0, 0, 0, 0]) {$0.hasOptionalSfixed64 && $0.optionalSfixed64 == 0} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\noptional_sfixed64: 0\n") {(o: inout MessageTestType) in o.optionalSfixed64 = 0} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\n") {(o: inout MessageTestType) in - o.optionalSfixed64 = 0 - o.clearOptionalSfixed64() + assertEncode([81, 0, 0, 0, 0, 0, 0, 0, 0]) { (o: inout MessageTestType) in o.optionalSfixed64 = 0 } + assertDecodeSucceeds([81, 0, 0, 0, 0, 0, 0, 0, 0]) { $0.hasOptionalSfixed64 && $0.optionalSfixed64 == 0 } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\noptional_sfixed64: 0\n") { + (o: inout MessageTestType) in o.optionalSfixed64 = 0 + } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\n") { + (o: inout MessageTestType) in + o.optionalSfixed64 = 0 + o.clearOptionalSfixed64() } let empty = MessageTestType() @@ -382,13 +417,15 @@ final class Test_AllTypes_Proto3_Optional: XCTestCase, PBTestHelpers { } func testEncoding_optionalFloat() { - assertEncode([93, 0, 0, 0, 0]) {(o: inout MessageTestType) in o.optionalFloat = 0.0} - assertDecodeSucceeds([93, 0, 0, 0, 0]) {$0.hasOptionalFloat && $0.optionalFloat == 0} + assertEncode([93, 0, 0, 0, 0]) { (o: inout MessageTestType) in o.optionalFloat = 0.0 } + assertDecodeSucceeds([93, 0, 0, 0, 0]) { $0.hasOptionalFloat && $0.optionalFloat == 0 } assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\noptional_float: 0.0\n") { - (o: inout MessageTestType) in o.optionalFloat = 0.0} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\n") { (o: inout MessageTestType) in - o.optionalFloat = 1.0 - o.clearOptionalFloat() + (o: inout MessageTestType) in o.optionalFloat = 0.0 + } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\n") { + (o: inout MessageTestType) in + o.optionalFloat = 1.0 + o.clearOptionalFloat() } let empty = MessageTestType() @@ -418,14 +455,15 @@ final class Test_AllTypes_Proto3_Optional: XCTestCase, PBTestHelpers { } func testEncoding_optionalDouble() { - assertEncode([97, 0, 0, 0, 0, 0, 0, 0, 0]) {(o: inout MessageTestType) in o.optionalDouble = 0.0} - assertDecodeSucceeds([97, 0, 0, 0, 0, 0, 0, 0, 0]) {$0.hasOptionalDouble && $0.optionalDouble == 0.0} + assertEncode([97, 0, 0, 0, 0, 0, 0, 0, 0]) { (o: inout MessageTestType) in o.optionalDouble = 0.0 } + assertDecodeSucceeds([97, 0, 0, 0, 0, 0, 0, 0, 0]) { $0.hasOptionalDouble && $0.optionalDouble == 0.0 } assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\noptional_double: 0.0\n") { - (o: inout MessageTestType) in o.optionalDouble = 0.0} + (o: inout MessageTestType) in o.optionalDouble = 0.0 + } assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\n") { (o: inout MessageTestType) in - o.optionalDouble = 0.0 - o.clearOptionalDouble() + o.optionalDouble = 0.0 + o.clearOptionalDouble() } let empty = MessageTestType() @@ -455,12 +493,15 @@ final class Test_AllTypes_Proto3_Optional: XCTestCase, PBTestHelpers { } func testEncoding_optionalBool() { - assertEncode([104, 0]) {(o: inout MessageTestType) in o.optionalBool = false} + assertEncode([104, 0]) { (o: inout MessageTestType) in o.optionalBool = false } assertDecodeSucceeds([104, 0]) { $0.hasOptionalBool && $0.optionalBool == false } - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\noptional_bool: false\n") {(o: inout MessageTestType) in o.optionalBool = false} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\n") {(o: inout MessageTestType) in - o.optionalBool = false - o.clearOptionalBool() + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\noptional_bool: false\n") { + (o: inout MessageTestType) in o.optionalBool = false + } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\n") { + (o: inout MessageTestType) in + o.optionalBool = false + o.clearOptionalBool() } let empty = MessageTestType() @@ -490,12 +531,15 @@ final class Test_AllTypes_Proto3_Optional: XCTestCase, PBTestHelpers { } func testEncoding_optionalString() { - assertEncode([114, 0]) {(o: inout MessageTestType) in o.optionalString = ""} + assertEncode([114, 0]) { (o: inout MessageTestType) in o.optionalString = "" } assertDecodeSucceeds([114, 0]) { $0.hasOptionalString && $0.optionalString == "" } - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\noptional_string: \"\"\n") {(o: inout MessageTestType) in o.optionalString = ""} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\n") {(o: inout MessageTestType) in - o.optionalString = "" - o.clearOptionalString() + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\noptional_string: \"\"\n") { + (o: inout MessageTestType) in o.optionalString = "" + } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\n") { + (o: inout MessageTestType) in + o.optionalString = "" + o.clearOptionalString() } let empty = MessageTestType() @@ -525,12 +569,15 @@ final class Test_AllTypes_Proto3_Optional: XCTestCase, PBTestHelpers { } func testEncoding_optionalBytes() { - assertEncode([122, 0]) {(o: inout MessageTestType) in o.optionalBytes = Data()} + assertEncode([122, 0]) { (o: inout MessageTestType) in o.optionalBytes = Data() } assertDecodeSucceeds([122, 0]) { $0.hasOptionalBytes && $0.optionalBytes == Data() } - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\noptional_bytes: \"\"\n") {(o: inout MessageTestType) in o.optionalBytes = Data()} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\n") {(o: inout MessageTestType) in - o.optionalBytes = Data() - o.clearOptionalBytes() + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\noptional_bytes: \"\"\n") { + (o: inout MessageTestType) in o.optionalBytes = Data() + } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\n") { + (o: inout MessageTestType) in + o.optionalBytes = Data() + o.clearOptionalBytes() } let empty = MessageTestType() @@ -559,52 +606,60 @@ final class Test_AllTypes_Proto3_Optional: XCTestCase, PBTestHelpers { XCTAssertFalse(d.hasOptionalBytes) } - func testEncoding_optionalCord() { - // The `ctype = CORD` option has no meaning in SwiftProtobuf, - // but test is for completeness. - assertEncode([130, 1, 0]) {(o: inout MessageTestType) in o.optionalCord = ""} - assertDecodeSucceeds([130, 1, 0]) { $0.hasOptionalCord && $0.optionalCord == "" } - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\noptional_cord: \"\"\n") {(o: inout MessageTestType) in o.optionalCord = ""} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\n") {(o: inout MessageTestType) in - o.optionalCord = "" - o.clearOptionalCord() - } - - let empty = MessageTestType() - var a = empty - a.optionalCord = "" - XCTAssertNotEqual(a, empty) - var b = empty - b.optionalCord = "a" - XCTAssertNotEqual(a, b) - b.clearOptionalCord() - XCTAssertNotEqual(a, b) - b.optionalCord = "" - XCTAssertEqual(a, b) - - // Ensure storage is uniqued for clear. - let c = MessageTestType.with { - $0.optionalCord = "blah" - } - var d = c - XCTAssertEqual(c, d) - XCTAssertTrue(c.hasOptionalCord) - XCTAssertTrue(d.hasOptionalCord) - d.clearOptionalCord() - XCTAssertNotEqual(c, d) - XCTAssertTrue(c.hasOptionalCord) - XCTAssertFalse(d.hasOptionalCord) - } + func testEncoding_optionalCord() { + // The `ctype = CORD` option has no meaning in SwiftProtobuf, + // but test is for completeness. + assertEncode([130, 1, 0]) { (o: inout MessageTestType) in o.optionalCord = "" } + assertDecodeSucceeds([130, 1, 0]) { $0.hasOptionalCord && $0.optionalCord == "" } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\noptional_cord: \"\"\n") { + (o: inout MessageTestType) in o.optionalCord = "" + } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\n") { + (o: inout MessageTestType) in + o.optionalCord = "" + o.clearOptionalCord() + } + + let empty = MessageTestType() + var a = empty + a.optionalCord = "" + XCTAssertNotEqual(a, empty) + var b = empty + b.optionalCord = "a" + XCTAssertNotEqual(a, b) + b.clearOptionalCord() + XCTAssertNotEqual(a, b) + b.optionalCord = "" + XCTAssertEqual(a, b) + + // Ensure storage is uniqued for clear. + let c = MessageTestType.with { + $0.optionalCord = "blah" + } + var d = c + XCTAssertEqual(c, d) + XCTAssertTrue(c.hasOptionalCord) + XCTAssertTrue(d.hasOptionalCord) + d.clearOptionalCord() + XCTAssertNotEqual(c, d) + XCTAssertTrue(c.hasOptionalCord) + XCTAssertFalse(d.hasOptionalCord) + } func testEncoding_optionalNestedMessage() { - assertEncode([146, 1, 0]) {(o: inout MessageTestType) in + assertEncode([146, 1, 0]) { (o: inout MessageTestType) in o.optionalNestedMessage = MessageTestType.NestedMessage() } - assertDecodeSucceeds([146, 1, 0]) {$0.hasOptionalNestedMessage && $0.optionalNestedMessage == MessageTestType.NestedMessage()} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\noptional_nested_message {\n}\n") {(o: inout MessageTestType) in + assertDecodeSucceeds([146, 1, 0]) { + $0.hasOptionalNestedMessage && $0.optionalNestedMessage == MessageTestType.NestedMessage() + } + assertDebugDescription( + "SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\noptional_nested_message {\n}\n" + ) { (o: inout MessageTestType) in o.optionalNestedMessage = MessageTestType.NestedMessage() } - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\n") {(o: inout MessageTestType) in + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\n") { + (o: inout MessageTestType) in o.optionalNestedMessage = MessageTestType.NestedMessage() o.clearOptionalNestedMessage() } @@ -623,46 +678,53 @@ final class Test_AllTypes_Proto3_Optional: XCTestCase, PBTestHelpers { XCTAssertFalse(d.hasOptionalNestedMessage) } - func testEncoding_lazyNestedMessage() { - // The `lazy = true` option has no meaning in SwiftProtobuf, - // but test is for completeness. - assertEncode([154, 1, 0]) {(o: inout MessageTestType) in - o.lazyNestedMessage = MessageTestType.NestedMessage() - } - assertDecodeSucceeds([154, 1, 0]) {$0.hasLazyNestedMessage && $0.lazyNestedMessage == MessageTestType.NestedMessage()} - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\nlazy_nested_message {\n}\n") {(o: inout MessageTestType) in - o.lazyNestedMessage = MessageTestType.NestedMessage() - } - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\n") {(o: inout MessageTestType) in - o.lazyNestedMessage = MessageTestType.NestedMessage() - o.clearLazyNestedMessage() - } - - // Ensure storage is uniqued for clear. - let c = MessageTestType.with { - $0.lazyNestedMessage.bb = 1 - } - var d = c - XCTAssertEqual(c, d) - XCTAssertTrue(c.hasLazyNestedMessage) - XCTAssertTrue(d.hasLazyNestedMessage) - d.clearLazyNestedMessage() - XCTAssertNotEqual(c, d) - XCTAssertTrue(c.hasLazyNestedMessage) - XCTAssertFalse(d.hasLazyNestedMessage) - } + func testEncoding_lazyNestedMessage() { + // The `lazy = true` option has no meaning in SwiftProtobuf, + // but test is for completeness. + assertEncode([154, 1, 0]) { (o: inout MessageTestType) in + o.lazyNestedMessage = MessageTestType.NestedMessage() + } + assertDecodeSucceeds([154, 1, 0]) { + $0.hasLazyNestedMessage && $0.lazyNestedMessage == MessageTestType.NestedMessage() + } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\nlazy_nested_message {\n}\n") { + (o: inout MessageTestType) in + o.lazyNestedMessage = MessageTestType.NestedMessage() + } + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\n") { + (o: inout MessageTestType) in + o.lazyNestedMessage = MessageTestType.NestedMessage() + o.clearLazyNestedMessage() + } + + // Ensure storage is uniqued for clear. + let c = MessageTestType.with { + $0.lazyNestedMessage.bb = 1 + } + var d = c + XCTAssertEqual(c, d) + XCTAssertTrue(c.hasLazyNestedMessage) + XCTAssertTrue(d.hasLazyNestedMessage) + d.clearLazyNestedMessage() + XCTAssertNotEqual(c, d) + XCTAssertTrue(c.hasLazyNestedMessage) + XCTAssertFalse(d.hasLazyNestedMessage) + } func testEncoding_optionalNestedEnum() throws { - assertEncode([168, 1, 0]) {(o: inout MessageTestType) in + assertEncode([168, 1, 0]) { (o: inout MessageTestType) in o.optionalNestedEnum = .unspecified } assertDecodeSucceeds([168, 1, 0]) { $0.hasOptionalNestedEnum && $0.optionalNestedEnum == .unspecified } - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\noptional_nested_enum: UNSPECIFIED\n") {(o: inout MessageTestType) in + assertDebugDescription( + "SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\noptional_nested_enum: UNSPECIFIED\n" + ) { (o: inout MessageTestType) in o.optionalNestedEnum = .unspecified } - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\n") {(o: inout MessageTestType) in + assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestProto3Optional:\n") { + (o: inout MessageTestType) in o.optionalNestedEnum = .unspecified o.clearOptionalNestedEnum() } diff --git a/Tests/SwiftProtobufTests/Test_Any.swift b/Tests/SwiftProtobufTests/Test_Any.swift index 602c81ac5..832ad90c5 100644 --- a/Tests/SwiftProtobufTests/Test_Any.swift +++ b/Tests/SwiftProtobufTests/Test_Any.swift @@ -16,8 +16,8 @@ // ----------------------------------------------------------------------------- import Foundation -import XCTest import SwiftProtobuf +import XCTest final class Test_Any: XCTestCase { @@ -33,7 +33,14 @@ final class Test_Any: XCTestCase { XCTAssertNotNil(try m.jsonString()) let encoded: [UInt8] = try m.serializedBytes() - XCTAssertEqual(encoded, [8, 12, 18, 58, 10, 52, 116, 121, 112, 101, 46, 103, 111, 111, 103, 108, 101, 97, 112, 105, 115, 46, 99, 111, 109, 47, 115, 119, 105, 102, 116, 95, 112, 114, 111, 116, 111, 95, 116, 101, 115, 116, 105, 110, 103, 46, 84, 101, 115, 116, 65, 108, 108, 84, 121, 112, 101, 115, 18, 2, 8, 7]) + XCTAssertEqual( + encoded, + [ + 8, 12, 18, 58, 10, 52, 116, 121, 112, 101, 46, 103, 111, 111, 103, 108, 101, 97, 112, 105, 115, 46, 99, + 111, 109, 47, 115, 119, 105, 102, 116, 95, 112, 114, 111, 116, 111, 95, 116, 101, 115, 116, 105, 110, + 103, 46, 84, 101, 115, 116, 65, 108, 108, 84, 121, 112, 101, 115, 18, 2, 8, 7, + ] + ) let decoded = try SwiftProtoTesting_TestAny(serializedBytes: encoded) XCTAssertEqual(decoded.anyValue.typeURL, "type.googleapis.com/swift_proto_testing.TestAllTypes") XCTAssertEqual(decoded.anyValue.value, Data([8, 7])) @@ -58,7 +65,10 @@ final class Test_Any: XCTestCase { /// /// This test verifies that we can decode an Any with a different prefix func test_Any_different_prefix() throws { - let encoded: [UInt8] = [8, 12, 18, 42, 10, 36, 88, 47, 89, 47, 115, 119, 105, 102, 116, 95, 112, 114, 111, 116, 111, 95, 116, 101, 115, 116, 105, 110, 103, 46, 84, 101, 115, 116, 65, 108, 108, 84, 121, 112, 101, 115, 18, 2, 8, 7] + let encoded: [UInt8] = [ + 8, 12, 18, 42, 10, 36, 88, 47, 89, 47, 115, 119, 105, 102, 116, 95, 112, 114, 111, 116, 111, 95, 116, 101, + 115, 116, 105, 110, 103, 46, 84, 101, 115, 116, 65, 108, 108, 84, 121, 112, 101, 115, 18, 2, 8, 7, + ] let decoded: SwiftProtoTesting_TestAny do { decoded = try SwiftProtoTesting_TestAny(serializedBytes: encoded) @@ -89,7 +99,10 @@ final class Test_Any: XCTestCase { /// /// This test verifies that we can decode an Any with an empty prefix func test_Any_noprefix() throws { - let encoded: [UInt8] = [8, 12, 18, 39, 10, 33, 47, 115, 119, 105, 102, 116, 95, 112, 114, 111, 116, 111, 95, 116, 101, 115, 116, 105, 110, 103, 46, 84, 101, 115, 116, 65, 108, 108, 84, 121, 112, 101, 115, 18, 2, 8, 7] + let encoded: [UInt8] = [ + 8, 12, 18, 39, 10, 33, 47, 115, 119, 105, 102, 116, 95, 112, 114, 111, 116, 111, 95, 116, 101, 115, 116, + 105, 110, 103, 46, 84, 101, 115, 116, 65, 108, 108, 84, 121, 112, 101, 115, 18, 2, 8, 7, + ] let decoded: SwiftProtoTesting_TestAny do { decoded = try SwiftProtoTesting_TestAny(serializedBytes: encoded) @@ -117,7 +130,10 @@ final class Test_Any: XCTestCase { /// Though Google discourages this, we should be able to match and decode an Any /// if the typeURL holds just the type name: func test_Any_shortesttype() throws { - let encoded: [UInt8] = [8, 12, 18, 38, 10, 32, 115, 119, 105, 102, 116, 95, 112, 114, 111, 116, 111, 95, 116, 101, 115, 116, 105, 110, 103, 46, 84, 101, 115, 116, 65, 108, 108, 84, 121, 112, 101, 115, 18, 2, 8, 7] + let encoded: [UInt8] = [ + 8, 12, 18, 38, 10, 32, 115, 119, 105, 102, 116, 95, 112, 114, 111, 116, 111, 95, 116, 101, 115, 116, 105, + 110, 103, 46, 84, 101, 115, 116, 65, 108, 108, 84, 121, 112, 101, 115, 18, 2, 8, 7, + ] let decoded: SwiftProtoTesting_TestAny do { decoded = try SwiftProtoTesting_TestAny(serializedBytes: encoded) @@ -152,7 +168,10 @@ final class Test_Any: XCTestCase { m.anyValue = try Google_Protobuf_Any(message: content) let encoded = try m.jsonString() - XCTAssertEqual(encoded, "{\"int32Value\":12,\"anyValue\":{\"@type\":\"type.googleapis.com/swift_proto_testing.TestAllTypes\",\"optionalInt32\":7}}") + XCTAssertEqual( + encoded, + "{\"int32Value\":12,\"anyValue\":{\"@type\":\"type.googleapis.com/swift_proto_testing.TestAllTypes\",\"optionalInt32\":7}}" + ) do { let decoded = try SwiftProtoTesting_TestAny(jsonString: encoded) XCTAssertNotNil(decoded.anyValue) @@ -169,7 +188,14 @@ final class Test_Any: XCTestCase { } let recoded = try decoded.jsonString() XCTAssertEqual(encoded, recoded) - XCTAssertEqual([8, 12, 18, 58, 10, 52, 116, 121, 112, 101, 46, 103, 111, 111, 103, 108, 101, 97, 112, 105, 115, 46, 99, 111, 109, 47, 115, 119, 105, 102, 116, 95, 112, 114, 111, 116, 111, 95, 116, 101, 115, 116, 105, 110, 103, 46, 84, 101, 115, 116, 65, 108, 108, 84, 121, 112, 101, 115, 18, 2, 8, 7], try decoded.serializedBytes()) + XCTAssertEqual( + [ + 8, 12, 18, 58, 10, 52, 116, 121, 112, 101, 46, 103, 111, 111, 103, 108, 101, 97, 112, 105, 115, 46, + 99, 111, 109, 47, 115, 119, 105, 102, 116, 95, 112, 114, 111, 116, 111, 95, 116, 101, 115, 116, 105, + 110, 103, 46, 84, 101, 115, 116, 65, 108, 108, 84, 121, 112, 101, 115, 18, 2, 8, 7, + ], + try decoded.serializedBytes() + ) } catch { XCTFail("Failed to decode \(encoded)") } @@ -189,7 +215,10 @@ final class Test_Any: XCTestCase { XCTAssertEqual(anyValue.typeURL, "type.googleapis.com/UNKNOWN") XCTAssertEqual(anyValue.value, Data()) - XCTAssertEqual(anyValue.textFormatString(), "type_url: \"type.googleapis.com/UNKNOWN\"\n#json: \"{\\\"optionalInt32\\\":7}\"\n") + XCTAssertEqual( + anyValue.textFormatString(), + "type_url: \"type.googleapis.com/UNKNOWN\"\n#json: \"{\\\"optionalInt32\\\":7}\"\n" + ) // Verify: JSON-to-protobuf transcoding should fail here // since the Any does not have type information @@ -198,7 +227,10 @@ final class Test_Any: XCTestCase { func test_Any_UnknownUserMessage_protobuf() throws { Google_Protobuf_Any.register(messageType: SwiftProtoTesting_TestAllTypes.self) - let start: [UInt8] = [8, 12, 18, 33, 10, 27, 116, 121, 112, 101, 46, 103, 111, 111, 103, 108, 101, 97, 112, 105, 115, 46, 99, 111, 109, 47, 85, 78, 75, 78, 79, 87, 78, 18, 2, 8, 7] + let start: [UInt8] = [ + 8, 12, 18, 33, 10, 27, 116, 121, 112, 101, 46, 103, 111, 111, 103, 108, 101, 97, 112, 105, 115, 46, 99, 111, + 109, 47, 85, 78, 75, 78, 79, 87, 78, 18, 2, 8, 7, + ] let decoded = try SwiftProtoTesting_TestAny(serializedBytes: start) @@ -218,10 +250,11 @@ final class Test_Any: XCTestCase { } func test_Any_Any() throws { - let start = "{\"optionalAny\":{\"@type\":\"type.googleapis.com/google.protobuf.Any\",\"value\":{\"@type\":\"type.googleapis.com/google.protobuf.Int32Value\",\"value\":1}}}" + let start = + "{\"optionalAny\":{\"@type\":\"type.googleapis.com/google.protobuf.Any\",\"value\":{\"@type\":\"type.googleapis.com/google.protobuf.Int32Value\",\"value\":1}}}" let decoded: SwiftProtoTesting_Test3_TestAllTypesProto3 do { - decoded = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: start) + decoded = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: start) } catch { XCTFail("Failed to decode \(start)") return @@ -249,7 +282,16 @@ final class Test_Any: XCTestCase { XCTFail("Failed to serialize \(decoded)") return } - XCTAssertEqual(protobuf, [138, 19, 95, 10, 39, 116, 121, 112, 101, 46, 103, 111, 111, 103, 108, 101, 97, 112, 105, 115, 46, 99, 111, 109, 47, 103, 111, 111, 103, 108, 101, 46, 112, 114, 111, 116, 111, 98, 117, 102, 46, 65, 110, 121, 18, 52, 10, 46, 116, 121, 112, 101, 46, 103, 111, 111, 103, 108, 101, 97, 112, 105, 115, 46, 99, 111, 109, 47, 103, 111, 111, 103, 108, 101, 46, 112, 114, 111, 116, 111, 98, 117, 102, 46, 73, 110, 116, 51, 50, 86, 97, 108, 117, 101, 18, 2, 8, 1]) + XCTAssertEqual( + protobuf, + [ + 138, 19, 95, 10, 39, 116, 121, 112, 101, 46, 103, 111, 111, 103, 108, 101, 97, 112, 105, 115, 46, 99, + 111, 109, 47, 103, 111, 111, 103, 108, 101, 46, 112, 114, 111, 116, 111, 98, 117, 102, 46, 65, 110, 121, + 18, 52, 10, 46, 116, 121, 112, 101, 46, 103, 111, 111, 103, 108, 101, 97, 112, 105, 115, 46, 99, 111, + 109, 47, 103, 111, 111, 103, 108, 101, 46, 112, 114, 111, 116, 111, 98, 117, 102, 46, 73, 110, 116, 51, + 50, 86, 97, 108, 117, 101, 18, 2, 8, 1, + ] + ) let redecoded: SwiftProtoTesting_Test3_TestAllTypesProto3 do { @@ -278,8 +320,8 @@ final class Test_Any: XCTestCase { func test_Any_recursive() throws { func nestedAny(_ i: Int) throws -> Google_Protobuf_Any { - guard i > 0 else { return Google_Protobuf_Any() } - return try Google_Protobuf_Any(message: nestedAny(i - 1)) + guard i > 0 else { return Google_Protobuf_Any() } + return try Google_Protobuf_Any(message: nestedAny(i - 1)) } let any = try nestedAny(5) let encoded: [UInt8] = try any.serializedBytes() @@ -287,7 +329,8 @@ final class Test_Any: XCTestCase { } func test_Any_Duration_JSON_roundtrip() throws { - let start = "{\"optionalAny\":{\"@type\":\"type.googleapis.com/google.protobuf.Duration\",\"value\":\"99.001s\"}}" + let start = + "{\"optionalAny\":{\"@type\":\"type.googleapis.com/google.protobuf.Duration\",\"value\":\"99.001s\"}}" do { let decoded = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: start) @@ -296,7 +339,7 @@ final class Test_Any: XCTestCase { do { let unpacked = try Google_Protobuf_Duration(unpackingAny: anyField) XCTAssertEqual(unpacked.seconds, 99) - XCTAssertEqual(unpacked.nanos, 1000000) + XCTAssertEqual(unpacked.nanos, 1_000_000) } catch { XCTFail("Failed to unpack \(anyField)") } @@ -309,11 +352,19 @@ final class Test_Any: XCTestCase { } func test_Any_Duration_transcode() throws { - let start = "{\"optionalAny\":{\"@type\":\"type.googleapis.com/google.protobuf.Duration\",\"value\":\"99.001s\"}}" + let start = + "{\"optionalAny\":{\"@type\":\"type.googleapis.com/google.protobuf.Duration\",\"value\":\"99.001s\"}}" do { let decoded = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: start) let protobuf: [UInt8] = try decoded.serializedBytes() - XCTAssertEqual(protobuf, [138, 19, 54, 10, 44, 116, 121, 112, 101, 46, 103, 111, 111, 103, 108, 101, 97, 112, 105, 115, 46, 99, 111, 109, 47, 103, 111, 111, 103, 108, 101, 46, 112, 114, 111, 116, 111, 98, 117, 102, 46, 68, 117, 114, 97, 116, 105, 111, 110, 18, 6, 8, 99, 16, 192, 132, 61]) + XCTAssertEqual( + protobuf, + [ + 138, 19, 54, 10, 44, 116, 121, 112, 101, 46, 103, 111, 111, 103, 108, 101, 97, 112, 105, 115, 46, + 99, 111, 109, 47, 103, 111, 111, 103, 108, 101, 46, 112, 114, 111, 116, 111, 98, 117, 102, 46, 68, + 117, 114, 97, 116, 105, 111, 110, 18, 6, 8, 99, 16, 192, 132, 61, + ] + ) do { let redecoded = try SwiftProtoTesting_Test3_TestAllTypesProto3(serializedBytes: protobuf) let json = try redecoded.jsonString() @@ -327,7 +378,8 @@ final class Test_Any: XCTestCase { } func test_Any_FieldMask_JSON_roundtrip() throws { - let start = "{\"optionalAny\":{\"@type\":\"type.googleapis.com/google.protobuf.FieldMask\",\"value\":\"foo,bar.bazQuux\"}}" + let start = + "{\"optionalAny\":{\"@type\":\"type.googleapis.com/google.protobuf.FieldMask\",\"value\":\"foo,bar.bazQuux\"}}" do { let decoded = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: start) XCTAssertNotNil(decoded.optionalAny) @@ -347,11 +399,20 @@ final class Test_Any: XCTestCase { } func test_Any_FieldMask_transcode() throws { - let start = "{\"optionalAny\":{\"@type\":\"type.googleapis.com/google.protobuf.FieldMask\",\"value\":\"foo,bar.bazQuux\"}}" + let start = + "{\"optionalAny\":{\"@type\":\"type.googleapis.com/google.protobuf.FieldMask\",\"value\":\"foo,bar.bazQuux\"}}" do { let decoded = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: start) let protobuf: [UInt8] = try decoded.serializedBytes() - XCTAssertEqual(protobuf, [138, 19, 68, 10, 45, 116, 121, 112, 101, 46, 103, 111, 111, 103, 108, 101, 97, 112, 105, 115, 46, 99, 111, 109, 47, 103, 111, 111, 103, 108, 101, 46, 112, 114, 111, 116, 111, 98, 117, 102, 46, 70, 105, 101, 108, 100, 77, 97, 115, 107, 18, 19, 10, 3, 102, 111, 111, 10, 12, 98, 97, 114, 46, 98, 97, 122, 95, 113, 117, 117, 120]) + XCTAssertEqual( + protobuf, + [ + 138, 19, 68, 10, 45, 116, 121, 112, 101, 46, 103, 111, 111, 103, 108, 101, 97, 112, 105, 115, 46, + 99, 111, 109, 47, 103, 111, 111, 103, 108, 101, 46, 112, 114, 111, 116, 111, 98, 117, 102, 46, 70, + 105, 101, 108, 100, 77, 97, 115, 107, 18, 19, 10, 3, 102, 111, 111, 10, 12, 98, 97, 114, 46, 98, 97, + 122, 95, 113, 117, 117, 120, + ] + ) do { let redecoded = try SwiftProtoTesting_Test3_TestAllTypesProto3(serializedBytes: protobuf) let json = try redecoded.jsonString() @@ -389,7 +450,14 @@ final class Test_Any: XCTestCase { do { let decoded = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: start) let protobuf: [UInt8] = try decoded.serializedBytes() - XCTAssertEqual(protobuf, [138, 19, 52, 10, 46, 116, 121, 112, 101, 46, 103, 111, 111, 103, 108, 101, 97, 112, 105, 115, 46, 99, 111, 109, 47, 103, 111, 111, 103, 108, 101, 46, 112, 114, 111, 116, 111, 98, 117, 102, 46, 73, 110, 116, 51, 50, 86, 97, 108, 117, 101, 18, 2, 8, 1]) + XCTAssertEqual( + protobuf, + [ + 138, 19, 52, 10, 46, 116, 121, 112, 101, 46, 103, 111, 111, 103, 108, 101, 97, 112, 105, 115, 46, + 99, 111, 109, 47, 103, 111, 111, 103, 108, 101, 46, 112, 114, 111, 116, 111, 98, 117, 102, 46, 73, + 110, 116, 51, 50, 86, 97, 108, 117, 101, 18, 2, 8, 1, + ] + ) do { let redecoded = try SwiftProtoTesting_Test3_TestAllTypesProto3(serializedBytes: protobuf) let json = try redecoded.jsonString() @@ -412,7 +480,7 @@ final class Test_Any: XCTestCase { let anyField = decoded.optionalAny do { let unpacked = try Google_Protobuf_Struct(unpackingAny: anyField) - XCTAssertEqual(unpacked.fields["foo"], Google_Protobuf_Value(numberValue:1)) + XCTAssertEqual(unpacked.fields["foo"], Google_Protobuf_Value(numberValue: 1)) } catch { XCTFail("Failed to unpack \(anyField)") } @@ -425,11 +493,19 @@ final class Test_Any: XCTestCase { } func test_Any_Struct_transcode() throws { - let start = "{\"optionalAny\":{\"@type\":\"type.googleapis.com/google.protobuf.Struct\",\"value\":{\"foo\":1.0}}}" + let start = + "{\"optionalAny\":{\"@type\":\"type.googleapis.com/google.protobuf.Struct\",\"value\":{\"foo\":1.0}}}" do { let decoded = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: start) let protobuf: [UInt8] = try decoded.serializedBytes() - XCTAssertEqual(protobuf, [138, 19, 64, 10, 42, 116, 121, 112, 101, 46, 103, 111, 111, 103, 108, 101, 97, 112, 105, 115, 46, 99, 111, 109, 47, 103, 111, 111, 103, 108, 101, 46, 112, 114, 111, 116, 111, 98, 117, 102, 46, 83, 116, 114, 117, 99, 116, 18, 18, 10, 16, 10, 3, 102, 111, 111, 18, 9, 17, 0, 0, 0, 0, 0, 0, 240, 63]) + XCTAssertEqual( + protobuf, + [ + 138, 19, 64, 10, 42, 116, 121, 112, 101, 46, 103, 111, 111, 103, 108, 101, 97, 112, 105, 115, 46, + 99, 111, 109, 47, 103, 111, 111, 103, 108, 101, 46, 112, 114, 111, 116, 111, 98, 117, 102, 46, 83, + 116, 114, 117, 99, 116, 18, 18, 10, 16, 10, 3, 102, 111, 111, 18, 9, 17, 0, 0, 0, 0, 0, 0, 240, 63, + ] + ) do { let redecoded = try SwiftProtoTesting_Test3_TestAllTypesProto3(serializedBytes: protobuf) let json = try redecoded.jsonString() @@ -443,7 +519,8 @@ final class Test_Any: XCTestCase { } func test_Any_Timestamp_JSON_roundtrip() throws { - let start = "{\"optionalAny\":{\"@type\":\"type.googleapis.com/google.protobuf.Timestamp\",\"value\":\"1970-01-01T00:00:01Z\"}}" + let start = + "{\"optionalAny\":{\"@type\":\"type.googleapis.com/google.protobuf.Timestamp\",\"value\":\"1970-01-01T00:00:01Z\"}}" do { let decoded = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: start) XCTAssertNotNil(decoded.optionalAny) @@ -464,11 +541,19 @@ final class Test_Any: XCTestCase { } func test_Any_Timestamp_transcode() throws { - let start = "{\"optionalAny\":{\"@type\":\"type.googleapis.com/google.protobuf.Timestamp\",\"value\":\"1970-01-01T00:00:01.000000001Z\"}}" + let start = + "{\"optionalAny\":{\"@type\":\"type.googleapis.com/google.protobuf.Timestamp\",\"value\":\"1970-01-01T00:00:01.000000001Z\"}}" do { let decoded = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: start) let protobuf: [UInt8] = try decoded.serializedBytes() - XCTAssertEqual(protobuf, [138, 19, 53, 10, 45, 116, 121, 112, 101, 46, 103, 111, 111, 103, 108, 101, 97, 112, 105, 115, 46, 99, 111, 109, 47, 103, 111, 111, 103, 108, 101, 46, 112, 114, 111, 116, 111, 98, 117, 102, 46, 84, 105, 109, 101, 115, 116, 97, 109, 112, 18, 4, 8, 1, 16, 1]) + XCTAssertEqual( + protobuf, + [ + 138, 19, 53, 10, 45, 116, 121, 112, 101, 46, 103, 111, 111, 103, 108, 101, 97, 112, 105, 115, 46, + 99, 111, 109, 47, 103, 111, 111, 103, 108, 101, 46, 112, 114, 111, 116, 111, 98, 117, 102, 46, 84, + 105, 109, 101, 115, 116, 97, 109, 112, 18, 4, 8, 1, 16, 1, + ] + ) do { let redecoded = try SwiftProtoTesting_Test3_TestAllTypesProto3(serializedBytes: protobuf) let json = try redecoded.jsonString() @@ -482,13 +567,17 @@ final class Test_Any: XCTestCase { } func test_Any_ListValue_JSON_roundtrip() throws { - let start = "{\"optionalAny\":{\"@type\":\"type.googleapis.com/google.protobuf.ListValue\",\"value\":[\"foo\",1]}}" + let start = + "{\"optionalAny\":{\"@type\":\"type.googleapis.com/google.protobuf.ListValue\",\"value\":[\"foo\",1]}}" do { let decoded = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: start) let anyField = decoded.optionalAny do { let unpacked = try Google_Protobuf_ListValue(unpackingAny: anyField) - XCTAssertEqual(unpacked.values, [Google_Protobuf_Value(stringValue: "foo"), Google_Protobuf_Value(numberValue: 1)]) + XCTAssertEqual( + unpacked.values, + [Google_Protobuf_Value(stringValue: "foo"), Google_Protobuf_Value(numberValue: 1)] + ) } catch { XCTFail("Failed to unpack \(anyField)") } @@ -501,11 +590,20 @@ final class Test_Any: XCTestCase { } func test_Any_ListValue_transcode() throws { - let start = "{\"optionalAny\":{\"@type\":\"type.googleapis.com/google.protobuf.ListValue\",\"value\":[1.0,\"abc\"]}}" + let start = + "{\"optionalAny\":{\"@type\":\"type.googleapis.com/google.protobuf.ListValue\",\"value\":[1.0,\"abc\"]}}" do { let decoded = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: start) let protobuf: [UInt8] = try decoded.serializedBytes() - XCTAssertEqual(protobuf, [138, 19, 67, 10, 45, 116, 121, 112, 101, 46, 103, 111, 111, 103, 108, 101, 97, 112, 105, 115, 46, 99, 111, 109, 47, 103, 111, 111, 103, 108, 101, 46, 112, 114, 111, 116, 111, 98, 117, 102, 46, 76, 105, 115, 116, 86, 97, 108, 117, 101, 18, 18, 10, 9, 17, 0, 0, 0, 0, 0, 0, 240, 63, 10, 5, 26, 3, 97, 98, 99]) + XCTAssertEqual( + protobuf, + [ + 138, 19, 67, 10, 45, 116, 121, 112, 101, 46, 103, 111, 111, 103, 108, 101, 97, 112, 105, 115, 46, + 99, 111, 109, 47, 103, 111, 111, 103, 108, 101, 46, 112, 114, 111, 116, 111, 98, 117, 102, 46, 76, + 105, 115, 116, 86, 97, 108, 117, 101, 18, 18, 10, 9, 17, 0, 0, 0, 0, 0, 0, 240, 63, 10, 5, 26, 3, + 97, 98, 99, + ] + ) do { let redecoded = try SwiftProtoTesting_Test3_TestAllTypesProto3(serializedBytes: protobuf) let json = try redecoded.jsonString() @@ -528,7 +626,7 @@ final class Test_Any: XCTestCase { XCTAssertThrowsError(try Google_Protobuf_Struct(unpackingAny: anyField)) do { let unpacked = try Google_Protobuf_Value(unpackingAny: anyField) - XCTAssertEqual(unpacked.structValue.fields["foo"], Google_Protobuf_Value(numberValue:1)) + XCTAssertEqual(unpacked.structValue.fields["foo"], Google_Protobuf_Value(numberValue: 1)) } catch { XCTFail("failed to unpack \(anyField)") } @@ -541,11 +639,20 @@ final class Test_Any: XCTestCase { } func test_Any_Value_struct_transcode() throws { - let start = "{\"optionalAny\":{\"@type\":\"type.googleapis.com/google.protobuf.Value\",\"value\":{\"foo\":1.0}}}" + let start = + "{\"optionalAny\":{\"@type\":\"type.googleapis.com/google.protobuf.Value\",\"value\":{\"foo\":1.0}}}" do { let decoded = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: start) let protobuf: [UInt8] = try decoded.serializedBytes() - XCTAssertEqual(protobuf, [138, 19, 65, 10, 41, 116, 121, 112, 101, 46, 103, 111, 111, 103, 108, 101, 97, 112, 105, 115, 46, 99, 111, 109, 47, 103, 111, 111, 103, 108, 101, 46, 112, 114, 111, 116, 111, 98, 117, 102, 46, 86, 97, 108, 117, 101, 18, 20, 42, 18, 10, 16, 10, 3, 102, 111, 111, 18, 9, 17, 0, 0, 0, 0, 0, 0, 240, 63]) + XCTAssertEqual( + protobuf, + [ + 138, 19, 65, 10, 41, 116, 121, 112, 101, 46, 103, 111, 111, 103, 108, 101, 97, 112, 105, 115, 46, + 99, 111, 109, 47, 103, 111, 111, 103, 108, 101, 46, 112, 114, 111, 116, 111, 98, 117, 102, 46, 86, + 97, 108, 117, 101, 18, 20, 42, 18, 10, 16, 10, 3, 102, 111, 111, 18, 9, 17, 0, 0, 0, 0, 0, 0, 240, + 63, + ] + ) do { let redecoded = try SwiftProtoTesting_Test3_TestAllTypesProto3(serializedBytes: protobuf) let json = try redecoded.jsonString() @@ -585,7 +692,14 @@ final class Test_Any: XCTestCase { do { let decoded = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: start) let protobuf: [UInt8] = try decoded.serializedBytes() - XCTAssertEqual(protobuf, [138, 19, 54, 10, 41, 116, 121, 112, 101, 46, 103, 111, 111, 103, 108, 101, 97, 112, 105, 115, 46, 99, 111, 109, 47, 103, 111, 111, 103, 108, 101, 46, 112, 114, 111, 116, 111, 98, 117, 102, 46, 86, 97, 108, 117, 101, 18, 9, 17, 0, 0, 0, 0, 0, 0, 240, 63]) + XCTAssertEqual( + protobuf, + [ + 138, 19, 54, 10, 41, 116, 121, 112, 101, 46, 103, 111, 111, 103, 108, 101, 97, 112, 105, 115, 46, + 99, 111, 109, 47, 103, 111, 111, 103, 108, 101, 46, 112, 114, 111, 116, 111, 98, 117, 102, 46, 86, + 97, 108, 117, 101, 18, 9, 17, 0, 0, 0, 0, 0, 0, 240, 63, + ] + ) do { let redecoded = try SwiftProtoTesting_Test3_TestAllTypesProto3(serializedBytes: protobuf) let json = try redecoded.jsonString() @@ -624,7 +738,14 @@ final class Test_Any: XCTestCase { do { let decoded = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: start) let protobuf: [UInt8] = try decoded.serializedBytes() - XCTAssertEqual(protobuf, [138, 19, 50, 10, 41, 116, 121, 112, 101, 46, 103, 111, 111, 103, 108, 101, 97, 112, 105, 115, 46, 99, 111, 109, 47, 103, 111, 111, 103, 108, 101, 46, 112, 114, 111, 116, 111, 98, 117, 102, 46, 86, 97, 108, 117, 101, 18, 5, 26, 3, 97, 98, 99]) + XCTAssertEqual( + protobuf, + [ + 138, 19, 50, 10, 41, 116, 121, 112, 101, 46, 103, 111, 111, 103, 108, 101, 97, 112, 105, 115, 46, + 99, 111, 109, 47, 103, 111, 111, 103, 108, 101, 46, 112, 114, 111, 116, 111, 98, 117, 102, 46, 86, + 97, 108, 117, 101, 18, 5, 26, 3, 97, 98, 99, + ] + ) do { let redecoded = try SwiftProtoTesting_Test3_TestAllTypesProto3(serializedBytes: protobuf) let json = try redecoded.jsonString() @@ -638,186 +759,209 @@ final class Test_Any: XCTestCase { } func test_Any_OddTypeURL_FromValue() throws { - var msg = SwiftProtoTesting_Test3_TestAllTypesProto3() - msg.optionalAny.value = Data([0x1a, 0x03, 0x61, 0x62, 0x63]) - msg.optionalAny.typeURL = "Odd\nType\" prefix/google.protobuf.Value" - let newJSON = try msg.jsonString() - XCTAssertEqual(newJSON, "{\"optionalAny\":{\"@type\":\"Odd\\nType\\\" prefix/google.protobuf.Value\",\"value\":\"abc\"}}") + var msg = SwiftProtoTesting_Test3_TestAllTypesProto3() + msg.optionalAny.value = Data([0x1a, 0x03, 0x61, 0x62, 0x63]) + msg.optionalAny.typeURL = "Odd\nType\" prefix/google.protobuf.Value" + let newJSON = try msg.jsonString() + XCTAssertEqual( + newJSON, + "{\"optionalAny\":{\"@type\":\"Odd\\nType\\\" prefix/google.protobuf.Value\",\"value\":\"abc\"}}" + ) } func test_Any_OddTypeURL_FromMessage() throws { - let valueMsg = Google_Protobuf_Value.with { - $0.stringValue = "abc" - } - var msg = SwiftProtoTesting_Test3_TestAllTypesProto3() - msg.optionalAny = try Google_Protobuf_Any(message: valueMsg, typePrefix: "Odd\nPrefix\"") - let newJSON = try msg.jsonString() - XCTAssertEqual(newJSON, "{\"optionalAny\":{\"@type\":\"Odd\\nPrefix\\\"/google.protobuf.Value\",\"value\":\"abc\"}}") + let valueMsg = Google_Protobuf_Value.with { + $0.stringValue = "abc" + } + var msg = SwiftProtoTesting_Test3_TestAllTypesProto3() + msg.optionalAny = try Google_Protobuf_Any(message: valueMsg, typePrefix: "Odd\nPrefix\"") + let newJSON = try msg.jsonString() + XCTAssertEqual( + newJSON, + "{\"optionalAny\":{\"@type\":\"Odd\\nPrefix\\\"/google.protobuf.Value\",\"value\":\"abc\"}}" + ) } func test_Any_JSON_Extensions() throws { - var content = SwiftProtoTesting_TestAllExtensions() - content.SwiftProtoTesting_optionalInt32Extension = 17 - - var msg = SwiftProtoTesting_TestAny() - msg.anyValue = try Google_Protobuf_Any(message: content) - - let json = try msg.jsonString() - XCTAssertEqual(json, "{\"anyValue\":{\"@type\":\"type.googleapis.com/swift_proto_testing.TestAllExtensions\",\"[swift_proto_testing.optional_int32_extension]\":17}}") - - // Decode the outer message without any extension knowledge - let decoded = try SwiftProtoTesting_TestAny(jsonString: json) - // Decoding the inner content fails without extension info - XCTAssertThrowsError(try SwiftProtoTesting_TestAllExtensions(unpackingAny: decoded.anyValue)) - // Succeeds if you do provide extension info - let decodedContent = try SwiftProtoTesting_TestAllExtensions(unpackingAny: decoded.anyValue, - extensions: SwiftProtoTesting_Unittest_Extensions) - XCTAssertEqual(content, decodedContent) - - // Transcoding should fail without extension info - XCTAssertThrowsError(try decoded.serializedBytes() as [UInt8]) + var content = SwiftProtoTesting_TestAllExtensions() + content.SwiftProtoTesting_optionalInt32Extension = 17 + + var msg = SwiftProtoTesting_TestAny() + msg.anyValue = try Google_Protobuf_Any(message: content) + + let json = try msg.jsonString() + XCTAssertEqual( + json, + "{\"anyValue\":{\"@type\":\"type.googleapis.com/swift_proto_testing.TestAllExtensions\",\"[swift_proto_testing.optional_int32_extension]\":17}}" + ) + + // Decode the outer message without any extension knowledge + let decoded = try SwiftProtoTesting_TestAny(jsonString: json) + // Decoding the inner content fails without extension info + XCTAssertThrowsError(try SwiftProtoTesting_TestAllExtensions(unpackingAny: decoded.anyValue)) + // Succeeds if you do provide extension info + let decodedContent = try SwiftProtoTesting_TestAllExtensions( + unpackingAny: decoded.anyValue, + extensions: SwiftProtoTesting_Unittest_Extensions + ) + XCTAssertEqual(content, decodedContent) + + // Transcoding should fail without extension info + XCTAssertThrowsError(try decoded.serializedBytes() as [UInt8]) - // Decode the outer message with extension information - let decodedWithExtensions = try SwiftProtoTesting_TestAny(jsonString: json, - extensions: SwiftProtoTesting_Unittest_Extensions) - // Still fails; the Any doesn't record extensions that were in effect when the outer Any was decoded - XCTAssertThrowsError(try SwiftProtoTesting_TestAllExtensions(unpackingAny: decodedWithExtensions.anyValue)) - let decodedWithExtensionsContent = try SwiftProtoTesting_TestAllExtensions(unpackingAny: decodedWithExtensions.anyValue, - extensions: SwiftProtoTesting_Unittest_Extensions) - XCTAssertEqual(content, decodedWithExtensionsContent) + // Decode the outer message with extension information + let decodedWithExtensions = try SwiftProtoTesting_TestAny( + jsonString: json, + extensions: SwiftProtoTesting_Unittest_Extensions + ) + // Still fails; the Any doesn't record extensions that were in effect when the outer Any was decoded + XCTAssertThrowsError(try SwiftProtoTesting_TestAllExtensions(unpackingAny: decodedWithExtensions.anyValue)) + let decodedWithExtensionsContent = try SwiftProtoTesting_TestAllExtensions( + unpackingAny: decodedWithExtensions.anyValue, + extensions: SwiftProtoTesting_Unittest_Extensions + ) + XCTAssertEqual(content, decodedWithExtensionsContent) - XCTAssertTrue(Google_Protobuf_Any.register(messageType: SwiftProtoTesting_TestAllExtensions.self)) - // Throws because the extensions can't be implicitly transcoded - XCTAssertThrowsError(try decodedWithExtensions.serializedBytes() as [UInt8]) + XCTAssertTrue(Google_Protobuf_Any.register(messageType: SwiftProtoTesting_TestAllExtensions.self)) + // Throws because the extensions can't be implicitly transcoded + XCTAssertThrowsError(try decodedWithExtensions.serializedBytes() as [UInt8]) } func test_Any_WKT_UnknownFields() throws { - let testcases = [ - // unknown field before value - "{\"optionalAny\":{\"@type\":\"type.googleapis.com/google.protobuf.Duration\",\"fred\":1,\"value\":\"99.001s\"}}", - // unknown field after value - "{\"optionalAny\":{\"@type\":\"type.googleapis.com/google.protobuf.Duration\",\"value\":\"99.001s\",\"fred\":1}}", - ] - for json in testcases { - for ignoreUnknown in [false, true] { - var options = JSONDecodingOptions() - options.ignoreUnknownFields = ignoreUnknown - // This may appear a little odd, since Any lazy parses, this will - // always succeed because the Any isn't decoded until requested. - let decoded = try! SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: json, options: options) - - XCTAssertNotNil(decoded.optionalAny) - let anyField = decoded.optionalAny - do { - let unpacked = try Google_Protobuf_Duration(unpackingAny: anyField) - XCTAssertTrue(ignoreUnknown) // Should have throw if not ignoring unknowns. - XCTAssertEqual(unpacked.seconds, 99) - XCTAssertEqual(unpacked.nanos, 1000000) - } catch { - XCTAssertTrue(!ignoreUnknown) - } - - // The extra field should still be there. - let encoded = try decoded.jsonString() - XCTAssertEqual(encoded, json) - } - } + let testcases = [ + // unknown field before value + "{\"optionalAny\":{\"@type\":\"type.googleapis.com/google.protobuf.Duration\",\"fred\":1,\"value\":\"99.001s\"}}", + // unknown field after value + "{\"optionalAny\":{\"@type\":\"type.googleapis.com/google.protobuf.Duration\",\"value\":\"99.001s\",\"fred\":1}}", + ] + for json in testcases { + for ignoreUnknown in [false, true] { + var options = JSONDecodingOptions() + options.ignoreUnknownFields = ignoreUnknown + // This may appear a little odd, since Any lazy parses, this will + // always succeed because the Any isn't decoded until requested. + let decoded = try! SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: json, options: options) + + XCTAssertNotNil(decoded.optionalAny) + let anyField = decoded.optionalAny + do { + let unpacked = try Google_Protobuf_Duration(unpackingAny: anyField) + XCTAssertTrue(ignoreUnknown) // Should have throw if not ignoring unknowns. + XCTAssertEqual(unpacked.seconds, 99) + XCTAssertEqual(unpacked.nanos, 1_000_000) + } catch { + XCTAssertTrue(!ignoreUnknown) + } + + // The extra field should still be there. + let encoded = try decoded.jsonString() + XCTAssertEqual(encoded, json) + } + } } func test_Any_empty() throws { - let start = "{\"optionalAny\":{}}" - let decoded = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: start) - let protobuf: [UInt8] = try decoded.serializedBytes() - XCTAssertEqual(protobuf, [138, 19, 0]) - let redecoded = try SwiftProtoTesting_Test3_TestAllTypesProto3(serializedBytes: protobuf) - let retext = redecoded.textFormatString() - XCTAssertEqual(retext, "optional_any {\n}\n") - let reredecoded = try SwiftProtoTesting_Test3_TestAllTypesProto3(textFormatString: retext) - let rejson = try reredecoded.jsonString() - XCTAssertEqual(rejson, start) + let start = "{\"optionalAny\":{}}" + let decoded = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: start) + let protobuf: [UInt8] = try decoded.serializedBytes() + XCTAssertEqual(protobuf, [138, 19, 0]) + let redecoded = try SwiftProtoTesting_Test3_TestAllTypesProto3(serializedBytes: protobuf) + let retext = redecoded.textFormatString() + XCTAssertEqual(retext, "optional_any {\n}\n") + let reredecoded = try SwiftProtoTesting_Test3_TestAllTypesProto3(textFormatString: retext) + let rejson = try reredecoded.jsonString() + XCTAssertEqual(rejson, start) } func test_Any_nestedList() throws { - var start = "{\"optionalAny\":{\"x\":" - for _ in 0...10000 { - start.append("[") - } - XCTAssertThrowsError( - // This should fail because the deeply-nested array is not closed - // It should not crash from exhausting stack space - try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: start) - ) - for _ in 0...10000 { - start.append("]") - } - start.append("}}") - // This should succeed because the deeply-nested array is properly closed - // It should not crash from exhausting stack space and should - // not fail due to recursion limits (because when skipping, those are - // only applied to objects). - _ = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: start) + var start = "{\"optionalAny\":{\"x\":" + for _ in 0...10000 { + start.append("[") + } + XCTAssertThrowsError( + // This should fail because the deeply-nested array is not closed + // It should not crash from exhausting stack space + try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: start) + ) + for _ in 0...10000 { + start.append("]") + } + start.append("}}") + // This should succeed because the deeply-nested array is properly closed + // It should not crash from exhausting stack space and should + // not fail due to recursion limits (because when skipping, those are + // only applied to objects). + _ = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: start) } func test_IsA() { - var msg = Google_Protobuf_Any() - - msg.typeURL = "type.googleapis.com/swift_proto_testing.TestAllTypes" - XCTAssertTrue(msg.isA(SwiftProtoTesting_TestAllTypes.self)) - XCTAssertFalse(msg.isA(Google_Protobuf_Empty.self)) - msg.typeURL = "random.site.org/swift_proto_testing.TestAllTypes" - XCTAssertTrue(msg.isA(SwiftProtoTesting_TestAllTypes.self)) - XCTAssertFalse(msg.isA(Google_Protobuf_Empty.self)) - msg.typeURL = "/swift_proto_testing.TestAllTypes" - XCTAssertTrue(msg.isA(SwiftProtoTesting_TestAllTypes.self)) - XCTAssertFalse(msg.isA(Google_Protobuf_Empty.self)) - msg.typeURL = "swift_proto_testing.TestAllTypes" - XCTAssertTrue(msg.isA(SwiftProtoTesting_TestAllTypes.self)) - XCTAssertFalse(msg.isA(Google_Protobuf_Empty.self)) - - msg.typeURL = "" - XCTAssertFalse(msg.isA(SwiftProtoTesting_TestAllTypes.self)) - XCTAssertFalse(msg.isA(Google_Protobuf_Empty.self)) + var msg = Google_Protobuf_Any() + + msg.typeURL = "type.googleapis.com/swift_proto_testing.TestAllTypes" + XCTAssertTrue(msg.isA(SwiftProtoTesting_TestAllTypes.self)) + XCTAssertFalse(msg.isA(Google_Protobuf_Empty.self)) + msg.typeURL = "random.site.org/swift_proto_testing.TestAllTypes" + XCTAssertTrue(msg.isA(SwiftProtoTesting_TestAllTypes.self)) + XCTAssertFalse(msg.isA(Google_Protobuf_Empty.self)) + msg.typeURL = "/swift_proto_testing.TestAllTypes" + XCTAssertTrue(msg.isA(SwiftProtoTesting_TestAllTypes.self)) + XCTAssertFalse(msg.isA(Google_Protobuf_Empty.self)) + msg.typeURL = "swift_proto_testing.TestAllTypes" + XCTAssertTrue(msg.isA(SwiftProtoTesting_TestAllTypes.self)) + XCTAssertFalse(msg.isA(Google_Protobuf_Empty.self)) + + msg.typeURL = "" + XCTAssertFalse(msg.isA(SwiftProtoTesting_TestAllTypes.self)) + XCTAssertFalse(msg.isA(Google_Protobuf_Empty.self)) } func test_Any_Registry() { - // Registering the same type multiple times is ok. - XCTAssertTrue(Google_Protobuf_Any.register(messageType: SwiftProtoTesting_Import_ImportMessage.self)) - XCTAssertTrue(Google_Protobuf_Any.register(messageType: SwiftProtoTesting_Import_ImportMessage.self)) - - // Registering a different type with the same messageName will fail. - XCTAssertFalse(Google_Protobuf_Any.register(messageType: ConflictingImportMessage.self)) - - // Sanity check that the .proto files weren't changed, and they do have the same name. - XCTAssertEqual(ConflictingImportMessage.protoMessageName, SwiftProtoTesting_Import_ImportMessage.protoMessageName) - - // Lookup - XCTAssertTrue(Google_Protobuf_Any.messageType(forMessageName: SwiftProtoTesting_Import_ImportMessage.protoMessageName) == SwiftProtoTesting_Import_ImportMessage.self) - XCTAssertNil(Google_Protobuf_Any.messageType(forMessageName: SwiftProtoTesting_TestMap.protoMessageName)) - - // All the WKTs should be registered. - let wkts: [any Message.Type] = [ - Google_Protobuf_Any.self, - Google_Protobuf_BoolValue.self, - Google_Protobuf_BytesValue.self, - Google_Protobuf_DoubleValue.self, - Google_Protobuf_Duration.self, - Google_Protobuf_Empty.self, - Google_Protobuf_FieldMask.self, - Google_Protobuf_FloatValue.self, - Google_Protobuf_Int32Value.self, - Google_Protobuf_Int64Value.self, - Google_Protobuf_ListValue.self, - Google_Protobuf_StringValue.self, - Google_Protobuf_Struct.self, - Google_Protobuf_Timestamp.self, - Google_Protobuf_UInt32Value.self, - Google_Protobuf_UInt64Value.self, - Google_Protobuf_Value.self, - ] - for t in wkts { - XCTAssertTrue(Google_Protobuf_Any.messageType(forMessageName: t.protoMessageName) == t, - "Looking up \(t.protoMessageName)") - } + // Registering the same type multiple times is ok. + XCTAssertTrue(Google_Protobuf_Any.register(messageType: SwiftProtoTesting_Import_ImportMessage.self)) + XCTAssertTrue(Google_Protobuf_Any.register(messageType: SwiftProtoTesting_Import_ImportMessage.self)) + + // Registering a different type with the same messageName will fail. + XCTAssertFalse(Google_Protobuf_Any.register(messageType: ConflictingImportMessage.self)) + + // Sanity check that the .proto files weren't changed, and they do have the same name. + XCTAssertEqual( + ConflictingImportMessage.protoMessageName, + SwiftProtoTesting_Import_ImportMessage.protoMessageName + ) + + // Lookup + XCTAssertTrue( + Google_Protobuf_Any.messageType(forMessageName: SwiftProtoTesting_Import_ImportMessage.protoMessageName) + == SwiftProtoTesting_Import_ImportMessage.self + ) + XCTAssertNil(Google_Protobuf_Any.messageType(forMessageName: SwiftProtoTesting_TestMap.protoMessageName)) + + // All the WKTs should be registered. + let wkts: [any Message.Type] = [ + Google_Protobuf_Any.self, + Google_Protobuf_BoolValue.self, + Google_Protobuf_BytesValue.self, + Google_Protobuf_DoubleValue.self, + Google_Protobuf_Duration.self, + Google_Protobuf_Empty.self, + Google_Protobuf_FieldMask.self, + Google_Protobuf_FloatValue.self, + Google_Protobuf_Int32Value.self, + Google_Protobuf_Int64Value.self, + Google_Protobuf_ListValue.self, + Google_Protobuf_StringValue.self, + Google_Protobuf_Struct.self, + Google_Protobuf_Timestamp.self, + Google_Protobuf_UInt32Value.self, + Google_Protobuf_UInt64Value.self, + Google_Protobuf_Value.self, + ] + for t in wkts { + XCTAssertTrue( + Google_Protobuf_Any.messageType(forMessageName: t.protoMessageName) == t, + "Looking up \(t.protoMessageName)" + ) + } } } @@ -828,26 +972,27 @@ struct ConflictingImportMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding, - @unchecked Sendable { // Once swift(>=5.9) the '@unchecked' can be removed, it is needed for Data on linux. - static let protoMessageName: String = "swift_proto_testing.import.ImportMessage" + @unchecked Sendable +{ // Once swift(>=5.9) the '@unchecked' can be removed, it is needed for Data on linux. + static let protoMessageName: String = "swift_proto_testing.import.ImportMessage" - var unknownFields = SwiftProtobuf.UnknownStorage() + var unknownFields = SwiftProtobuf.UnknownStorage() - init() {} + init() {} - mutating func decodeMessage(decoder: inout D) throws { - while let _ = try decoder.nextFieldNumber() { + mutating func decodeMessage(decoder: inout D) throws { + while let _ = try decoder.nextFieldNumber() { + } } - } - func traverse(visitor: inout V) throws { - try unknownFields.traverse(visitor: &visitor) - } + func traverse(visitor: inout V) throws { + try unknownFields.traverse(visitor: &visitor) + } - static let _protobuf_nameMap: SwiftProtobuf._NameMap = SwiftProtobuf._NameMap() + static let _protobuf_nameMap: SwiftProtobuf._NameMap = SwiftProtobuf._NameMap() - static func ==(lhs: ConflictingImportMessage, rhs: ConflictingImportMessage) -> Bool { - if lhs.unknownFields != rhs.unknownFields {return false} - return true - } + static func == (lhs: ConflictingImportMessage, rhs: ConflictingImportMessage) -> Bool { + if lhs.unknownFields != rhs.unknownFields { return false } + return true + } } diff --git a/Tests/SwiftProtobufTests/Test_Api.swift b/Tests/SwiftProtobufTests/Test_Api.swift index 20e97f37c..4dd85deb0 100644 --- a/Tests/SwiftProtobufTests/Test_Api.swift +++ b/Tests/SwiftProtobufTests/Test_Api.swift @@ -15,14 +15,14 @@ // ----------------------------------------------------------------------------- import Foundation -import XCTest import SwiftProtobuf +import XCTest final class Test_Api: XCTestCase, PBTestHelpers { typealias MessageTestType = Google_Protobuf_Api func testExists() { - assertEncode([10,7,97,112,105,78,97,109,101,34,1,49]) { (o: inout MessageTestType) in + assertEncode([10, 7, 97, 112, 105, 78, 97, 109, 101, 34, 1, 49]) { (o: inout MessageTestType) in o.name = "apiName" o.version = "1" } @@ -41,7 +41,9 @@ final class Test_Api: XCTestCase, PBTestHelpers { m.version = "1.0.0" m.syntax = .proto3 - XCTAssertEqual(try m.jsonString(), "{\"name\":\"apiName\",\"methods\":[{\"name\":\"method1\"}],\"options\":[{\"name\":\"option1\",\"value\":{\"@type\":\"type.googleapis.com/google.protobuf.StringValue\",\"value\":\"value1\"}}],\"version\":\"1.0.0\",\"syntax\":\"SYNTAX_PROTO3\"}") + XCTAssertEqual( + try m.jsonString(), + "{\"name\":\"apiName\",\"methods\":[{\"name\":\"method1\"}],\"options\":[{\"name\":\"option1\",\"value\":{\"@type\":\"type.googleapis.com/google.protobuf.StringValue\",\"value\":\"value1\"}}],\"version\":\"1.0.0\",\"syntax\":\"SYNTAX_PROTO3\"}" + ) } } - diff --git a/Tests/SwiftProtobufTests/Test_AsyncMessageSequence.swift b/Tests/SwiftProtobufTests/Test_AsyncMessageSequence.swift index 375b18b27..5795bfc53 100644 --- a/Tests/SwiftProtobufTests/Test_AsyncMessageSequence.swift +++ b/Tests/SwiftProtobufTests/Test_AsyncMessageSequence.swift @@ -1,4 +1,4 @@ -// Tests/SwiftProtobufTests/Test_AsyncMessageSequence.swift - +// Tests/SwiftProtobufTests/Test_AsyncMessageSequence.swift - // // Copyright (c) 2023 Apple Inc. and the project authors // Licensed under Apache License v2.0 with Runtime Library Exception @@ -14,226 +14,230 @@ // ----------------------------------------------------------------------------- import Foundation -import XCTest import SwiftProtobuf +import XCTest final class Test_AsyncMessageSequence: XCTestCase { - - // Decode a valid binary delimited stream - func testValidSequence() async throws { - let expected: [Int32] = Array(1...5) - var messages = [SwiftProtoTesting_TestAllTypes]() - for messageNumber in expected { - let message = SwiftProtoTesting_TestAllTypes.with { - $0.optionalInt32 = messageNumber - } - messages.append(message) - } - let serialized = try serializedMessageData(messages: messages) - let asyncBytes = asyncByteStream(bytes: serialized) - - // Recreate the original array - let decoded = asyncBytes.binaryProtobufDelimitedMessages(of: SwiftProtoTesting_TestAllTypes.self) - let observed = try await decoded.reduce(into: [Int32]()) { array, element in - array.append(element.optionalInt32) - } - XCTAssertEqual(observed, expected, "The original and re-created arrays should be equal.") - } - - // Decode a message from a stream, discarding unknown fields - func testBinaryDecodingOptions() async throws { - let unknownFields: [UInt8] = [ - // Field 1, 150 - 0x08, 0x96, 0x01, - // Field 2, string "testing" - 0x12, 0x07, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67 - ] - let message = try SwiftProtoTesting_TestEmptyMessage(serializedBytes: unknownFields) - let serialized = try serializedMessageData(messages: [message]) - var asyncBytes = asyncByteStream(bytes: serialized) - var decodingOptions = BinaryDecodingOptions() - let decodedWithUnknown = asyncBytes.binaryProtobufDelimitedMessages( - of: SwiftProtoTesting_TestEmptyMessage.self, - options: decodingOptions - ) - - // First ensure unknown fields are decoded - for try await message in decodedWithUnknown { - XCTAssertEqual(Array(message.unknownFields.data), unknownFields) - } - asyncBytes = asyncByteStream(bytes: serialized) - // Then re-run ensuring unknowh fields are discarded - decodingOptions.discardUnknownFields = true - let decodedWithUnknownDiscarded = asyncBytes.binaryProtobufDelimitedMessages( - of: SwiftProtoTesting_TestEmptyMessage.self, - options: decodingOptions - ) - var count = 0; - for try await message in decodedWithUnknownDiscarded { - XCTAssertTrue(message.unknownFields.data.isEmpty) - count += 1 + + // Decode a valid binary delimited stream + func testValidSequence() async throws { + let expected: [Int32] = Array(1...5) + var messages = [SwiftProtoTesting_TestAllTypes]() + for messageNumber in expected { + let message = SwiftProtoTesting_TestAllTypes.with { + $0.optionalInt32 = messageNumber + } + messages.append(message) + } + let serialized = try serializedMessageData(messages: messages) + let asyncBytes = asyncByteStream(bytes: serialized) + + // Recreate the original array + let decoded = asyncBytes.binaryProtobufDelimitedMessages(of: SwiftProtoTesting_TestAllTypes.self) + let observed = try await decoded.reduce(into: [Int32]()) { array, element in + array.append(element.optionalInt32) + } + XCTAssertEqual(observed, expected, "The original and re-created arrays should be equal.") } - XCTAssertEqual(count, 1, "Expected one message with unknown fields discarded.") - } - - // Decode zero length messages - func testZeroLengthMessages() async throws { - var messages = [SwiftProtoTesting_TestAllTypes]() - for _ in 1...5 { - messages.append(SwiftProtoTesting_TestAllTypes()) + + // Decode a message from a stream, discarding unknown fields + func testBinaryDecodingOptions() async throws { + let unknownFields: [UInt8] = [ + // Field 1, 150 + 0x08, 0x96, 0x01, + // Field 2, string "testing" + 0x12, 0x07, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, + ] + let message = try SwiftProtoTesting_TestEmptyMessage(serializedBytes: unknownFields) + let serialized = try serializedMessageData(messages: [message]) + var asyncBytes = asyncByteStream(bytes: serialized) + var decodingOptions = BinaryDecodingOptions() + let decodedWithUnknown = asyncBytes.binaryProtobufDelimitedMessages( + of: SwiftProtoTesting_TestEmptyMessage.self, + options: decodingOptions + ) + + // First ensure unknown fields are decoded + for try await message in decodedWithUnknown { + XCTAssertEqual(Array(message.unknownFields.data), unknownFields) + } + asyncBytes = asyncByteStream(bytes: serialized) + // Then re-run ensuring unknowh fields are discarded + decodingOptions.discardUnknownFields = true + let decodedWithUnknownDiscarded = asyncBytes.binaryProtobufDelimitedMessages( + of: SwiftProtoTesting_TestEmptyMessage.self, + options: decodingOptions + ) + var count = 0 + for try await message in decodedWithUnknownDiscarded { + XCTAssertTrue(message.unknownFields.data.isEmpty) + count += 1 + } + XCTAssertEqual(count, 1, "Expected one message with unknown fields discarded.") } - let serialized = try serializedMessageData(messages: messages) - let asyncBytes = asyncByteStream(bytes: serialized) - - var count = 0 - let decoded = AsyncMessageSequence, SwiftProtoTesting_TestAllTypes>(base: asyncBytes) - for try await message in decoded { - XCTAssertEqual(message, SwiftProtoTesting_TestAllTypes()) - count += 1 + + // Decode zero length messages + func testZeroLengthMessages() async throws { + var messages = [SwiftProtoTesting_TestAllTypes]() + for _ in 1...5 { + messages.append(SwiftProtoTesting_TestAllTypes()) + } + let serialized = try serializedMessageData(messages: messages) + let asyncBytes = asyncByteStream(bytes: serialized) + + var count = 0 + let decoded = AsyncMessageSequence, SwiftProtoTesting_TestAllTypes>(base: asyncBytes) + for try await message in decoded { + XCTAssertEqual(message, SwiftProtoTesting_TestAllTypes()) + count += 1 + } + XCTAssertEqual(count, 5, "Expected five messages with default fields.") } - XCTAssertEqual(count, 5, "Expected five messages with default fields.") - } - - // Stream with a single zero varint - func testStreamZeroVarintOnly() async throws { - let seq = asyncByteStream(bytes: [0]) - let decoded = seq.binaryProtobufDelimitedMessages(of: SwiftProtoTesting_TestAllTypes.self) - - var count = 0 - for try await message in decoded { - XCTAssertEqual(message, SwiftProtoTesting_TestAllTypes()) - count += 1 + + // Stream with a single zero varint + func testStreamZeroVarintOnly() async throws { + let seq = asyncByteStream(bytes: [0]) + let decoded = seq.binaryProtobufDelimitedMessages(of: SwiftProtoTesting_TestAllTypes.self) + + var count = 0 + for try await message in decoded { + XCTAssertEqual(message, SwiftProtoTesting_TestAllTypes()) + count += 1 + } + XCTAssertEqual(count, 1) } - XCTAssertEqual(count, 1) - } - - // Empty stream with zero bytes - func testEmptyStream() async throws { - let asyncBytes = asyncByteStream(bytes: []) - let messages = asyncBytes.binaryProtobufDelimitedMessages(of: SwiftProtoTesting_TestAllTypes.self) - for try await _ in messages { - XCTFail("Shouldn't have returned a value for an empty stream.") + + // Empty stream with zero bytes + func testEmptyStream() async throws { + let asyncBytes = asyncByteStream(bytes: []) + let messages = asyncBytes.binaryProtobufDelimitedMessages(of: SwiftProtoTesting_TestAllTypes.self) + for try await _ in messages { + XCTFail("Shouldn't have returned a value for an empty stream.") + } } - } - - // A stream with legal non-zero varint but no message - func testNonZeroVarintNoMessage() async throws { - let asyncBytes = asyncByteStream(bytes: [0x96, 0x01]) - let decoded = asyncBytes.binaryProtobufDelimitedMessages(of: SwiftProtoTesting_TestAllTypes.self) - var truncatedThrown = false - do { - for try await _ in decoded { - XCTFail("Shouldn't have returned a value for an empty stream.") - } - } catch { - if error as! BinaryDelimited.Error == .truncated { - truncatedThrown = true - } + + // A stream with legal non-zero varint but no message + func testNonZeroVarintNoMessage() async throws { + let asyncBytes = asyncByteStream(bytes: [0x96, 0x01]) + let decoded = asyncBytes.binaryProtobufDelimitedMessages(of: SwiftProtoTesting_TestAllTypes.self) + var truncatedThrown = false + do { + for try await _ in decoded { + XCTFail("Shouldn't have returned a value for an empty stream.") + } + } catch { + if error as! BinaryDelimited.Error == .truncated { + truncatedThrown = true + } + } + XCTAssertTrue(truncatedThrown, "Should throw a SwiftProtobufError.BinaryStreamDecoding.truncated") } - XCTAssertTrue(truncatedThrown, "Should throw a SwiftProtobufError.BinaryStreamDecoding.truncated") - } - - // Single varint describing a 2GB message - func testTooLarge() async throws { - let asyncBytes = asyncByteStream(bytes: [128, 128, 128, 128, 8]) - let decoded = asyncBytes.binaryProtobufDelimitedMessages(of: SwiftProtoTesting_TestAllTypes.self) - do { - for try await _ in decoded { - XCTFail("Shouldn't have returned a value for an invalid stream.") - } - } catch { - XCTAssertTrue(self.isSwiftProtobufErrorEqual(error as! SwiftProtobufError, .BinaryDecoding.tooLarge())) + + // Single varint describing a 2GB message + func testTooLarge() async throws { + let asyncBytes = asyncByteStream(bytes: [128, 128, 128, 128, 8]) + let decoded = asyncBytes.binaryProtobufDelimitedMessages(of: SwiftProtoTesting_TestAllTypes.self) + do { + for try await _ in decoded { + XCTFail("Shouldn't have returned a value for an invalid stream.") + } + } catch { + XCTAssertTrue(self.isSwiftProtobufErrorEqual(error as! SwiftProtobufError, .BinaryDecoding.tooLarge())) + } } - } - - // Stream with truncated varint - func testTruncatedVarint() async throws { - let asyncBytes = asyncByteStream(bytes: [192]) - - let decoded = asyncBytes.binaryProtobufDelimitedMessages(of: SwiftProtoTesting_TestAllTypes.self) - var truncatedThrown = false - do { - for try await _ in decoded { - XCTFail("Shouldn't have returned a value for an empty stream.") - } - } catch { - if error as! BinaryDelimited.Error == .truncated { - truncatedThrown = true - } + + // Stream with truncated varint + func testTruncatedVarint() async throws { + let asyncBytes = asyncByteStream(bytes: [192]) + + let decoded = asyncBytes.binaryProtobufDelimitedMessages(of: SwiftProtoTesting_TestAllTypes.self) + var truncatedThrown = false + do { + for try await _ in decoded { + XCTFail("Shouldn't have returned a value for an empty stream.") + } + } catch { + if error as! BinaryDelimited.Error == .truncated { + truncatedThrown = true + } + } + XCTAssertTrue(truncatedThrown, "Should throw a SwiftProtobufError.BinaryStreamDecoding.truncated") } - XCTAssertTrue(truncatedThrown, "Should throw a SwiftProtobufError.BinaryStreamDecoding.truncated") - } - - // Stream with a valid varint and message, but the following varint is truncated - func testValidMessageThenTruncatedVarint() async throws { - var truncatedThrown = false - let msg = SwiftProtoTesting_TestAllTypes.with { - $0.optionalInt64 = 123456789 + + // Stream with a valid varint and message, but the following varint is truncated + func testValidMessageThenTruncatedVarint() async throws { + var truncatedThrown = false + let msg = SwiftProtoTesting_TestAllTypes.with { + $0.optionalInt64 = 123_456_789 + } + let truncatedVarint: [UInt8] = [224, 216] + var serialized = try serializedMessageData(messages: [msg]) + serialized += truncatedVarint + let asyncBytes = asyncByteStream(bytes: serialized) + + do { + var count = 0 + let decoded = asyncBytes.binaryProtobufDelimitedMessages(of: SwiftProtoTesting_TestAllTypes.self) + for try await message in decoded { + XCTAssertEqual( + message, + SwiftProtoTesting_TestAllTypes.with { + $0.optionalInt64 = 123_456_789 + } + ) + count += 1 + if count > 1 { + XCTFail("Expected one message only.") + } + } + XCTAssertEqual(count, 1, "One message should be deserialized") + } catch { + if error as! BinaryDelimited.Error == .truncated { + truncatedThrown = true + } + } + XCTAssertTrue(truncatedThrown, "Should throw a SwiftProtobuf.BinaryStreamDecoding.truncated") } - let truncatedVarint: [UInt8] = [224, 216] - var serialized = try serializedMessageData(messages: [msg]) - serialized += truncatedVarint - let asyncBytes = asyncByteStream(bytes: serialized) - - do { - var count = 0 - let decoded = asyncBytes.binaryProtobufDelimitedMessages(of: SwiftProtoTesting_TestAllTypes.self) - for try await message in decoded { - XCTAssertEqual(message, SwiftProtoTesting_TestAllTypes.with { - $0.optionalInt64 = 123456789 - }) - count += 1 - if count > 1 { - XCTFail("Expected one message only.") - } - } - XCTAssertEqual(count, 1, "One message should be deserialized") - } catch { - if error as! BinaryDelimited.Error == .truncated { - truncatedThrown = true - } + + // Slow test case found by oss-fuzz: 1 million zero-sized messages + // A similar test with BinaryDelimited is about 4x faster, showing + // that we have some room for improvement here. + // (Note this currently only tests 100,000 zero-sized messages, + // but the constant below is easy to edit if you want to experiment.) + func testLargeExample() async throws { + let messageCount = 100_000 + let bytes = [UInt8](repeating: 0, count: messageCount) + let byteStream = asyncByteStream(bytes: bytes) + let decodedStream = byteStream.binaryProtobufDelimitedMessages( + of: SwiftProtoTesting_TestAllTypes.self, + extensions: SwiftProtoTesting_Fuzz_FuzzTesting_Extensions + ) + var count = 0 + for try await message in decodedStream { + XCTAssertEqual(message, SwiftProtoTesting_TestAllTypes()) + count += 1 + } + XCTAssertEqual(count, messageCount) } - XCTAssertTrue(truncatedThrown, "Should throw a SwiftProtobuf.BinaryStreamDecoding.truncated") - } - - // Slow test case found by oss-fuzz: 1 million zero-sized messages - // A similar test with BinaryDelimited is about 4x faster, showing - // that we have some room for improvement here. - // (Note this currently only tests 100,000 zero-sized messages, - // but the constant below is easy to edit if you want to experiment.) - func testLargeExample() async throws { - let messageCount = 100_000 - let bytes = [UInt8](repeating: 0, count: messageCount) - let byteStream = asyncByteStream(bytes: bytes) - let decodedStream = byteStream.binaryProtobufDelimitedMessages( - of: SwiftProtoTesting_TestAllTypes.self, - extensions: SwiftProtoTesting_Fuzz_FuzzTesting_Extensions) - var count = 0 - for try await message in decodedStream { - XCTAssertEqual(message, SwiftProtoTesting_TestAllTypes()) - count += 1 + + fileprivate func asyncByteStream(bytes: [UInt8]) -> AsyncStream { + AsyncStream(UInt8.self) { continuation in + for byte in bytes { + continuation.yield(byte) + } + continuation.finish() + } } - XCTAssertEqual(count, messageCount) - } - - fileprivate func asyncByteStream(bytes: [UInt8]) -> AsyncStream { - AsyncStream(UInt8.self) { continuation in - for byte in bytes { - continuation.yield(byte) - } - continuation.finish() - } - } - - fileprivate func serializedMessageData(messages: [any Message]) throws -> [UInt8] { - let memoryOutputStream = OutputStream.toMemory() - memoryOutputStream.open() - for message in messages { - XCTAssertNoThrow(try BinaryDelimited.serialize(message: message, to: memoryOutputStream)) + + fileprivate func serializedMessageData(messages: [any Message]) throws -> [UInt8] { + let memoryOutputStream = OutputStream.toMemory() + memoryOutputStream.open() + for message in messages { + XCTAssertNoThrow(try BinaryDelimited.serialize(message: message, to: memoryOutputStream)) + } + memoryOutputStream.close() + let nsData = memoryOutputStream.property(forKey: .dataWrittenToMemoryStreamKey) as! NSData + let data = Data(referencing: nsData) + return [UInt8](data) } - memoryOutputStream.close() - let nsData = memoryOutputStream.property(forKey: .dataWrittenToMemoryStreamKey) as! NSData - let data = Data(referencing: nsData) - return [UInt8](data) - } } diff --git a/Tests/SwiftProtobufTests/Test_BasicFields_Access_Proto2.swift b/Tests/SwiftProtobufTests/Test_BasicFields_Access_Proto2.swift index 1c842bafa..6337fc3ba 100644 --- a/Tests/SwiftProtobufTests/Test_BasicFields_Access_Proto2.swift +++ b/Tests/SwiftProtobufTests/Test_BasicFields_Access_Proto2.swift @@ -12,8 +12,8 @@ /// // ----------------------------------------------------------------------------- -import XCTest import Foundation +import XCTest // NOTE: The generator changes what is generated based on the number/types // of fields (using a nested storage class or not), to be completel, all @@ -22,849 +22,849 @@ import Foundation final class Test_BasicFields_Access_Proto2: XCTestCase { - // Optional - - func testOptionalInt32() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.optionalInt32, 0) - XCTAssertFalse(msg.hasOptionalInt32) - msg.optionalInt32 = 1 - XCTAssertTrue(msg.hasOptionalInt32) - XCTAssertEqual(msg.optionalInt32, 1) - msg.clearOptionalInt32() - XCTAssertEqual(msg.optionalInt32, 0) - XCTAssertFalse(msg.hasOptionalInt32) - } - - func testOptionalInt64() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.optionalInt64, 0) - XCTAssertFalse(msg.hasOptionalInt64) - msg.optionalInt64 = 2 - XCTAssertTrue(msg.hasOptionalInt64) - XCTAssertEqual(msg.optionalInt64, 2) - msg.clearOptionalInt64() - XCTAssertEqual(msg.optionalInt64, 0) - XCTAssertFalse(msg.hasOptionalInt64) - } - - func testOptionalUint32() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.optionalUint32, 0) - XCTAssertFalse(msg.hasOptionalUint32) - msg.optionalUint32 = 3 - XCTAssertTrue(msg.hasOptionalUint32) - XCTAssertEqual(msg.optionalUint32, 3) - msg.clearOptionalUint32() - XCTAssertEqual(msg.optionalUint32, 0) - XCTAssertFalse(msg.hasOptionalUint32) - } - - func testOptionalUint64() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.optionalUint64, 0) - XCTAssertFalse(msg.hasOptionalUint64) - msg.optionalUint64 = 4 - XCTAssertTrue(msg.hasOptionalUint64) - XCTAssertEqual(msg.optionalUint64, 4) - msg.clearOptionalUint64() - XCTAssertEqual(msg.optionalUint64, 0) - XCTAssertFalse(msg.hasOptionalUint64) - } - - func testOptionalSint32() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.optionalSint32, 0) - XCTAssertFalse(msg.hasOptionalSint32) - msg.optionalSint32 = 5 - XCTAssertTrue(msg.hasOptionalSint32) - XCTAssertEqual(msg.optionalSint32, 5) - msg.clearOptionalSint32() - XCTAssertEqual(msg.optionalSint32, 0) - XCTAssertFalse(msg.hasOptionalSint32) - } - - func testOptionalSint64() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.optionalSint64, 0) - XCTAssertFalse(msg.hasOptionalSint64) - msg.optionalSint64 = 6 - XCTAssertTrue(msg.hasOptionalSint64) - XCTAssertEqual(msg.optionalSint64, 6) - msg.clearOptionalSint64() - XCTAssertEqual(msg.optionalSint64, 0) - XCTAssertFalse(msg.hasOptionalSint64) - } - - func testOptionalFixed32() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.optionalFixed32, 0) - XCTAssertFalse(msg.hasOptionalFixed32) - msg.optionalFixed32 = 7 - XCTAssertTrue(msg.hasOptionalFixed32) - XCTAssertEqual(msg.optionalFixed32, 7) - msg.clearOptionalFixed32() - XCTAssertEqual(msg.optionalFixed32, 0) - XCTAssertFalse(msg.hasOptionalFixed32) - } - - func testOptionalFixed64() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.optionalFixed64, 0) - XCTAssertFalse(msg.hasOptionalFixed64) - msg.optionalFixed64 = 8 - XCTAssertTrue(msg.hasOptionalFixed64) - XCTAssertEqual(msg.optionalFixed64, 8) - msg.clearOptionalFixed64() - XCTAssertEqual(msg.optionalFixed64, 0) - XCTAssertFalse(msg.hasOptionalFixed64) - } - - func testOptionalSfixed32() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.optionalSfixed32, 0) - XCTAssertFalse(msg.hasOptionalSfixed32) - msg.optionalSfixed32 = 9 - XCTAssertTrue(msg.hasOptionalSfixed32) - XCTAssertEqual(msg.optionalSfixed32, 9) - msg.clearOptionalSfixed32() - XCTAssertEqual(msg.optionalSfixed32, 0) - XCTAssertFalse(msg.hasOptionalSfixed32) - } - - func testOptionalSfixed64() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.optionalSfixed64, 0) - XCTAssertFalse(msg.hasOptionalSfixed64) - msg.optionalSfixed64 = 10 - XCTAssertTrue(msg.hasOptionalSfixed64) - XCTAssertEqual(msg.optionalSfixed64, 10) - msg.clearOptionalSfixed64() - XCTAssertEqual(msg.optionalSfixed64, 0) - XCTAssertFalse(msg.hasOptionalSfixed64) - } - - func testOptionalFloat() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.optionalFloat, 0.0) - XCTAssertFalse(msg.hasOptionalFloat) - msg.optionalFloat = 11.0 - XCTAssertTrue(msg.hasOptionalFloat) - XCTAssertEqual(msg.optionalFloat, 11.0) - msg.clearOptionalFloat() - XCTAssertEqual(msg.optionalFloat, 0.0) - XCTAssertFalse(msg.hasOptionalFloat) - } - - func testOptionalDouble() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.optionalDouble, 0.0) - XCTAssertFalse(msg.hasOptionalDouble) - msg.optionalDouble = 12.0 - XCTAssertTrue(msg.hasOptionalDouble) - XCTAssertEqual(msg.optionalDouble, 12.0) - msg.clearOptionalDouble() - XCTAssertEqual(msg.optionalDouble, 0.0) - XCTAssertFalse(msg.hasOptionalDouble) - } - - func testOptionalBool() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.optionalBool, false) - XCTAssertFalse(msg.hasOptionalBool) - msg.optionalBool = true - XCTAssertTrue(msg.hasOptionalBool) - XCTAssertEqual(msg.optionalBool, true) - msg.clearOptionalBool() - XCTAssertEqual(msg.optionalBool, false) - XCTAssertFalse(msg.hasOptionalBool) - } - - func testOptionalString() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.optionalString, "") - XCTAssertFalse(msg.hasOptionalString) - msg.optionalString = "14" - XCTAssertTrue(msg.hasOptionalString) - XCTAssertEqual(msg.optionalString, "14") - msg.clearOptionalString() - XCTAssertEqual(msg.optionalString, "") - XCTAssertFalse(msg.hasOptionalString) - } - - func testOptionalBytes() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.optionalBytes, Data()) - XCTAssertFalse(msg.hasOptionalBytes) - msg.optionalBytes = Data([15]) - XCTAssertTrue(msg.hasOptionalBytes) - XCTAssertEqual(msg.optionalBytes, Data([15])) - msg.clearOptionalBytes() - XCTAssertEqual(msg.optionalBytes, Data()) - XCTAssertFalse(msg.hasOptionalBytes) - } - - func testOptionalGroup() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.optionalGroup.a, 0) - XCTAssertFalse(msg.hasOptionalGroup) - var grp = SwiftProtoTesting_TestAllTypes.OptionalGroup() - grp.a = 16 - msg.optionalGroup = grp - XCTAssertTrue(msg.hasOptionalGroup) - XCTAssertEqual(msg.optionalGroup.a, 16) - msg.clearOptionalGroup() - XCTAssertEqual(msg.optionalGroup.a, 0) - XCTAssertFalse(msg.hasOptionalGroup) - } - - func testOptionalNestedMessage() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.optionalNestedMessage.bb, 0) - XCTAssertFalse(msg.hasOptionalNestedMessage) - var nestedMsg = SwiftProtoTesting_TestAllTypes.NestedMessage() - nestedMsg.bb = 18 - msg.optionalNestedMessage = nestedMsg - XCTAssertTrue(msg.hasOptionalNestedMessage) - XCTAssertEqual(msg.optionalNestedMessage.bb, 18) - XCTAssertEqual(msg.optionalNestedMessage, nestedMsg) - msg.clearOptionalNestedMessage() - XCTAssertEqual(msg.optionalNestedMessage.bb, 0) - XCTAssertFalse(msg.hasOptionalNestedMessage) - } - - func testOptionalForeignMessage() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.optionalForeignMessage.c, 0) - XCTAssertFalse(msg.hasOptionalForeignMessage) - var foreignMsg = SwiftProtoTesting_ForeignMessage() - foreignMsg.c = 19 - msg.optionalForeignMessage = foreignMsg - XCTAssertTrue(msg.hasOptionalForeignMessage) - XCTAssertEqual(msg.optionalForeignMessage.c, 19) - XCTAssertEqual(msg.optionalForeignMessage, foreignMsg) - msg.clearOptionalForeignMessage() - XCTAssertEqual(msg.optionalForeignMessage.c, 0) - XCTAssertFalse(msg.hasOptionalForeignMessage) - } - - func testOptionalImportMessage() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.optionalImportMessage.d, 0) - XCTAssertFalse(msg.hasOptionalImportMessage) - var importedMsg = SwiftProtoTesting_Import_ImportMessage() - importedMsg.d = 20 - msg.optionalImportMessage = importedMsg - XCTAssertTrue(msg.hasOptionalImportMessage) - XCTAssertEqual(msg.optionalImportMessage.d, 20) - XCTAssertEqual(msg.optionalImportMessage, importedMsg) - msg.clearOptionalImportMessage() - XCTAssertEqual(msg.optionalImportMessage.d, 0) - XCTAssertFalse(msg.hasOptionalImportMessage) - } - - func testOptionalNestedEnum() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.optionalNestedEnum, .foo) - XCTAssertFalse(msg.hasOptionalNestedEnum) - msg.optionalNestedEnum = .bar - XCTAssertTrue(msg.hasOptionalNestedEnum) - XCTAssertEqual(msg.optionalNestedEnum, .bar) - msg.clearOptionalNestedEnum() - XCTAssertEqual(msg.optionalNestedEnum, .foo) - XCTAssertFalse(msg.hasOptionalNestedEnum) - } - - func testOptionalForeignEnum() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.optionalForeignEnum, .foreignFoo) - XCTAssertFalse(msg.hasOptionalForeignEnum) - msg.optionalForeignEnum = .foreignBar - XCTAssertTrue(msg.hasOptionalForeignEnum) - XCTAssertEqual(msg.optionalForeignEnum, .foreignBar) - msg.clearOptionalForeignEnum() - XCTAssertEqual(msg.optionalForeignEnum, .foreignFoo) - XCTAssertFalse(msg.hasOptionalForeignEnum) - } - - func testOptionalImportEnum() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.optionalImportEnum, .importFoo) - XCTAssertFalse(msg.hasOptionalImportEnum) - msg.optionalImportEnum = .importBar - XCTAssertTrue(msg.hasOptionalImportEnum) - XCTAssertEqual(msg.optionalImportEnum, .importBar) - msg.clearOptionalImportEnum() - XCTAssertEqual(msg.optionalImportEnum, .importFoo) - XCTAssertFalse(msg.hasOptionalImportEnum) - } - - func testOptionalStringPiece() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.optionalStringPiece, "") - XCTAssertFalse(msg.hasOptionalStringPiece) - msg.optionalStringPiece = "24" - XCTAssertTrue(msg.hasOptionalStringPiece) - XCTAssertEqual(msg.optionalStringPiece, "24") - msg.clearOptionalStringPiece() - XCTAssertEqual(msg.optionalStringPiece, "") - XCTAssertFalse(msg.hasOptionalStringPiece) - } - - func testOptionalCord() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.optionalCord, "") - XCTAssertFalse(msg.hasOptionalCord) - msg.optionalCord = "25" - XCTAssertTrue(msg.hasOptionalCord) - XCTAssertEqual(msg.optionalCord, "25") - msg.clearOptionalCord() - XCTAssertEqual(msg.optionalCord, "") - XCTAssertFalse(msg.hasOptionalCord) - } - - func testOptionalPublicImportMessage() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.optionalPublicImportMessage.e, 0) - XCTAssertFalse(msg.hasOptionalPublicImportMessage) - var pubImportedMsg = SwiftProtoTesting_Import_PublicImportMessage() - pubImportedMsg.e = 26 - msg.optionalPublicImportMessage = pubImportedMsg - XCTAssertTrue(msg.hasOptionalPublicImportMessage) - XCTAssertEqual(msg.optionalPublicImportMessage.e, 26) - XCTAssertEqual(msg.optionalPublicImportMessage, pubImportedMsg) - msg.clearOptionalPublicImportMessage() - XCTAssertEqual(msg.optionalPublicImportMessage.e, 0) - XCTAssertFalse(msg.hasOptionalPublicImportMessage) - } - - func testOptionalLazyMessage() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.optionalLazyMessage.bb, 0) - XCTAssertFalse(msg.hasOptionalLazyMessage) - var nestedMsg = SwiftProtoTesting_TestAllTypes.NestedMessage() - nestedMsg.bb = 27 - msg.optionalLazyMessage = nestedMsg - XCTAssertTrue(msg.hasOptionalLazyMessage) - XCTAssertEqual(msg.optionalLazyMessage.bb, 27) - XCTAssertEqual(msg.optionalLazyMessage, nestedMsg) - msg.clearOptionalLazyMessage() - XCTAssertEqual(msg.optionalLazyMessage.bb, 0) - XCTAssertFalse(msg.hasOptionalLazyMessage) - } - - // Optional with explicit default values (non zero) - - func testDefaultInt32() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.defaultInt32, 41) - XCTAssertFalse(msg.hasDefaultInt32) - msg.defaultInt32 = 61 - XCTAssertTrue(msg.hasDefaultInt32) - XCTAssertEqual(msg.defaultInt32, 61) - msg.clearDefaultInt32() - XCTAssertEqual(msg.defaultInt32, 41) - XCTAssertFalse(msg.hasDefaultInt32) - } - - func testDefaultInt64() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.defaultInt64, 42) - XCTAssertFalse(msg.hasDefaultInt64) - msg.defaultInt64 = 62 - XCTAssertTrue(msg.hasDefaultInt64) - XCTAssertEqual(msg.defaultInt64, 62) - msg.clearDefaultInt64() - XCTAssertEqual(msg.defaultInt64, 42) - XCTAssertFalse(msg.hasDefaultInt64) - } - - func testDefaultUint32() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.defaultUint32, 43) - XCTAssertFalse(msg.hasDefaultUint32) - msg.defaultUint32 = 63 - XCTAssertTrue(msg.hasDefaultUint32) - XCTAssertEqual(msg.defaultUint32, 63) - msg.clearDefaultUint32() - XCTAssertEqual(msg.defaultUint32, 43) - XCTAssertFalse(msg.hasDefaultUint32) - } - - func testDefaultUint64() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.defaultUint64, 44) - XCTAssertFalse(msg.hasDefaultUint64) - msg.defaultUint64 = 64 - XCTAssertTrue(msg.hasDefaultUint64) - XCTAssertEqual(msg.defaultUint64, 64) - msg.clearDefaultUint64() - XCTAssertEqual(msg.defaultUint64, 44) - XCTAssertFalse(msg.hasDefaultUint64) - } - - func testDefaultSint32() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.defaultSint32, -45) - XCTAssertFalse(msg.hasDefaultSint32) - msg.defaultSint32 = 65 - XCTAssertTrue(msg.hasDefaultSint32) - XCTAssertEqual(msg.defaultSint32, 65) - msg.clearDefaultSint32() - XCTAssertEqual(msg.defaultSint32, -45) - XCTAssertFalse(msg.hasDefaultSint32) - } - - func testDefaultSint64() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.defaultSint64, 46) - XCTAssertFalse(msg.hasDefaultSint64) - msg.defaultSint64 = 66 - XCTAssertTrue(msg.hasDefaultSint64) - XCTAssertEqual(msg.defaultSint64, 66) - msg.clearDefaultSint64() - XCTAssertEqual(msg.defaultSint64, 46) - XCTAssertFalse(msg.hasDefaultSint64) - } - - func testDefaultFixed32() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.defaultFixed32, 47) - XCTAssertFalse(msg.hasDefaultFixed32) - msg.defaultFixed32 = 67 - XCTAssertTrue(msg.hasDefaultFixed32) - XCTAssertEqual(msg.defaultFixed32, 67) - msg.clearDefaultFixed32() - XCTAssertEqual(msg.defaultFixed32, 47) - XCTAssertFalse(msg.hasDefaultFixed32) - } - - func testDefaultFixed64() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.defaultFixed64, 48) - XCTAssertFalse(msg.hasDefaultFixed64) - msg.defaultFixed64 = 68 - XCTAssertTrue(msg.hasDefaultFixed64) - XCTAssertEqual(msg.defaultFixed64, 68) - msg.clearDefaultFixed64() - XCTAssertEqual(msg.defaultFixed64, 48) - XCTAssertFalse(msg.hasDefaultFixed64) - } - - func testDefaultSfixed32() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.defaultSfixed32, 49) - XCTAssertFalse(msg.hasDefaultSfixed32) - msg.defaultSfixed32 = 69 - XCTAssertTrue(msg.hasDefaultSfixed32) - XCTAssertEqual(msg.defaultSfixed32, 69) - msg.clearDefaultSfixed32() - XCTAssertEqual(msg.defaultSfixed32, 49) - XCTAssertFalse(msg.hasDefaultSfixed32) - } - - func testDefaultSfixed64() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.defaultSfixed64, -50) - XCTAssertFalse(msg.hasDefaultSfixed64) - msg.defaultSfixed64 = 70 - XCTAssertTrue(msg.hasDefaultSfixed64) - XCTAssertEqual(msg.defaultSfixed64, 70) - msg.clearDefaultSfixed64() - XCTAssertEqual(msg.defaultSfixed64, -50) - XCTAssertFalse(msg.hasDefaultSfixed64) - } - - func testDefaultFloat() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.defaultFloat, 51.5) - XCTAssertFalse(msg.hasDefaultFloat) - msg.defaultFloat = 71 - XCTAssertTrue(msg.hasDefaultFloat) - XCTAssertEqual(msg.defaultFloat, 71) - msg.clearDefaultFloat() - XCTAssertEqual(msg.defaultFloat, 51.5) - XCTAssertFalse(msg.hasDefaultFloat) - } - - func testDefaultDouble() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.defaultDouble, 52e3) - XCTAssertFalse(msg.hasDefaultDouble) - msg.defaultDouble = 72 - XCTAssertTrue(msg.hasDefaultDouble) - XCTAssertEqual(msg.defaultDouble, 72) - msg.clearDefaultDouble() - XCTAssertEqual(msg.defaultDouble, 52e3) - XCTAssertFalse(msg.hasDefaultDouble) - } - - func testDefaultBool() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.defaultBool, true) - XCTAssertFalse(msg.hasDefaultBool) - msg.defaultBool = false - XCTAssertTrue(msg.hasDefaultBool) - XCTAssertEqual(msg.defaultBool, false) - msg.clearDefaultBool() - XCTAssertEqual(msg.defaultBool, true) - XCTAssertFalse(msg.hasDefaultBool) - } - - func testDefaultString() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.defaultString, "hello") - XCTAssertFalse(msg.hasDefaultString) - msg.defaultString = "74" - XCTAssertTrue(msg.hasDefaultString) - XCTAssertEqual(msg.defaultString, "74") - msg.clearDefaultString() - XCTAssertEqual(msg.defaultString, "hello") - XCTAssertFalse(msg.hasDefaultString) - } - - func testDefaultBytes() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.defaultBytes, "world".data(using: .utf8)) - XCTAssertFalse(msg.hasDefaultBytes) - msg.defaultBytes = Data([75]) - XCTAssertTrue(msg.hasDefaultBytes) - XCTAssertEqual(msg.defaultBytes, Data([75])) - msg.clearDefaultBytes() - XCTAssertEqual(msg.defaultBytes, "world".data(using: .utf8)) - XCTAssertFalse(msg.hasDefaultBytes) - } - - func testDefaultNestedEnum() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.defaultNestedEnum, .bar) - XCTAssertFalse(msg.hasDefaultNestedEnum) - msg.defaultNestedEnum = .baz - XCTAssertTrue(msg.hasDefaultNestedEnum) - XCTAssertEqual(msg.defaultNestedEnum, .baz) - msg.clearDefaultNestedEnum() - XCTAssertEqual(msg.defaultNestedEnum, .bar) - XCTAssertFalse(msg.hasDefaultNestedEnum) - } - - func testDefaultForeignEnum() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.defaultForeignEnum, .foreignBar) - XCTAssertFalse(msg.hasDefaultForeignEnum) - msg.defaultForeignEnum = .foreignBaz - XCTAssertTrue(msg.hasDefaultForeignEnum) - XCTAssertEqual(msg.defaultForeignEnum, .foreignBaz) - msg.clearDefaultForeignEnum() - XCTAssertEqual(msg.defaultForeignEnum, .foreignBar) - XCTAssertFalse(msg.hasDefaultForeignEnum) - } - - func testDefaultImportEnum() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.defaultImportEnum, .importBar) - XCTAssertFalse(msg.hasDefaultImportEnum) - msg.defaultImportEnum = .importBaz - XCTAssertTrue(msg.hasDefaultImportEnum) - XCTAssertEqual(msg.defaultImportEnum, .importBaz) - msg.clearDefaultImportEnum() - XCTAssertEqual(msg.defaultImportEnum, .importBar) - XCTAssertFalse(msg.hasDefaultImportEnum) - } - - func testDefaultStringPiece() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.defaultStringPiece, "abc") - XCTAssertFalse(msg.hasDefaultStringPiece) - msg.defaultStringPiece = "84" - XCTAssertTrue(msg.hasDefaultStringPiece) - XCTAssertEqual(msg.defaultStringPiece, "84") - msg.clearDefaultStringPiece() - XCTAssertEqual(msg.defaultStringPiece, "abc") - XCTAssertFalse(msg.hasDefaultStringPiece) - } - - func testDefaultCord() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.defaultCord, "123") - XCTAssertFalse(msg.hasDefaultCord) - msg.defaultCord = "85" - XCTAssertTrue(msg.hasDefaultCord) - XCTAssertEqual(msg.defaultCord, "85") - msg.clearDefaultCord() - XCTAssertEqual(msg.defaultCord, "123") - XCTAssertFalse(msg.hasDefaultCord) - } - - // Repeated - - func testRepeatedInt32() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.repeatedInt32, []) - msg.repeatedInt32 = [31] - XCTAssertEqual(msg.repeatedInt32, [31]) - msg.repeatedInt32.append(131) - XCTAssertEqual(msg.repeatedInt32, [31, 131]) - } - - func testRepeatedInt64() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.repeatedInt64, []) - msg.repeatedInt64 = [32] - XCTAssertEqual(msg.repeatedInt64, [32]) - msg.repeatedInt64.append(132) - XCTAssertEqual(msg.repeatedInt64, [32, 132]) - } - - func testRepeatedUint32() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.repeatedUint32, []) - msg.repeatedUint32 = [33] - XCTAssertEqual(msg.repeatedUint32, [33]) - msg.repeatedUint32.append(133) - XCTAssertEqual(msg.repeatedUint32, [33, 133]) - } - - func testRepeatedUint64() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.repeatedUint64, []) - msg.repeatedUint64 = [34] - XCTAssertEqual(msg.repeatedUint64, [34]) - msg.repeatedUint64.append(134) - XCTAssertEqual(msg.repeatedUint64, [34, 134]) - } - - func testRepeatedSint32() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.repeatedSint32, []) - msg.repeatedSint32 = [35] - XCTAssertEqual(msg.repeatedSint32, [35]) - msg.repeatedSint32.append(135) - XCTAssertEqual(msg.repeatedSint32, [35, 135]) - } - - func testRepeatedSint64() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.repeatedSint64, []) - msg.repeatedSint64 = [36] - XCTAssertEqual(msg.repeatedSint64, [36]) - msg.repeatedSint64.append(136) - XCTAssertEqual(msg.repeatedSint64, [36, 136]) - } - - func testRepeatedFixed32() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.repeatedFixed32, []) - msg.repeatedFixed32 = [37] - XCTAssertEqual(msg.repeatedFixed32, [37]) - msg.repeatedFixed32.append(137) - XCTAssertEqual(msg.repeatedFixed32, [37, 137]) - } - - func testRepeatedFixed64() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.repeatedFixed64, []) - msg.repeatedFixed64 = [38] - XCTAssertEqual(msg.repeatedFixed64, [38]) - msg.repeatedFixed64.append(138) - XCTAssertEqual(msg.repeatedFixed64, [38, 138]) - } - - func testRepeatedSfixed32() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.repeatedSfixed32, []) - msg.repeatedSfixed32 = [39] - XCTAssertEqual(msg.repeatedSfixed32, [39]) - msg.repeatedSfixed32.append(139) - XCTAssertEqual(msg.repeatedSfixed32, [39, 139]) - } - - func testRepeatedSfixed64() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.repeatedSfixed64, []) - msg.repeatedSfixed64 = [40] - XCTAssertEqual(msg.repeatedSfixed64, [40]) - msg.repeatedSfixed64.append(140) - XCTAssertEqual(msg.repeatedSfixed64, [40, 140]) - } - - func testRepeatedFloat() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.repeatedFloat, []) - msg.repeatedFloat = [41.0] - XCTAssertEqual(msg.repeatedFloat, [41.0]) - msg.repeatedFloat.append(141.0) - XCTAssertEqual(msg.repeatedFloat, [41.0, 141.0]) - } - - func testRepeatedDouble() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.repeatedDouble, []) - msg.repeatedDouble = [42.0] - XCTAssertEqual(msg.repeatedDouble, [42.0]) - msg.repeatedDouble.append(142.0) - XCTAssertEqual(msg.repeatedDouble, [42.0, 142.0]) - } - - func testRepeatedBool() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.repeatedBool, []) - msg.repeatedBool = [true] - XCTAssertEqual(msg.repeatedBool, [true]) - msg.repeatedBool.append(false) - XCTAssertEqual(msg.repeatedBool, [true, false]) - } - - func testRepeatedString() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.repeatedString, []) - msg.repeatedString = ["44"] - XCTAssertEqual(msg.repeatedString, ["44"]) - msg.repeatedString.append("144") - XCTAssertEqual(msg.repeatedString, ["44", "144"]) - } - - func testRepeatedBytes() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.repeatedBytes, []) - msg.repeatedBytes = [Data([45])] - XCTAssertEqual(msg.repeatedBytes, [Data([45])]) - msg.repeatedBytes.append(Data([145])) - XCTAssertEqual(msg.repeatedBytes, [Data([45]), Data([145])]) - } - - func testRepeatedGroup() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.repeatedGroup, []) - var grp = SwiftProtoTesting_TestAllTypes.RepeatedGroup() - grp.a = 46 - msg.repeatedGroup = [grp] - XCTAssertEqual(msg.repeatedGroup.count, 1) - XCTAssertEqual(msg.repeatedGroup[0].a, 46) - XCTAssertEqual(msg.repeatedGroup, [grp]) - var grp2 = SwiftProtoTesting_TestAllTypes.RepeatedGroup() - grp2.a = 146 - msg.repeatedGroup.append(grp2) - XCTAssertEqual(msg.repeatedGroup.count, 2) - XCTAssertEqual(msg.repeatedGroup[0].a, 46) - XCTAssertEqual(msg.repeatedGroup[1].a, 146) - XCTAssertEqual(msg.repeatedGroup, [grp, grp2]) - } - - func testRepeatedNestedMessage() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.repeatedNestedMessage, []) - var nestedMsg = SwiftProtoTesting_TestAllTypes.NestedMessage() - nestedMsg.bb = 48 - msg.repeatedNestedMessage = [nestedMsg] - XCTAssertEqual(msg.repeatedNestedMessage.count, 1) - XCTAssertEqual(msg.repeatedNestedMessage[0].bb, 48) - XCTAssertEqual(msg.repeatedNestedMessage, [nestedMsg]) - var nestedMsg2 = SwiftProtoTesting_TestAllTypes.NestedMessage() - nestedMsg2.bb = 148 - msg.repeatedNestedMessage.append(nestedMsg2) - XCTAssertEqual(msg.repeatedNestedMessage.count, 2) - XCTAssertEqual(msg.repeatedNestedMessage[0].bb, 48) - XCTAssertEqual(msg.repeatedNestedMessage[1].bb, 148) - XCTAssertEqual(msg.repeatedNestedMessage, [nestedMsg, nestedMsg2]) - } - - func testRepeatedForeignMessage() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.repeatedForeignMessage, []) - var foreignMsg = SwiftProtoTesting_ForeignMessage() - foreignMsg.c = 49 - msg.repeatedForeignMessage = [foreignMsg] - XCTAssertEqual(msg.repeatedForeignMessage.count, 1) - XCTAssertEqual(msg.repeatedForeignMessage[0].c, 49) - XCTAssertEqual(msg.repeatedForeignMessage, [foreignMsg]) - var foreignMsg2 = SwiftProtoTesting_ForeignMessage() - foreignMsg2.c = 149 - msg.repeatedForeignMessage.append(foreignMsg2) - XCTAssertEqual(msg.repeatedForeignMessage.count, 2) - XCTAssertEqual(msg.repeatedForeignMessage[0].c, 49) - XCTAssertEqual(msg.repeatedForeignMessage[1].c, 149) - XCTAssertEqual(msg.repeatedForeignMessage, [foreignMsg, foreignMsg2]) - } - - func testRepeatedImportMessage() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.repeatedImportMessage, []) - var importedMsg = SwiftProtoTesting_Import_ImportMessage() - importedMsg.d = 50 - msg.repeatedImportMessage = [importedMsg] - XCTAssertEqual(msg.repeatedImportMessage.count, 1) - XCTAssertEqual(msg.repeatedImportMessage[0].d, 50) - XCTAssertEqual(msg.repeatedImportMessage, [importedMsg]) - var importedMsg2 = SwiftProtoTesting_Import_ImportMessage() - importedMsg2.d = 150 - msg.repeatedImportMessage.append(importedMsg2) - XCTAssertEqual(msg.repeatedImportMessage.count, 2) - XCTAssertEqual(msg.repeatedImportMessage[0].d, 50) - XCTAssertEqual(msg.repeatedImportMessage[1].d, 150) - XCTAssertEqual(msg.repeatedImportMessage, [importedMsg, importedMsg2]) - } - - func testRepeatedNestedEnum() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.repeatedNestedEnum, []) - msg.repeatedNestedEnum = [.bar] - XCTAssertEqual(msg.repeatedNestedEnum, [.bar]) - msg.repeatedNestedEnum.append(.baz) - XCTAssertEqual(msg.repeatedNestedEnum, [.bar, .baz]) - } - - func testRepeatedForeignEnum() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.repeatedForeignEnum, []) - msg.repeatedForeignEnum = [.foreignBar] - XCTAssertEqual(msg.repeatedForeignEnum, [.foreignBar]) - msg.repeatedForeignEnum.append(.foreignBaz) - XCTAssertEqual(msg.repeatedForeignEnum, [.foreignBar, .foreignBaz]) - } - - func testRepeatedImportEnum() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.repeatedImportEnum, []) - msg.repeatedImportEnum = [.importBar] - XCTAssertEqual(msg.repeatedImportEnum, [.importBar]) - msg.repeatedImportEnum.append(.importBaz) - XCTAssertEqual(msg.repeatedImportEnum, [.importBar, .importBaz]) - } - - func testRepeatedStringPiece() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.repeatedStringPiece, []) - msg.repeatedStringPiece = ["54"] - XCTAssertEqual(msg.repeatedStringPiece, ["54"]) - msg.repeatedStringPiece.append("154") - XCTAssertEqual(msg.repeatedStringPiece, ["54", "154"]) - } - - func testRepeatedCord() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.repeatedCord, []) - msg.repeatedCord = ["55"] - XCTAssertEqual(msg.repeatedCord, ["55"]) - msg.repeatedCord.append("155") - XCTAssertEqual(msg.repeatedCord, ["55", "155"]) - } - - func testRepeatedLazyMessage() { - var msg = SwiftProtoTesting_TestAllTypes() - XCTAssertEqual(msg.repeatedLazyMessage, []) - var nestedMsg = SwiftProtoTesting_TestAllTypes.NestedMessage() - nestedMsg.bb = 57 - msg.repeatedLazyMessage = [nestedMsg] - XCTAssertEqual(msg.repeatedLazyMessage.count, 1) - XCTAssertEqual(msg.repeatedLazyMessage[0].bb, 57) - XCTAssertEqual(msg.repeatedLazyMessage, [nestedMsg]) - var nestedMsg2 = SwiftProtoTesting_TestAllTypes.NestedMessage() - nestedMsg2.bb = 157 - msg.repeatedLazyMessage.append(nestedMsg2) - XCTAssertEqual(msg.repeatedLazyMessage.count, 2) - XCTAssertEqual(msg.repeatedLazyMessage[0].bb, 57) - XCTAssertEqual(msg.repeatedLazyMessage[1].bb, 157) - XCTAssertEqual(msg.repeatedLazyMessage, [nestedMsg, nestedMsg2]) - } + // Optional + + func testOptionalInt32() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.optionalInt32, 0) + XCTAssertFalse(msg.hasOptionalInt32) + msg.optionalInt32 = 1 + XCTAssertTrue(msg.hasOptionalInt32) + XCTAssertEqual(msg.optionalInt32, 1) + msg.clearOptionalInt32() + XCTAssertEqual(msg.optionalInt32, 0) + XCTAssertFalse(msg.hasOptionalInt32) + } + + func testOptionalInt64() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.optionalInt64, 0) + XCTAssertFalse(msg.hasOptionalInt64) + msg.optionalInt64 = 2 + XCTAssertTrue(msg.hasOptionalInt64) + XCTAssertEqual(msg.optionalInt64, 2) + msg.clearOptionalInt64() + XCTAssertEqual(msg.optionalInt64, 0) + XCTAssertFalse(msg.hasOptionalInt64) + } + + func testOptionalUint32() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.optionalUint32, 0) + XCTAssertFalse(msg.hasOptionalUint32) + msg.optionalUint32 = 3 + XCTAssertTrue(msg.hasOptionalUint32) + XCTAssertEqual(msg.optionalUint32, 3) + msg.clearOptionalUint32() + XCTAssertEqual(msg.optionalUint32, 0) + XCTAssertFalse(msg.hasOptionalUint32) + } + + func testOptionalUint64() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.optionalUint64, 0) + XCTAssertFalse(msg.hasOptionalUint64) + msg.optionalUint64 = 4 + XCTAssertTrue(msg.hasOptionalUint64) + XCTAssertEqual(msg.optionalUint64, 4) + msg.clearOptionalUint64() + XCTAssertEqual(msg.optionalUint64, 0) + XCTAssertFalse(msg.hasOptionalUint64) + } + + func testOptionalSint32() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.optionalSint32, 0) + XCTAssertFalse(msg.hasOptionalSint32) + msg.optionalSint32 = 5 + XCTAssertTrue(msg.hasOptionalSint32) + XCTAssertEqual(msg.optionalSint32, 5) + msg.clearOptionalSint32() + XCTAssertEqual(msg.optionalSint32, 0) + XCTAssertFalse(msg.hasOptionalSint32) + } + + func testOptionalSint64() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.optionalSint64, 0) + XCTAssertFalse(msg.hasOptionalSint64) + msg.optionalSint64 = 6 + XCTAssertTrue(msg.hasOptionalSint64) + XCTAssertEqual(msg.optionalSint64, 6) + msg.clearOptionalSint64() + XCTAssertEqual(msg.optionalSint64, 0) + XCTAssertFalse(msg.hasOptionalSint64) + } + + func testOptionalFixed32() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.optionalFixed32, 0) + XCTAssertFalse(msg.hasOptionalFixed32) + msg.optionalFixed32 = 7 + XCTAssertTrue(msg.hasOptionalFixed32) + XCTAssertEqual(msg.optionalFixed32, 7) + msg.clearOptionalFixed32() + XCTAssertEqual(msg.optionalFixed32, 0) + XCTAssertFalse(msg.hasOptionalFixed32) + } + + func testOptionalFixed64() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.optionalFixed64, 0) + XCTAssertFalse(msg.hasOptionalFixed64) + msg.optionalFixed64 = 8 + XCTAssertTrue(msg.hasOptionalFixed64) + XCTAssertEqual(msg.optionalFixed64, 8) + msg.clearOptionalFixed64() + XCTAssertEqual(msg.optionalFixed64, 0) + XCTAssertFalse(msg.hasOptionalFixed64) + } + + func testOptionalSfixed32() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.optionalSfixed32, 0) + XCTAssertFalse(msg.hasOptionalSfixed32) + msg.optionalSfixed32 = 9 + XCTAssertTrue(msg.hasOptionalSfixed32) + XCTAssertEqual(msg.optionalSfixed32, 9) + msg.clearOptionalSfixed32() + XCTAssertEqual(msg.optionalSfixed32, 0) + XCTAssertFalse(msg.hasOptionalSfixed32) + } + + func testOptionalSfixed64() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.optionalSfixed64, 0) + XCTAssertFalse(msg.hasOptionalSfixed64) + msg.optionalSfixed64 = 10 + XCTAssertTrue(msg.hasOptionalSfixed64) + XCTAssertEqual(msg.optionalSfixed64, 10) + msg.clearOptionalSfixed64() + XCTAssertEqual(msg.optionalSfixed64, 0) + XCTAssertFalse(msg.hasOptionalSfixed64) + } + + func testOptionalFloat() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.optionalFloat, 0.0) + XCTAssertFalse(msg.hasOptionalFloat) + msg.optionalFloat = 11.0 + XCTAssertTrue(msg.hasOptionalFloat) + XCTAssertEqual(msg.optionalFloat, 11.0) + msg.clearOptionalFloat() + XCTAssertEqual(msg.optionalFloat, 0.0) + XCTAssertFalse(msg.hasOptionalFloat) + } + + func testOptionalDouble() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.optionalDouble, 0.0) + XCTAssertFalse(msg.hasOptionalDouble) + msg.optionalDouble = 12.0 + XCTAssertTrue(msg.hasOptionalDouble) + XCTAssertEqual(msg.optionalDouble, 12.0) + msg.clearOptionalDouble() + XCTAssertEqual(msg.optionalDouble, 0.0) + XCTAssertFalse(msg.hasOptionalDouble) + } + + func testOptionalBool() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.optionalBool, false) + XCTAssertFalse(msg.hasOptionalBool) + msg.optionalBool = true + XCTAssertTrue(msg.hasOptionalBool) + XCTAssertEqual(msg.optionalBool, true) + msg.clearOptionalBool() + XCTAssertEqual(msg.optionalBool, false) + XCTAssertFalse(msg.hasOptionalBool) + } + + func testOptionalString() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.optionalString, "") + XCTAssertFalse(msg.hasOptionalString) + msg.optionalString = "14" + XCTAssertTrue(msg.hasOptionalString) + XCTAssertEqual(msg.optionalString, "14") + msg.clearOptionalString() + XCTAssertEqual(msg.optionalString, "") + XCTAssertFalse(msg.hasOptionalString) + } + + func testOptionalBytes() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.optionalBytes, Data()) + XCTAssertFalse(msg.hasOptionalBytes) + msg.optionalBytes = Data([15]) + XCTAssertTrue(msg.hasOptionalBytes) + XCTAssertEqual(msg.optionalBytes, Data([15])) + msg.clearOptionalBytes() + XCTAssertEqual(msg.optionalBytes, Data()) + XCTAssertFalse(msg.hasOptionalBytes) + } + + func testOptionalGroup() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.optionalGroup.a, 0) + XCTAssertFalse(msg.hasOptionalGroup) + var grp = SwiftProtoTesting_TestAllTypes.OptionalGroup() + grp.a = 16 + msg.optionalGroup = grp + XCTAssertTrue(msg.hasOptionalGroup) + XCTAssertEqual(msg.optionalGroup.a, 16) + msg.clearOptionalGroup() + XCTAssertEqual(msg.optionalGroup.a, 0) + XCTAssertFalse(msg.hasOptionalGroup) + } + + func testOptionalNestedMessage() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.optionalNestedMessage.bb, 0) + XCTAssertFalse(msg.hasOptionalNestedMessage) + var nestedMsg = SwiftProtoTesting_TestAllTypes.NestedMessage() + nestedMsg.bb = 18 + msg.optionalNestedMessage = nestedMsg + XCTAssertTrue(msg.hasOptionalNestedMessage) + XCTAssertEqual(msg.optionalNestedMessage.bb, 18) + XCTAssertEqual(msg.optionalNestedMessage, nestedMsg) + msg.clearOptionalNestedMessage() + XCTAssertEqual(msg.optionalNestedMessage.bb, 0) + XCTAssertFalse(msg.hasOptionalNestedMessage) + } + + func testOptionalForeignMessage() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.optionalForeignMessage.c, 0) + XCTAssertFalse(msg.hasOptionalForeignMessage) + var foreignMsg = SwiftProtoTesting_ForeignMessage() + foreignMsg.c = 19 + msg.optionalForeignMessage = foreignMsg + XCTAssertTrue(msg.hasOptionalForeignMessage) + XCTAssertEqual(msg.optionalForeignMessage.c, 19) + XCTAssertEqual(msg.optionalForeignMessage, foreignMsg) + msg.clearOptionalForeignMessage() + XCTAssertEqual(msg.optionalForeignMessage.c, 0) + XCTAssertFalse(msg.hasOptionalForeignMessage) + } + + func testOptionalImportMessage() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.optionalImportMessage.d, 0) + XCTAssertFalse(msg.hasOptionalImportMessage) + var importedMsg = SwiftProtoTesting_Import_ImportMessage() + importedMsg.d = 20 + msg.optionalImportMessage = importedMsg + XCTAssertTrue(msg.hasOptionalImportMessage) + XCTAssertEqual(msg.optionalImportMessage.d, 20) + XCTAssertEqual(msg.optionalImportMessage, importedMsg) + msg.clearOptionalImportMessage() + XCTAssertEqual(msg.optionalImportMessage.d, 0) + XCTAssertFalse(msg.hasOptionalImportMessage) + } + + func testOptionalNestedEnum() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.optionalNestedEnum, .foo) + XCTAssertFalse(msg.hasOptionalNestedEnum) + msg.optionalNestedEnum = .bar + XCTAssertTrue(msg.hasOptionalNestedEnum) + XCTAssertEqual(msg.optionalNestedEnum, .bar) + msg.clearOptionalNestedEnum() + XCTAssertEqual(msg.optionalNestedEnum, .foo) + XCTAssertFalse(msg.hasOptionalNestedEnum) + } + + func testOptionalForeignEnum() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.optionalForeignEnum, .foreignFoo) + XCTAssertFalse(msg.hasOptionalForeignEnum) + msg.optionalForeignEnum = .foreignBar + XCTAssertTrue(msg.hasOptionalForeignEnum) + XCTAssertEqual(msg.optionalForeignEnum, .foreignBar) + msg.clearOptionalForeignEnum() + XCTAssertEqual(msg.optionalForeignEnum, .foreignFoo) + XCTAssertFalse(msg.hasOptionalForeignEnum) + } + + func testOptionalImportEnum() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.optionalImportEnum, .importFoo) + XCTAssertFalse(msg.hasOptionalImportEnum) + msg.optionalImportEnum = .importBar + XCTAssertTrue(msg.hasOptionalImportEnum) + XCTAssertEqual(msg.optionalImportEnum, .importBar) + msg.clearOptionalImportEnum() + XCTAssertEqual(msg.optionalImportEnum, .importFoo) + XCTAssertFalse(msg.hasOptionalImportEnum) + } + + func testOptionalStringPiece() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.optionalStringPiece, "") + XCTAssertFalse(msg.hasOptionalStringPiece) + msg.optionalStringPiece = "24" + XCTAssertTrue(msg.hasOptionalStringPiece) + XCTAssertEqual(msg.optionalStringPiece, "24") + msg.clearOptionalStringPiece() + XCTAssertEqual(msg.optionalStringPiece, "") + XCTAssertFalse(msg.hasOptionalStringPiece) + } + + func testOptionalCord() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.optionalCord, "") + XCTAssertFalse(msg.hasOptionalCord) + msg.optionalCord = "25" + XCTAssertTrue(msg.hasOptionalCord) + XCTAssertEqual(msg.optionalCord, "25") + msg.clearOptionalCord() + XCTAssertEqual(msg.optionalCord, "") + XCTAssertFalse(msg.hasOptionalCord) + } + + func testOptionalPublicImportMessage() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.optionalPublicImportMessage.e, 0) + XCTAssertFalse(msg.hasOptionalPublicImportMessage) + var pubImportedMsg = SwiftProtoTesting_Import_PublicImportMessage() + pubImportedMsg.e = 26 + msg.optionalPublicImportMessage = pubImportedMsg + XCTAssertTrue(msg.hasOptionalPublicImportMessage) + XCTAssertEqual(msg.optionalPublicImportMessage.e, 26) + XCTAssertEqual(msg.optionalPublicImportMessage, pubImportedMsg) + msg.clearOptionalPublicImportMessage() + XCTAssertEqual(msg.optionalPublicImportMessage.e, 0) + XCTAssertFalse(msg.hasOptionalPublicImportMessage) + } + + func testOptionalLazyMessage() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.optionalLazyMessage.bb, 0) + XCTAssertFalse(msg.hasOptionalLazyMessage) + var nestedMsg = SwiftProtoTesting_TestAllTypes.NestedMessage() + nestedMsg.bb = 27 + msg.optionalLazyMessage = nestedMsg + XCTAssertTrue(msg.hasOptionalLazyMessage) + XCTAssertEqual(msg.optionalLazyMessage.bb, 27) + XCTAssertEqual(msg.optionalLazyMessage, nestedMsg) + msg.clearOptionalLazyMessage() + XCTAssertEqual(msg.optionalLazyMessage.bb, 0) + XCTAssertFalse(msg.hasOptionalLazyMessage) + } + + // Optional with explicit default values (non zero) + + func testDefaultInt32() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.defaultInt32, 41) + XCTAssertFalse(msg.hasDefaultInt32) + msg.defaultInt32 = 61 + XCTAssertTrue(msg.hasDefaultInt32) + XCTAssertEqual(msg.defaultInt32, 61) + msg.clearDefaultInt32() + XCTAssertEqual(msg.defaultInt32, 41) + XCTAssertFalse(msg.hasDefaultInt32) + } + + func testDefaultInt64() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.defaultInt64, 42) + XCTAssertFalse(msg.hasDefaultInt64) + msg.defaultInt64 = 62 + XCTAssertTrue(msg.hasDefaultInt64) + XCTAssertEqual(msg.defaultInt64, 62) + msg.clearDefaultInt64() + XCTAssertEqual(msg.defaultInt64, 42) + XCTAssertFalse(msg.hasDefaultInt64) + } + + func testDefaultUint32() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.defaultUint32, 43) + XCTAssertFalse(msg.hasDefaultUint32) + msg.defaultUint32 = 63 + XCTAssertTrue(msg.hasDefaultUint32) + XCTAssertEqual(msg.defaultUint32, 63) + msg.clearDefaultUint32() + XCTAssertEqual(msg.defaultUint32, 43) + XCTAssertFalse(msg.hasDefaultUint32) + } + + func testDefaultUint64() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.defaultUint64, 44) + XCTAssertFalse(msg.hasDefaultUint64) + msg.defaultUint64 = 64 + XCTAssertTrue(msg.hasDefaultUint64) + XCTAssertEqual(msg.defaultUint64, 64) + msg.clearDefaultUint64() + XCTAssertEqual(msg.defaultUint64, 44) + XCTAssertFalse(msg.hasDefaultUint64) + } + + func testDefaultSint32() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.defaultSint32, -45) + XCTAssertFalse(msg.hasDefaultSint32) + msg.defaultSint32 = 65 + XCTAssertTrue(msg.hasDefaultSint32) + XCTAssertEqual(msg.defaultSint32, 65) + msg.clearDefaultSint32() + XCTAssertEqual(msg.defaultSint32, -45) + XCTAssertFalse(msg.hasDefaultSint32) + } + + func testDefaultSint64() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.defaultSint64, 46) + XCTAssertFalse(msg.hasDefaultSint64) + msg.defaultSint64 = 66 + XCTAssertTrue(msg.hasDefaultSint64) + XCTAssertEqual(msg.defaultSint64, 66) + msg.clearDefaultSint64() + XCTAssertEqual(msg.defaultSint64, 46) + XCTAssertFalse(msg.hasDefaultSint64) + } + + func testDefaultFixed32() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.defaultFixed32, 47) + XCTAssertFalse(msg.hasDefaultFixed32) + msg.defaultFixed32 = 67 + XCTAssertTrue(msg.hasDefaultFixed32) + XCTAssertEqual(msg.defaultFixed32, 67) + msg.clearDefaultFixed32() + XCTAssertEqual(msg.defaultFixed32, 47) + XCTAssertFalse(msg.hasDefaultFixed32) + } + + func testDefaultFixed64() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.defaultFixed64, 48) + XCTAssertFalse(msg.hasDefaultFixed64) + msg.defaultFixed64 = 68 + XCTAssertTrue(msg.hasDefaultFixed64) + XCTAssertEqual(msg.defaultFixed64, 68) + msg.clearDefaultFixed64() + XCTAssertEqual(msg.defaultFixed64, 48) + XCTAssertFalse(msg.hasDefaultFixed64) + } + + func testDefaultSfixed32() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.defaultSfixed32, 49) + XCTAssertFalse(msg.hasDefaultSfixed32) + msg.defaultSfixed32 = 69 + XCTAssertTrue(msg.hasDefaultSfixed32) + XCTAssertEqual(msg.defaultSfixed32, 69) + msg.clearDefaultSfixed32() + XCTAssertEqual(msg.defaultSfixed32, 49) + XCTAssertFalse(msg.hasDefaultSfixed32) + } + + func testDefaultSfixed64() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.defaultSfixed64, -50) + XCTAssertFalse(msg.hasDefaultSfixed64) + msg.defaultSfixed64 = 70 + XCTAssertTrue(msg.hasDefaultSfixed64) + XCTAssertEqual(msg.defaultSfixed64, 70) + msg.clearDefaultSfixed64() + XCTAssertEqual(msg.defaultSfixed64, -50) + XCTAssertFalse(msg.hasDefaultSfixed64) + } + + func testDefaultFloat() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.defaultFloat, 51.5) + XCTAssertFalse(msg.hasDefaultFloat) + msg.defaultFloat = 71 + XCTAssertTrue(msg.hasDefaultFloat) + XCTAssertEqual(msg.defaultFloat, 71) + msg.clearDefaultFloat() + XCTAssertEqual(msg.defaultFloat, 51.5) + XCTAssertFalse(msg.hasDefaultFloat) + } + + func testDefaultDouble() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.defaultDouble, 52e3) + XCTAssertFalse(msg.hasDefaultDouble) + msg.defaultDouble = 72 + XCTAssertTrue(msg.hasDefaultDouble) + XCTAssertEqual(msg.defaultDouble, 72) + msg.clearDefaultDouble() + XCTAssertEqual(msg.defaultDouble, 52e3) + XCTAssertFalse(msg.hasDefaultDouble) + } + + func testDefaultBool() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.defaultBool, true) + XCTAssertFalse(msg.hasDefaultBool) + msg.defaultBool = false + XCTAssertTrue(msg.hasDefaultBool) + XCTAssertEqual(msg.defaultBool, false) + msg.clearDefaultBool() + XCTAssertEqual(msg.defaultBool, true) + XCTAssertFalse(msg.hasDefaultBool) + } + + func testDefaultString() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.defaultString, "hello") + XCTAssertFalse(msg.hasDefaultString) + msg.defaultString = "74" + XCTAssertTrue(msg.hasDefaultString) + XCTAssertEqual(msg.defaultString, "74") + msg.clearDefaultString() + XCTAssertEqual(msg.defaultString, "hello") + XCTAssertFalse(msg.hasDefaultString) + } + + func testDefaultBytes() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.defaultBytes, "world".data(using: .utf8)) + XCTAssertFalse(msg.hasDefaultBytes) + msg.defaultBytes = Data([75]) + XCTAssertTrue(msg.hasDefaultBytes) + XCTAssertEqual(msg.defaultBytes, Data([75])) + msg.clearDefaultBytes() + XCTAssertEqual(msg.defaultBytes, "world".data(using: .utf8)) + XCTAssertFalse(msg.hasDefaultBytes) + } + + func testDefaultNestedEnum() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.defaultNestedEnum, .bar) + XCTAssertFalse(msg.hasDefaultNestedEnum) + msg.defaultNestedEnum = .baz + XCTAssertTrue(msg.hasDefaultNestedEnum) + XCTAssertEqual(msg.defaultNestedEnum, .baz) + msg.clearDefaultNestedEnum() + XCTAssertEqual(msg.defaultNestedEnum, .bar) + XCTAssertFalse(msg.hasDefaultNestedEnum) + } + + func testDefaultForeignEnum() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.defaultForeignEnum, .foreignBar) + XCTAssertFalse(msg.hasDefaultForeignEnum) + msg.defaultForeignEnum = .foreignBaz + XCTAssertTrue(msg.hasDefaultForeignEnum) + XCTAssertEqual(msg.defaultForeignEnum, .foreignBaz) + msg.clearDefaultForeignEnum() + XCTAssertEqual(msg.defaultForeignEnum, .foreignBar) + XCTAssertFalse(msg.hasDefaultForeignEnum) + } + + func testDefaultImportEnum() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.defaultImportEnum, .importBar) + XCTAssertFalse(msg.hasDefaultImportEnum) + msg.defaultImportEnum = .importBaz + XCTAssertTrue(msg.hasDefaultImportEnum) + XCTAssertEqual(msg.defaultImportEnum, .importBaz) + msg.clearDefaultImportEnum() + XCTAssertEqual(msg.defaultImportEnum, .importBar) + XCTAssertFalse(msg.hasDefaultImportEnum) + } + + func testDefaultStringPiece() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.defaultStringPiece, "abc") + XCTAssertFalse(msg.hasDefaultStringPiece) + msg.defaultStringPiece = "84" + XCTAssertTrue(msg.hasDefaultStringPiece) + XCTAssertEqual(msg.defaultStringPiece, "84") + msg.clearDefaultStringPiece() + XCTAssertEqual(msg.defaultStringPiece, "abc") + XCTAssertFalse(msg.hasDefaultStringPiece) + } + + func testDefaultCord() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.defaultCord, "123") + XCTAssertFalse(msg.hasDefaultCord) + msg.defaultCord = "85" + XCTAssertTrue(msg.hasDefaultCord) + XCTAssertEqual(msg.defaultCord, "85") + msg.clearDefaultCord() + XCTAssertEqual(msg.defaultCord, "123") + XCTAssertFalse(msg.hasDefaultCord) + } + + // Repeated + + func testRepeatedInt32() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.repeatedInt32, []) + msg.repeatedInt32 = [31] + XCTAssertEqual(msg.repeatedInt32, [31]) + msg.repeatedInt32.append(131) + XCTAssertEqual(msg.repeatedInt32, [31, 131]) + } + + func testRepeatedInt64() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.repeatedInt64, []) + msg.repeatedInt64 = [32] + XCTAssertEqual(msg.repeatedInt64, [32]) + msg.repeatedInt64.append(132) + XCTAssertEqual(msg.repeatedInt64, [32, 132]) + } + + func testRepeatedUint32() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.repeatedUint32, []) + msg.repeatedUint32 = [33] + XCTAssertEqual(msg.repeatedUint32, [33]) + msg.repeatedUint32.append(133) + XCTAssertEqual(msg.repeatedUint32, [33, 133]) + } + + func testRepeatedUint64() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.repeatedUint64, []) + msg.repeatedUint64 = [34] + XCTAssertEqual(msg.repeatedUint64, [34]) + msg.repeatedUint64.append(134) + XCTAssertEqual(msg.repeatedUint64, [34, 134]) + } + + func testRepeatedSint32() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.repeatedSint32, []) + msg.repeatedSint32 = [35] + XCTAssertEqual(msg.repeatedSint32, [35]) + msg.repeatedSint32.append(135) + XCTAssertEqual(msg.repeatedSint32, [35, 135]) + } + + func testRepeatedSint64() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.repeatedSint64, []) + msg.repeatedSint64 = [36] + XCTAssertEqual(msg.repeatedSint64, [36]) + msg.repeatedSint64.append(136) + XCTAssertEqual(msg.repeatedSint64, [36, 136]) + } + + func testRepeatedFixed32() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.repeatedFixed32, []) + msg.repeatedFixed32 = [37] + XCTAssertEqual(msg.repeatedFixed32, [37]) + msg.repeatedFixed32.append(137) + XCTAssertEqual(msg.repeatedFixed32, [37, 137]) + } + + func testRepeatedFixed64() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.repeatedFixed64, []) + msg.repeatedFixed64 = [38] + XCTAssertEqual(msg.repeatedFixed64, [38]) + msg.repeatedFixed64.append(138) + XCTAssertEqual(msg.repeatedFixed64, [38, 138]) + } + + func testRepeatedSfixed32() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.repeatedSfixed32, []) + msg.repeatedSfixed32 = [39] + XCTAssertEqual(msg.repeatedSfixed32, [39]) + msg.repeatedSfixed32.append(139) + XCTAssertEqual(msg.repeatedSfixed32, [39, 139]) + } + + func testRepeatedSfixed64() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.repeatedSfixed64, []) + msg.repeatedSfixed64 = [40] + XCTAssertEqual(msg.repeatedSfixed64, [40]) + msg.repeatedSfixed64.append(140) + XCTAssertEqual(msg.repeatedSfixed64, [40, 140]) + } + + func testRepeatedFloat() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.repeatedFloat, []) + msg.repeatedFloat = [41.0] + XCTAssertEqual(msg.repeatedFloat, [41.0]) + msg.repeatedFloat.append(141.0) + XCTAssertEqual(msg.repeatedFloat, [41.0, 141.0]) + } + + func testRepeatedDouble() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.repeatedDouble, []) + msg.repeatedDouble = [42.0] + XCTAssertEqual(msg.repeatedDouble, [42.0]) + msg.repeatedDouble.append(142.0) + XCTAssertEqual(msg.repeatedDouble, [42.0, 142.0]) + } + + func testRepeatedBool() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.repeatedBool, []) + msg.repeatedBool = [true] + XCTAssertEqual(msg.repeatedBool, [true]) + msg.repeatedBool.append(false) + XCTAssertEqual(msg.repeatedBool, [true, false]) + } + + func testRepeatedString() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.repeatedString, []) + msg.repeatedString = ["44"] + XCTAssertEqual(msg.repeatedString, ["44"]) + msg.repeatedString.append("144") + XCTAssertEqual(msg.repeatedString, ["44", "144"]) + } + + func testRepeatedBytes() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.repeatedBytes, []) + msg.repeatedBytes = [Data([45])] + XCTAssertEqual(msg.repeatedBytes, [Data([45])]) + msg.repeatedBytes.append(Data([145])) + XCTAssertEqual(msg.repeatedBytes, [Data([45]), Data([145])]) + } + + func testRepeatedGroup() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.repeatedGroup, []) + var grp = SwiftProtoTesting_TestAllTypes.RepeatedGroup() + grp.a = 46 + msg.repeatedGroup = [grp] + XCTAssertEqual(msg.repeatedGroup.count, 1) + XCTAssertEqual(msg.repeatedGroup[0].a, 46) + XCTAssertEqual(msg.repeatedGroup, [grp]) + var grp2 = SwiftProtoTesting_TestAllTypes.RepeatedGroup() + grp2.a = 146 + msg.repeatedGroup.append(grp2) + XCTAssertEqual(msg.repeatedGroup.count, 2) + XCTAssertEqual(msg.repeatedGroup[0].a, 46) + XCTAssertEqual(msg.repeatedGroup[1].a, 146) + XCTAssertEqual(msg.repeatedGroup, [grp, grp2]) + } + + func testRepeatedNestedMessage() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.repeatedNestedMessage, []) + var nestedMsg = SwiftProtoTesting_TestAllTypes.NestedMessage() + nestedMsg.bb = 48 + msg.repeatedNestedMessage = [nestedMsg] + XCTAssertEqual(msg.repeatedNestedMessage.count, 1) + XCTAssertEqual(msg.repeatedNestedMessage[0].bb, 48) + XCTAssertEqual(msg.repeatedNestedMessage, [nestedMsg]) + var nestedMsg2 = SwiftProtoTesting_TestAllTypes.NestedMessage() + nestedMsg2.bb = 148 + msg.repeatedNestedMessage.append(nestedMsg2) + XCTAssertEqual(msg.repeatedNestedMessage.count, 2) + XCTAssertEqual(msg.repeatedNestedMessage[0].bb, 48) + XCTAssertEqual(msg.repeatedNestedMessage[1].bb, 148) + XCTAssertEqual(msg.repeatedNestedMessage, [nestedMsg, nestedMsg2]) + } + + func testRepeatedForeignMessage() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.repeatedForeignMessage, []) + var foreignMsg = SwiftProtoTesting_ForeignMessage() + foreignMsg.c = 49 + msg.repeatedForeignMessage = [foreignMsg] + XCTAssertEqual(msg.repeatedForeignMessage.count, 1) + XCTAssertEqual(msg.repeatedForeignMessage[0].c, 49) + XCTAssertEqual(msg.repeatedForeignMessage, [foreignMsg]) + var foreignMsg2 = SwiftProtoTesting_ForeignMessage() + foreignMsg2.c = 149 + msg.repeatedForeignMessage.append(foreignMsg2) + XCTAssertEqual(msg.repeatedForeignMessage.count, 2) + XCTAssertEqual(msg.repeatedForeignMessage[0].c, 49) + XCTAssertEqual(msg.repeatedForeignMessage[1].c, 149) + XCTAssertEqual(msg.repeatedForeignMessage, [foreignMsg, foreignMsg2]) + } + + func testRepeatedImportMessage() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.repeatedImportMessage, []) + var importedMsg = SwiftProtoTesting_Import_ImportMessage() + importedMsg.d = 50 + msg.repeatedImportMessage = [importedMsg] + XCTAssertEqual(msg.repeatedImportMessage.count, 1) + XCTAssertEqual(msg.repeatedImportMessage[0].d, 50) + XCTAssertEqual(msg.repeatedImportMessage, [importedMsg]) + var importedMsg2 = SwiftProtoTesting_Import_ImportMessage() + importedMsg2.d = 150 + msg.repeatedImportMessage.append(importedMsg2) + XCTAssertEqual(msg.repeatedImportMessage.count, 2) + XCTAssertEqual(msg.repeatedImportMessage[0].d, 50) + XCTAssertEqual(msg.repeatedImportMessage[1].d, 150) + XCTAssertEqual(msg.repeatedImportMessage, [importedMsg, importedMsg2]) + } + + func testRepeatedNestedEnum() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.repeatedNestedEnum, []) + msg.repeatedNestedEnum = [.bar] + XCTAssertEqual(msg.repeatedNestedEnum, [.bar]) + msg.repeatedNestedEnum.append(.baz) + XCTAssertEqual(msg.repeatedNestedEnum, [.bar, .baz]) + } + + func testRepeatedForeignEnum() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.repeatedForeignEnum, []) + msg.repeatedForeignEnum = [.foreignBar] + XCTAssertEqual(msg.repeatedForeignEnum, [.foreignBar]) + msg.repeatedForeignEnum.append(.foreignBaz) + XCTAssertEqual(msg.repeatedForeignEnum, [.foreignBar, .foreignBaz]) + } + + func testRepeatedImportEnum() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.repeatedImportEnum, []) + msg.repeatedImportEnum = [.importBar] + XCTAssertEqual(msg.repeatedImportEnum, [.importBar]) + msg.repeatedImportEnum.append(.importBaz) + XCTAssertEqual(msg.repeatedImportEnum, [.importBar, .importBaz]) + } + + func testRepeatedStringPiece() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.repeatedStringPiece, []) + msg.repeatedStringPiece = ["54"] + XCTAssertEqual(msg.repeatedStringPiece, ["54"]) + msg.repeatedStringPiece.append("154") + XCTAssertEqual(msg.repeatedStringPiece, ["54", "154"]) + } + + func testRepeatedCord() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.repeatedCord, []) + msg.repeatedCord = ["55"] + XCTAssertEqual(msg.repeatedCord, ["55"]) + msg.repeatedCord.append("155") + XCTAssertEqual(msg.repeatedCord, ["55", "155"]) + } + + func testRepeatedLazyMessage() { + var msg = SwiftProtoTesting_TestAllTypes() + XCTAssertEqual(msg.repeatedLazyMessage, []) + var nestedMsg = SwiftProtoTesting_TestAllTypes.NestedMessage() + nestedMsg.bb = 57 + msg.repeatedLazyMessage = [nestedMsg] + XCTAssertEqual(msg.repeatedLazyMessage.count, 1) + XCTAssertEqual(msg.repeatedLazyMessage[0].bb, 57) + XCTAssertEqual(msg.repeatedLazyMessage, [nestedMsg]) + var nestedMsg2 = SwiftProtoTesting_TestAllTypes.NestedMessage() + nestedMsg2.bb = 157 + msg.repeatedLazyMessage.append(nestedMsg2) + XCTAssertEqual(msg.repeatedLazyMessage.count, 2) + XCTAssertEqual(msg.repeatedLazyMessage[0].bb, 57) + XCTAssertEqual(msg.repeatedLazyMessage[1].bb, 157) + XCTAssertEqual(msg.repeatedLazyMessage, [nestedMsg, nestedMsg2]) + } } diff --git a/Tests/SwiftProtobufTests/Test_BasicFields_Access_Proto3.swift b/Tests/SwiftProtobufTests/Test_BasicFields_Access_Proto3.swift index b5d49b6aa..7563615cf 100644 --- a/Tests/SwiftProtobufTests/Test_BasicFields_Access_Proto3.swift +++ b/Tests/SwiftProtobufTests/Test_BasicFields_Access_Proto3.swift @@ -12,8 +12,8 @@ /// // ----------------------------------------------------------------------------- -import XCTest import Foundation +import XCTest // NOTE: The generator changes what is generated based on the number/types // of fields (using a nested storage class or not), to be completel, all @@ -22,374 +22,374 @@ import Foundation final class Test_BasicFields_Access_Proto3: XCTestCase { - // Optional - - func testOptionalInt32() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.optionalInt32, 0) - msg.optionalInt32 = 1 - XCTAssertEqual(msg.optionalInt32, 1) - } - - func testOptionalInt64() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.optionalInt64, 0) - msg.optionalInt64 = 2 - XCTAssertEqual(msg.optionalInt64, 2) - } - - func testOptionalUint32() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.optionalUint32, 0) - msg.optionalUint32 = 3 - XCTAssertEqual(msg.optionalUint32, 3) - } - - func testOptionalUint64() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.optionalUint64, 0) - msg.optionalUint64 = 4 - XCTAssertEqual(msg.optionalUint64, 4) - } - - func testOptionalSint32() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.optionalSint32, 0) - msg.optionalSint32 = 5 - XCTAssertEqual(msg.optionalSint32, 5) - } - - func testOptionalSint64() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.optionalSint64, 0) - msg.optionalSint64 = 6 - XCTAssertEqual(msg.optionalSint64, 6) - } - - func testOptionalFixed32() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.optionalFixed32, 0) - msg.optionalFixed32 = 7 - XCTAssertEqual(msg.optionalFixed32, 7) - } - - func testOptionalFixed64() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.optionalFixed64, 0) - msg.optionalFixed64 = 8 - XCTAssertEqual(msg.optionalFixed64, 8) - } - - func testOptionalSfixed32() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.optionalSfixed32, 0) - msg.optionalSfixed32 = 9 - XCTAssertEqual(msg.optionalSfixed32, 9) - } - - func testOptionalSfixed64() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.optionalSfixed64, 0) - msg.optionalSfixed64 = 10 - XCTAssertEqual(msg.optionalSfixed64, 10) - } - - func testOptionalFloat() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.optionalFloat, 0.0) - msg.optionalFloat = 11.0 - XCTAssertEqual(msg.optionalFloat, 11.0) - } - - func testOptionalDouble() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.optionalDouble, 0.0) - msg.optionalDouble = 12.0 - XCTAssertEqual(msg.optionalDouble, 12.0) - } - - func testOptionalBool() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.optionalBool, false) - msg.optionalBool = true - XCTAssertEqual(msg.optionalBool, true) - } - - func testOptionalString() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.optionalString, "") - msg.optionalString = "14" - XCTAssertEqual(msg.optionalString, "14") - } - - func testOptionalBytes() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.optionalBytes, Data()) - msg.optionalBytes = Data([15]) - XCTAssertEqual(msg.optionalBytes, Data([15])) - } - - func testOptionalNestedMessage() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.optionalNestedMessage.bb, 0) - var nestedMsg = SwiftProtoTesting_Proto3_TestAllTypes.NestedMessage() - nestedMsg.bb = 18 - msg.optionalNestedMessage = nestedMsg - XCTAssertEqual(msg.optionalNestedMessage.bb, 18) - XCTAssertEqual(msg.optionalNestedMessage, nestedMsg) - } - - func testOptionalForeignMessage() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.optionalForeignMessage.c, 0) - var foreignMsg = SwiftProtoTesting_Proto3_ForeignMessage() - foreignMsg.c = 19 - msg.optionalForeignMessage = foreignMsg - XCTAssertEqual(msg.optionalForeignMessage.c, 19) - XCTAssertEqual(msg.optionalForeignMessage, foreignMsg) - } - - func testOptionalImportMessage() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.optionalImportMessage.d, 0) - var importedMsg = SwiftProtoTesting_Import_ImportMessage() - importedMsg.d = 20 - msg.optionalImportMessage = importedMsg - XCTAssertEqual(msg.optionalImportMessage.d, 20) - XCTAssertEqual(msg.optionalImportMessage, importedMsg) - } - - func testOptionalNestedEnum() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.optionalNestedEnum, .zero) - msg.optionalNestedEnum = .bar - XCTAssertEqual(msg.optionalNestedEnum, .bar) - } - - func testOptionalForeignEnum() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.optionalForeignEnum, .foreignZero) - msg.optionalForeignEnum = .foreignBar - XCTAssertEqual(msg.optionalForeignEnum, .foreignBar) - } - - func testOptionalPublicImportMessage() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.optionalPublicImportMessage.e, 0) - var pubImportedMsg = SwiftProtoTesting_Import_PublicImportMessage() - pubImportedMsg.e = 26 - msg.optionalPublicImportMessage = pubImportedMsg - XCTAssertEqual(msg.optionalPublicImportMessage.e, 26) - XCTAssertEqual(msg.optionalPublicImportMessage, pubImportedMsg) - } - - // Repeated - - func testRepeatedInt32() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.repeatedInt32, []) - msg.repeatedInt32 = [31] - XCTAssertEqual(msg.repeatedInt32, [31]) - msg.repeatedInt32.append(131) - XCTAssertEqual(msg.repeatedInt32, [31, 131]) - } - - func testRepeatedInt64() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.repeatedInt64, []) - msg.repeatedInt64 = [32] - XCTAssertEqual(msg.repeatedInt64, [32]) - msg.repeatedInt64.append(132) - XCTAssertEqual(msg.repeatedInt64, [32, 132]) - } - - func testRepeatedUint32() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.repeatedUint32, []) - msg.repeatedUint32 = [33] - XCTAssertEqual(msg.repeatedUint32, [33]) - msg.repeatedUint32.append(133) - XCTAssertEqual(msg.repeatedUint32, [33, 133]) - } - - func testRepeatedUint64() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.repeatedUint64, []) - msg.repeatedUint64 = [34] - XCTAssertEqual(msg.repeatedUint64, [34]) - msg.repeatedUint64.append(134) - XCTAssertEqual(msg.repeatedUint64, [34, 134]) - } - - func testRepeatedSint32() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.repeatedSint32, []) - msg.repeatedSint32 = [35] - XCTAssertEqual(msg.repeatedSint32, [35]) - msg.repeatedSint32.append(135) - XCTAssertEqual(msg.repeatedSint32, [35, 135]) - } - - func testRepeatedSint64() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.repeatedSint64, []) - msg.repeatedSint64 = [36] - XCTAssertEqual(msg.repeatedSint64, [36]) - msg.repeatedSint64.append(136) - XCTAssertEqual(msg.repeatedSint64, [36, 136]) - } - - func testRepeatedFixed32() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.repeatedFixed32, []) - msg.repeatedFixed32 = [37] - XCTAssertEqual(msg.repeatedFixed32, [37]) - msg.repeatedFixed32.append(137) - XCTAssertEqual(msg.repeatedFixed32, [37, 137]) - } - - func testRepeatedFixed64() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.repeatedFixed64, []) - msg.repeatedFixed64 = [38] - XCTAssertEqual(msg.repeatedFixed64, [38]) - msg.repeatedFixed64.append(138) - XCTAssertEqual(msg.repeatedFixed64, [38, 138]) - } - - func testRepeatedSfixed32() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.repeatedSfixed32, []) - msg.repeatedSfixed32 = [39] - XCTAssertEqual(msg.repeatedSfixed32, [39]) - msg.repeatedSfixed32.append(139) - XCTAssertEqual(msg.repeatedSfixed32, [39, 139]) - } - - func testRepeatedSfixed64() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.repeatedSfixed64, []) - msg.repeatedSfixed64 = [40] - XCTAssertEqual(msg.repeatedSfixed64, [40]) - msg.repeatedSfixed64.append(140) - XCTAssertEqual(msg.repeatedSfixed64, [40, 140]) - } - - func testRepeatedFloat() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.repeatedFloat, []) - msg.repeatedFloat = [41.0] - XCTAssertEqual(msg.repeatedFloat, [41.0]) - msg.repeatedFloat.append(141.0) - XCTAssertEqual(msg.repeatedFloat, [41.0, 141.0]) - } - - func testRepeatedDouble() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.repeatedDouble, []) - msg.repeatedDouble = [42.0] - XCTAssertEqual(msg.repeatedDouble, [42.0]) - msg.repeatedDouble.append(142.0) - XCTAssertEqual(msg.repeatedDouble, [42.0, 142.0]) - } - - func testRepeatedBool() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.repeatedBool, []) - msg.repeatedBool = [true] - XCTAssertEqual(msg.repeatedBool, [true]) - msg.repeatedBool.append(false) - XCTAssertEqual(msg.repeatedBool, [true, false]) - } - - func testRepeatedString() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.repeatedString, []) - msg.repeatedString = ["44"] - XCTAssertEqual(msg.repeatedString, ["44"]) - msg.repeatedString.append("144") - XCTAssertEqual(msg.repeatedString, ["44", "144"]) - } - - func testRepeatedBytes() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.repeatedBytes, []) - msg.repeatedBytes = [Data([45])] - XCTAssertEqual(msg.repeatedBytes, [Data([45])]) - msg.repeatedBytes.append(Data([145])) - XCTAssertEqual(msg.repeatedBytes, [Data([45]), Data([145])]) - } - - func testRepeatedNestedMessage() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.repeatedNestedMessage, []) - var nestedMsg = SwiftProtoTesting_Proto3_TestAllTypes.NestedMessage() - nestedMsg.bb = 48 - msg.repeatedNestedMessage = [nestedMsg] - XCTAssertEqual(msg.repeatedNestedMessage.count, 1) - XCTAssertEqual(msg.repeatedNestedMessage[0].bb, 48) - XCTAssertEqual(msg.repeatedNestedMessage, [nestedMsg]) - var nestedMsg2 = SwiftProtoTesting_Proto3_TestAllTypes.NestedMessage() - nestedMsg2.bb = 148 - msg.repeatedNestedMessage.append(nestedMsg2) - XCTAssertEqual(msg.repeatedNestedMessage.count, 2) - XCTAssertEqual(msg.repeatedNestedMessage[0].bb, 48) - XCTAssertEqual(msg.repeatedNestedMessage[1].bb, 148) - XCTAssertEqual(msg.repeatedNestedMessage, [nestedMsg, nestedMsg2]) - } - - func testRepeatedForeignMessage() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.repeatedForeignMessage, []) - var foreignMsg = SwiftProtoTesting_Proto3_ForeignMessage() - foreignMsg.c = 49 - msg.repeatedForeignMessage = [foreignMsg] - XCTAssertEqual(msg.repeatedForeignMessage.count, 1) - XCTAssertEqual(msg.repeatedForeignMessage[0].c, 49) - XCTAssertEqual(msg.repeatedForeignMessage, [foreignMsg]) - var foreignMsg2 = SwiftProtoTesting_Proto3_ForeignMessage() - foreignMsg2.c = 149 - msg.repeatedForeignMessage.append(foreignMsg2) - XCTAssertEqual(msg.repeatedForeignMessage.count, 2) - XCTAssertEqual(msg.repeatedForeignMessage[0].c, 49) - XCTAssertEqual(msg.repeatedForeignMessage[1].c, 149) - XCTAssertEqual(msg.repeatedForeignMessage, [foreignMsg, foreignMsg2]) - } - - func testRepeatedImportMessage() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.repeatedImportMessage, []) - var importedMsg = SwiftProtoTesting_Import_ImportMessage() - importedMsg.d = 50 - msg.repeatedImportMessage = [importedMsg] - XCTAssertEqual(msg.repeatedImportMessage.count, 1) - XCTAssertEqual(msg.repeatedImportMessage[0].d, 50) - XCTAssertEqual(msg.repeatedImportMessage, [importedMsg]) - var importedMsg2 = SwiftProtoTesting_Import_ImportMessage() - importedMsg2.d = 150 - msg.repeatedImportMessage.append(importedMsg2) - XCTAssertEqual(msg.repeatedImportMessage.count, 2) - XCTAssertEqual(msg.repeatedImportMessage[0].d, 50) - XCTAssertEqual(msg.repeatedImportMessage[1].d, 150) - XCTAssertEqual(msg.repeatedImportMessage, [importedMsg, importedMsg2]) - } - - func testRepeatedNestedEnum() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.repeatedNestedEnum, []) - msg.repeatedNestedEnum = [.bar] - XCTAssertEqual(msg.repeatedNestedEnum, [.bar]) - msg.repeatedNestedEnum.append(.baz) - XCTAssertEqual(msg.repeatedNestedEnum, [.bar, .baz]) - } - - func testRepeatedForeignEnum() { - var msg = SwiftProtoTesting_Proto3_TestAllTypes() - XCTAssertEqual(msg.repeatedForeignEnum, []) - msg.repeatedForeignEnum = [.foreignBar] - XCTAssertEqual(msg.repeatedForeignEnum, [.foreignBar]) - msg.repeatedForeignEnum.append(.foreignBaz) - XCTAssertEqual(msg.repeatedForeignEnum, [.foreignBar, .foreignBaz]) - } + // Optional + + func testOptionalInt32() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.optionalInt32, 0) + msg.optionalInt32 = 1 + XCTAssertEqual(msg.optionalInt32, 1) + } + + func testOptionalInt64() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.optionalInt64, 0) + msg.optionalInt64 = 2 + XCTAssertEqual(msg.optionalInt64, 2) + } + + func testOptionalUint32() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.optionalUint32, 0) + msg.optionalUint32 = 3 + XCTAssertEqual(msg.optionalUint32, 3) + } + + func testOptionalUint64() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.optionalUint64, 0) + msg.optionalUint64 = 4 + XCTAssertEqual(msg.optionalUint64, 4) + } + + func testOptionalSint32() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.optionalSint32, 0) + msg.optionalSint32 = 5 + XCTAssertEqual(msg.optionalSint32, 5) + } + + func testOptionalSint64() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.optionalSint64, 0) + msg.optionalSint64 = 6 + XCTAssertEqual(msg.optionalSint64, 6) + } + + func testOptionalFixed32() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.optionalFixed32, 0) + msg.optionalFixed32 = 7 + XCTAssertEqual(msg.optionalFixed32, 7) + } + + func testOptionalFixed64() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.optionalFixed64, 0) + msg.optionalFixed64 = 8 + XCTAssertEqual(msg.optionalFixed64, 8) + } + + func testOptionalSfixed32() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.optionalSfixed32, 0) + msg.optionalSfixed32 = 9 + XCTAssertEqual(msg.optionalSfixed32, 9) + } + + func testOptionalSfixed64() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.optionalSfixed64, 0) + msg.optionalSfixed64 = 10 + XCTAssertEqual(msg.optionalSfixed64, 10) + } + + func testOptionalFloat() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.optionalFloat, 0.0) + msg.optionalFloat = 11.0 + XCTAssertEqual(msg.optionalFloat, 11.0) + } + + func testOptionalDouble() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.optionalDouble, 0.0) + msg.optionalDouble = 12.0 + XCTAssertEqual(msg.optionalDouble, 12.0) + } + + func testOptionalBool() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.optionalBool, false) + msg.optionalBool = true + XCTAssertEqual(msg.optionalBool, true) + } + + func testOptionalString() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.optionalString, "") + msg.optionalString = "14" + XCTAssertEqual(msg.optionalString, "14") + } + + func testOptionalBytes() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.optionalBytes, Data()) + msg.optionalBytes = Data([15]) + XCTAssertEqual(msg.optionalBytes, Data([15])) + } + + func testOptionalNestedMessage() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.optionalNestedMessage.bb, 0) + var nestedMsg = SwiftProtoTesting_Proto3_TestAllTypes.NestedMessage() + nestedMsg.bb = 18 + msg.optionalNestedMessage = nestedMsg + XCTAssertEqual(msg.optionalNestedMessage.bb, 18) + XCTAssertEqual(msg.optionalNestedMessage, nestedMsg) + } + + func testOptionalForeignMessage() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.optionalForeignMessage.c, 0) + var foreignMsg = SwiftProtoTesting_Proto3_ForeignMessage() + foreignMsg.c = 19 + msg.optionalForeignMessage = foreignMsg + XCTAssertEqual(msg.optionalForeignMessage.c, 19) + XCTAssertEqual(msg.optionalForeignMessage, foreignMsg) + } + + func testOptionalImportMessage() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.optionalImportMessage.d, 0) + var importedMsg = SwiftProtoTesting_Import_ImportMessage() + importedMsg.d = 20 + msg.optionalImportMessage = importedMsg + XCTAssertEqual(msg.optionalImportMessage.d, 20) + XCTAssertEqual(msg.optionalImportMessage, importedMsg) + } + + func testOptionalNestedEnum() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.optionalNestedEnum, .zero) + msg.optionalNestedEnum = .bar + XCTAssertEqual(msg.optionalNestedEnum, .bar) + } + + func testOptionalForeignEnum() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.optionalForeignEnum, .foreignZero) + msg.optionalForeignEnum = .foreignBar + XCTAssertEqual(msg.optionalForeignEnum, .foreignBar) + } + + func testOptionalPublicImportMessage() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.optionalPublicImportMessage.e, 0) + var pubImportedMsg = SwiftProtoTesting_Import_PublicImportMessage() + pubImportedMsg.e = 26 + msg.optionalPublicImportMessage = pubImportedMsg + XCTAssertEqual(msg.optionalPublicImportMessage.e, 26) + XCTAssertEqual(msg.optionalPublicImportMessage, pubImportedMsg) + } + + // Repeated + + func testRepeatedInt32() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.repeatedInt32, []) + msg.repeatedInt32 = [31] + XCTAssertEqual(msg.repeatedInt32, [31]) + msg.repeatedInt32.append(131) + XCTAssertEqual(msg.repeatedInt32, [31, 131]) + } + + func testRepeatedInt64() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.repeatedInt64, []) + msg.repeatedInt64 = [32] + XCTAssertEqual(msg.repeatedInt64, [32]) + msg.repeatedInt64.append(132) + XCTAssertEqual(msg.repeatedInt64, [32, 132]) + } + + func testRepeatedUint32() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.repeatedUint32, []) + msg.repeatedUint32 = [33] + XCTAssertEqual(msg.repeatedUint32, [33]) + msg.repeatedUint32.append(133) + XCTAssertEqual(msg.repeatedUint32, [33, 133]) + } + + func testRepeatedUint64() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.repeatedUint64, []) + msg.repeatedUint64 = [34] + XCTAssertEqual(msg.repeatedUint64, [34]) + msg.repeatedUint64.append(134) + XCTAssertEqual(msg.repeatedUint64, [34, 134]) + } + + func testRepeatedSint32() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.repeatedSint32, []) + msg.repeatedSint32 = [35] + XCTAssertEqual(msg.repeatedSint32, [35]) + msg.repeatedSint32.append(135) + XCTAssertEqual(msg.repeatedSint32, [35, 135]) + } + + func testRepeatedSint64() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.repeatedSint64, []) + msg.repeatedSint64 = [36] + XCTAssertEqual(msg.repeatedSint64, [36]) + msg.repeatedSint64.append(136) + XCTAssertEqual(msg.repeatedSint64, [36, 136]) + } + + func testRepeatedFixed32() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.repeatedFixed32, []) + msg.repeatedFixed32 = [37] + XCTAssertEqual(msg.repeatedFixed32, [37]) + msg.repeatedFixed32.append(137) + XCTAssertEqual(msg.repeatedFixed32, [37, 137]) + } + + func testRepeatedFixed64() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.repeatedFixed64, []) + msg.repeatedFixed64 = [38] + XCTAssertEqual(msg.repeatedFixed64, [38]) + msg.repeatedFixed64.append(138) + XCTAssertEqual(msg.repeatedFixed64, [38, 138]) + } + + func testRepeatedSfixed32() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.repeatedSfixed32, []) + msg.repeatedSfixed32 = [39] + XCTAssertEqual(msg.repeatedSfixed32, [39]) + msg.repeatedSfixed32.append(139) + XCTAssertEqual(msg.repeatedSfixed32, [39, 139]) + } + + func testRepeatedSfixed64() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.repeatedSfixed64, []) + msg.repeatedSfixed64 = [40] + XCTAssertEqual(msg.repeatedSfixed64, [40]) + msg.repeatedSfixed64.append(140) + XCTAssertEqual(msg.repeatedSfixed64, [40, 140]) + } + + func testRepeatedFloat() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.repeatedFloat, []) + msg.repeatedFloat = [41.0] + XCTAssertEqual(msg.repeatedFloat, [41.0]) + msg.repeatedFloat.append(141.0) + XCTAssertEqual(msg.repeatedFloat, [41.0, 141.0]) + } + + func testRepeatedDouble() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.repeatedDouble, []) + msg.repeatedDouble = [42.0] + XCTAssertEqual(msg.repeatedDouble, [42.0]) + msg.repeatedDouble.append(142.0) + XCTAssertEqual(msg.repeatedDouble, [42.0, 142.0]) + } + + func testRepeatedBool() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.repeatedBool, []) + msg.repeatedBool = [true] + XCTAssertEqual(msg.repeatedBool, [true]) + msg.repeatedBool.append(false) + XCTAssertEqual(msg.repeatedBool, [true, false]) + } + + func testRepeatedString() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.repeatedString, []) + msg.repeatedString = ["44"] + XCTAssertEqual(msg.repeatedString, ["44"]) + msg.repeatedString.append("144") + XCTAssertEqual(msg.repeatedString, ["44", "144"]) + } + + func testRepeatedBytes() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.repeatedBytes, []) + msg.repeatedBytes = [Data([45])] + XCTAssertEqual(msg.repeatedBytes, [Data([45])]) + msg.repeatedBytes.append(Data([145])) + XCTAssertEqual(msg.repeatedBytes, [Data([45]), Data([145])]) + } + + func testRepeatedNestedMessage() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.repeatedNestedMessage, []) + var nestedMsg = SwiftProtoTesting_Proto3_TestAllTypes.NestedMessage() + nestedMsg.bb = 48 + msg.repeatedNestedMessage = [nestedMsg] + XCTAssertEqual(msg.repeatedNestedMessage.count, 1) + XCTAssertEqual(msg.repeatedNestedMessage[0].bb, 48) + XCTAssertEqual(msg.repeatedNestedMessage, [nestedMsg]) + var nestedMsg2 = SwiftProtoTesting_Proto3_TestAllTypes.NestedMessage() + nestedMsg2.bb = 148 + msg.repeatedNestedMessage.append(nestedMsg2) + XCTAssertEqual(msg.repeatedNestedMessage.count, 2) + XCTAssertEqual(msg.repeatedNestedMessage[0].bb, 48) + XCTAssertEqual(msg.repeatedNestedMessage[1].bb, 148) + XCTAssertEqual(msg.repeatedNestedMessage, [nestedMsg, nestedMsg2]) + } + + func testRepeatedForeignMessage() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.repeatedForeignMessage, []) + var foreignMsg = SwiftProtoTesting_Proto3_ForeignMessage() + foreignMsg.c = 49 + msg.repeatedForeignMessage = [foreignMsg] + XCTAssertEqual(msg.repeatedForeignMessage.count, 1) + XCTAssertEqual(msg.repeatedForeignMessage[0].c, 49) + XCTAssertEqual(msg.repeatedForeignMessage, [foreignMsg]) + var foreignMsg2 = SwiftProtoTesting_Proto3_ForeignMessage() + foreignMsg2.c = 149 + msg.repeatedForeignMessage.append(foreignMsg2) + XCTAssertEqual(msg.repeatedForeignMessage.count, 2) + XCTAssertEqual(msg.repeatedForeignMessage[0].c, 49) + XCTAssertEqual(msg.repeatedForeignMessage[1].c, 149) + XCTAssertEqual(msg.repeatedForeignMessage, [foreignMsg, foreignMsg2]) + } + + func testRepeatedImportMessage() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.repeatedImportMessage, []) + var importedMsg = SwiftProtoTesting_Import_ImportMessage() + importedMsg.d = 50 + msg.repeatedImportMessage = [importedMsg] + XCTAssertEqual(msg.repeatedImportMessage.count, 1) + XCTAssertEqual(msg.repeatedImportMessage[0].d, 50) + XCTAssertEqual(msg.repeatedImportMessage, [importedMsg]) + var importedMsg2 = SwiftProtoTesting_Import_ImportMessage() + importedMsg2.d = 150 + msg.repeatedImportMessage.append(importedMsg2) + XCTAssertEqual(msg.repeatedImportMessage.count, 2) + XCTAssertEqual(msg.repeatedImportMessage[0].d, 50) + XCTAssertEqual(msg.repeatedImportMessage[1].d, 150) + XCTAssertEqual(msg.repeatedImportMessage, [importedMsg, importedMsg2]) + } + + func testRepeatedNestedEnum() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.repeatedNestedEnum, []) + msg.repeatedNestedEnum = [.bar] + XCTAssertEqual(msg.repeatedNestedEnum, [.bar]) + msg.repeatedNestedEnum.append(.baz) + XCTAssertEqual(msg.repeatedNestedEnum, [.bar, .baz]) + } + + func testRepeatedForeignEnum() { + var msg = SwiftProtoTesting_Proto3_TestAllTypes() + XCTAssertEqual(msg.repeatedForeignEnum, []) + msg.repeatedForeignEnum = [.foreignBar] + XCTAssertEqual(msg.repeatedForeignEnum, [.foreignBar]) + msg.repeatedForeignEnum.append(.foreignBaz) + XCTAssertEqual(msg.repeatedForeignEnum, [.foreignBar, .foreignBaz]) + } } diff --git a/Tests/SwiftProtobufTests/Test_BinaryDecodingOptions.swift b/Tests/SwiftProtobufTests/Test_BinaryDecodingOptions.swift index 8285db8b0..2a799275b 100644 --- a/Tests/SwiftProtobufTests/Test_BinaryDecodingOptions.swift +++ b/Tests/SwiftProtobufTests/Test_BinaryDecodingOptions.swift @@ -13,8 +13,8 @@ // ----------------------------------------------------------------------------- import Foundation -import XCTest import SwiftProtobuf +import XCTest final class Test_BinaryDecodingOptions: XCTestCase { @@ -31,13 +31,17 @@ final class Test_BinaryDecodingOptions: XCTestCase { // } // } // } - ([10, 6, 10, 4, 18, 2, 8, 99], - SwiftProtoTesting_NestedTestAllTypes.self, - nil, // No Extensions - [( 10, true ), - ( 4, true ), - ( 3, false ), - ( 2, false )]), + ( + [10, 6, 10, 4, 18, 2, 8, 99], + SwiftProtoTesting_NestedTestAllTypes.self, + nil, // No Extensions + [ + (10, true), + (4, true), + (3, false), + (2, false), + ] + ), // Group within messages: // outer is msg 1 // child { // sub msg 2 @@ -49,13 +53,17 @@ final class Test_BinaryDecodingOptions: XCTestCase { // } // } // } - ([10, 11, 10, 9, 18, 7, 131, 1, 136, 1, 98, 132, 1], - SwiftProtoTesting_NestedTestAllTypes.self, - nil, // No Extensions - [( 10, true ), - ( 5, true ), - ( 4, false ), - ( 3, false )]), + ( + [10, 11, 10, 9, 18, 7, 131, 1, 136, 1, 98, 132, 1], + SwiftProtoTesting_NestedTestAllTypes.self, + nil, // No Extensions + [ + (10, true), + (5, true), + (4, false), + (3, false), + ] + ), // Nesting of unknown groups: // outer is msg 1 // 4 { // sub msg 2 @@ -68,13 +76,17 @@ final class Test_BinaryDecodingOptions: XCTestCase { // 35 = 0b100011 -> field 4/start group // 8, 1 -> field 1/varint, value of 1 // 36 = 0b100100 -> field 4/end group - ([35, 35, 35, 8, 1, 36, 36, 36], - SwiftProtoTesting_TestEmptyMessage.self, - nil, // No Extensions - [( 10, true ), - ( 4, true ), - ( 3, false ), - ( 2, false )]), + ( + [35, 35, 35, 8, 1, 36, 36, 36], + SwiftProtoTesting_TestEmptyMessage.self, + nil, // No Extensions + [ + (10, true), + (4, true), + (3, false), + (2, false), + ] + ), // Nested message are on the wire as length delimited, so no depth comes into // play when they are unknown message fields. @@ -83,23 +95,31 @@ final class Test_BinaryDecodingOptions: XCTestCase { // [swift_proto_testing.optional_nested_message_extension] { // sub msg 2 // bb: 1 // } - ([146, 1, 2, 8, 1], - SwiftProtoTesting_TestAllExtensions.self, - SwiftProtoTesting_Unittest_Extensions, - [( 10, true ), - ( 2, true ), - ( 1, false )]), + ( + [146, 1, 2, 8, 1], + SwiftProtoTesting_TestAllExtensions.self, + SwiftProtoTesting_Unittest_Extensions, + [ + (10, true), + (2, true), + (1, false), + ] + ), // Limit applies to group extension fields: // outer is msg 1 // [swift_proto_testing.optionalgroup_extension] { // sub msg 2 // a: 1 // } - ([131, 1, 136, 1, 1, 132, 1], - SwiftProtoTesting_TestAllExtensions.self, - SwiftProtoTesting_Unittest_Extensions, - [( 10, true ), - ( 2, true ), - ( 1, false )]), + ( + [131, 1, 136, 1, 1, 132, 1], + SwiftProtoTesting_TestAllExtensions.self, + SwiftProtoTesting_Unittest_Extensions, + [ + (10, true), + (2, true), + (1, false), + ] + ), ] for (i, (binaryInput, messageType, extensions, testCases)) in tests.enumerated() { @@ -107,9 +127,11 @@ final class Test_BinaryDecodingOptions: XCTestCase { do { var options = BinaryDecodingOptions() options.messageDepthLimit = limit - let _ = try messageType.init(serializedBytes: binaryInput, - extensions: extensions, - options: options) + let _ = try messageType.init( + serializedBytes: binaryInput, + extensions: extensions, + options: options + ) if !expectSuccess { XCTFail("Should not have succeed, pass: \(i), limit: \(limit)") } @@ -119,7 +141,7 @@ final class Test_BinaryDecodingOptions: XCTestCase { } else { // Nothing, this is what was expected. } - } catch let e { + } catch let e { XCTFail("Decode failed (pass: \(i), limit: \(limit) with unexpected error: \(e)") } } @@ -157,8 +179,10 @@ final class Test_BinaryDecodingOptions: XCTestCase { let msg1 = try SwiftProtoTesting_TestEmptyMessage(serializedBytes: inputCurrentLevel) XCTAssertEqual(Array(msg1.unknownFields.data), inputCurrentLevel) - let msg2 = try SwiftProtoTesting_TestEmptyMessage(serializedBytes: inputCurrentLevel, - options: discardOptions) + let msg2 = try SwiftProtoTesting_TestEmptyMessage( + serializedBytes: inputCurrentLevel, + options: discardOptions + ) XCTAssertTrue(msg2.unknownFields.data.isEmpty) } @@ -172,17 +196,20 @@ final class Test_BinaryDecodingOptions: XCTestCase { // } // 6: 0x00000005 // } - let inputSubMessage: [UInt8] = [ - // Field 18, length of data, plus the data - 146, 1, UInt8(inputCurrentLevel.count), - ] + inputCurrentLevel + let inputSubMessage: [UInt8] = + [ + // Field 18, length of data, plus the data + 146, 1, UInt8(inputCurrentLevel.count), + ] + inputCurrentLevel do { let msg1 = try SwiftProtoTesting_TestAllTypes(serializedBytes: inputSubMessage) XCTAssertTrue(msg1.unknownFields.data.isEmpty) XCTAssertEqual(Array(msg1.optionalNestedMessage.unknownFields.data), inputCurrentLevel) - let msg2 = try SwiftProtoTesting_TestAllTypes(serializedBytes: inputSubMessage, - options: discardOptions) + let msg2 = try SwiftProtoTesting_TestAllTypes( + serializedBytes: inputSubMessage, + options: discardOptions + ) XCTAssertTrue(msg2.unknownFields.data.isEmpty) XCTAssertTrue(msg2.optionalNestedMessage.unknownFields.data.isEmpty) } @@ -199,16 +226,20 @@ final class Test_BinaryDecodingOptions: XCTestCase { // } do { let msg1 = try SwiftProtoTesting_TestAllExtensions( - serializedBytes: inputSubMessage, - extensions: SwiftProtoTesting_Unittest_Extensions) + serializedBytes: inputSubMessage, + extensions: SwiftProtoTesting_Unittest_Extensions + ) XCTAssertTrue(msg1.unknownFields.data.isEmpty) - XCTAssertEqual(Array(msg1.SwiftProtoTesting_optionalNestedMessageExtension.unknownFields.data), - inputCurrentLevel) + XCTAssertEqual( + Array(msg1.SwiftProtoTesting_optionalNestedMessageExtension.unknownFields.data), + inputCurrentLevel + ) let msg2 = try SwiftProtoTesting_TestAllExtensions( - serializedBytes: inputSubMessage, - extensions: SwiftProtoTesting_Unittest_Extensions, - options: discardOptions) + serializedBytes: inputSubMessage, + extensions: SwiftProtoTesting_Unittest_Extensions, + options: discardOptions + ) XCTAssertTrue(msg2.unknownFields.data.isEmpty) XCTAssertTrue(msg2.SwiftProtoTesting_optionalNestedMessageExtension.unknownFields.data.isEmpty) } @@ -223,25 +254,30 @@ final class Test_BinaryDecodingOptions: XCTestCase { // } // 6: 0x00000005 // } - let inputGroup: [UInt8] = [ - // Field 16, start_group - 131, 1, - ] + inputCurrentLevel + [ - // Field 16, end_group - 132, 1, - ] + let inputGroup: [UInt8] = + [ + // Field 16, start_group + 131, 1, + ] + inputCurrentLevel + [ + // Field 16, end_group + 132, 1, + ] do { let msg1 = try SwiftProtoTesting_TestAllExtensions( - serializedBytes: inputGroup, - extensions: SwiftProtoTesting_Unittest_Extensions) + serializedBytes: inputGroup, + extensions: SwiftProtoTesting_Unittest_Extensions + ) XCTAssertTrue(msg1.unknownFields.data.isEmpty) - XCTAssertEqual(Array(msg1.SwiftProtoTesting_optionalGroupExtension.unknownFields.data), - inputCurrentLevel) + XCTAssertEqual( + Array(msg1.SwiftProtoTesting_optionalGroupExtension.unknownFields.data), + inputCurrentLevel + ) let msg2 = try SwiftProtoTesting_TestAllExtensions( - serializedBytes: inputGroup, - extensions: SwiftProtoTesting_Unittest_Extensions, - options: discardOptions) + serializedBytes: inputGroup, + extensions: SwiftProtoTesting_Unittest_Extensions, + options: discardOptions + ) XCTAssertTrue(msg2.unknownFields.data.isEmpty) XCTAssertTrue(msg2.SwiftProtoTesting_optionalGroupExtension.unknownFields.data.isEmpty) } @@ -261,8 +297,10 @@ final class Test_BinaryDecodingOptions: XCTestCase { XCTAssertTrue(msg1.unknownFields.data.isEmpty) XCTAssertEqual(Array(msg1.optionalGroup.unknownFields.data), inputCurrentLevel) - let msg2 = try SwiftProtoTesting_TestAllTypes(serializedBytes: inputGroup, - options: discardOptions) + let msg2 = try SwiftProtoTesting_TestAllTypes( + serializedBytes: inputGroup, + options: discardOptions + ) XCTAssertTrue(msg2.unknownFields.data.isEmpty) XCTAssertTrue(msg2.optionalGroup.unknownFields.data.isEmpty) } @@ -273,14 +311,16 @@ final class Test_BinaryDecodingOptions: XCTestCase { // optional_nested_enum: 13 let inputUnknownEnum: [UInt8] = [ // Field 21, varint - 168, 1, 13 + 168, 1, 13, ] do { let msg1 = try SwiftProtoTesting_TestAllTypes(serializedBytes: inputUnknownEnum) XCTAssertEqual(Array(msg1.unknownFields.data), inputUnknownEnum) - let msg2 = try SwiftProtoTesting_TestAllTypes(serializedBytes: inputUnknownEnum, - options: discardOptions) + let msg2 = try SwiftProtoTesting_TestAllTypes( + serializedBytes: inputUnknownEnum, + options: discardOptions + ) XCTAssertTrue(msg2.unknownFields.data.isEmpty) } } diff --git a/Tests/SwiftProtobufTests/Test_BinaryDelimited.swift b/Tests/SwiftProtobufTests/Test_BinaryDelimited.swift index 977b94276..753da08e4 100644 --- a/Tests/SwiftProtobufTests/Test_BinaryDelimited.swift +++ b/Tests/SwiftProtobufTests/Test_BinaryDelimited.swift @@ -9,157 +9,182 @@ // ----------------------------------------------------------------------------- import Foundation -import XCTest import SwiftProtobuf +import XCTest -fileprivate func openInputStream(_ bytes: [UInt8]) -> InputStream { - let istream = InputStream(data: Data(bytes)) - istream.open() - return istream +private func openInputStream(_ bytes: [UInt8]) -> InputStream { + let istream = InputStream(data: Data(bytes)) + istream.open() + return istream } final class Test_BinaryDelimited: XCTestCase { - /// Helper to assert the next message read matches and expected one. - func assertParse(expected: M, onStream istream: InputStream) { - do { - let msg = try BinaryDelimited.parse( - messageType: M.self, - from: istream) - XCTAssertEqual(msg, expected) - } catch let e { - XCTFail("Unexpected failure: \(e)") - } - } - - /// Helper to assert we're at the end of the stream. - /// - /// `hasBytesAvailable` is documented as maybe returning True and a read - /// has to happen to really know if ones at the end. This is especially - /// true with file based streams. - func assertParseFails(atEndOfStream istream: InputStream) { - XCTAssertThrowsError(try BinaryDelimited.parse(messageType: SwiftProtoTesting_TestAllTypes.self, - from: istream)) { error in - XCTAssertTrue(self.isSwiftProtobufErrorEqual(error as! SwiftProtobufError, .BinaryStreamDecoding.noBytesAvailable())) + /// Helper to assert the next message read matches and expected one. + func assertParse(expected: M, onStream istream: InputStream) { + do { + let msg = try BinaryDelimited.parse( + messageType: M.self, + from: istream + ) + XCTAssertEqual(msg, expected) + } catch let e { + XCTFail("Unexpected failure: \(e)") + } } - } - func assertParsing(failsWithTruncatedStream istream: InputStream) { - XCTAssertThrowsError(try BinaryDelimited.parse(messageType: SwiftProtoTesting_TestAllTypes.self, - from: istream)) { error in - XCTAssertEqual(error as? BinaryDelimited.Error, BinaryDelimited.Error.truncated) + /// Helper to assert we're at the end of the stream. + /// + /// `hasBytesAvailable` is documented as maybe returning True and a read + /// has to happen to really know if ones at the end. This is especially + /// true with file based streams. + func assertParseFails(atEndOfStream istream: InputStream) { + XCTAssertThrowsError( + try BinaryDelimited.parse( + messageType: SwiftProtoTesting_TestAllTypes.self, + from: istream + ) + ) { error in + XCTAssertTrue( + self.isSwiftProtobufErrorEqual(error as! SwiftProtobufError, .BinaryStreamDecoding.noBytesAvailable()) + ) + } } - } - - func testNoData() { - let istream = openInputStream([]) - - assertParseFails(atEndOfStream: istream) - } - - func testZeroLengthMessage() { - let istream = openInputStream([0]) - assertParse(expected: SwiftProtoTesting_TestAllTypes(), onStream: istream) + func assertParsing(failsWithTruncatedStream istream: InputStream) { + XCTAssertThrowsError( + try BinaryDelimited.parse( + messageType: SwiftProtoTesting_TestAllTypes.self, + from: istream + ) + ) { error in + XCTAssertEqual(error as? BinaryDelimited.Error, BinaryDelimited.Error.truncated) + } + } - assertParseFails(atEndOfStream: istream) - } + func testNoData() { + let istream = openInputStream([]) - func testNoDataForMessage() { - let istream = openInputStream([0x96, 0x01]) + assertParseFails(atEndOfStream: istream) + } - // Length will be read, then the no data for the message, so .truncated. - assertParsing(failsWithTruncatedStream: istream) - } + func testZeroLengthMessage() { + let istream = openInputStream([0]) - func testNotEnoughDataForMessage() { - let istream = openInputStream([0x96, 0x01, 0x01, 0x02, 0x03]) + assertParse(expected: SwiftProtoTesting_TestAllTypes(), onStream: istream) - // Length will be read, but not enought data, so .truncated - assertParsing(failsWithTruncatedStream: istream) - } + assertParseFails(atEndOfStream: istream) + } - func testTruncatedLength() { - let istream = openInputStream([0x96]) // Needs something like `, 0x01` + func testNoDataForMessage() { + let istream = openInputStream([0x96, 0x01]) - assertParsing(failsWithTruncatedStream: istream) - } + // Length will be read, then the no data for the message, so .truncated. + assertParsing(failsWithTruncatedStream: istream) + } - func testTooLarge() { - let istream = openInputStream([0x80, 0x80, 0x80, 0x80, 0x08]) // 2GB + func testNotEnoughDataForMessage() { + let istream = openInputStream([0x96, 0x01, 0x01, 0x02, 0x03]) - XCTAssertThrowsError(try BinaryDelimited.parse(messageType: SwiftProtoTesting_TestAllTypes.self, - from: istream)) { error in - XCTAssertEqual(error as! BinaryDecodingError, .malformedProtobuf) + // Length will be read, but not enought data, so .truncated + assertParsing(failsWithTruncatedStream: istream) } - } - func testOverEncodedLength() { - let istream = openInputStream([0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 ,0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x08]) + func testTruncatedLength() { + let istream = openInputStream([0x96]) // Needs something like `, 0x01` - XCTAssertThrowsError(try BinaryDelimited.parse(messageType: SwiftProtoTesting_TestAllTypes.self, - from: istream)) { error in - XCTAssertEqual(error as! BinaryDecodingError, .malformedProtobuf) + assertParsing(failsWithTruncatedStream: istream) } - } - - func testTwoMessages() { - let stream1 = OutputStream.toMemory() - stream1.open() - - let msg1 = SwiftProtoTesting_TestAllTypes.with { - $0.optionalBool = true - $0.optionalInt32 = 123 - $0.optionalInt64 = 123456789 - $0.optionalGroup.a = 456 - $0.optionalNestedEnum = .baz - $0.repeatedString.append("wee") - $0.repeatedFloat.append(1.23) + + func testTooLarge() { + let istream = openInputStream([0x80, 0x80, 0x80, 0x80, 0x08]) // 2GB + + XCTAssertThrowsError( + try BinaryDelimited.parse( + messageType: SwiftProtoTesting_TestAllTypes.self, + from: istream + ) + ) { error in + XCTAssertEqual(error as! BinaryDecodingError, .malformedProtobuf) + } } - XCTAssertNoThrow(try BinaryDelimited.serialize(message: msg1, to: stream1)) + func testOverEncodedLength() { + let istream = openInputStream([ + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x08, + ]) + + XCTAssertThrowsError( + try BinaryDelimited.parse( + messageType: SwiftProtoTesting_TestAllTypes.self, + from: istream + ) + ) { error in + XCTAssertEqual(error as! BinaryDecodingError, .malformedProtobuf) + } + } - let msg2 = SwiftProtoTesting_TestPackedTypes.with { - $0.packedBool.append(true) - $0.packedInt32.append(234) - $0.packedDouble.append(345.67) + func testTwoMessages() { + let stream1 = OutputStream.toMemory() + stream1.open() + + let msg1 = SwiftProtoTesting_TestAllTypes.with { + $0.optionalBool = true + $0.optionalInt32 = 123 + $0.optionalInt64 = 123_456_789 + $0.optionalGroup.a = 456 + $0.optionalNestedEnum = .baz + $0.repeatedString.append("wee") + $0.repeatedFloat.append(1.23) + } + + XCTAssertNoThrow(try BinaryDelimited.serialize(message: msg1, to: stream1)) + + let msg2 = SwiftProtoTesting_TestPackedTypes.with { + $0.packedBool.append(true) + $0.packedInt32.append(234) + $0.packedDouble.append(345.67) + } + + XCTAssertNoThrow(try BinaryDelimited.serialize(message: msg2, to: stream1)) + + stream1.close() + // See https://bugs.swift.org/browse/SR-5404 + let nsData = stream1.property(forKey: .dataWrittenToMemoryStreamKey) as! NSData + let data = Data(referencing: nsData) + let stream2 = InputStream(data: data) + stream2.open() + + // Test using `merge` + var msg1a = SwiftProtoTesting_TestAllTypes() + XCTAssertNoThrow(try BinaryDelimited.merge(into: &msg1a, from: stream2)) + XCTAssertEqual(msg1, msg1a) + + // Test using `parse` + assertParse(expected: msg2, onStream: stream2) + + assertParseFails(atEndOfStream: stream2) } - XCTAssertNoThrow(try BinaryDelimited.serialize(message: msg2, to: stream1)) - - stream1.close() - // See https://bugs.swift.org/browse/SR-5404 - let nsData = stream1.property(forKey: .dataWrittenToMemoryStreamKey) as! NSData - let data = Data(referencing: nsData) - let stream2 = InputStream(data: data) - stream2.open() - - // Test using `merge` - var msg1a = SwiftProtoTesting_TestAllTypes() - XCTAssertNoThrow(try BinaryDelimited.merge(into: &msg1a, from: stream2)) - XCTAssertEqual(msg1, msg1a) - - // Test using `parse` - assertParse(expected: msg2, onStream: stream2) - - assertParseFails(atEndOfStream: stream2) - } - - // oss-fuzz found this case that runs slowly for AsyncMessageSequence - // Copied here as well for comparison. - func testLargeExample() throws { - let messageCount = 100_000 - let bytes = [UInt8](repeating: 0, count: messageCount) - let istream = openInputStream(bytes) - - for _ in 0.. Bool in - return (m.fieldname1 == 1) && (m.fieldName2 == 2) && (m.fieldName3 == 3) + (m.fieldname1 == 1) && (m.fieldName2 == 2) && (m.fieldName3 == 3) } do { let decoded = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: json) @@ -38,7 +38,7 @@ final class Test_Conformance: XCTestCase, PBTestHelpers { // Also accept the names in the .proto when decoding let json = "{\n \"fieldname1\": 1,\n \"field_name2\": 2,\n \"_field_name3\": 3\n }" assertJSONDecodeSucceeds(json) { (m: MessageTestType) -> Bool in - return (m.fieldname1 == 1) && (m.fieldName2 == 2) && (m.fieldName3 == 3) + (m.fieldname1 == 1) && (m.fieldName2 == 2) && (m.fieldName3 == 3) } do { let decoded = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: json) @@ -51,7 +51,7 @@ final class Test_Conformance: XCTestCase, PBTestHelpers { func testFieldNaming_escapeInName() throws { assertJSONDecodeSucceeds("{\"fieldn\\u0061me1\": 1}") { - return $0.fieldname1 == 1 + $0.fieldname1 == 1 } } @@ -73,16 +73,16 @@ final class Test_Conformance: XCTestCase, PBTestHelpers { func testRepeatedBoolWrapper() { assertJSONDecodeSucceeds("{\"repeatedBoolWrapper\": [true, false]}") { (o: SwiftProtoTesting_Test3_TestAllTypesProto3) -> Bool in - return o.repeatedBoolWrapper == [Google_Protobuf_BoolValue(true), Google_Protobuf_BoolValue(false)] + o.repeatedBoolWrapper == [Google_Protobuf_BoolValue(true), Google_Protobuf_BoolValue(false)] } } func testString_unicodeEscape() { assertTextFormatDecodeSucceeds("optional_string: \"\\u1234\"") { - return $0.optionalString == "\u{1234}" + $0.optionalString == "\u{1234}" } assertTextFormatDecodeSucceeds("optional_string: \"\\U0001F601\"") { - return $0.optionalString == "\u{1F601}" + $0.optionalString == "\u{1F601}" } assertTextFormatDecodeFails("optional_string: \"\\u") @@ -95,7 +95,7 @@ final class Test_Conformance: XCTestCase, PBTestHelpers { assertTextFormatDecodeFails("optional_string: \"\\U1234DCXY\"") assertJSONDecodeSucceeds("{\"optional_string\": \"\\u1234\"}") { - return $0.optionalString == "\u{1234}" + $0.optionalString == "\u{1234}" } assertJSONDecodeFails("{\"optionalString\": \"\\u") @@ -140,16 +140,16 @@ final class Test_Conformance: XCTestCase, PBTestHelpers { assertJSONDecodeFails("{\"optionalString\": \"\\uDE01\\uD83D\"}") // Correct surrogate assertJSONDecodeSucceeds("{\"optionalString\": \"\\uD83D\\uDE01\"}") { - return $0.optionalString == "\u{1F601}" + $0.optionalString == "\u{1F601}" } } func testBytes_unicodeEscape() { assertTextFormatDecodeSucceeds("optional_bytes: \"\\u1234\"") { - return $0.optionalBytes == Data("\u{1234}".utf8) + $0.optionalBytes == Data("\u{1234}".utf8) } assertTextFormatDecodeSucceeds("optional_bytes: \"\\U0001F601\"") { - return $0.optionalBytes == Data("\u{1F601}".utf8) + $0.optionalBytes == Data("\u{1F601}".utf8) } assertTextFormatDecodeFails("optional_bytes: \"\\u") @@ -195,13 +195,19 @@ final class Test_Conformance: XCTestCase, PBTestHelpers { } func testMaps_TextFormatKeysSorted() { - assertTextFormatEncode("map_string_string {\n key: \"a\"\n value: \"value\"\n}\nmap_string_string {\n key: \"b\"\n value: \"value\"\n}\nmap_string_string {\n key: \"c\"\n value: \"value\"\n}\n") {(o: inout MessageTestType) in - o.mapStringString = ["c":"value", "b":"value", "a":"value"] - } - assertTextFormatEncode("map_int32_int32 {\n key: 1\n value: 0\n}\nmap_int32_int32 {\n key: 2\n value: 0\n}\nmap_int32_int32 {\n key: 3\n value: 0\n}\n") {(o: inout MessageTestType) in - o.mapInt32Int32 = [3:0, 2:0, 1:0] - } - assertTextFormatEncode("map_bool_bool {\n key: false\n value: false\n}\nmap_bool_bool {\n key: true\n value: false\n}\n") {(o: inout MessageTestType) in + assertTextFormatEncode( + "map_string_string {\n key: \"a\"\n value: \"value\"\n}\nmap_string_string {\n key: \"b\"\n value: \"value\"\n}\nmap_string_string {\n key: \"c\"\n value: \"value\"\n}\n" + ) { (o: inout MessageTestType) in + o.mapStringString = ["c": "value", "b": "value", "a": "value"] + } + assertTextFormatEncode( + "map_int32_int32 {\n key: 1\n value: 0\n}\nmap_int32_int32 {\n key: 2\n value: 0\n}\nmap_int32_int32 {\n key: 3\n value: 0\n}\n" + ) { (o: inout MessageTestType) in + o.mapInt32Int32 = [3: 0, 2: 0, 1: 0] + } + assertTextFormatEncode( + "map_bool_bool {\n key: false\n value: false\n}\nmap_bool_bool {\n key: true\n value: false\n}\n" + ) { (o: inout MessageTestType) in o.mapBoolBool = [true: false, false: false] } } diff --git a/Tests/SwiftProtobufTests/Test_Duration.swift b/Tests/SwiftProtobufTests/Test_Duration.swift index cff47c2cd..07b6b6478 100644 --- a/Tests/SwiftProtobufTests/Test_Duration.swift +++ b/Tests/SwiftProtobufTests/Test_Duration.swift @@ -13,9 +13,9 @@ /// // ----------------------------------------------------------------------------- -import XCTest -import SwiftProtobuf import Foundation +import SwiftProtobuf +import XCTest final class Test_Duration: XCTestCase, PBTestHelpers { typealias MessageTestType = Google_Protobuf_Duration @@ -31,16 +31,16 @@ final class Test_Duration: XCTestCase, PBTestHelpers { } assertJSONEncode("\"-0.500s\"") { (o: inout MessageTestType) in o.seconds = 0 - o.nanos = -500000000 + o.nanos = -500_000_000 } // Always prints exactly 3, 6, or 9 digits assertJSONEncode("\"100.100s\"") { (o: inout MessageTestType) in o.seconds = 100 - o.nanos = 100000000 + o.nanos = 100_000_000 } assertJSONEncode("\"100.001s\"") { (o: inout MessageTestType) in o.seconds = 100 - o.nanos = 1000000 + o.nanos = 1_000_000 } assertJSONEncode("\"100.000100s\"") { (o: inout MessageTestType) in o.seconds = 100 @@ -62,28 +62,28 @@ final class Test_Duration: XCTestCase, PBTestHelpers { // Negative durations assertJSONEncode("\"-100.100s\"") { (o: inout MessageTestType) in o.seconds = -100 - o.nanos = -100000000 + o.nanos = -100_000_000 } } func testJSON_decode() throws { - assertJSONDecodeSucceeds("\"1.000000000s\"") {(o:MessageTestType) in + assertJSONDecodeSucceeds("\"1.000000000s\"") { (o: MessageTestType) in o.seconds == 1 && o.nanos == 0 } - assertJSONDecodeSucceeds("\"-5s\"") {(o:MessageTestType) in + assertJSONDecodeSucceeds("\"-5s\"") { (o: MessageTestType) in o.seconds == -5 && o.nanos == 0 } - assertJSONDecodeSucceeds("\"-0.5s\"") {(o:MessageTestType) in - o.seconds == 0 && o.nanos == -500000000 + assertJSONDecodeSucceeds("\"-0.5s\"") { (o: MessageTestType) in + o.seconds == 0 && o.nanos == -500_000_000 } - assertJSONDecodeSucceeds("\"-315576000000.999999999s\"") {(o:MessageTestType) in - o.seconds == -315576000000 && o.nanos == -999999999 + assertJSONDecodeSucceeds("\"-315576000000.999999999s\"") { (o: MessageTestType) in + o.seconds == -315_576_000_000 && o.nanos == -999_999_999 } assertJSONDecodeFails("\"-315576000001s\"") - assertJSONDecodeSucceeds("\"315576000000.999999999s\"") {(o:MessageTestType) in - o.seconds == 315576000000 && o.nanos == 999999999 + assertJSONDecodeSucceeds("\"315576000000.999999999s\"") { (o: MessageTestType) in + o.seconds == 315_576_000_000 && o.nanos == 999_999_999 } assertJSONDecodeFails("\"315576000001s\"") @@ -96,13 +96,13 @@ final class Test_Duration: XCTestCase, PBTestHelpers { } func testSerializationFailure() throws { - let maxOutOfRange = Google_Protobuf_Duration(seconds:-315576000001) + let maxOutOfRange = Google_Protobuf_Duration(seconds: -315_576_000_001) XCTAssertThrowsError(try maxOutOfRange.jsonString()) - let minInRange = Google_Protobuf_Duration(seconds:-315576000000, nanos: -999999999) - let _ = try minInRange.jsonString() // Assert does not throw - let maxInRange = Google_Protobuf_Duration(seconds:315576000000, nanos: 999999999) - let _ = try maxInRange.jsonString() // Assert does not throw - let minOutOfRange = Google_Protobuf_Duration(seconds:315576000001) + let minInRange = Google_Protobuf_Duration(seconds: -315_576_000_000, nanos: -999_999_999) + let _ = try minInRange.jsonString() // Assert does not throw + let maxInRange = Google_Protobuf_Duration(seconds: 315_576_000_000, nanos: 999_999_999) + let _ = try maxInRange.jsonString() // Assert does not throw + let minOutOfRange = Google_Protobuf_Duration(seconds: 315_576_000_001) XCTAssertThrowsError(try minOutOfRange.jsonString()) } @@ -110,26 +110,39 @@ final class Test_Duration: XCTestCase, PBTestHelpers { func testJSON_durationField() throws { do { let valid = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: "{\"optionalDuration\": \"1.001s\"}") - XCTAssertEqual(valid.optionalDuration, Google_Protobuf_Duration(seconds: 1, nanos: 1000000)) + XCTAssertEqual(valid.optionalDuration, Google_Protobuf_Duration(seconds: 1, nanos: 1_000_000)) } catch { XCTFail("Should have decoded correctly") } - XCTAssertThrowsError(try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: "{\"optionalDuration\": \"-315576000001.000000000s\"}")) - - XCTAssertThrowsError(try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: "{\"optionalDuration\": \"315576000001.000000000s\"}")) - XCTAssertThrowsError(try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: "{\"optionalDuration\": \"1.001\"}")) + XCTAssertThrowsError( + try SwiftProtoTesting_Test3_TestAllTypesProto3( + jsonString: "{\"optionalDuration\": \"-315576000001.000000000s\"}" + ) + ) + + XCTAssertThrowsError( + try SwiftProtoTesting_Test3_TestAllTypesProto3( + jsonString: "{\"optionalDuration\": \"315576000001.000000000s\"}" + ) + ) + XCTAssertThrowsError( + try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: "{\"optionalDuration\": \"1.001\"}") + ) } func testFieldMember() throws { // Verify behavior when a duration appears as a field on a larger object let json1 = "{\"optionalDuration\": \"-315576000000.999999999s\"}" let m1 = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: json1) - XCTAssertEqual(m1.optionalDuration.seconds, -315576000000) - XCTAssertEqual(m1.optionalDuration.nanos, -999999999) + XCTAssertEqual(m1.optionalDuration.seconds, -315_576_000_000) + XCTAssertEqual(m1.optionalDuration.nanos, -999_999_999) let json2 = "{\"repeatedDuration\": [\"1.5s\", \"-1.5s\"]}" - let expected2 = [Google_Protobuf_Duration(seconds:1, nanos:500000000), Google_Protobuf_Duration(seconds:-1, nanos:-500000000)] + let expected2 = [ + Google_Protobuf_Duration(seconds: 1, nanos: 500_000_000), + Google_Protobuf_Duration(seconds: -1, nanos: -500_000_000), + ] let actual2 = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: json2) XCTAssertEqual(actual2.repeatedDuration, expected2) } @@ -137,24 +150,37 @@ final class Test_Duration: XCTestCase, PBTestHelpers { func testTranscode() throws { let jsonMax = "{\"optionalDuration\": \"315576000000.999999999s\"}" let parsedMax = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: jsonMax) - XCTAssertEqual(parsedMax.optionalDuration.seconds, 315576000000) - XCTAssertEqual(parsedMax.optionalDuration.nanos, 999999999) - XCTAssertEqual(try parsedMax.serializedBytes(), [234, 18, 13, 8, 128, 188, 174, 206, 151, 9, 16, 255, 147, 235, 220, 3]) + XCTAssertEqual(parsedMax.optionalDuration.seconds, 315_576_000_000) + XCTAssertEqual(parsedMax.optionalDuration.nanos, 999_999_999) + XCTAssertEqual( + try parsedMax.serializedBytes(), + [234, 18, 13, 8, 128, 188, 174, 206, 151, 9, 16, 255, 147, 235, 220, 3] + ) let jsonMin = "{\"optionalDuration\": \"-315576000000.999999999s\"}" let parsedMin = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: jsonMin) - XCTAssertEqual(parsedMin.optionalDuration.seconds, -315576000000) - XCTAssertEqual(parsedMin.optionalDuration.nanos, -999999999) - XCTAssertEqual(try parsedMin.serializedBytes(), [234, 18, 22, 8, 128, 196, 209, 177, 232, 246, 255, 255, 255, 1, 16, 129, 236, 148, 163, 252, 255, 255, 255, 255, 1]) + XCTAssertEqual(parsedMin.optionalDuration.seconds, -315_576_000_000) + XCTAssertEqual(parsedMin.optionalDuration.nanos, -999_999_999) + XCTAssertEqual( + try parsedMin.serializedBytes(), + [ + 234, 18, 22, 8, 128, 196, 209, 177, 232, 246, 255, 255, 255, 1, 16, 129, 236, 148, 163, 252, 255, 255, + 255, 255, 1, + ] + ) } func testConformance() throws { - let tooSmall = try SwiftProtoTesting_Test3_TestAllTypesProto3(serializedBytes: [234, 18, 11, 8, 255, 195, 209, 177, 232, 246, 255, 255, 255, 1]) - XCTAssertEqual(tooSmall.optionalDuration.seconds, -315576000001) + let tooSmall = try SwiftProtoTesting_Test3_TestAllTypesProto3(serializedBytes: [ + 234, 18, 11, 8, 255, 195, 209, 177, 232, 246, 255, 255, 255, 1, + ]) + XCTAssertEqual(tooSmall.optionalDuration.seconds, -315_576_000_001) XCTAssertEqual(tooSmall.optionalDuration.nanos, 0) XCTAssertThrowsError(try tooSmall.jsonString()) - let tooBig = try SwiftProtoTesting_Test3_TestAllTypesProto3(serializedBytes: [234, 18, 7, 8, 129, 188, 174, 206, 151, 9]) - XCTAssertEqual(tooBig.optionalDuration.seconds, 315576000001) + let tooBig = try SwiftProtoTesting_Test3_TestAllTypesProto3(serializedBytes: [ + 234, 18, 7, 8, 129, 188, 174, 206, 151, 9, + ]) + XCTAssertEqual(tooBig.optionalDuration.seconds, 315_576_000_001) XCTAssertEqual(tooBig.optionalDuration.nanos, 0) XCTAssertThrowsError(try tooBig.jsonString()) } @@ -181,33 +207,49 @@ final class Test_Duration: XCTestCase, PBTestHelpers { func testArithmeticNormalizes() throws { // Addition normalizes the result - XCTAssertEqual(Google_Protobuf_Duration() + Google_Protobuf_Duration(seconds: 0, nanos: 2000000001), - Google_Protobuf_Duration(seconds: 2, nanos: 1)) + XCTAssertEqual( + Google_Protobuf_Duration() + Google_Protobuf_Duration(seconds: 0, nanos: 2_000_000_001), + Google_Protobuf_Duration(seconds: 2, nanos: 1) + ) // Subtraction normalizes the result - XCTAssertEqual(Google_Protobuf_Duration() - Google_Protobuf_Duration(seconds: 0, nanos: 2000000001), - Google_Protobuf_Duration(seconds: -2, nanos: -1)) + XCTAssertEqual( + Google_Protobuf_Duration() - Google_Protobuf_Duration(seconds: 0, nanos: 2_000_000_001), + Google_Protobuf_Duration(seconds: -2, nanos: -1) + ) // Unary minus normalizes the result - XCTAssertEqual(-Google_Protobuf_Duration(seconds: 0, nanos: 2000000001), - Google_Protobuf_Duration(seconds: -2, nanos: -1)) - XCTAssertEqual(-Google_Protobuf_Duration(seconds: 0, nanos: -2000000001), - Google_Protobuf_Duration(seconds: 2, nanos: 1)) - XCTAssertEqual(-Google_Protobuf_Duration(seconds: 1, nanos: -2000000001), - Google_Protobuf_Duration(seconds: 1, nanos: 1)) - XCTAssertEqual(-Google_Protobuf_Duration(seconds: -1, nanos: 2000000001), - Google_Protobuf_Duration(seconds: -1, nanos: -1)) - XCTAssertEqual(-Google_Protobuf_Duration(seconds: -1, nanos: -2000000001), - Google_Protobuf_Duration(seconds: 3, nanos: 1)) - XCTAssertEqual(-Google_Protobuf_Duration(seconds: 1, nanos: 2000000001), - Google_Protobuf_Duration(seconds: -3, nanos: -1)) + XCTAssertEqual( + -Google_Protobuf_Duration(seconds: 0, nanos: 2_000_000_001), + Google_Protobuf_Duration(seconds: -2, nanos: -1) + ) + XCTAssertEqual( + -Google_Protobuf_Duration(seconds: 0, nanos: -2_000_000_001), + Google_Protobuf_Duration(seconds: 2, nanos: 1) + ) + XCTAssertEqual( + -Google_Protobuf_Duration(seconds: 1, nanos: -2_000_000_001), + Google_Protobuf_Duration(seconds: 1, nanos: 1) + ) + XCTAssertEqual( + -Google_Protobuf_Duration(seconds: -1, nanos: 2_000_000_001), + Google_Protobuf_Duration(seconds: -1, nanos: -1) + ) + XCTAssertEqual( + -Google_Protobuf_Duration(seconds: -1, nanos: -2_000_000_001), + Google_Protobuf_Duration(seconds: 3, nanos: 1) + ) + XCTAssertEqual( + -Google_Protobuf_Duration(seconds: 1, nanos: 2_000_000_001), + Google_Protobuf_Duration(seconds: -3, nanos: -1) + ) } func testFloatLiteralConvertible() throws { var a: Google_Protobuf_Duration = 1.5 - XCTAssertEqual(a, Google_Protobuf_Duration(seconds: 1, nanos: 500000000)) + XCTAssertEqual(a, Google_Protobuf_Duration(seconds: 1, nanos: 500_000_000)) a = 100.000000001 XCTAssertEqual(a, Google_Protobuf_Duration(seconds: 100, nanos: 1)) a = 1.9999999991 - XCTAssertEqual(a, Google_Protobuf_Duration(seconds: 1, nanos: 999999999)) + XCTAssertEqual(a, Google_Protobuf_Duration(seconds: 1, nanos: 999_999_999)) a = 1.9999999999 XCTAssertEqual(a, Google_Protobuf_Duration(seconds: 2, nanos: 0)) @@ -221,17 +263,17 @@ final class Test_Duration: XCTestCase, PBTestHelpers { // Negative interval let t1 = Google_Protobuf_Duration(timeInterval: -123.456) XCTAssertEqual(t1.seconds, -123) - XCTAssertEqual(t1.nanos, -456000000) + XCTAssertEqual(t1.nanos, -456_000_000) // Full precision let t2 = Google_Protobuf_Duration(timeInterval: -123.999999999) XCTAssertEqual(t2.seconds, -123) - XCTAssertEqual(t2.nanos, -999999999) + XCTAssertEqual(t2.nanos, -999_999_999) // Round up let t3 = Google_Protobuf_Duration(timeInterval: -123.9999999994) XCTAssertEqual(t3.seconds, -123) - XCTAssertEqual(t3.nanos, -999999999) + XCTAssertEqual(t3.nanos, -999_999_999) // Round down let t4 = Google_Protobuf_Duration(timeInterval: -123.9999999996) @@ -245,17 +287,17 @@ final class Test_Duration: XCTestCase, PBTestHelpers { // Positive interval let t6 = Google_Protobuf_Duration(timeInterval: 123.456) XCTAssertEqual(t6.seconds, 123) - XCTAssertEqual(t6.nanos, 456000000) + XCTAssertEqual(t6.nanos, 456_000_000) // Full precision let t7 = Google_Protobuf_Duration(timeInterval: 123.999999999) XCTAssertEqual(t7.seconds, 123) - XCTAssertEqual(t7.nanos, 999999999) + XCTAssertEqual(t7.nanos, 999_999_999) // Round down let t8 = Google_Protobuf_Duration(timeInterval: 123.9999999994) XCTAssertEqual(t8.seconds, 123) - XCTAssertEqual(t8.nanos, 999999999) + XCTAssertEqual(t8.nanos, 999_999_999) // Round up let t9 = Google_Protobuf_Duration(timeInterval: 123.9999999996) @@ -264,10 +306,10 @@ final class Test_Duration: XCTestCase, PBTestHelpers { } func testGetters() throws { - let t1 = Google_Protobuf_Duration(seconds: -123, nanos: -123456789) + let t1 = Google_Protobuf_Duration(seconds: -123, nanos: -123_456_789) XCTAssertEqual(t1.timeInterval, -123.123456789) - let t2 = Google_Protobuf_Duration(seconds: 123, nanos: 123456789) + let t2 = Google_Protobuf_Duration(seconds: 123, nanos: 123_456_789) XCTAssertEqual(t2.timeInterval, 123.123456789) } } diff --git a/Tests/SwiftProtobufTests/Test_Empty.swift b/Tests/SwiftProtobufTests/Test_Empty.swift index d49b5c755..890e0a8f2 100644 --- a/Tests/SwiftProtobufTests/Test_Empty.swift +++ b/Tests/SwiftProtobufTests/Test_Empty.swift @@ -15,8 +15,8 @@ // ----------------------------------------------------------------------------- import Foundation -import XCTest import SwiftProtobuf +import XCTest final class Test_Empty: XCTestCase, PBTestHelpers { typealias MessageTestType = Google_Protobuf_Empty diff --git a/Tests/SwiftProtobufTests/Test_Enum.swift b/Tests/SwiftProtobufTests/Test_Enum.swift index 3af7876b2..87f981e1f 100644 --- a/Tests/SwiftProtobufTests/Test_Enum.swift +++ b/Tests/SwiftProtobufTests/Test_Enum.swift @@ -15,8 +15,8 @@ // ----------------------------------------------------------------------------- import Foundation -import XCTest import SwiftProtobuf +import XCTest final class Test_Enum: XCTestCase, PBTestHelpers { typealias MessageTestType = SwiftProtoTesting_Proto3_TestAllTypes @@ -57,7 +57,7 @@ final class Test_Enum: XCTestCase, PBTestHelpers { m.optionalNestedEnum == .zero } assertJSONDecodeSucceeds(json_with_unknown_enum_repeated, options: options) { (m: MessageTestType) -> Bool in - m.repeatedNestedEnum == [.foo, .bar] + m.repeatedNestedEnum == [.foo, .bar] } // SwiftProtoTesting_Proto3_TestAllTypes doesn't have a map<>, Test_Map_JSON covers this case. @@ -106,7 +106,7 @@ final class Test_Enum: XCTestCase, PBTestHelpers { func testEnumPrefixStripping_TextFormat() throws { var txt = "values1: [ENUM_TEST_1_FIRST_VALUE, ENUM_TEST_1_SECOND_VALUE]\n" var msg = SwiftProtoTesting_Enum3_SwiftEnumTest.with { - $0.values1 = [ .firstValue, .secondValue ] + $0.values1 = [.firstValue, .secondValue] } XCTAssertEqual(msg.textFormatString(), txt) var msg2 = try SwiftProtoTesting_Enum3_SwiftEnumTest(textFormatString: txt) @@ -114,7 +114,7 @@ final class Test_Enum: XCTestCase, PBTestHelpers { txt = "values2: [ENUM_TEST_2_FIRST_VALUE, SECOND_VALUE]\n" msg = SwiftProtoTesting_Enum3_SwiftEnumTest.with { - $0.values2 = [ .firstValue, .secondValue ] + $0.values2 = [.firstValue, .secondValue] } XCTAssertEqual(msg.textFormatString(), txt) msg2 = try SwiftProtoTesting_Enum3_SwiftEnumTest(textFormatString: txt) @@ -122,7 +122,7 @@ final class Test_Enum: XCTestCase, PBTestHelpers { txt = "values3: [ENUM_TEST_NO_STEM_1, ENUM_TEST_NO_STEM_2]\n" msg = SwiftProtoTesting_Enum3_SwiftEnumTest.with { - $0.values3 = [ .enumTestNoStem1, .enumTestNoStem2 ] + $0.values3 = [.enumTestNoStem1, .enumTestNoStem2] } XCTAssertEqual(msg.textFormatString(), txt) msg2 = try SwiftProtoTesting_Enum3_SwiftEnumTest(textFormatString: txt) @@ -130,7 +130,7 @@ final class Test_Enum: XCTestCase, PBTestHelpers { txt = "values4: [ENUM_TEST_RESERVED_WORD_VAR, ENUM_TEST_RESERVED_WORD_NOT_RESERVED]\n" msg = SwiftProtoTesting_Enum3_SwiftEnumTest.with { - $0.values4 = [ .var, .notReserved ] + $0.values4 = [.var, .notReserved] } XCTAssertEqual(msg.textFormatString(), txt) msg2 = try SwiftProtoTesting_Enum3_SwiftEnumTest(textFormatString: txt) @@ -140,7 +140,7 @@ final class Test_Enum: XCTestCase, PBTestHelpers { func testEnumPrefixStripping_JSON() throws { var json = "{\"values1\":[\"ENUM_TEST_1_FIRST_VALUE\",\"ENUM_TEST_1_SECOND_VALUE\"]}" var msg = SwiftProtoTesting_Enum3_SwiftEnumTest.with { - $0.values1 = [ .firstValue, .secondValue ] + $0.values1 = [.firstValue, .secondValue] } XCTAssertEqual(try msg.jsonString(), json) var msg2 = try SwiftProtoTesting_Enum3_SwiftEnumTest(jsonString: json) @@ -148,7 +148,7 @@ final class Test_Enum: XCTestCase, PBTestHelpers { json = "{\"values2\":[\"ENUM_TEST_2_FIRST_VALUE\",\"SECOND_VALUE\"]}" msg = SwiftProtoTesting_Enum3_SwiftEnumTest.with { - $0.values2 = [ .firstValue, .secondValue ] + $0.values2 = [.firstValue, .secondValue] } XCTAssertEqual(try msg.jsonString(), json) msg2 = try SwiftProtoTesting_Enum3_SwiftEnumTest(jsonString: json) @@ -156,7 +156,7 @@ final class Test_Enum: XCTestCase, PBTestHelpers { json = "{\"values3\":[\"ENUM_TEST_NO_STEM_1\",\"ENUM_TEST_NO_STEM_2\"]}" msg = SwiftProtoTesting_Enum3_SwiftEnumTest.with { - $0.values3 = [ .enumTestNoStem1, .enumTestNoStem2 ] + $0.values3 = [.enumTestNoStem1, .enumTestNoStem2] } XCTAssertEqual(try msg.jsonString(), json) msg2 = try SwiftProtoTesting_Enum3_SwiftEnumTest(jsonString: json) @@ -164,7 +164,7 @@ final class Test_Enum: XCTestCase, PBTestHelpers { json = "{\"values4\":[\"ENUM_TEST_RESERVED_WORD_VAR\",\"ENUM_TEST_RESERVED_WORD_NOT_RESERVED\"]}" msg = SwiftProtoTesting_Enum3_SwiftEnumTest.with { - $0.values4 = [ .var, .notReserved ] + $0.values4 = [.var, .notReserved] } XCTAssertEqual(try msg.jsonString(), json) msg2 = try SwiftProtoTesting_Enum3_SwiftEnumTest(jsonString: json) @@ -172,26 +172,26 @@ final class Test_Enum: XCTestCase, PBTestHelpers { } func testCaseIterable() { - // Open enums require the generator to create allCases, - // ensure it is works as expected (order of the file, no aliases). - var i = SwiftProtoTesting_Enum3_SwiftEnumWithAliasTest.EnumWithAlias.allCases.makeIterator() - guard let e1 = i.next() else { - XCTFail("Couldn't get first value") - return - } - guard let e2 = i.next() else { - XCTFail("Couldn't get second value") - return - } - guard let e3 = i.next() else { - XCTFail("Couldn't get third value") - return - } - // Should be the end. - XCTAssertNil(i.next()) - - XCTAssertEqual(e1, .foo1) - XCTAssertEqual(e2, .baz1) - XCTAssertEqual(e3, .bar1) + // Open enums require the generator to create allCases, + // ensure it is works as expected (order of the file, no aliases). + var i = SwiftProtoTesting_Enum3_SwiftEnumWithAliasTest.EnumWithAlias.allCases.makeIterator() + guard let e1 = i.next() else { + XCTFail("Couldn't get first value") + return + } + guard let e2 = i.next() else { + XCTFail("Couldn't get second value") + return + } + guard let e3 = i.next() else { + XCTFail("Couldn't get third value") + return + } + // Should be the end. + XCTAssertNil(i.next()) + + XCTAssertEqual(e1, .foo1) + XCTAssertEqual(e2, .baz1) + XCTAssertEqual(e3, .bar1) } } diff --git a/Tests/SwiftProtobufTests/Test_EnumWithAliases.swift b/Tests/SwiftProtobufTests/Test_EnumWithAliases.swift index 7a15b7986..ea02ba034 100644 --- a/Tests/SwiftProtobufTests/Test_EnumWithAliases.swift +++ b/Tests/SwiftProtobufTests/Test_EnumWithAliases.swift @@ -18,45 +18,45 @@ import Foundation import XCTest final class Test_EnumWithAliases: XCTestCase, PBTestHelpers { - typealias MessageTestType = SwiftProtoTesting_Enum2_SwiftEnumWithAliasTest + typealias MessageTestType = SwiftProtoTesting_Enum2_SwiftEnumWithAliasTest - func testJSONEncodeUsesOriginalNames() { - assertJSONEncode("{\"values\":[\"FOO1\",\"BAR1\"]}") { (m: inout MessageTestType) in - m.values = [.foo1, .bar1] - } + func testJSONEncodeUsesOriginalNames() { + assertJSONEncode("{\"values\":[\"FOO1\",\"BAR1\"]}") { (m: inout MessageTestType) in + m.values = [.foo1, .bar1] + } - assertJSONEncode("{\"values\":[\"FOO1\",\"BAR1\"]}") { (m: inout MessageTestType) in - m.values = [.foo2, .bar2] + assertJSONEncode("{\"values\":[\"FOO1\",\"BAR1\"]}") { (m: inout MessageTestType) in + m.values = [.foo2, .bar2] + } } - } - func testJSONDecodeAcceptsAllNames() throws { - assertJSONDecodeSucceeds("{\"values\":[\"FOO1\",\"BAR1\"]}") { (m: MessageTestType) in - return m.values == [.foo1, .bar1] - } + func testJSONDecodeAcceptsAllNames() throws { + assertJSONDecodeSucceeds("{\"values\":[\"FOO1\",\"BAR1\"]}") { (m: MessageTestType) in + m.values == [.foo1, .bar1] + } - assertJSONDecodeSucceeds("{\"values\":[\"FOO2\",\"BAR2\"]}") { (m: MessageTestType) in - return m.values == [.foo1, .bar1] + assertJSONDecodeSucceeds("{\"values\":[\"FOO2\",\"BAR2\"]}") { (m: MessageTestType) in + m.values == [.foo1, .bar1] + } } - } - func testTextFormatEncodeUsesOriginalNames() { - assertTextFormatEncode("values: [FOO1, BAR1]\n") { (m: inout MessageTestType) in - m.values = [.foo1, .bar1] - } + func testTextFormatEncodeUsesOriginalNames() { + assertTextFormatEncode("values: [FOO1, BAR1]\n") { (m: inout MessageTestType) in + m.values = [.foo1, .bar1] + } - assertTextFormatEncode("values: [FOO1, BAR1]\n") { (m: inout MessageTestType) in - m.values = [.foo2, .bar2] + assertTextFormatEncode("values: [FOO1, BAR1]\n") { (m: inout MessageTestType) in + m.values = [.foo2, .bar2] + } } - } - func testTextFormatDecodeAcceptsAllNames() throws { - assertTextFormatDecodeSucceeds("values: [FOO1, BAR1]\n") { (m: MessageTestType) in - return m.values == [.foo1, .bar1] - } + func testTextFormatDecodeAcceptsAllNames() throws { + assertTextFormatDecodeSucceeds("values: [FOO1, BAR1]\n") { (m: MessageTestType) in + m.values == [.foo1, .bar1] + } - assertTextFormatDecodeSucceeds("values: [FOO2, BAR2]\n") { (m: MessageTestType) in - return m.values == [.foo1, .bar1] + assertTextFormatDecodeSucceeds("values: [FOO2, BAR2]\n") { (m: MessageTestType) in + m.values == [.foo1, .bar1] + } } - } } diff --git a/Tests/SwiftProtobufTests/Test_Enum_Proto2.swift b/Tests/SwiftProtobufTests/Test_Enum_Proto2.swift index f1cfefd53..0861ac9ed 100644 --- a/Tests/SwiftProtobufTests/Test_Enum_Proto2.swift +++ b/Tests/SwiftProtobufTests/Test_Enum_Proto2.swift @@ -103,7 +103,7 @@ final class Test_Enum_Proto2: XCTestCase, PBTestHelpers { func testEnumPrefixStripping_TextFormat() throws { var txt = "values1: ENUM_TEST_1_FIRST_VALUE\nvalues1: ENUM_TEST_1_SECOND_VALUE\n" var msg = SwiftProtoTesting_Enum2_SwiftEnumTest.with { - $0.values1 = [ .firstValue, .secondValue ] + $0.values1 = [.firstValue, .secondValue] } XCTAssertEqual(msg.textFormatString(), txt) var msg2 = try SwiftProtoTesting_Enum2_SwiftEnumTest(textFormatString: txt) @@ -111,7 +111,7 @@ final class Test_Enum_Proto2: XCTestCase, PBTestHelpers { txt = "values2: ENUM_TEST_2_FIRST_VALUE\nvalues2: SECOND_VALUE\n" msg = SwiftProtoTesting_Enum2_SwiftEnumTest.with { - $0.values2 = [ .firstValue, .secondValue ] + $0.values2 = [.firstValue, .secondValue] } XCTAssertEqual(msg.textFormatString(), txt) msg2 = try SwiftProtoTesting_Enum2_SwiftEnumTest(textFormatString: txt) @@ -119,7 +119,7 @@ final class Test_Enum_Proto2: XCTestCase, PBTestHelpers { txt = "values3: ENUM_TEST_NO_STEM_1\nvalues3: ENUM_TEST_NO_STEM_2\n" msg = SwiftProtoTesting_Enum2_SwiftEnumTest.with { - $0.values3 = [ .enumTestNoStem1, .enumTestNoStem2 ] + $0.values3 = [.enumTestNoStem1, .enumTestNoStem2] } XCTAssertEqual(msg.textFormatString(), txt) msg2 = try SwiftProtoTesting_Enum2_SwiftEnumTest(textFormatString: txt) @@ -127,7 +127,7 @@ final class Test_Enum_Proto2: XCTestCase, PBTestHelpers { txt = "values4: ENUM_TEST_RESERVED_WORD_VAR\nvalues4: ENUM_TEST_RESERVED_WORD_NOT_RESERVED\n" msg = SwiftProtoTesting_Enum2_SwiftEnumTest.with { - $0.values4 = [ .var, .notReserved ] + $0.values4 = [.var, .notReserved] } XCTAssertEqual(msg.textFormatString(), txt) msg2 = try SwiftProtoTesting_Enum2_SwiftEnumTest(textFormatString: txt) @@ -137,7 +137,7 @@ final class Test_Enum_Proto2: XCTestCase, PBTestHelpers { func testEnumPrefixStripping_JSON() throws { var json = "{\"values1\":[\"ENUM_TEST_1_FIRST_VALUE\",\"ENUM_TEST_1_SECOND_VALUE\"]}" var msg = SwiftProtoTesting_Enum2_SwiftEnumTest.with { - $0.values1 = [ .firstValue, .secondValue ] + $0.values1 = [.firstValue, .secondValue] } XCTAssertEqual(try msg.jsonString(), json) var msg2 = try SwiftProtoTesting_Enum2_SwiftEnumTest(jsonString: json) @@ -145,7 +145,7 @@ final class Test_Enum_Proto2: XCTestCase, PBTestHelpers { json = "{\"values2\":[\"ENUM_TEST_2_FIRST_VALUE\",\"SECOND_VALUE\"]}" msg = SwiftProtoTesting_Enum2_SwiftEnumTest.with { - $0.values2 = [ .firstValue, .secondValue ] + $0.values2 = [.firstValue, .secondValue] } XCTAssertEqual(try msg.jsonString(), json) msg2 = try SwiftProtoTesting_Enum2_SwiftEnumTest(jsonString: json) @@ -153,7 +153,7 @@ final class Test_Enum_Proto2: XCTestCase, PBTestHelpers { json = "{\"values3\":[\"ENUM_TEST_NO_STEM_1\",\"ENUM_TEST_NO_STEM_2\"]}" msg = SwiftProtoTesting_Enum2_SwiftEnumTest.with { - $0.values3 = [ .enumTestNoStem1, .enumTestNoStem2 ] + $0.values3 = [.enumTestNoStem1, .enumTestNoStem2] } XCTAssertEqual(try msg.jsonString(), json) msg2 = try SwiftProtoTesting_Enum2_SwiftEnumTest(jsonString: json) @@ -161,7 +161,7 @@ final class Test_Enum_Proto2: XCTestCase, PBTestHelpers { json = "{\"values4\":[\"ENUM_TEST_RESERVED_WORD_VAR\",\"ENUM_TEST_RESERVED_WORD_NOT_RESERVED\"]}" msg = SwiftProtoTesting_Enum2_SwiftEnumTest.with { - $0.values4 = [ .var, .notReserved ] + $0.values4 = [.var, .notReserved] } XCTAssertEqual(try msg.jsonString(), json) msg2 = try SwiftProtoTesting_Enum2_SwiftEnumTest(jsonString: json) @@ -169,27 +169,27 @@ final class Test_Enum_Proto2: XCTestCase, PBTestHelpers { } func testCaseIterable() { - // Closed enums have allCases generated by the compiled, this - // just ensures the generator pereserved the order of the file and - // the handing of aliases doesn't confuse things. - var i = SwiftProtoTesting_Enum2_SwiftEnumWithAliasTest.EnumWithAlias.allCases.makeIterator() - guard let e1 = i.next() else { - XCTFail("Couldn't get first value") - return - } - guard let e2 = i.next() else { - XCTFail("Couldn't get second value") - return - } - guard let e3 = i.next() else { - XCTFail("Couldn't get second value") - return - } - // Should be the end. - XCTAssertNil(i.next()) - - XCTAssertEqual(e1, .foo1) - XCTAssertEqual(e2, .baz1) - XCTAssertEqual(e3, .bar1) + // Closed enums have allCases generated by the compiled, this + // just ensures the generator pereserved the order of the file and + // the handing of aliases doesn't confuse things. + var i = SwiftProtoTesting_Enum2_SwiftEnumWithAliasTest.EnumWithAlias.allCases.makeIterator() + guard let e1 = i.next() else { + XCTFail("Couldn't get first value") + return + } + guard let e2 = i.next() else { + XCTFail("Couldn't get second value") + return + } + guard let e3 = i.next() else { + XCTFail("Couldn't get second value") + return + } + // Should be the end. + XCTAssertNil(i.next()) + + XCTAssertEqual(e1, .foo1) + XCTAssertEqual(e2, .baz1) + XCTAssertEqual(e3, .bar1) } } diff --git a/Tests/SwiftProtobufTests/Test_Extensions.swift b/Tests/SwiftProtobufTests/Test_Extensions.swift index cb7eb9867..b60f7e33b 100644 --- a/Tests/SwiftProtobufTests/Test_Extensions.swift +++ b/Tests/SwiftProtobufTests/Test_Extensions.swift @@ -13,8 +13,8 @@ // ----------------------------------------------------------------------------- import Foundation -import XCTest import SwiftProtobuf +import XCTest // Exercise the support for Proto2 extensions. @@ -22,7 +22,12 @@ final class Test_Extensions: XCTestCase, PBTestHelpers { typealias MessageTestType = SwiftProtoTesting_TestAllExtensions var extensions = SwiftProtobuf.SimpleExtensionMap() - func assertEncode(_ expected: [UInt8], file: XCTestFileArgType = #file, line: UInt = #line, configure: (inout MessageTestType) -> Void) { + func assertEncode( + _ expected: [UInt8], + file: XCTestFileArgType = #file, + line: UInt = #line, + configure: (inout MessageTestType) -> Void + ) { let empty = MessageTestType() var configured = empty configure(&configured) @@ -32,7 +37,12 @@ final class Test_Extensions: XCTestCase, PBTestHelpers { XCTAssert(expected == encoded, "Did not encode correctly: got \(encoded)", file: file, line: line) do { let decoded = try MessageTestType(serializedBytes: encoded, extensions: extensions) - XCTAssert(decoded == configured, "Encode/decode cycle should generate equal object: \(decoded) != \(configured)", file: file, line: line) + XCTAssert( + decoded == configured, + "Encode/decode cycle should generate equal object: \(decoded) != \(configured)", + file: file, + line: line + ) } catch { XCTFail("Failed to decode protobuf: \(encoded)", file: file, line: line) } @@ -41,7 +51,12 @@ final class Test_Extensions: XCTestCase, PBTestHelpers { } } - func assertDecodeSucceeds(_ bytes: [UInt8], file: XCTestFileArgType = #file, line: UInt = #line, check: (MessageTestType) -> Bool) { + func assertDecodeSucceeds( + _ bytes: [UInt8], + file: XCTestFileArgType = #file, + line: UInt = #line, + check: (MessageTestType) -> Bool + ) { do { let decoded = try MessageTestType(serializedBytes: bytes, extensions: extensions) XCTAssert(check(decoded), "Condition failed for \(decoded)", file: file, line: line) @@ -69,18 +84,16 @@ final class Test_Extensions: XCTestCase, PBTestHelpers { } - override func setUp() { // Start with all the extensions from the unittest.proto file: extensions = SwiftProtoTesting_Unittest_Extensions // Append another file's worth: extensions.formUnion(SwiftProtoTesting_Extend_UnittestSwiftExtension_Extensions) // Append an array of extensions - extensions.insert(contentsOf: - [ - Extensions_RepeatedExtensionGroup, - Extensions_ExtensionGroup - ] + extensions.insert(contentsOf: [ + Extensions_RepeatedExtensionGroup, + Extensions_ExtensionGroup, + ] ) } @@ -88,7 +101,7 @@ final class Test_Extensions: XCTestCase, PBTestHelpers { assertEncode([8, 17]) { (o: inout MessageTestType) in o.SwiftProtoTesting_optionalInt32Extension = 17 } - assertDecodeSucceeds([8, 99]) {$0.SwiftProtoTesting_optionalInt32Extension == 99} + assertDecodeSucceeds([8, 99]) { $0.SwiftProtoTesting_optionalInt32Extension == 99 } assertDecodeFails([9]) assertDecodeFails([9, 0]) assertDecodesAsUnknownFields([9, 0, 0, 0, 0, 0, 0, 0, 0]) // Wrong wire type (fixed64), valid as an unknown field @@ -115,7 +128,10 @@ final class Test_Extensions: XCTestCase, PBTestHelpers { m2.SwiftProtoTesting_optionalInt32Extension = 18 XCTAssertNotEqual(m1, m2) - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllExtensions:\n[swift_proto_testing.optional_int32_extension]: 18\n", m2) + assertDebugDescription( + "SwiftProtobufTests.SwiftProtoTesting_TestAllExtensions:\n[swift_proto_testing.optional_int32_extension]: 18\n", + m2 + ) XCTAssertNotEqual(m1.hashValue, m2.hashValue) } @@ -139,20 +155,23 @@ final class Test_Extensions: XCTestCase, PBTestHelpers { assertEncode([114, 5, 104, 101, 108, 108, 111]) { (o: inout MessageTestType) in o.SwiftProtoTesting_optionalStringExtension = "hello" } - assertDecodeSucceeds([114, 2, 97, 98]) {$0.SwiftProtoTesting_optionalStringExtension == "ab"} + assertDecodeSucceeds([114, 2, 97, 98]) { $0.SwiftProtoTesting_optionalStringExtension == "ab" } var m1 = SwiftProtoTesting_TestAllExtensions() m1.SwiftProtoTesting_optionalStringExtension = "ab" - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllExtensions:\n[swift_proto_testing.optional_string_extension]: \"ab\"\n", m1) + assertDebugDescription( + "SwiftProtobufTests.SwiftProtoTesting_TestAllExtensions:\n[swift_proto_testing.optional_string_extension]: \"ab\"\n", + m1 + ) } func test_repeatedInt32Extension() throws { assertEncode([248, 1, 7, 248, 1, 8]) { (o: inout MessageTestType) in o.SwiftProtoTesting_repeatedInt32Extension = [7, 8] } - assertDecodeSucceeds([248, 1, 7]) {$0.SwiftProtoTesting_repeatedInt32Extension == [7]} - assertDecodeSucceeds([248, 1, 7, 248, 1, 8]) {$0.SwiftProtoTesting_repeatedInt32Extension == [7, 8]} - assertDecodeSucceeds([250, 1, 2, 7, 8]) {$0.SwiftProtoTesting_repeatedInt32Extension == [7, 8]} + assertDecodeSucceeds([248, 1, 7]) { $0.SwiftProtoTesting_repeatedInt32Extension == [7] } + assertDecodeSucceeds([248, 1, 7, 248, 1, 8]) { $0.SwiftProtoTesting_repeatedInt32Extension == [7, 8] } + assertDecodeSucceeds([250, 1, 2, 7, 8]) { $0.SwiftProtoTesting_repeatedInt32Extension == [7, 8] } // Verify that the usual array access/modification operations work correctly var m = SwiftProtoTesting_TestAllExtensions() @@ -164,7 +183,10 @@ final class Test_Extensions: XCTestCase, PBTestHelpers { XCTAssertNotEqual(m.SwiftProtoTesting_repeatedInt32Extension, [7, 8]) XCTAssertEqual(m.SwiftProtoTesting_repeatedInt32Extension, [7, 9]) - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllExtensions:\n[swift_proto_testing.repeated_int32_extension]: 7\n[swift_proto_testing.repeated_int32_extension]: 9\n", m) + assertDebugDescription( + "SwiftProtobufTests.SwiftProtoTesting_TestAllExtensions:\n[swift_proto_testing.repeated_int32_extension]: 7\n[swift_proto_testing.repeated_int32_extension]: 9\n", + m + ) XCTAssertFalse(m.SwiftProtoTesting_repeatedInt32Extension.isEmpty) m.SwiftProtoTesting_repeatedInt32Extension = [] @@ -178,13 +200,19 @@ final class Test_Extensions: XCTestCase, PBTestHelpers { assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllExtensions:\n", m) m.SwiftProtoTesting_defaultInt32Extension = 100 XCTAssertEqual(try m.serializedBytes(), [232, 3, 100]) - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllExtensions:\n[swift_proto_testing.default_int32_extension]: 100\n", m) + assertDebugDescription( + "SwiftProtobufTests.SwiftProtoTesting_TestAllExtensions:\n[swift_proto_testing.default_int32_extension]: 100\n", + m + ) m.clearSwiftProtoTesting_defaultInt32Extension() XCTAssertEqual(try m.serializedBytes(), []) assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllExtensions:\n", m) - m.SwiftProtoTesting_defaultInt32Extension = 41 // Default value + m.SwiftProtoTesting_defaultInt32Extension = 41 // Default value XCTAssertEqual(try m.serializedBytes(), [232, 3, 41]) - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_TestAllExtensions:\n[swift_proto_testing.default_int32_extension]: 41\n", m) + assertDebugDescription( + "SwiftProtobufTests.SwiftProtoTesting_TestAllExtensions:\n[swift_proto_testing.default_int32_extension]: 41\n", + m + ) assertEncode([232, 3, 17]) { (o: inout MessageTestType) in o.SwiftProtoTesting_defaultInt32Extension = 17 @@ -220,7 +248,6 @@ final class Test_Extensions: XCTestCase, PBTestHelpers { } } - func test_repeatedGroupExtension() throws { var m = SwiftTestGroupExtensions() var group1 = RepeatedExtensionGroup() diff --git a/Tests/SwiftProtobufTests/Test_ExtremeDefaultValues.swift b/Tests/SwiftProtobufTests/Test_ExtremeDefaultValues.swift index 9bd48f6ad..ccd4de3b5 100644 --- a/Tests/SwiftProtobufTests/Test_ExtremeDefaultValues.swift +++ b/Tests/SwiftProtobufTests/Test_ExtremeDefaultValues.swift @@ -30,37 +30,37 @@ final class Test_ExtremeDefaultValues: XCTestCase { func test_largeUint32() { let m = SwiftProtoTesting_TestExtremeDefaultValues() - XCTAssertEqual(m.largeUint32, 0xFFFFFFFF) + XCTAssertEqual(m.largeUint32, 0xFFFF_FFFF) } func test_largeUint64() { let m = SwiftProtoTesting_TestExtremeDefaultValues() - XCTAssertEqual(m.largeUint64, 0xFFFFFFFFFFFFFFFF) + XCTAssertEqual(m.largeUint64, 0xFFFF_FFFF_FFFF_FFFF) } func test_smallInt32() { let m = SwiftProtoTesting_TestExtremeDefaultValues() - XCTAssertEqual(m.smallInt32, -0x7fffffff) + XCTAssertEqual(m.smallInt32, -0x7fff_ffff) } func test_smallInt64() { let m = SwiftProtoTesting_TestExtremeDefaultValues() - XCTAssertEqual(m.smallInt64, -0x7fffffffffffffff) + XCTAssertEqual(m.smallInt64, -0x7fff_ffff_ffff_ffff) } func test_reallySmallInt32() { let m = SwiftProtoTesting_TestExtremeDefaultValues() - XCTAssertEqual(m.reallySmallInt32, -0x80000000) + XCTAssertEqual(m.reallySmallInt32, -0x8000_0000) } func test_reallySmallInt64() { let m = SwiftProtoTesting_TestExtremeDefaultValues() - XCTAssertEqual(m.reallySmallInt64, -0x8000000000000000) + XCTAssertEqual(m.reallySmallInt64, -0x8000_0000_0000_0000) } func test_utf8String() { let m = SwiftProtoTesting_TestExtremeDefaultValues() - XCTAssertEqual(m.utf8String, "ሴ") // Unicode u1234 + XCTAssertEqual(m.utf8String, "ሴ") // Unicode u1234 XCTAssertEqual(m.utf8String, "\u{1234}") } diff --git a/Tests/SwiftProtobufTests/Test_FieldMask.swift b/Tests/SwiftProtobufTests/Test_FieldMask.swift index be0a8b9b4..7ad8c75c9 100644 --- a/Tests/SwiftProtobufTests/Test_FieldMask.swift +++ b/Tests/SwiftProtobufTests/Test_FieldMask.swift @@ -14,8 +14,8 @@ // ----------------------------------------------------------------------------- import Foundation -import XCTest import SwiftProtobuf +import XCTest final class Test_FieldMask: XCTestCase, PBTestHelpers { typealias MessageTestType = Google_Protobuf_FieldMask @@ -41,8 +41,8 @@ final class Test_FieldMask: XCTestCase, PBTestHelpers { assertJSONDecodeFails("\"foo,,bar\"") assertJSONDecodeFails("\"foo,bar") assertJSONDecodeFails("foo,bar\"") - assertJSONDecodeFails("\"H̱ܻ̻ܻ̻ܶܶAܻD\"") // Reject non-ASCII - assertJSONDecodeFails("abc_def") // Reject underscores + assertJSONDecodeFails("\"H̱ܻ̻ܻ̻ܶܶAܻD\"") // Reject non-ASCII + assertJSONDecodeFails("abc_def") // Reject underscores } func testProtobuf() { @@ -79,7 +79,9 @@ final class Test_FieldMask: XCTestCase, PBTestHelpers { // Make sure field mask works correctly when stored in a field func testJSON_field() throws { do { - let valid = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: "{\"optionalFieldMask\": \"foo,barBaz\"}") + let valid = try SwiftProtoTesting_Test3_TestAllTypesProto3( + jsonString: "{\"optionalFieldMask\": \"foo,barBaz\"}" + ) XCTAssertEqual(valid.optionalFieldMask, Google_Protobuf_FieldMask(protoPaths: "foo", "bar_baz")) } catch { XCTFail("Should have decoded correctly") @@ -94,7 +96,9 @@ final class Test_FieldMask: XCTestCase, PBTestHelpers { XCTFail("Should have decoded correctly") } - XCTAssertThrowsError(try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: "{\"optionalFieldMask\": \"foo,bar_bar\"}")) + XCTAssertThrowsError( + try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: "{\"optionalFieldMask\": \"foo,bar_bar\"}") + ) } func testSerializationFailure() { @@ -106,7 +110,7 @@ final class Test_FieldMask: XCTestCase, PBTestHelpers { XCTAssertThrowsError(try m.jsonString()) } } - + // Checks merge functionality for field masks. func testMergeFieldsOfMessage() throws { var message = SwiftProtoTesting_TestAllTypes.with { model in @@ -254,19 +258,19 @@ final class Test_FieldMask: XCTestCase, PBTestHelpers { let m1 = Google_Protobuf_FieldMask() let m2 = Google_Protobuf_FieldMask(protoPaths: [ "optional_int32", - "optional_nested_message.bb" + "optional_nested_message.bb", ]) let m3 = Google_Protobuf_FieldMask(protoPaths: [ "optional_int32", - "optional_nested_message" + "optional_nested_message", ]) let m4 = Google_Protobuf_FieldMask(protoPaths: [ "optional_int32", - "optional_nested_message.bc" + "optional_nested_message.bc", ]) let m5 = Google_Protobuf_FieldMask(protoPaths: [ "optional_int", - "optional_nested_message.bb" + "optional_nested_message.bb", ]) XCTAssertTrue(m1.isValid(for: SwiftProtoTesting_TestAllTypes.self)) XCTAssertTrue(m2.isValid(for: SwiftProtoTesting_TestAllTypes.self)) @@ -618,7 +622,7 @@ final class Test_FieldMask: XCTestCase, PBTestHelpers { "singular_fixed32", "singular_fixed64", "singular_sfixed32", - "singular_sfixed64" + "singular_sfixed64", ]) try m1.merge(from: m2, fieldMask: mask) XCTAssertEqual(m1.singularInt32, m2.singularInt32) @@ -725,24 +729,27 @@ final class Test_FieldMask: XCTestCase, PBTestHelpers { m.optionalNestedEnum = .bar } let m2 = SwiftProtoTesting_Proto3_TestAllTypes() - try m1.merge(from: m2, fieldMask: .init(protoPaths: [ - "optional_int32", - "optional_int64", - "optional_double", - "optional_float", - "optional_string", - "optional_bool", - "optional_bytes", - "optional_uint32", - "optional_uint64", - "optional_sint32", - "optional_sint64", - "optional_fixed32", - "optional_fixed64", - "optional_sfixed32", - "optional_sfixed64", - "optional_nested_enum" - ])) + try m1.merge( + from: m2, + fieldMask: .init(protoPaths: [ + "optional_int32", + "optional_int64", + "optional_double", + "optional_float", + "optional_string", + "optional_bool", + "optional_bytes", + "optional_uint32", + "optional_uint64", + "optional_sint32", + "optional_sint64", + "optional_fixed32", + "optional_fixed64", + "optional_sfixed32", + "optional_sfixed64", + "optional_nested_enum", + ]) + ) XCTAssertEqual(m1.optionalInt32, m2.optionalInt32) XCTAssertEqual(m1.optionalInt64, m2.optionalInt64) XCTAssertEqual(m1.optionalDouble, m2.optionalDouble) diff --git a/Tests/SwiftProtobufTests/Test_FieldOrdering.swift b/Tests/SwiftProtobufTests/Test_FieldOrdering.swift index 3bccb71b1..ccfaf5ff3 100644 --- a/Tests/SwiftProtobufTests/Test_FieldOrdering.swift +++ b/Tests/SwiftProtobufTests/Test_FieldOrdering.swift @@ -37,10 +37,22 @@ final class Test_FieldOrdering: XCTestCase { m.oneofInt32 = 7 let encoded1: [UInt8] = try m.serializedBytes() - XCTAssertEqual([8, 1, 40, 12, 80, 7, 90, 3, 97, 98, 99, 146, 3, 3, 100, 101, 102, 173, 6, 0, 0, 128, 63, 194, 12, 4, 8, 2, 16, 1], encoded1) + XCTAssertEqual( + [ + 8, 1, 40, 12, 80, 7, 90, 3, 97, 98, 99, 146, 3, 3, 100, 101, 102, 173, 6, 0, 0, 128, 63, 194, 12, 4, 8, + 2, 16, 1, + ], + encoded1 + ) m.oneofInt64 = 8 let encoded2: [UInt8] = try m.serializedBytes() - XCTAssertEqual([8, 1, 40, 12, 90, 3, 97, 98, 99, 146, 3, 3, 100, 101, 102, 224, 3, 8, 173, 6, 0, 0, 128, 63, 194, 12, 4, 8, 2, 16, 1], encoded2) + XCTAssertEqual( + [ + 8, 1, 40, 12, 90, 3, 97, 98, 99, 146, 3, 3, 100, 101, 102, 224, 3, 8, 173, 6, 0, 0, 128, 63, 194, 12, 4, + 8, 2, 16, 1, + ], + encoded2 + ) } } diff --git a/Tests/SwiftProtobufTests/Test_FuzzTests.swift b/Tests/SwiftProtobufTests/Test_FuzzTests.swift index 463c7a056..c5f9ec184 100644 --- a/Tests/SwiftProtobufTests/Test_FuzzTests.swift +++ b/Tests/SwiftProtobufTests/Test_FuzzTests.swift @@ -21,143 +21,197 @@ // Easily get the hex dumps via `xxd -i < _test_case_file_` import Foundation -import XCTest - import SwiftProtobuf +import XCTest final class Test_FuzzTests: XCTestCase { - func assertBinaryFails(_ bytes: [UInt8], file: XCTestFileArgType = #file, line: UInt = #line) { - XCTAssertThrowsError( - try SwiftProtoTesting_Fuzz_Message(serializedBytes: bytes, extensions: SwiftProtoTesting_Fuzz_FuzzTesting_Extensions), - file: file, line: line) - } - - func assertJSONFails(_ jsonBytes: [UInt8], file: XCTestFileArgType = #file, line: UInt = #line) { - XCTAssertThrowsError( - try SwiftProtoTesting_Fuzz_Message(jsonUTF8Bytes: jsonBytes, extensions: SwiftProtoTesting_Fuzz_FuzzTesting_Extensions), - file: file, line: line) - } - - func assertJSONFails(_ json: String, file: XCTestFileArgType = #file, line: UInt = #line) { - XCTAssertThrowsError( - try SwiftProtoTesting_Fuzz_Message(jsonString: json, extensions: SwiftProtoTesting_Fuzz_FuzzTesting_Extensions), - file: file, line: line) - } - - func assertTextFormatFails(_ textFormat: String, options: TextFormatDecodingOptions = TextFormatDecodingOptions(), file: XCTestFileArgType = #file, line: UInt = #line) { - XCTAssertThrowsError( - try SwiftProtoTesting_Fuzz_Message(textFormatString: textFormat, - options: options, - extensions: SwiftProtoTesting_Fuzz_FuzzTesting_Extensions), - file: file, line: line) - } - - func assertTextFormatFails(_ asBytes: [UInt8], options: TextFormatDecodingOptions = TextFormatDecodingOptions(), file: XCTestFileArgType = #file, line: UInt = #line) { - guard let str = String(data: Data(asBytes), encoding: .utf8) else { - print( - """ - Failed to make string (at \(file):\(line)): nothing to try and decode. - The fuzzer does not fail in this case and neither should we, skipping test. - """ - ) - return + func assertBinaryFails(_ bytes: [UInt8], file: XCTestFileArgType = #file, line: UInt = #line) { + XCTAssertThrowsError( + try SwiftProtoTesting_Fuzz_Message( + serializedBytes: bytes, + extensions: SwiftProtoTesting_Fuzz_FuzzTesting_Extensions + ), + file: file, + line: line + ) + } + + func assertJSONFails(_ jsonBytes: [UInt8], file: XCTestFileArgType = #file, line: UInt = #line) { + XCTAssertThrowsError( + try SwiftProtoTesting_Fuzz_Message( + jsonUTF8Bytes: jsonBytes, + extensions: SwiftProtoTesting_Fuzz_FuzzTesting_Extensions + ), + file: file, + line: line + ) + } + + func assertJSONFails(_ json: String, file: XCTestFileArgType = #file, line: UInt = #line) { + XCTAssertThrowsError( + try SwiftProtoTesting_Fuzz_Message( + jsonString: json, + extensions: SwiftProtoTesting_Fuzz_FuzzTesting_Extensions + ), + file: file, + line: line + ) + } + + func assertTextFormatFails( + _ textFormat: String, + options: TextFormatDecodingOptions = TextFormatDecodingOptions(), + file: XCTestFileArgType = #file, + line: UInt = #line + ) { + XCTAssertThrowsError( + try SwiftProtoTesting_Fuzz_Message( + textFormatString: textFormat, + options: options, + extensions: SwiftProtoTesting_Fuzz_FuzzTesting_Extensions + ), + file: file, + line: line + ) + } + + func assertTextFormatFails( + _ asBytes: [UInt8], + options: TextFormatDecodingOptions = TextFormatDecodingOptions(), + file: XCTestFileArgType = #file, + line: UInt = #line + ) { + guard let str = String(data: Data(asBytes), encoding: .utf8) else { + print( + """ + Failed to make string (at \(file):\(line)): nothing to try and decode. + The fuzzer does not fail in this case and neither should we, skipping test. + """ + ) + return + } + XCTAssertThrowsError( + try SwiftProtoTesting_Fuzz_Message( + textFormatString: str, + options: options, + extensions: SwiftProtoTesting_Fuzz_FuzzTesting_Extensions + ), + file: file, + line: line + ) + } + + func assertTextFormatSucceeds( + _ textFormat: String, + options: TextFormatDecodingOptions = TextFormatDecodingOptions(), + file: XCTestFileArgType = #file, + line: UInt = #line + ) { + XCTAssertNoThrow( + try SwiftProtoTesting_Fuzz_Message( + textFormatString: textFormat, + options: options, + extensions: SwiftProtoTesting_Fuzz_FuzzTesting_Extensions + ), + file: file, + line: line + ) + } + + func assertTextFormatSucceeds( + _ asBytes: [UInt8], + options: TextFormatDecodingOptions = TextFormatDecodingOptions(), + file: XCTestFileArgType = #file, + line: UInt = #line + ) { + guard let str = String(data: Data(asBytes), encoding: .utf8) else { + print( + """ + Failed to make string (at \(file):\(line)): nothing to try and decode. + The fuzzer does not fail in this case and neither should we, skipping test. + """ + ) + return + } + XCTAssertNoThrow( + try SwiftProtoTesting_Fuzz_Message( + textFormatString: str, + options: options, + extensions: SwiftProtoTesting_Fuzz_FuzzTesting_Extensions + ), + file: file, + line: line + ) + } + + func test_Binary() { + // FailCases/Binary-packed-float-double-growth + assertBinaryFails([ + 0x8a, 0x41, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0x8a, 0x41, 0x8d, + 0x8c, + ]) + } + + func test_JSON() { + // FailCases/JSON-malformed-utf8 + assertJSONFails([ + 0x7b, 0x22, 0xf4, 0x7b, 0x22, 0x3a, 0x5c, 0x00, 0x2e, 0x20, 0x22, 0x3a, + 0x5c, 0x00, 0x2e, 0x20, + ]) + + // FailCases/clusterfuzz-testcase-minimized-FuzzJSON_debug-4506617283477504 + // FailCases/clusterfuzz-testcase-minimized-FuzzJSON_release-5689942715006976 + assertJSONFails("{\"[fuzz.testing.singular_sint32_ext]\":null") + + // FailCases/JSON-Any + assertJSONFails(" {\"wktAny\":{\"ny\":{") + + // FuzzTesting/FailCases/clusterfuzz-testcase-minimized-FuzzJSON_release-4929034878844928 + // This actually fails when the fuzzer was trying to write it back out again. + let msg = try! SwiftProtoTesting_Fuzz_Message(jsonString: " {\"wktAny\": {}} ") + XCTAssertEqual(try! msg.jsonString(), "{\"wktAny\":{}}") + + // FailCases/clusterfuzz-testcase-minimized-FuzzJSON_debug-6286338012282880 + assertJSONFails("{\"wktTimestamp\":\"9999-12-31T23:59:60Z\"}") } - XCTAssertThrowsError( - try SwiftProtoTesting_Fuzz_Message(textFormatString: str, - options: options, - extensions: SwiftProtoTesting_Fuzz_FuzzTesting_Extensions), - file: file, line: line) - } - - func assertTextFormatSucceeds(_ textFormat: String, options: TextFormatDecodingOptions = TextFormatDecodingOptions(), file: XCTestFileArgType = #file, line: UInt = #line) { - XCTAssertNoThrow( - try SwiftProtoTesting_Fuzz_Message(textFormatString: textFormat, - options: options, - extensions: SwiftProtoTesting_Fuzz_FuzzTesting_Extensions), - file: file, line: line) - } - - func assertTextFormatSucceeds(_ asBytes: [UInt8], options: TextFormatDecodingOptions = TextFormatDecodingOptions(), file: XCTestFileArgType = #file, line: UInt = #line) { - guard let str = String(data: Data(asBytes), encoding: .utf8) else { - print( - """ - Failed to make string (at \(file):\(line)): nothing to try and decode. - The fuzzer does not fail in this case and neither should we, skipping test. - """ - ) - return + + func test_TextFormat() { + // FailCases/TextFormat-map-loops-forever + // FailCases/TextFormat-map-loops-forever2 + assertTextFormatFails("104<") + assertTextFormatFails("104{") + + // FailCases/TextFormat-octal-out-of-range + assertTextFormatSucceeds([ + 0x34, 0x34, 0x3a, 0x27, 0x32, 0x5c, 0x35, 0x30, 0x31, 0x39, 0x31, 0x3c, + 0x31, 0x0f, 0x3a, 0x27, + ]) + + // FailCases/TextFormat-ending-zero + assertTextFormatSucceeds(" 1:0 1:0 1:0") + // FailCases/TextFormat-ending-minus + assertTextFormatFails(" 1:0 1:0 5:-") + + // FailCases/clusterfuzz-testcase-minimized-FuzzTextFormat_release-5836572361621504 + assertTextFormatFails([ + 0x31, 0x35, 0x3a, 0x27, 0xa9, 0xa9, 0x5c, 0x75, 0x41, 0x62, + ]) + + assertTextFormatSucceeds( + "500<[google.protobuf.Any]<[google.protobuf.Any]<[google.protobuf.Any]<[google.protobuf.Any]<[google.protobuf.Any]<[google.protobuf.Any]<[google.protobuf.Any]<[google.protobuf.Any]<[google.protobuf.Any]<[google.protobuf.Any]<[google.protobuf.Any]<[google.protobuf.Any]<[google.protobuf.Any]<[google.protobuf.Any]<[google.protobuf.Any]<>>>>>>>>>>>>>>>>500<1:''\n2:''>" + ) + + assertTextFormatFails( + "500<[fvwzz_exobuf.Aob/google.protobuf.Any]<[oeFgb/google.protobuf.Any]<[xlob/google.protobuf.Any]<[oeee0FFFFgb/google.protobuf.Any]<[oglob/google.protobuf.Any]<[oogoFFFFFFFFRFfuzz.tebool_extFFFFFFFBFFFFegleeeeeeeeeeeeeeeeeeemeeeeeeeeeeeneeeeeeeekeeeeFFFFFFFFFIFFFFFFFgb/google.protobuf.Any]<[oglob/google.protobuf.Any]<[oogoFFFFFFFFRFfuzz.tebool_extFFFFFFFBFFFFegleeeeeeeeeeeeeeeeeeemeeeeeeeeeeeneeeeeeeekeeeeFFFFFFFFFIFFFFFFFgb/google.protobuf.Any]<[oglob/google.protobuf.Any]<[oogoFFFFFFFFRFfuzz.tebool_extFFFFFFFBFFFFegleeeeeeeeeeeeeeeeeeemeeeeeeeeeeeneeeeeeeekeeeeFFFFFFFFFIFFFFFFFgb/google.protobuf.Any]<[oglob/google.protobuf.Any]<[oogoFFFFFFFFRFfuzz.tebool_extFFFFFFFBFFFFegleeeeeeeeeeeeeeeeeeemeeeeeeeeeeeneeeeeeeekeeeeFFFFFFFFFIFFFFFFFgb/google.protobuf.Any]<[oglob/google.protobuf.Any]<[oogoFFFFFFFFRFfuzz.tebool_extFFFFFFFBFFFFegleeeeeeeeeeeeeeeeeeemeeeeeeeeeeeneeeeeeeekeeeeFFFFFFFFFIFFFFFFFgb/google.protobuf.Any]<[oglob/google.protobuf.Any]<[oogoFFFFFFFFRFfuzz.tebool_extFFFFFFFBFFFFegleeeeeeeeeeeeeeeeeeemeeeeeeeeeeeneeeeeeeekeeeeFFFFFFFFFIFFFFFFFgb/google.protobuf.Any]<>>>>>>>>>>>>>>>>>500<1:''\n1:''\n1:''\n2:''\n1:'roto" + ) + + // FailCases/clusterfuzz-testcase-FuzzTextFormat_release-4619956026146816 + // FailCases/clusterfuzz-testcase-minimized-FuzzTextFormat_release-4619956026146816 + var opts = TextFormatDecodingOptions() + opts.ignoreUnknownFields = true + opts.ignoreUnknownExtensionFields = true + assertTextFormatFails("rsingular_sint:-", options: opts) + assertTextFormatFails(" l :-", options: opts) } - XCTAssertNoThrow( - try SwiftProtoTesting_Fuzz_Message(textFormatString: str, - options: options, - extensions: SwiftProtoTesting_Fuzz_FuzzTesting_Extensions), - file: file, line: line) - } - - func test_Binary() { - // FailCases/Binary-packed-float-double-growth - assertBinaryFails([ - 0x8a, 0x41, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0x8a, 0x41, 0x8d, - 0x8c, - ]) - } - - func test_JSON() { - // FailCases/JSON-malformed-utf8 - assertJSONFails([ - 0x7b, 0x22, 0xf4, 0x7b, 0x22, 0x3a, 0x5c, 0x00, 0x2e, 0x20, 0x22, 0x3a, - 0x5c, 0x00, 0x2e, 0x20 - ]) - - // FailCases/clusterfuzz-testcase-minimized-FuzzJSON_debug-4506617283477504 - // FailCases/clusterfuzz-testcase-minimized-FuzzJSON_release-5689942715006976 - assertJSONFails("{\"[fuzz.testing.singular_sint32_ext]\":null") - - // FailCases/JSON-Any - assertJSONFails(" {\"wktAny\":{\"ny\":{") - - // FuzzTesting/FailCases/clusterfuzz-testcase-minimized-FuzzJSON_release-4929034878844928 - // This actually fails when the fuzzer was trying to write it back out again. - let msg = try! SwiftProtoTesting_Fuzz_Message(jsonString: " {\"wktAny\": {}} ") - XCTAssertEqual(try! msg.jsonString(), "{\"wktAny\":{}}") - - // FailCases/clusterfuzz-testcase-minimized-FuzzJSON_debug-6286338012282880 - assertJSONFails("{\"wktTimestamp\":\"9999-12-31T23:59:60Z\"}") - } - - func test_TextFormat() { - // FailCases/TextFormat-map-loops-forever - // FailCases/TextFormat-map-loops-forever2 - assertTextFormatFails("104<") - assertTextFormatFails("104{") - - // FailCases/TextFormat-octal-out-of-range - assertTextFormatSucceeds([ - 0x34, 0x34, 0x3a, 0x27, 0x32, 0x5c, 0x35, 0x30, 0x31, 0x39, 0x31, 0x3c, - 0x31, 0x0f, 0x3a, 0x27 - ]) - - // FailCases/TextFormat-ending-zero - assertTextFormatSucceeds(" 1:0 1:0 1:0") - // FailCases/TextFormat-ending-minus - assertTextFormatFails(" 1:0 1:0 5:-") - - // FailCases/clusterfuzz-testcase-minimized-FuzzTextFormat_release-5836572361621504 - assertTextFormatFails([ - 0x31, 0x35, 0x3a, 0x27, 0xa9, 0xa9, 0x5c, 0x75, 0x41, 0x62 - ]) - - assertTextFormatSucceeds("500<[google.protobuf.Any]<[google.protobuf.Any]<[google.protobuf.Any]<[google.protobuf.Any]<[google.protobuf.Any]<[google.protobuf.Any]<[google.protobuf.Any]<[google.protobuf.Any]<[google.protobuf.Any]<[google.protobuf.Any]<[google.protobuf.Any]<[google.protobuf.Any]<[google.protobuf.Any]<[google.protobuf.Any]<[google.protobuf.Any]<>>>>>>>>>>>>>>>>500<1:''\n2:''>") - - assertTextFormatFails("500<[fvwzz_exobuf.Aob/google.protobuf.Any]<[oeFgb/google.protobuf.Any]<[xlob/google.protobuf.Any]<[oeee0FFFFgb/google.protobuf.Any]<[oglob/google.protobuf.Any]<[oogoFFFFFFFFRFfuzz.tebool_extFFFFFFFBFFFFegleeeeeeeeeeeeeeeeeeemeeeeeeeeeeeneeeeeeeekeeeeFFFFFFFFFIFFFFFFFgb/google.protobuf.Any]<[oglob/google.protobuf.Any]<[oogoFFFFFFFFRFfuzz.tebool_extFFFFFFFBFFFFegleeeeeeeeeeeeeeeeeeemeeeeeeeeeeeneeeeeeeekeeeeFFFFFFFFFIFFFFFFFgb/google.protobuf.Any]<[oglob/google.protobuf.Any]<[oogoFFFFFFFFRFfuzz.tebool_extFFFFFFFBFFFFegleeeeeeeeeeeeeeeeeeemeeeeeeeeeeeneeeeeeeekeeeeFFFFFFFFFIFFFFFFFgb/google.protobuf.Any]<[oglob/google.protobuf.Any]<[oogoFFFFFFFFRFfuzz.tebool_extFFFFFFFBFFFFegleeeeeeeeeeeeeeeeeeemeeeeeeeeeeeneeeeeeeekeeeeFFFFFFFFFIFFFFFFFgb/google.protobuf.Any]<[oglob/google.protobuf.Any]<[oogoFFFFFFFFRFfuzz.tebool_extFFFFFFFBFFFFegleeeeeeeeeeeeeeeeeeemeeeeeeeeeeeneeeeeeeekeeeeFFFFFFFFFIFFFFFFFgb/google.protobuf.Any]<[oglob/google.protobuf.Any]<[oogoFFFFFFFFRFfuzz.tebool_extFFFFFFFBFFFFegleeeeeeeeeeeeeeeeeeemeeeeeeeeeeeneeeeeeeekeeeeFFFFFFFFFIFFFFFFFgb/google.protobuf.Any]<>>>>>>>>>>>>>>>>>500<1:''\n1:''\n1:''\n2:''\n1:'roto") - - // FailCases/clusterfuzz-testcase-FuzzTextFormat_release-4619956026146816 - // FailCases/clusterfuzz-testcase-minimized-FuzzTextFormat_release-4619956026146816 - var opts = TextFormatDecodingOptions() - opts.ignoreUnknownFields = true - opts.ignoreUnknownExtensionFields = true - assertTextFormatFails("rsingular_sint:-", options: opts) - assertTextFormatFails(" l :-", options: opts) - } } diff --git a/Tests/SwiftProtobufTests/Test_GroupWithGroups.swift b/Tests/SwiftProtobufTests/Test_GroupWithGroups.swift index 535a1bfb8..7428e047d 100644 --- a/Tests/SwiftProtobufTests/Test_GroupWithGroups.swift +++ b/Tests/SwiftProtobufTests/Test_GroupWithGroups.swift @@ -12,50 +12,46 @@ import Foundation import XCTest final class Test_GroupWithinGroup: XCTestCase, PBTestHelpers { - typealias MessageTestType = SwiftTestNestingGroupsMessage + typealias MessageTestType = SwiftTestNestingGroupsMessage - func testGroupWithGroup_Single() { - assertEncode([8, 1, 19, 8, 2, 19, 8, 3, 20, 20]) {(o: inout MessageTestType) in - o.outerA = 1 - o.subGroup1.sub1A = 2 - o.subGroup1.subGroup2.sub2A = 3 + func testGroupWithGroup_Single() { + assertEncode([8, 1, 19, 8, 2, 19, 8, 3, 20, 20]) { (o: inout MessageTestType) in + o.outerA = 1 + o.subGroup1.sub1A = 2 + o.subGroup1.subGroup2.sub2A = 3 + } + assertDecodeSucceeds([19, 19, 8, 1, 20, 20]) { + $0.subGroup1.subGroup2.sub2A == 1 + } + // Empty group + assertDecodeSucceeds([19, 19, 20, 20]) { + $0.subGroup1.hasSubGroup2 && $0.subGroup1.subGroup2 == MessageTestType.SubGroup1.SubGroup2() + } + assertDecodeFails([8, 1, 19, 8, 2, 19, 8, 3, 20]) // End group missing. + assertDecodeFails([8, 1, 19, 8, 2, 19, 8, 3, 20, 28]) // Wrong end group. } - assertDecodeSucceeds([19, 19, 8, 1, 20, 20]) { - $0.subGroup1.subGroup2.sub2A == 1 - } - // Empty group - assertDecodeSucceeds([19, 19, 20, 20]) { - $0.subGroup1.hasSubGroup2 && - $0.subGroup1.subGroup2 == MessageTestType.SubGroup1.SubGroup2() - } - assertDecodeFails([8, 1, 19, 8, 2, 19, 8, 3, 20]) // End group missing. - assertDecodeFails([8, 1, 19, 8, 2, 19, 8, 3, 20, 28]) // Wrong end group. - } - func testGroupWithGroup_Repeated() { - assertEncode([8, 4, 27, 8, 5, 19, 8, 6, 20, 28]) {(o: inout MessageTestType) in - var grp2 = MessageTestType.SubGroup3.SubGroup4() - grp2.sub4A = 6 + func testGroupWithGroup_Repeated() { + assertEncode([8, 4, 27, 8, 5, 19, 8, 6, 20, 28]) { (o: inout MessageTestType) in + var grp2 = MessageTestType.SubGroup3.SubGroup4() + grp2.sub4A = 6 - var grp = MessageTestType.SubGroup3() - grp.sub3A = 5 - grp.subGroup4.append(grp2) + var grp = MessageTestType.SubGroup3() + grp.sub3A = 5 + grp.subGroup4.append(grp2) - o.outerA = 4 - o.subGroup3.append(grp) - } - assertDecodeSucceeds([27, 19, 8, 1, 20, 28]) { - $0.subGroup3.count == 1 && - $0.subGroup3[0].subGroup4.count == 1 && - $0.subGroup3[0].subGroup4[0].sub4A == 1 - } - // Empty group - assertDecodeSucceeds([27, 19, 20, 28]) { (o: MessageTestType) -> Bool in - o.subGroup3.count == 1 && - o.subGroup3[0].subGroup4.count == 1 && - o.subGroup3[0].subGroup4[0] == MessageTestType.SubGroup3.SubGroup4() + o.outerA = 4 + o.subGroup3.append(grp) + } + assertDecodeSucceeds([27, 19, 8, 1, 20, 28]) { + $0.subGroup3.count == 1 && $0.subGroup3[0].subGroup4.count == 1 && $0.subGroup3[0].subGroup4[0].sub4A == 1 + } + // Empty group + assertDecodeSucceeds([27, 19, 20, 28]) { (o: MessageTestType) -> Bool in + o.subGroup3.count == 1 && o.subGroup3[0].subGroup4.count == 1 + && o.subGroup3[0].subGroup4[0] == MessageTestType.SubGroup3.SubGroup4() + } + assertDecodeFails([8, 4, 27, 8, 5, 19, 8, 6, 20]) // End group missing. + assertDecodeFails([8, 4, 27, 8, 5, 19, 8, 6, 28, 20]) // Wrong end groups (reversed). } - assertDecodeFails([8, 4, 27, 8, 5, 19, 8, 6, 20]) // End group missing. - assertDecodeFails([8, 4, 27, 8, 5, 19, 8, 6, 28, 20]) // Wrong end groups (reversed). - } } diff --git a/Tests/SwiftProtobufTests/Test_JSON.swift b/Tests/SwiftProtobufTests/Test_JSON.swift index d80dcbcde..3aeb09750 100644 --- a/Tests/SwiftProtobufTests/Test_JSON.swift +++ b/Tests/SwiftProtobufTests/Test_JSON.swift @@ -15,8 +15,8 @@ // ----------------------------------------------------------------------------- import Foundation -import XCTest import SwiftProtobuf +import XCTest final class Test_JSON: XCTestCase, PBTestHelpers { typealias MessageTestType = SwiftProtoTesting_Proto3_TestAllTypes @@ -83,107 +83,106 @@ final class Test_JSON: XCTestCase, PBTestHelpers { } func testMultipleFields() { - let expected: String = ("{" - + "\"optionalInt32\":1," - + "\"optionalInt64\":\"2\"," - + "\"optionalUint32\":3," - + "\"optionalUint64\":\"4\"," - + "\"optionalSint32\":5," - + "\"optionalSint64\":\"6\"," - + "\"optionalFixed32\":7," - + "\"optionalFixed64\":\"8\"," - + "\"optionalSfixed32\":9," - + "\"optionalSfixed64\":\"10\"," - + "\"optionalFloat\":11.0," - + "\"optionalDouble\":12.0," - + "\"optionalBool\":true," - + "\"optionalString\":\"abc\"," - + "\"optionalBytes\":\"QUI=\"," - + "\"optionalNestedMessage\":{\"bb\":7}," - + "\"optionalForeignMessage\":{\"c\":88}," - + "\"optionalImportMessage\":{\"d\":-9}," - + "\"optionalNestedEnum\":\"BAZ\"," - + "\"optionalForeignEnum\":\"FOREIGN_BAZ\"," - + "\"optionalPublicImportMessage\":{\"e\":-999999}," - + "\"repeatedInt32\":[1,2]," - + "\"repeatedInt64\":[\"3\",\"4\"]," - + "\"repeatedUint32\":[5,6]," - + "\"repeatedUint64\":[\"7\",\"8\"]," - + "\"repeatedSint32\":[9,10]," - + "\"repeatedSint64\":[\"11\",\"12\"]," - + "\"repeatedFixed32\":[13,14]," - + "\"repeatedFixed64\":[\"15\",\"16\"]," - + "\"repeatedSfixed32\":[17,18]," - + "\"repeatedSfixed64\":[\"19\",\"20\"]," - + "\"repeatedFloat\":[21.0,22.0]," - + "\"repeatedDouble\":[23.0,24.0]," - + "\"repeatedBool\":[true,false]," - + "\"repeatedString\":[\"abc\",\"def\"]," - + "\"repeatedBytes\":[\"\",\"QUI=\"]," - + "\"repeatedNestedMessage\":[{\"bb\":7},{\"bb\":-7}]," - + "\"repeatedForeignMessage\":[{\"c\":88},{\"c\":-88}]," - + "\"repeatedImportMessage\":[{\"d\":-9},{\"d\":999999}]," - + "\"repeatedNestedEnum\":[\"BAR\",\"BAZ\"]," - + "\"repeatedForeignEnum\":[\"FOREIGN_BAR\",\"FOREIGN_BAZ\"]," - + "\"oneofUint32\":99" - + "}") + let expected: String = + ("{" + + "\"optionalInt32\":1," + + "\"optionalInt64\":\"2\"," + + "\"optionalUint32\":3," + + "\"optionalUint64\":\"4\"," + + "\"optionalSint32\":5," + + "\"optionalSint64\":\"6\"," + + "\"optionalFixed32\":7," + + "\"optionalFixed64\":\"8\"," + + "\"optionalSfixed32\":9," + + "\"optionalSfixed64\":\"10\"," + + "\"optionalFloat\":11.0," + + "\"optionalDouble\":12.0," + + "\"optionalBool\":true," + + "\"optionalString\":\"abc\"," + + "\"optionalBytes\":\"QUI=\"," + + "\"optionalNestedMessage\":{\"bb\":7}," + + "\"optionalForeignMessage\":{\"c\":88}," + + "\"optionalImportMessage\":{\"d\":-9}," + + "\"optionalNestedEnum\":\"BAZ\"," + + "\"optionalForeignEnum\":\"FOREIGN_BAZ\"," + + "\"optionalPublicImportMessage\":{\"e\":-999999}," + + "\"repeatedInt32\":[1,2]," + + "\"repeatedInt64\":[\"3\",\"4\"]," + + "\"repeatedUint32\":[5,6]," + + "\"repeatedUint64\":[\"7\",\"8\"]," + + "\"repeatedSint32\":[9,10]," + + "\"repeatedSint64\":[\"11\",\"12\"]," + + "\"repeatedFixed32\":[13,14]," + + "\"repeatedFixed64\":[\"15\",\"16\"]," + + "\"repeatedSfixed32\":[17,18]," + + "\"repeatedSfixed64\":[\"19\",\"20\"]," + + "\"repeatedFloat\":[21.0,22.0]," + + "\"repeatedDouble\":[23.0,24.0]," + + "\"repeatedBool\":[true,false]," + + "\"repeatedString\":[\"abc\",\"def\"]," + + "\"repeatedBytes\":[\"\",\"QUI=\"]," + + "\"repeatedNestedMessage\":[{\"bb\":7},{\"bb\":-7}]," + + "\"repeatedForeignMessage\":[{\"c\":88},{\"c\":-88}]," + + "\"repeatedImportMessage\":[{\"d\":-9},{\"d\":999999}]," + + "\"repeatedNestedEnum\":[\"BAR\",\"BAZ\"]," + + "\"repeatedForeignEnum\":[\"FOREIGN_BAR\",\"FOREIGN_BAZ\"]," + + "\"oneofUint32\":99" + + "}") assertJSONEncode(expected, configure: configureLargeObject) } - - func testMultipleFields_whenJSONEncodingInt64AsNumbers() { - let expected: String = ("{" - + "\"optionalInt32\":1," - + "\"optionalInt64\":2," - + "\"optionalUint32\":3," - + "\"optionalUint64\":4," - + "\"optionalSint32\":5," - + "\"optionalSint64\":6," - + "\"optionalFixed32\":7," - + "\"optionalFixed64\":8," - + "\"optionalSfixed32\":9," - + "\"optionalSfixed64\":10," - + "\"optionalFloat\":11.0," - + "\"optionalDouble\":12.0," - + "\"optionalBool\":true," - + "\"optionalString\":\"abc\"," - + "\"optionalBytes\":\"QUI=\"," - + "\"optionalNestedMessage\":{\"bb\":7}," - + "\"optionalForeignMessage\":{\"c\":88}," - + "\"optionalImportMessage\":{\"d\":-9}," - + "\"optionalNestedEnum\":\"BAZ\"," - + "\"optionalForeignEnum\":\"FOREIGN_BAZ\"," - + "\"optionalPublicImportMessage\":{\"e\":-999999}," - + "\"repeatedInt32\":[1,2]," - + "\"repeatedInt64\":[3,4]," - + "\"repeatedUint32\":[5,6]," - + "\"repeatedUint64\":[7,8]," - + "\"repeatedSint32\":[9,10]," - + "\"repeatedSint64\":[11,12]," - + "\"repeatedFixed32\":[13,14]," - + "\"repeatedFixed64\":[15,16]," - + "\"repeatedSfixed32\":[17,18]," - + "\"repeatedSfixed64\":[19,20]," - + "\"repeatedFloat\":[21.0,22.0]," - + "\"repeatedDouble\":[23.0,24.0]," - + "\"repeatedBool\":[true,false]," - + "\"repeatedString\":[\"abc\",\"def\"]," - + "\"repeatedBytes\":[\"\",\"QUI=\"]," - + "\"repeatedNestedMessage\":[{\"bb\":7},{\"bb\":-7}]," - + "\"repeatedForeignMessage\":[{\"c\":88},{\"c\":-88}]," - + "\"repeatedImportMessage\":[{\"d\":-9},{\"d\":999999}]," - + "\"repeatedNestedEnum\":[\"BAR\",\"BAZ\"]," - + "\"repeatedForeignEnum\":[\"FOREIGN_BAR\",\"FOREIGN_BAZ\"]," - + "\"oneofUint32\":99" - + "}") + let expected: String = + ("{" + + "\"optionalInt32\":1," + + "\"optionalInt64\":2," + + "\"optionalUint32\":3," + + "\"optionalUint64\":4," + + "\"optionalSint32\":5," + + "\"optionalSint64\":6," + + "\"optionalFixed32\":7," + + "\"optionalFixed64\":8," + + "\"optionalSfixed32\":9," + + "\"optionalSfixed64\":10," + + "\"optionalFloat\":11.0," + + "\"optionalDouble\":12.0," + + "\"optionalBool\":true," + + "\"optionalString\":\"abc\"," + + "\"optionalBytes\":\"QUI=\"," + + "\"optionalNestedMessage\":{\"bb\":7}," + + "\"optionalForeignMessage\":{\"c\":88}," + + "\"optionalImportMessage\":{\"d\":-9}," + + "\"optionalNestedEnum\":\"BAZ\"," + + "\"optionalForeignEnum\":\"FOREIGN_BAZ\"," + + "\"optionalPublicImportMessage\":{\"e\":-999999}," + + "\"repeatedInt32\":[1,2]," + + "\"repeatedInt64\":[3,4]," + + "\"repeatedUint32\":[5,6]," + + "\"repeatedUint64\":[7,8]," + + "\"repeatedSint32\":[9,10]," + + "\"repeatedSint64\":[11,12]," + + "\"repeatedFixed32\":[13,14]," + + "\"repeatedFixed64\":[15,16]," + + "\"repeatedSfixed32\":[17,18]," + + "\"repeatedSfixed64\":[19,20]," + + "\"repeatedFloat\":[21.0,22.0]," + + "\"repeatedDouble\":[23.0,24.0]," + + "\"repeatedBool\":[true,false]," + + "\"repeatedString\":[\"abc\",\"def\"]," + + "\"repeatedBytes\":[\"\",\"QUI=\"]," + + "\"repeatedNestedMessage\":[{\"bb\":7},{\"bb\":-7}]," + + "\"repeatedForeignMessage\":[{\"c\":88},{\"c\":-88}]," + + "\"repeatedImportMessage\":[{\"d\":-9},{\"d\":999999}]," + + "\"repeatedNestedEnum\":[\"BAR\",\"BAZ\"]," + + "\"repeatedForeignEnum\":[\"FOREIGN_BAR\",\"FOREIGN_BAZ\"]," + + "\"oneofUint32\":99" + + "}") var encodingOptions = JSONEncodingOptions() encodingOptions.alwaysPrintInt64sAsNumbers = true assertJSONEncode(expected, encodingOptions: encodingOptions, configure: configureLargeObject) } - // See if we can crash the JSON parser by trying every possible // truncation of the large message above. func testTruncation() throws { @@ -202,57 +201,73 @@ final class Test_JSON: XCTestCase, PBTestHelpers { } func testOptionalInt32() { - assertJSONEncode("{\"optionalInt32\":1}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalInt32\":1}") { (o: inout MessageTestType) in o.optionalInt32 = 1 } - assertJSONEncode("{\"optionalInt32\":2147483647}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalInt32\":2147483647}") { (o: inout MessageTestType) in o.optionalInt32 = Int32.max } - assertJSONEncode("{\"optionalInt32\":-2147483648}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalInt32\":-2147483648}") { (o: inout MessageTestType) in o.optionalInt32 = Int32.min } // 32-bit overflow assertJSONDecodeFails("{\"optionalInt32\":2147483648}") // Explicit 'null' is permitted, proto3 decodes it to default value - assertJSONDecodeSucceeds("{\"optionalInt32\":null}") {(o:MessageTestType) in - o.optionalInt32 == 0} + assertJSONDecodeSucceeds("{\"optionalInt32\":null}") { (o: MessageTestType) in + o.optionalInt32 == 0 + } // Quoted or unquoted numbers, positive, negative, or zero - assertJSONDecodeSucceeds("{\"optionalInt32\":1}") {(o:MessageTestType) in - o.optionalInt32 == 1} - assertJSONDecodeSucceeds("{\"optionalInt32\":\"1\"}") {(o:MessageTestType) in - o.optionalInt32 == 1} - assertJSONDecodeSucceeds("{\"optionalInt32\":\"\\u0030\"}") {(o:MessageTestType) in - o.optionalInt32 == 0} - assertJSONDecodeSucceeds("{\"optionalInt32\":\"\\u0031\"}") {(o:MessageTestType) in - o.optionalInt32 == 1} - assertJSONDecodeSucceeds("{\"optionalInt32\":\"\\u00310\"}") {(o:MessageTestType) in - o.optionalInt32 == 10} - assertJSONDecodeSucceeds("{\"optionalInt32\":0}") {(o:MessageTestType) in - o.optionalInt32 == 0} - assertJSONDecodeSucceeds("{\"optionalInt32\":\"0\"}") {(o:MessageTestType) in - o.optionalInt32 == 0} - assertJSONDecodeSucceeds("{\"optionalInt32\":-0}") {(o:MessageTestType) in - o.optionalInt32 == 0} - assertJSONDecodeSucceeds("{\"optionalInt32\":\"-0\"}") {(o:MessageTestType) in - o.optionalInt32 == 0} - assertJSONDecodeSucceeds("{\"optionalInt32\":-1}") {(o:MessageTestType) in - o.optionalInt32 == -1} - assertJSONDecodeSucceeds("{\"optionalInt32\":\"-1\"}") {(o:MessageTestType) in - o.optionalInt32 == -1} + assertJSONDecodeSucceeds("{\"optionalInt32\":1}") { (o: MessageTestType) in + o.optionalInt32 == 1 + } + assertJSONDecodeSucceeds("{\"optionalInt32\":\"1\"}") { (o: MessageTestType) in + o.optionalInt32 == 1 + } + assertJSONDecodeSucceeds("{\"optionalInt32\":\"\\u0030\"}") { (o: MessageTestType) in + o.optionalInt32 == 0 + } + assertJSONDecodeSucceeds("{\"optionalInt32\":\"\\u0031\"}") { (o: MessageTestType) in + o.optionalInt32 == 1 + } + assertJSONDecodeSucceeds("{\"optionalInt32\":\"\\u00310\"}") { (o: MessageTestType) in + o.optionalInt32 == 10 + } + assertJSONDecodeSucceeds("{\"optionalInt32\":0}") { (o: MessageTestType) in + o.optionalInt32 == 0 + } + assertJSONDecodeSucceeds("{\"optionalInt32\":\"0\"}") { (o: MessageTestType) in + o.optionalInt32 == 0 + } + assertJSONDecodeSucceeds("{\"optionalInt32\":-0}") { (o: MessageTestType) in + o.optionalInt32 == 0 + } + assertJSONDecodeSucceeds("{\"optionalInt32\":\"-0\"}") { (o: MessageTestType) in + o.optionalInt32 == 0 + } + assertJSONDecodeSucceeds("{\"optionalInt32\":-1}") { (o: MessageTestType) in + o.optionalInt32 == -1 + } + assertJSONDecodeSucceeds("{\"optionalInt32\":\"-1\"}") { (o: MessageTestType) in + o.optionalInt32 == -1 + } // JSON RFC does not accept leading zeros assertJSONDecodeFails("{\"optionalInt32\":00000000000000000000001}") assertJSONDecodeFails("{\"optionalInt32\":\"01\"}") assertJSONDecodeFails("{\"optionalInt32\":-01}") assertJSONDecodeFails("{\"optionalInt32\":\"-00000000000000000000001\"}") // Exponents are okay, as long as result is integer - assertJSONDecodeSucceeds("{\"optionalInt32\":2.147483647e9}") {(o:MessageTestType) in - o.optionalInt32 == Int32.max} - assertJSONDecodeSucceeds("{\"optionalInt32\":-2.147483648e9}") {(o:MessageTestType) in - o.optionalInt32 == Int32.min} - assertJSONDecodeSucceeds("{\"optionalInt32\":1e3}") {(o:MessageTestType) in - o.optionalInt32 == 1000} - assertJSONDecodeSucceeds("{\"optionalInt32\":100e-2}") {(o:MessageTestType) in - o.optionalInt32 == 1} + assertJSONDecodeSucceeds("{\"optionalInt32\":2.147483647e9}") { (o: MessageTestType) in + o.optionalInt32 == Int32.max + } + assertJSONDecodeSucceeds("{\"optionalInt32\":-2.147483648e9}") { (o: MessageTestType) in + o.optionalInt32 == Int32.min + } + assertJSONDecodeSucceeds("{\"optionalInt32\":1e3}") { (o: MessageTestType) in + o.optionalInt32 == 1000 + } + assertJSONDecodeSucceeds("{\"optionalInt32\":100e-2}") { (o: MessageTestType) in + o.optionalInt32 == 1 + } assertJSONDecodeFails("{\"optionalInt32\":1e-1}") // Reject malformed input assertJSONDecodeFails("{\"optionalInt32\":\\u0031}") @@ -283,30 +298,30 @@ final class Test_JSON: XCTestCase, PBTestHelpers { } func testOptionalUInt32() { - assertJSONEncode("{\"optionalUint32\":1}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalUint32\":1}") { (o: inout MessageTestType) in o.optionalUint32 = 1 } - assertJSONEncode("{\"optionalUint32\":4294967295}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalUint32\":4294967295}") { (o: inout MessageTestType) in o.optionalUint32 = UInt32.max } assertJSONDecodeFails("{\"optionalUint32\":4294967296}") // Explicit 'null' is permitted, decodes to default - assertJSONDecodeSucceeds("{\"optionalUint32\":null}") {$0.optionalUint32 == 0} + assertJSONDecodeSucceeds("{\"optionalUint32\":null}") { $0.optionalUint32 == 0 } // Quoted or unquoted numbers, positive, negative, or zero - assertJSONDecodeSucceeds("{\"optionalUint32\":1}") {$0.optionalUint32 == 1} - assertJSONDecodeSucceeds("{\"optionalUint32\":\"1\"}") {$0.optionalUint32 == 1} - assertJSONDecodeSucceeds("{\"optionalUint32\":0}") {$0.optionalUint32 == 0} - assertJSONDecodeSucceeds("{\"optionalUint32\":\"0\"}") {$0.optionalUint32 == 0} + assertJSONDecodeSucceeds("{\"optionalUint32\":1}") { $0.optionalUint32 == 1 } + assertJSONDecodeSucceeds("{\"optionalUint32\":\"1\"}") { $0.optionalUint32 == 1 } + assertJSONDecodeSucceeds("{\"optionalUint32\":0}") { $0.optionalUint32 == 0 } + assertJSONDecodeSucceeds("{\"optionalUint32\":\"0\"}") { $0.optionalUint32 == 0 } // Protobuf JSON does not accept leading zeros assertJSONDecodeFails("{\"optionalUint32\":01}") assertJSONDecodeFails("{\"optionalUint32\":\"01\"}") // But it does accept exponential (as long as result is integral) - assertJSONDecodeSucceeds("{\"optionalUint32\":4.294967295e9}") {$0.optionalUint32 == UInt32.max} - assertJSONDecodeSucceeds("{\"optionalUint32\":1e3}") {$0.optionalUint32 == 1000} - assertJSONDecodeSucceeds("{\"optionalUint32\":1.2e3}") {$0.optionalUint32 == 1200} - assertJSONDecodeSucceeds("{\"optionalUint32\":1000e-2}") {$0.optionalUint32 == 10} - assertJSONDecodeSucceeds("{\"optionalUint32\":1.0}") {$0.optionalUint32 == 1} - assertJSONDecodeSucceeds("{\"optionalUint32\":1.000000e2}") {$0.optionalUint32 == 100} + assertJSONDecodeSucceeds("{\"optionalUint32\":4.294967295e9}") { $0.optionalUint32 == UInt32.max } + assertJSONDecodeSucceeds("{\"optionalUint32\":1e3}") { $0.optionalUint32 == 1000 } + assertJSONDecodeSucceeds("{\"optionalUint32\":1.2e3}") { $0.optionalUint32 == 1200 } + assertJSONDecodeSucceeds("{\"optionalUint32\":1000e-2}") { $0.optionalUint32 == 10 } + assertJSONDecodeSucceeds("{\"optionalUint32\":1.0}") { $0.optionalUint32 == 1 } + assertJSONDecodeSucceeds("{\"optionalUint32\":1.000000e2}") { $0.optionalUint32 == 100 } assertJSONDecodeFails("{\"optionalUint32\":1e-3}") assertJSONDecodeFails("{\"optionalUint32\":1") assertJSONDecodeFails("{\"optionalUint32\":\"") @@ -324,28 +339,28 @@ final class Test_JSON: XCTestCase, PBTestHelpers { func testOptionalInt64() throws { // Protoc JSON always quotes Int64 values - assertJSONEncode("{\"optionalInt64\":\"9007199254740992\"}") {(o: inout MessageTestType) in - o.optionalInt64 = 0x20000000000000 + assertJSONEncode("{\"optionalInt64\":\"9007199254740992\"}") { (o: inout MessageTestType) in + o.optionalInt64 = 0x20_0000_0000_0000 } - assertJSONEncode("{\"optionalInt64\":\"9007199254740991\"}") {(o: inout MessageTestType) in - o.optionalInt64 = 0x1fffffffffffff + assertJSONEncode("{\"optionalInt64\":\"9007199254740991\"}") { (o: inout MessageTestType) in + o.optionalInt64 = 0x1f_ffff_ffff_ffff } - assertJSONEncode("{\"optionalInt64\":\"-9007199254740992\"}") {(o: inout MessageTestType) in - o.optionalInt64 = -0x20000000000000 + assertJSONEncode("{\"optionalInt64\":\"-9007199254740992\"}") { (o: inout MessageTestType) in + o.optionalInt64 = -0x20_0000_0000_0000 } - assertJSONEncode("{\"optionalInt64\":\"-9007199254740991\"}") {(o: inout MessageTestType) in - o.optionalInt64 = -0x1fffffffffffff + assertJSONEncode("{\"optionalInt64\":\"-9007199254740991\"}") { (o: inout MessageTestType) in + o.optionalInt64 = -0x1f_ffff_ffff_ffff } - assertJSONEncode("{\"optionalInt64\":\"9223372036854775807\"}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalInt64\":\"9223372036854775807\"}") { (o: inout MessageTestType) in o.optionalInt64 = Int64.max } - assertJSONEncode("{\"optionalInt64\":\"-9223372036854775808\"}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalInt64\":\"-9223372036854775808\"}") { (o: inout MessageTestType) in o.optionalInt64 = Int64.min } - assertJSONEncode("{\"optionalInt64\":\"1\"}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalInt64\":\"1\"}") { (o: inout MessageTestType) in o.optionalInt64 = 1 } - assertJSONEncode("{\"optionalInt64\":\"-1\"}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalInt64\":\"-1\"}") { (o: inout MessageTestType) in o.optionalInt64 = -1 } @@ -355,25 +370,25 @@ final class Test_JSON: XCTestCase, PBTestHelpers { XCTAssertEqual(try a.jsonString(), "{}") // Decode should work even with unquoted large numbers - assertJSONDecodeSucceeds("{\"optionalInt64\":9223372036854775807}") {$0.optionalInt64 == Int64.max} + assertJSONDecodeSucceeds("{\"optionalInt64\":9223372036854775807}") { $0.optionalInt64 == Int64.max } assertJSONDecodeFails("{\"optionalInt64\":9223372036854775808}") - assertJSONDecodeSucceeds("{\"optionalInt64\":-9223372036854775808}") {$0.optionalInt64 == Int64.min} + assertJSONDecodeSucceeds("{\"optionalInt64\":-9223372036854775808}") { $0.optionalInt64 == Int64.min } assertJSONDecodeFails("{\"optionalInt64\":-9223372036854775809}") // Protobuf JSON does not accept leading zeros assertJSONDecodeFails("{\"optionalInt64\": \"01\" }") - assertJSONDecodeSucceeds("{\"optionalInt64\": \"1\" }") {$0.optionalInt64 == 1} + assertJSONDecodeSucceeds("{\"optionalInt64\": \"1\" }") { $0.optionalInt64 == 1 } assertJSONDecodeFails("{\"optionalInt64\": \"-01\" }") - assertJSONDecodeSucceeds("{\"optionalInt64\": \"-1\" }") {$0.optionalInt64 == -1} - assertJSONDecodeSucceeds("{\"optionalInt64\": \"0\" }") {$0.optionalInt64 == 0} + assertJSONDecodeSucceeds("{\"optionalInt64\": \"-1\" }") { $0.optionalInt64 == -1 } + assertJSONDecodeSucceeds("{\"optionalInt64\": \"0\" }") { $0.optionalInt64 == 0 } // Protobuf JSON does accept exponential format for integer fields - assertJSONDecodeSucceeds("{\"optionalInt64\":1e3}") {$0.optionalInt64 == 1000} - assertJSONDecodeSucceeds("{\"optionalInt64\":\"9223372036854775807\"}") {$0.optionalInt64 == Int64.max} - assertJSONDecodeSucceeds("{\"optionalInt64\":-9.223372036854775808e18}") {$0.optionalInt64 == Int64.min} - assertJSONDecodeFails("{\"optionalInt64\":9.223372036854775808e18}") // Out of range + assertJSONDecodeSucceeds("{\"optionalInt64\":1e3}") { $0.optionalInt64 == 1000 } + assertJSONDecodeSucceeds("{\"optionalInt64\":\"9223372036854775807\"}") { $0.optionalInt64 == Int64.max } + assertJSONDecodeSucceeds("{\"optionalInt64\":-9.223372036854775808e18}") { $0.optionalInt64 == Int64.min } + assertJSONDecodeFails("{\"optionalInt64\":9.223372036854775808e18}") // Out of range // Explicit 'null' is permitted, decodes to default (in proto3) - assertJSONDecodeSucceeds("{\"optionalInt64\":null}") {$0.optionalInt64 == 0} - assertJSONDecodeSucceeds("{\"optionalInt64\":2147483648}") {$0.optionalInt64 == 2147483648} - assertJSONDecodeSucceeds("{\"optionalInt64\":2147483648}") {$0.optionalInt64 == 2147483648} + assertJSONDecodeSucceeds("{\"optionalInt64\":null}") { $0.optionalInt64 == 0 } + assertJSONDecodeSucceeds("{\"optionalInt64\":2147483648}") { $0.optionalInt64 == 2_147_483_648 } + assertJSONDecodeSucceeds("{\"optionalInt64\":2147483648}") { $0.optionalInt64 == 2_147_483_648 } assertJSONDecodeFails("{\"optionalInt64\":1") assertJSONDecodeFails("{\"optionalInt64\":\"") @@ -382,19 +397,21 @@ final class Test_JSON: XCTestCase, PBTestHelpers { } func testOptionalUInt64() { - assertJSONEncode("{\"optionalUint64\":\"1\"}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalUint64\":\"1\"}") { (o: inout MessageTestType) in o.optionalUint64 = 1 } - assertJSONEncode("{\"optionalUint64\":\"4294967295\"}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalUint64\":\"4294967295\"}") { (o: inout MessageTestType) in o.optionalUint64 = UInt64(UInt32.max) } - assertJSONEncode("{\"optionalUint64\":\"18446744073709551615\"}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalUint64\":\"18446744073709551615\"}") { (o: inout MessageTestType) in o.optionalUint64 = UInt64.max } // Parse unquoted 64-bit integers - assertJSONDecodeSucceeds("{\"optionalUint64\":18446744073709551615}") {$0.optionalUint64 == UInt64.max} + assertJSONDecodeSucceeds("{\"optionalUint64\":18446744073709551615}") { $0.optionalUint64 == UInt64.max } // Accept quoted 64-bit integers with backslash escapes in them - assertJSONDecodeSucceeds("{\"optionalUint64\":\"184467\\u00344073709551615\"}") {$0.optionalUint64 == UInt64.max} + assertJSONDecodeSucceeds("{\"optionalUint64\":\"184467\\u00344073709551615\"}") { + $0.optionalUint64 == UInt64.max + } // Reject unquoted 64-bit integers with backslash escapes assertJSONDecodeFails("{\"optionalUint64\":184467\\u00344073709551615}") // Reject out-of-range integers, whether or not quoted @@ -404,22 +421,22 @@ final class Test_JSON: XCTestCase, PBTestHelpers { assertJSONDecodeFails("{\"optionalUint64\":184467440737095516109}") // Explicit 'null' is permitted, decodes to default - assertJSONDecodeSucceeds("{\"optionalUint64\":null}") {$0.optionalUint64 == 0} + assertJSONDecodeSucceeds("{\"optionalUint64\":null}") { $0.optionalUint64 == 0 } // Quoted or unquoted numbers, positive or zero - assertJSONDecodeSucceeds("{\"optionalUint64\":1}") {$0.optionalUint64 == 1} - assertJSONDecodeSucceeds("{\"optionalUint64\":\"1\"}") {$0.optionalUint64 == 1} - assertJSONDecodeSucceeds("{\"optionalUint64\":0}") {$0.optionalUint64 == 0} - assertJSONDecodeSucceeds("{\"optionalUint64\":\"0\"}") {$0.optionalUint64 == 0} + assertJSONDecodeSucceeds("{\"optionalUint64\":1}") { $0.optionalUint64 == 1 } + assertJSONDecodeSucceeds("{\"optionalUint64\":\"1\"}") { $0.optionalUint64 == 1 } + assertJSONDecodeSucceeds("{\"optionalUint64\":0}") { $0.optionalUint64 == 0 } + assertJSONDecodeSucceeds("{\"optionalUint64\":\"0\"}") { $0.optionalUint64 == 0 } // Protobuf JSON does not accept leading zeros assertJSONDecodeFails("{\"optionalUint64\":01}") assertJSONDecodeFails("{\"optionalUint64\":\"01\"}") // But it does accept exponential (as long as result is integral) - assertJSONDecodeSucceeds("{\"optionalUint64\":4.294967295e9}") {$0.optionalUint64 == UInt64(UInt32.max)} - assertJSONDecodeSucceeds("{\"optionalUint64\":1e3}") {$0.optionalUint64 == 1000} - assertJSONDecodeSucceeds("{\"optionalUint64\":1.2e3}") {$0.optionalUint64 == 1200} - assertJSONDecodeSucceeds("{\"optionalUint64\":1000e-2}") {$0.optionalUint64 == 10} - assertJSONDecodeSucceeds("{\"optionalUint64\":1.0}") {$0.optionalUint64 == 1} - assertJSONDecodeSucceeds("{\"optionalUint64\":1.000000e2}") {$0.optionalUint64 == 100} + assertJSONDecodeSucceeds("{\"optionalUint64\":4.294967295e9}") { $0.optionalUint64 == UInt64(UInt32.max) } + assertJSONDecodeSucceeds("{\"optionalUint64\":1e3}") { $0.optionalUint64 == 1000 } + assertJSONDecodeSucceeds("{\"optionalUint64\":1.2e3}") { $0.optionalUint64 == 1200 } + assertJSONDecodeSucceeds("{\"optionalUint64\":1000e-2}") { $0.optionalUint64 == 10 } + assertJSONDecodeSucceeds("{\"optionalUint64\":1.0}") { $0.optionalUint64 == 1 } + assertJSONDecodeSucceeds("{\"optionalUint64\":1.000000e2}") { $0.optionalUint64 == 100 } assertJSONDecodeFails("{\"optionalUint64\":1e-3}") assertJSONDecodeFails("{\"optionalUint64\":1.11e1}") // Reject truncated JSON (ending at the beginning, end, or middle of the number @@ -437,7 +454,11 @@ final class Test_JSON: XCTestCase, PBTestHelpers { assertJSONDecodeFails("{\"optionalUint64\":[]}") } - private func assertRoundTripJSON(file: XCTestFileArgType = #file, line: UInt = #line, configure: (inout MessageTestType) -> Void) { + private func assertRoundTripJSON( + file: XCTestFileArgType = #file, + line: UInt = #line, + configure: (inout MessageTestType) -> Void + ) { var original = MessageTestType() configure(&original) do { @@ -454,32 +475,32 @@ final class Test_JSON: XCTestCase, PBTestHelpers { } func testOptionalDouble() throws { - assertJSONEncode("{\"optionalDouble\":1.0}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalDouble\":1.0}") { (o: inout MessageTestType) in o.optionalDouble = 1.0 } - assertJSONEncode("{\"optionalDouble\":\"Infinity\"}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalDouble\":\"Infinity\"}") { (o: inout MessageTestType) in o.optionalDouble = Double.infinity } - assertJSONEncode("{\"optionalDouble\":\"-Infinity\"}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalDouble\":\"-Infinity\"}") { (o: inout MessageTestType) in o.optionalDouble = -Double.infinity } - assertJSONDecodeSucceeds("{\"optionalDouble\":\"Inf\"}") {$0.optionalDouble == Double.infinity} - assertJSONDecodeSucceeds("{\"optionalDouble\":\"-Inf\"}") {$0.optionalDouble == -Double.infinity} - assertJSONDecodeSucceeds("{\"optionalDouble\":\"1\"}") {$0.optionalDouble == 1} - assertJSONDecodeSucceeds("{\"optionalDouble\":\"1.0\"}") {$0.optionalDouble == 1.0} - assertJSONDecodeSucceeds("{\"optionalDouble\":\"1.5\"}") {$0.optionalDouble == 1.5} - assertJSONDecodeSucceeds("{\"optionalDouble\":\"1.5e1\"}") {$0.optionalDouble == 15} - assertJSONDecodeSucceeds("{\"optionalDouble\":\"1.5E1\"}") {$0.optionalDouble == 15} - assertJSONDecodeSucceeds("{\"optionalDouble\":\"1\\u002e5e1\"}") {$0.optionalDouble == 15} - assertJSONDecodeSucceeds("{\"optionalDouble\":\"1.\\u0035e1\"}") {$0.optionalDouble == 15} - assertJSONDecodeSucceeds("{\"optionalDouble\":\"1.5\\u00651\"}") {$0.optionalDouble == 15} - assertJSONDecodeSucceeds("{\"optionalDouble\":\"1.5e\\u002b1\"}") {$0.optionalDouble == 15} - assertJSONDecodeSucceeds("{\"optionalDouble\":\"1.5e+\\u0031\"}") {$0.optionalDouble == 15} - assertJSONDecodeSucceeds("{\"optionalDouble\":\"1.5e+1\"}") {$0.optionalDouble == 15} - assertJSONDecodeSucceeds("{\"optionalDouble\":\"15e-1\"}") {$0.optionalDouble == 1.5} - assertJSONDecodeSucceeds("{\"optionalDouble\":\"1.0e0\"}") {$0.optionalDouble == 1.0} - assertJSONDecodeSucceeds("{\"optionalDouble\":\"0\"}") {$0.optionalDouble == 0.0} - assertJSONDecodeSucceeds("{\"optionalDouble\":0}") {$0.optionalDouble == 0.0} + assertJSONDecodeSucceeds("{\"optionalDouble\":\"Inf\"}") { $0.optionalDouble == Double.infinity } + assertJSONDecodeSucceeds("{\"optionalDouble\":\"-Inf\"}") { $0.optionalDouble == -Double.infinity } + assertJSONDecodeSucceeds("{\"optionalDouble\":\"1\"}") { $0.optionalDouble == 1 } + assertJSONDecodeSucceeds("{\"optionalDouble\":\"1.0\"}") { $0.optionalDouble == 1.0 } + assertJSONDecodeSucceeds("{\"optionalDouble\":\"1.5\"}") { $0.optionalDouble == 1.5 } + assertJSONDecodeSucceeds("{\"optionalDouble\":\"1.5e1\"}") { $0.optionalDouble == 15 } + assertJSONDecodeSucceeds("{\"optionalDouble\":\"1.5E1\"}") { $0.optionalDouble == 15 } + assertJSONDecodeSucceeds("{\"optionalDouble\":\"1\\u002e5e1\"}") { $0.optionalDouble == 15 } + assertJSONDecodeSucceeds("{\"optionalDouble\":\"1.\\u0035e1\"}") { $0.optionalDouble == 15 } + assertJSONDecodeSucceeds("{\"optionalDouble\":\"1.5\\u00651\"}") { $0.optionalDouble == 15 } + assertJSONDecodeSucceeds("{\"optionalDouble\":\"1.5e\\u002b1\"}") { $0.optionalDouble == 15 } + assertJSONDecodeSucceeds("{\"optionalDouble\":\"1.5e+\\u0031\"}") { $0.optionalDouble == 15 } + assertJSONDecodeSucceeds("{\"optionalDouble\":\"1.5e+1\"}") { $0.optionalDouble == 15 } + assertJSONDecodeSucceeds("{\"optionalDouble\":\"15e-1\"}") { $0.optionalDouble == 1.5 } + assertJSONDecodeSucceeds("{\"optionalDouble\":\"1.0e0\"}") { $0.optionalDouble == 1.0 } + assertJSONDecodeSucceeds("{\"optionalDouble\":\"0\"}") { $0.optionalDouble == 0.0 } + assertJSONDecodeSucceeds("{\"optionalDouble\":0}") { $0.optionalDouble == 0.0 } // We preserve signed zero when decoding let d1 = try MessageTestType(jsonString: "{\"optionalDouble\":\"-0\"}") XCTAssertEqual(d1.optionalDouble, 0.0) @@ -488,12 +509,12 @@ final class Test_JSON: XCTestCase, PBTestHelpers { XCTAssertEqual(d2.optionalDouble, 0.0) XCTAssertEqual(d2.optionalDouble.sign, .minus) // But re-encoding treats the field as defaulted, so the sign gets lost - assertJSONDecodeSucceeds("{\"optionalDouble\":\"-0\"}") {$0.optionalDouble == 0.0} - assertJSONDecodeSucceeds("{\"optionalDouble\":-0}") {$0.optionalDouble == 0.0} + assertJSONDecodeSucceeds("{\"optionalDouble\":\"-0\"}") { $0.optionalDouble == 0.0 } + assertJSONDecodeSucceeds("{\"optionalDouble\":-0}") { $0.optionalDouble == 0.0 } // Malformed numbers should fail assertJSONDecodeFails("{\"optionalDouble\":Infinity}") - assertJSONDecodeFails("{\"optionalDouble\":-Infinity}") // Must be quoted + assertJSONDecodeFails("{\"optionalDouble\":-Infinity}") // Must be quoted assertJSONDecodeFails("{\"optionalDouble\":\"inf\"}") assertJSONDecodeFails("{\"optionalDouble\":\"-inf\"}") assertJSONDecodeFails("{\"optionalDouble\":NaN}") @@ -514,57 +535,57 @@ final class Test_JSON: XCTestCase, PBTestHelpers { assertJSONDecodeFails("{\"optionalDouble\":1.0.0}") // A wide range of numbers should exactly round-trip - assertRoundTripJSON {$0.optionalDouble = 0.1} - assertRoundTripJSON {$0.optionalDouble = 0.01} - assertRoundTripJSON {$0.optionalDouble = 0.001} - assertRoundTripJSON {$0.optionalDouble = 0.0001} - assertRoundTripJSON {$0.optionalDouble = 0.00001} - assertRoundTripJSON {$0.optionalDouble = 0.000001} - assertRoundTripJSON {$0.optionalDouble = 1e-10} - assertRoundTripJSON {$0.optionalDouble = 1e-20} - assertRoundTripJSON {$0.optionalDouble = 1e-30} - assertRoundTripJSON {$0.optionalDouble = 1e-40} - assertRoundTripJSON {$0.optionalDouble = 1e-50} - assertRoundTripJSON {$0.optionalDouble = 1e-60} - assertRoundTripJSON {$0.optionalDouble = 1e-100} - assertRoundTripJSON {$0.optionalDouble = 1e-200} - assertRoundTripJSON {$0.optionalDouble = Double.pi} - assertRoundTripJSON {$0.optionalDouble = 123456.789123456789123} - assertRoundTripJSON {$0.optionalDouble = 1.7976931348623157e+308} - assertRoundTripJSON {$0.optionalDouble = 2.22507385850720138309e-308} + assertRoundTripJSON { $0.optionalDouble = 0.1 } + assertRoundTripJSON { $0.optionalDouble = 0.01 } + assertRoundTripJSON { $0.optionalDouble = 0.001 } + assertRoundTripJSON { $0.optionalDouble = 0.0001 } + assertRoundTripJSON { $0.optionalDouble = 0.00001 } + assertRoundTripJSON { $0.optionalDouble = 0.000001 } + assertRoundTripJSON { $0.optionalDouble = 1e-10 } + assertRoundTripJSON { $0.optionalDouble = 1e-20 } + assertRoundTripJSON { $0.optionalDouble = 1e-30 } + assertRoundTripJSON { $0.optionalDouble = 1e-40 } + assertRoundTripJSON { $0.optionalDouble = 1e-50 } + assertRoundTripJSON { $0.optionalDouble = 1e-60 } + assertRoundTripJSON { $0.optionalDouble = 1e-100 } + assertRoundTripJSON { $0.optionalDouble = 1e-200 } + assertRoundTripJSON { $0.optionalDouble = Double.pi } + assertRoundTripJSON { $0.optionalDouble = 123456.789123456789123 } + assertRoundTripJSON { $0.optionalDouble = 1.7976931348623157e+308 } + assertRoundTripJSON { $0.optionalDouble = 2.22507385850720138309e-308 } } func testOptionalFloat() throws { - assertJSONEncode("{\"optionalFloat\":1.0}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalFloat\":1.0}") { (o: inout MessageTestType) in o.optionalFloat = 1.0 } - assertJSONEncode("{\"optionalFloat\":-1.0}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalFloat\":-1.0}") { (o: inout MessageTestType) in o.optionalFloat = -1.0 } - assertJSONEncode("{\"optionalFloat\":\"Infinity\"}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalFloat\":\"Infinity\"}") { (o: inout MessageTestType) in o.optionalFloat = Float.infinity } - assertJSONEncode("{\"optionalFloat\":\"-Infinity\"}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalFloat\":\"-Infinity\"}") { (o: inout MessageTestType) in o.optionalFloat = -Float.infinity } - assertJSONDecodeSucceeds("{\"optionalFloat\":\"Inf\"}") {$0.optionalFloat == Float.infinity} - assertJSONDecodeSucceeds("{\"optionalFloat\":\"-Inf\"}") {$0.optionalFloat == -Float.infinity} - assertJSONDecodeSucceeds("{\"optionalFloat\":\"1\"}") {$0.optionalFloat == 1} - assertJSONDecodeSucceeds("{\"optionalFloat\":\"-1\"}") {$0.optionalFloat == -1} - assertJSONDecodeSucceeds("{\"optionalFloat\":\"1.0\"}") {$0.optionalFloat == 1.0} - assertJSONDecodeSucceeds("{\"optionalFloat\":\"1.5\"}") {$0.optionalFloat == 1.5} - assertJSONDecodeSucceeds("{\"optionalFloat\":\"1.5e1\"}") {$0.optionalFloat == 15} - assertJSONDecodeSucceeds("{\"optionalFloat\":\"1\\u002e5e1\"}") {$0.optionalFloat == 15} - assertJSONDecodeSucceeds("{\"optionalFloat\":\"1.\\u0035e1\"}") {$0.optionalFloat == 15} - assertJSONDecodeSucceeds("{\"optionalFloat\":\"1.5\\u00651\"}") {$0.optionalFloat == 15} - assertJSONDecodeSucceeds("{\"optionalFloat\":\"1.5e\\u002b1\"}") {$0.optionalFloat == 15} - assertJSONDecodeSucceeds("{\"optionalFloat\":\"1.5e+\\u0031\"}") {$0.optionalFloat == 15} - assertJSONDecodeSucceeds("{\"optionalFloat\":\"1.5e+1\"}") {$0.optionalFloat == 15} - assertJSONDecodeSucceeds("{\"optionalFloat\":\"15e-1\"}") {$0.optionalFloat == 1.5} - assertJSONDecodeSucceeds("{\"optionalFloat\":\"1.0e0\"}") {$0.optionalFloat == 1.0} - assertJSONDecodeSucceeds("{\"optionalFloat\":1.0e0}") {$0.optionalFloat == 1.0} - assertJSONDecodeSucceeds("{\"optionalFloat\":\"0\"}") {$0.optionalFloat == 0.0} - assertJSONDecodeSucceeds("{\"optionalFloat\":0}") {$0.optionalFloat == 0.0} + assertJSONDecodeSucceeds("{\"optionalFloat\":\"Inf\"}") { $0.optionalFloat == Float.infinity } + assertJSONDecodeSucceeds("{\"optionalFloat\":\"-Inf\"}") { $0.optionalFloat == -Float.infinity } + assertJSONDecodeSucceeds("{\"optionalFloat\":\"1\"}") { $0.optionalFloat == 1 } + assertJSONDecodeSucceeds("{\"optionalFloat\":\"-1\"}") { $0.optionalFloat == -1 } + assertJSONDecodeSucceeds("{\"optionalFloat\":\"1.0\"}") { $0.optionalFloat == 1.0 } + assertJSONDecodeSucceeds("{\"optionalFloat\":\"1.5\"}") { $0.optionalFloat == 1.5 } + assertJSONDecodeSucceeds("{\"optionalFloat\":\"1.5e1\"}") { $0.optionalFloat == 15 } + assertJSONDecodeSucceeds("{\"optionalFloat\":\"1\\u002e5e1\"}") { $0.optionalFloat == 15 } + assertJSONDecodeSucceeds("{\"optionalFloat\":\"1.\\u0035e1\"}") { $0.optionalFloat == 15 } + assertJSONDecodeSucceeds("{\"optionalFloat\":\"1.5\\u00651\"}") { $0.optionalFloat == 15 } + assertJSONDecodeSucceeds("{\"optionalFloat\":\"1.5e\\u002b1\"}") { $0.optionalFloat == 15 } + assertJSONDecodeSucceeds("{\"optionalFloat\":\"1.5e+\\u0031\"}") { $0.optionalFloat == 15 } + assertJSONDecodeSucceeds("{\"optionalFloat\":\"1.5e+1\"}") { $0.optionalFloat == 15 } + assertJSONDecodeSucceeds("{\"optionalFloat\":\"15e-1\"}") { $0.optionalFloat == 1.5 } + assertJSONDecodeSucceeds("{\"optionalFloat\":\"1.0e0\"}") { $0.optionalFloat == 1.0 } + assertJSONDecodeSucceeds("{\"optionalFloat\":1.0e0}") { $0.optionalFloat == 1.0 } + assertJSONDecodeSucceeds("{\"optionalFloat\":\"0\"}") { $0.optionalFloat == 0.0 } + assertJSONDecodeSucceeds("{\"optionalFloat\":0}") { $0.optionalFloat == 0.0 } // We preserve signed zero when decoding let d1 = try MessageTestType(jsonString: "{\"optionalFloat\":\"-0\"}") XCTAssertEqual(d1.optionalFloat, 0.0) @@ -573,11 +594,11 @@ final class Test_JSON: XCTestCase, PBTestHelpers { XCTAssertEqual(d2.optionalFloat, 0.0) XCTAssertEqual(d2.optionalFloat.sign, .minus) // But re-encoding treats the field as defaulted, so the sign gets lost - assertJSONDecodeSucceeds("{\"optionalFloat\":\"-0\"}") {$0.optionalFloat == 0.0} - assertJSONDecodeSucceeds("{\"optionalFloat\":-0}") {$0.optionalFloat == 0.0} + assertJSONDecodeSucceeds("{\"optionalFloat\":\"-0\"}") { $0.optionalFloat == 0.0 } + assertJSONDecodeSucceeds("{\"optionalFloat\":-0}") { $0.optionalFloat == 0.0 } // Malformed numbers should fail assertJSONDecodeFails("{\"optionalFloat\":Infinity}") - assertJSONDecodeFails("{\"optionalFloat\":-Infinity}") // Must be quoted + assertJSONDecodeFails("{\"optionalFloat\":-Infinity}") // Must be quoted assertJSONDecodeFails("{\"optionalFloat\":NaN}") assertJSONDecodeFails("{\"optionalFloat\":\"nan\"}") assertJSONDecodeFails("{\"optionalFloat\":\"1.0.0\"}") @@ -602,29 +623,29 @@ final class Test_JSON: XCTestCase, PBTestHelpers { assertJSONDecodeFails("{\"optionalFloat\":1e39}") // A wide range of numbers should exactly round-trip - assertRoundTripJSON {$0.optionalFloat = 0.1} - assertRoundTripJSON {$0.optionalFloat = 0.01} - assertRoundTripJSON {$0.optionalFloat = 0.001} - assertRoundTripJSON {$0.optionalFloat = 0.0001} - assertRoundTripJSON {$0.optionalFloat = 0.00001} - assertRoundTripJSON {$0.optionalFloat = 0.000001} - assertRoundTripJSON {$0.optionalFloat = 1.00000075e-36} - assertRoundTripJSON {$0.optionalFloat = 1e-10} - assertRoundTripJSON {$0.optionalFloat = 1e-20} - assertRoundTripJSON {$0.optionalFloat = 1e-30} - assertRoundTripJSON {$0.optionalFloat = Float(1e-40)} - assertRoundTripJSON {$0.optionalFloat = Float(1e-50)} - assertRoundTripJSON {$0.optionalFloat = Float(1e-60)} - assertRoundTripJSON {$0.optionalFloat = Float(1e-100)} - assertRoundTripJSON {$0.optionalFloat = Float(1e-200)} - assertRoundTripJSON {$0.optionalFloat = Float.pi} - assertRoundTripJSON {$0.optionalFloat = 123456.789123456789123} - assertRoundTripJSON {$0.optionalFloat = 1999.9999999999} - assertRoundTripJSON {$0.optionalFloat = 1999.9} - assertRoundTripJSON {$0.optionalFloat = 1999.99} - assertRoundTripJSON {$0.optionalFloat = 1999.99} - assertRoundTripJSON {$0.optionalFloat = 3.402823567e+38} - assertRoundTripJSON {$0.optionalFloat = 1.1754944e-38} + assertRoundTripJSON { $0.optionalFloat = 0.1 } + assertRoundTripJSON { $0.optionalFloat = 0.01 } + assertRoundTripJSON { $0.optionalFloat = 0.001 } + assertRoundTripJSON { $0.optionalFloat = 0.0001 } + assertRoundTripJSON { $0.optionalFloat = 0.00001 } + assertRoundTripJSON { $0.optionalFloat = 0.000001 } + assertRoundTripJSON { $0.optionalFloat = 1.00000075e-36 } + assertRoundTripJSON { $0.optionalFloat = 1e-10 } + assertRoundTripJSON { $0.optionalFloat = 1e-20 } + assertRoundTripJSON { $0.optionalFloat = 1e-30 } + assertRoundTripJSON { $0.optionalFloat = Float(1e-40) } + assertRoundTripJSON { $0.optionalFloat = Float(1e-50) } + assertRoundTripJSON { $0.optionalFloat = Float(1e-60) } + assertRoundTripJSON { $0.optionalFloat = Float(1e-100) } + assertRoundTripJSON { $0.optionalFloat = Float(1e-200) } + assertRoundTripJSON { $0.optionalFloat = Float.pi } + assertRoundTripJSON { $0.optionalFloat = 123456.789123456789123 } + assertRoundTripJSON { $0.optionalFloat = 1999.9999999999 } + assertRoundTripJSON { $0.optionalFloat = 1999.9 } + assertRoundTripJSON { $0.optionalFloat = 1999.99 } + assertRoundTripJSON { $0.optionalFloat = 1999.99 } + assertRoundTripJSON { $0.optionalFloat = 3.402823567e+38 } + assertRoundTripJSON { $0.optionalFloat = 1.1754944e-38 } } func testOptionalDouble_NaN() throws { @@ -654,19 +675,19 @@ final class Test_JSON: XCTestCase, PBTestHelpers { func testOptionalDouble_roundtrip() throws { for _ in 0..<10000 { let d = drand48() - assertRoundTripJSON {$0.optionalDouble = d} + assertRoundTripJSON { $0.optionalDouble = d } } } func testOptionalFloat_roundtrip() throws { for _ in 0..<10000 { let f = Float(drand48()) - assertRoundTripJSON {$0.optionalFloat = f} + assertRoundTripJSON { $0.optionalFloat = f } } } func testOptionalBool() throws { - assertJSONEncode("{\"optionalBool\":true}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalBool\":true}") { (o: inout MessageTestType) in o.optionalBool = true } @@ -677,15 +698,15 @@ final class Test_JSON: XCTestCase, PBTestHelpers { } func testOptionalString() { - assertJSONEncode("{\"optionalString\":\"hello\"}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalString\":\"hello\"}") { (o: inout MessageTestType) in o.optionalString = "hello" } // Start of the C1 range - assertJSONEncode("{\"optionalString\":\"~\\u007F\\u0080\\u0081\"}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalString\":\"~\\u007F\\u0080\\u0081\"}") { (o: inout MessageTestType) in o.optionalString = "\u{7e}\u{7f}\u{80}\u{81}" } // End of the C1 range - assertJSONEncode("{\"optionalString\":\"\\u009E\\u009F ¡¢£\"}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalString\":\"\\u009E\\u009F ¡¢£\"}") { (o: inout MessageTestType) in o.optionalString = "\u{9e}\u{9f}\u{a0}\u{a1}\u{a2}\u{a3}" } @@ -695,11 +716,11 @@ final class Test_JSON: XCTestCase, PBTestHelpers { XCTAssertEqual(try a.jsonString(), "{}") // Example from RFC 7159: G clef coded as escaped surrogate pair - assertJSONDecodeSucceeds("{\"optionalString\":\"\\uD834\\uDD1E\"}") {$0.optionalString == "𝄞"} + assertJSONDecodeSucceeds("{\"optionalString\":\"\\uD834\\uDD1E\"}") { $0.optionalString == "𝄞" } // Ditto, with lowercase hex - assertJSONDecodeSucceeds("{\"optionalString\":\"\\ud834\\udd1e\"}") {$0.optionalString == "𝄞"} + assertJSONDecodeSucceeds("{\"optionalString\":\"\\ud834\\udd1e\"}") { $0.optionalString == "𝄞" } // Same character represented directly - assertJSONDecodeSucceeds("{\"optionalString\":\"𝄞\"}") {$0.optionalString == "𝄞"} + assertJSONDecodeSucceeds("{\"optionalString\":\"𝄞\"}") { $0.optionalString == "𝄞" } // Various broken surrogate forms assertJSONDecodeFails("{\"optionalString\":\"\\uDD1E\\uD834\"}") assertJSONDecodeFails("{\"optionalString\":\"\\uDD1E\"}") @@ -709,16 +730,20 @@ final class Test_JSON: XCTestCase, PBTestHelpers { func testOptionalString_controlCharacters() { // Verify that all C0 controls are correctly escaped - assertJSONEncode("{\"optionalString\":\"\\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\"}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalString\":\"\\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\"}") { + (o: inout MessageTestType) in o.optionalString = "\u{00}\u{01}\u{02}\u{03}\u{04}\u{05}\u{06}\u{07}" } - assertJSONEncode("{\"optionalString\":\"\\b\\t\\n\\u000B\\f\\r\\u000E\\u000F\"}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalString\":\"\\b\\t\\n\\u000B\\f\\r\\u000E\\u000F\"}") { + (o: inout MessageTestType) in o.optionalString = "\u{08}\u{09}\u{0a}\u{0b}\u{0c}\u{0d}\u{0e}\u{0f}" } - assertJSONEncode("{\"optionalString\":\"\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017\"}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalString\":\"\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017\"}") { + (o: inout MessageTestType) in o.optionalString = "\u{10}\u{11}\u{12}\u{13}\u{14}\u{15}\u{16}\u{17}" } - assertJSONEncode("{\"optionalString\":\"\\u0018\\u0019\\u001A\\u001B\\u001C\\u001D\\u001E\\u001F\"}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalString\":\"\\u0018\\u0019\\u001A\\u001B\\u001C\\u001D\\u001E\\u001F\"}") { + (o: inout MessageTestType) in o.optionalString = "\u{18}\u{19}\u{1a}\u{1b}\u{1c}\u{1d}\u{1e}\u{1f}" } } @@ -729,41 +754,41 @@ final class Test_JSON: XCTestCase, PBTestHelpers { a.optionalBytes = Data() XCTAssertEqual(try a.jsonString(), "{}") - assertJSONEncode("{\"optionalBytes\":\"AA==\"}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalBytes\":\"AA==\"}") { (o: inout MessageTestType) in o.optionalBytes = Data([0]) } - assertJSONEncode("{\"optionalBytes\":\"AAA=\"}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalBytes\":\"AAA=\"}") { (o: inout MessageTestType) in o.optionalBytes = Data([0, 0]) } - assertJSONEncode("{\"optionalBytes\":\"AAAA\"}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalBytes\":\"AAAA\"}") { (o: inout MessageTestType) in o.optionalBytes = Data([0, 0, 0]) } - assertJSONEncode("{\"optionalBytes\":\"/w==\"}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalBytes\":\"/w==\"}") { (o: inout MessageTestType) in o.optionalBytes = Data([255]) } - assertJSONEncode("{\"optionalBytes\":\"//8=\"}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalBytes\":\"//8=\"}") { (o: inout MessageTestType) in o.optionalBytes = Data([255, 255]) } - assertJSONEncode("{\"optionalBytes\":\"////\"}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalBytes\":\"////\"}") { (o: inout MessageTestType) in o.optionalBytes = Data([255, 255, 255]) } - assertJSONEncode("{\"optionalBytes\":\"QQ==\"}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalBytes\":\"QQ==\"}") { (o: inout MessageTestType) in o.optionalBytes = Data([65]) } assertJSONDecodeFails("{\"optionalBytes\":\"QQ=\"}") assertJSONDecodeSucceeds("{\"optionalBytes\":\"QQ\"}") { $0.optionalBytes == Data([65]) } - assertJSONEncode("{\"optionalBytes\":\"QUI=\"}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalBytes\":\"QUI=\"}") { (o: inout MessageTestType) in o.optionalBytes = Data([65, 66]) } assertJSONDecodeSucceeds("{\"optionalBytes\":\"QUI\"}") { $0.optionalBytes == Data([65, 66]) } - assertJSONEncode("{\"optionalBytes\":\"QUJD\"}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalBytes\":\"QUJD\"}") { (o: inout MessageTestType) in o.optionalBytes = Data([65, 66, 67]) } - assertJSONEncode("{\"optionalBytes\":\"QUJDRA==\"}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalBytes\":\"QUJDRA==\"}") { (o: inout MessageTestType) in o.optionalBytes = Data([65, 66, 67, 68]) } assertJSONDecodeFails("{\"optionalBytes\":\"QUJDRA===\"}") @@ -771,14 +796,14 @@ final class Test_JSON: XCTestCase, PBTestHelpers { assertJSONDecodeSucceeds("{\"optionalBytes\":\"QUJDRA\"}") { $0.optionalBytes == Data([65, 66, 67, 68]) } - assertJSONEncode("{\"optionalBytes\":\"QUJDREU=\"}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalBytes\":\"QUJDREU=\"}") { (o: inout MessageTestType) in o.optionalBytes = Data([65, 66, 67, 68, 69]) } assertJSONDecodeFails("{\"optionalBytes\":\"QUJDREU==\"}") assertJSONDecodeSucceeds("{\"optionalBytes\":\"QUJDREU\"}") { $0.optionalBytes == Data([65, 66, 67, 68, 69]) } - assertJSONEncode("{\"optionalBytes\":\"QUJDREVG\"}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalBytes\":\"QUJDREVG\"}") { (o: inout MessageTestType) in o.optionalBytes = Data([65, 66, 67, 68, 69, 70]) } assertJSONDecodeFails("{\"optionalBytes\":\"QUJDREVG=\"}") @@ -853,7 +878,7 @@ final class Test_JSON: XCTestCase, PBTestHelpers { } func testOptionalNestedMessage() { - assertJSONEncode("{\"optionalNestedMessage\":{\"bb\":1}}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalNestedMessage\":{\"bb\":1}}") { (o: inout MessageTestType) in var sub = SwiftProtoTesting_Proto3_TestAllTypes.NestedMessage() sub.bb = 1 o.optionalNestedMessage = sub @@ -861,12 +886,12 @@ final class Test_JSON: XCTestCase, PBTestHelpers { } func testOptionalNestedEnum() { - assertJSONEncode("{\"optionalNestedEnum\":\"FOO\"}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalNestedEnum\":\"FOO\"}") { (o: inout MessageTestType) in o.optionalNestedEnum = SwiftProtoTesting_Proto3_TestAllTypes.NestedEnum.foo } - assertJSONDecodeSucceeds("{\"optionalNestedEnum\":1}") {$0.optionalNestedEnum == .foo} + assertJSONDecodeSucceeds("{\"optionalNestedEnum\":1}") { $0.optionalNestedEnum == .foo } // Out-of-range values should be serialized to an int - assertJSONEncode("{\"optionalNestedEnum\":123}") {(o: inout MessageTestType) in + assertJSONEncode("{\"optionalNestedEnum\":123}") { (o: inout MessageTestType) in o.optionalNestedEnum = .UNRECOGNIZED(123) } // TODO: Check whether Google's spec agrees that unknown Enum tags @@ -875,44 +900,44 @@ final class Test_JSON: XCTestCase, PBTestHelpers { } func testRepeatedInt32() { - assertJSONEncode("{\"repeatedInt32\":[1]}") {(o: inout MessageTestType) in + assertJSONEncode("{\"repeatedInt32\":[1]}") { (o: inout MessageTestType) in o.repeatedInt32 = [1] } - assertJSONEncode("{\"repeatedInt32\":[1,2]}") {(o: inout MessageTestType) in + assertJSONEncode("{\"repeatedInt32\":[1,2]}") { (o: inout MessageTestType) in o.repeatedInt32 = [1, 2] } - assertEncode([250, 1, 2, 1, 2]) {(o: inout MessageTestType) in + assertEncode([250, 1, 2, 1, 2]) { (o: inout MessageTestType) in // Proto3 seems to default to packed for repeated int fields o.repeatedInt32 = [1, 2] } - assertJSONDecodeSucceeds("{\"repeatedInt32\":null}") {$0.repeatedInt32 == []} - assertJSONDecodeSucceeds("{\"repeatedInt32\":[]}") {$0.repeatedInt32 == []} - assertJSONDecodeSucceeds("{\"repeatedInt32\":[1]}") {$0.repeatedInt32 == [1]} - assertJSONDecodeSucceeds("{\"repeatedInt32\":[1,2]}") {$0.repeatedInt32 == [1, 2]} + assertJSONDecodeSucceeds("{\"repeatedInt32\":null}") { $0.repeatedInt32 == [] } + assertJSONDecodeSucceeds("{\"repeatedInt32\":[]}") { $0.repeatedInt32 == [] } + assertJSONDecodeSucceeds("{\"repeatedInt32\":[1]}") { $0.repeatedInt32 == [1] } + assertJSONDecodeSucceeds("{\"repeatedInt32\":[1,2]}") { $0.repeatedInt32 == [1, 2] } } func testRepeatedString() { - assertJSONEncode("{\"repeatedString\":[\"\"]}") {(o: inout MessageTestType) in + assertJSONEncode("{\"repeatedString\":[\"\"]}") { (o: inout MessageTestType) in o.repeatedString = [""] } - assertJSONEncode("{\"repeatedString\":[\"abc\",\"\"]}") {(o: inout MessageTestType) in + assertJSONEncode("{\"repeatedString\":[\"abc\",\"\"]}") { (o: inout MessageTestType) in o.repeatedString = ["abc", ""] } - assertJSONDecodeSucceeds("{\"repeatedString\":null}") {$0.repeatedString == []} - assertJSONDecodeSucceeds("{\"repeatedString\":[]}") {$0.repeatedString == []} + assertJSONDecodeSucceeds("{\"repeatedString\":null}") { $0.repeatedString == [] } + assertJSONDecodeSucceeds("{\"repeatedString\":[]}") { $0.repeatedString == [] } assertJSONDecodeSucceeds(" { \"repeatedString\" : [ \"1\" , \"2\" ] } ") { $0.repeatedString == ["1", "2"] } } func testRepeatedNestedMessage() { - assertJSONEncode("{\"repeatedNestedMessage\":[{\"bb\":1}]}") {(o: inout MessageTestType) in + assertJSONEncode("{\"repeatedNestedMessage\":[{\"bb\":1}]}") { (o: inout MessageTestType) in var sub = SwiftProtoTesting_Proto3_TestAllTypes.NestedMessage() sub.bb = 1 o.repeatedNestedMessage = [sub] } - assertJSONEncode("{\"repeatedNestedMessage\":[{\"bb\":1},{\"bb\":2}]}") {(o: inout MessageTestType) in + assertJSONEncode("{\"repeatedNestedMessage\":[{\"bb\":1},{\"bb\":2}]}") { (o: inout MessageTestType) in var sub1 = SwiftProtoTesting_Proto3_TestAllTypes.NestedMessage() sub1.bb = 1 var sub2 = SwiftProtoTesting_Proto3_TestAllTypes.NestedMessage() @@ -925,22 +950,22 @@ final class Test_JSON: XCTestCase, PBTestHelpers { } func testRepeatedEnum() { - assertJSONEncode("{\"repeatedNestedEnum\":[\"FOO\"]}") {(o: inout MessageTestType) in + assertJSONEncode("{\"repeatedNestedEnum\":[\"FOO\"]}") { (o: inout MessageTestType) in o.repeatedNestedEnum = [.foo] } - assertJSONEncode("{\"repeatedNestedEnum\":[\"FOO\",\"BAR\"]}") {(o: inout MessageTestType) in + assertJSONEncode("{\"repeatedNestedEnum\":[\"FOO\",\"BAR\"]}") { (o: inout MessageTestType) in o.repeatedNestedEnum = [.foo, .bar] } - assertJSONDecodeSucceeds("{\"repeatedNestedEnum\":[\"FOO\",0,1,\"BAR\",-1]}") {(o:MessageTestType) in + assertJSONDecodeSucceeds("{\"repeatedNestedEnum\":[\"FOO\",0,1,\"BAR\",-1]}") { (o: MessageTestType) in o.repeatedNestedEnum == [.foo, .zero, .foo, .bar, .neg] } assertJSONDecodeFails("{\"repeatedNestedEnum\":[null]}") assertJSONDecodeFails("{\"repeatedNestedEnum\":\"FOO\"}") assertJSONDecodeFails("{\"repeatedNestedEnum\":0}") - assertJSONDecodeSucceeds("{\"repeatedNestedEnum\":null}") {(o:MessageTestType) in + assertJSONDecodeSucceeds("{\"repeatedNestedEnum\":null}") { (o: MessageTestType) in o.repeatedNestedEnum == [] } - assertJSONDecodeSucceeds("{\"repeatedNestedEnum\":[]}") {(o:MessageTestType) in + assertJSONDecodeSucceeds("{\"repeatedNestedEnum\":[]}") { (o: MessageTestType) in o.repeatedNestedEnum == [] } } @@ -948,13 +973,13 @@ final class Test_JSON: XCTestCase, PBTestHelpers { // TODO: Test other repeated field types func testOneof() { - assertJSONEncode("{\"oneofUint32\":1}") {(o: inout MessageTestType) in + assertJSONEncode("{\"oneofUint32\":1}") { (o: inout MessageTestType) in o.oneofUint32 = 1 } - assertJSONEncode("{\"oneofString\":\"abc\"}") {(o: inout MessageTestType) in + assertJSONEncode("{\"oneofString\":\"abc\"}") { (o: inout MessageTestType) in o.oneofString = "abc" } - assertJSONEncode("{\"oneofNestedMessage\":{\"bb\":1}}") {(o: inout MessageTestType) in + assertJSONEncode("{\"oneofNestedMessage\":{\"bb\":1}}") { (o: inout MessageTestType) in var sub = SwiftProtoTesting_Proto3_TestAllTypes.NestedMessage() sub.bb = 1 o.oneofNestedMessage = sub @@ -965,219 +990,218 @@ final class Test_JSON: XCTestCase, PBTestHelpers { } func testEmptyMessage() { - assertJSONDecodeSucceeds("{}") {MessageTestType -> Bool in true} + assertJSONDecodeSucceeds("{}") { MessageTestType -> Bool in true } assertJSONDecodeFails("") assertJSONDecodeFails("{") assertJSONDecodeFails("}") } } - final class Test_JSONPacked: XCTestCase, PBTestHelpers { typealias MessageTestType = SwiftProtoTesting_Proto3_TestPackedTypes func testPackedFloat() { - assertJSONEncode("{\"packedFloat\":[1.0]}") {(o: inout MessageTestType) in + assertJSONEncode("{\"packedFloat\":[1.0]}") { (o: inout MessageTestType) in o.packedFloat = [1] } - assertJSONEncode("{\"packedFloat\":[1.0,0.25,0.125]}") {(o: inout MessageTestType) in + assertJSONEncode("{\"packedFloat\":[1.0,0.25,0.125]}") { (o: inout MessageTestType) in o.packedFloat = [1, 0.25, 0.125] } assertJSONDecodeSucceeds("{\"packedFloat\":[1,0.25,125e-3]}") { $0.packedFloat == [1, 0.25, 0.125] } - assertJSONDecodeSucceeds("{\"packedFloat\":null}") {$0.packedFloat == []} - assertJSONDecodeSucceeds("{\"packedFloat\":[]}") {$0.packedFloat == []} - assertJSONDecodeSucceeds("{\"packedFloat\":[\"1\"]}") {$0.packedFloat == [1]} - assertJSONDecodeSucceeds("{\"packedFloat\":[\"1\",2]}") {$0.packedFloat == [1, 2]} + assertJSONDecodeSucceeds("{\"packedFloat\":null}") { $0.packedFloat == [] } + assertJSONDecodeSucceeds("{\"packedFloat\":[]}") { $0.packedFloat == [] } + assertJSONDecodeSucceeds("{\"packedFloat\":[\"1\"]}") { $0.packedFloat == [1] } + assertJSONDecodeSucceeds("{\"packedFloat\":[\"1\",2]}") { $0.packedFloat == [1, 2] } } func testPackedDouble() { - assertJSONEncode("{\"packedDouble\":[1.0]}") {(o: inout MessageTestType) in + assertJSONEncode("{\"packedDouble\":[1.0]}") { (o: inout MessageTestType) in o.packedDouble = [1] } - assertJSONEncode("{\"packedDouble\":[1.0,0.25,0.125]}") {(o: inout MessageTestType) in + assertJSONEncode("{\"packedDouble\":[1.0,0.25,0.125]}") { (o: inout MessageTestType) in o.packedDouble = [1, 0.25, 0.125] } assertJSONDecodeSucceeds("{\"packedDouble\":[1,0.25,125e-3]}") { $0.packedDouble == [1, 0.25, 0.125] } - assertJSONDecodeSucceeds("{\"packedDouble\":null}") {$0.packedDouble == []} - assertJSONDecodeSucceeds("{\"packedDouble\":[]}") {$0.packedDouble == []} - assertJSONDecodeSucceeds("{\"packedDouble\":[\"1\"]}") {$0.packedDouble == [1]} - assertJSONDecodeSucceeds("{\"packedDouble\":[\"1\",2]}") {$0.packedDouble == [1, 2]} + assertJSONDecodeSucceeds("{\"packedDouble\":null}") { $0.packedDouble == [] } + assertJSONDecodeSucceeds("{\"packedDouble\":[]}") { $0.packedDouble == [] } + assertJSONDecodeSucceeds("{\"packedDouble\":[\"1\"]}") { $0.packedDouble == [1] } + assertJSONDecodeSucceeds("{\"packedDouble\":[\"1\",2]}") { $0.packedDouble == [1, 2] } } func testPackedInt32() { - assertJSONEncode("{\"packedInt32\":[1]}") {(o: inout MessageTestType) in + assertJSONEncode("{\"packedInt32\":[1]}") { (o: inout MessageTestType) in o.packedInt32 = [1] } - assertJSONEncode("{\"packedInt32\":[1,2]}") {(o: inout MessageTestType) in + assertJSONEncode("{\"packedInt32\":[1,2]}") { (o: inout MessageTestType) in o.packedInt32 = [1, 2] } - assertJSONEncode("{\"packedInt32\":[-2147483648,2147483647]}") {(o: inout MessageTestType) in + assertJSONEncode("{\"packedInt32\":[-2147483648,2147483647]}") { (o: inout MessageTestType) in o.packedInt32 = [Int32.min, Int32.max] } - assertJSONDecodeSucceeds("{\"packedInt32\":null}") {$0.packedInt32 == []} - assertJSONDecodeSucceeds("{\"packedInt32\":[]}") {$0.packedInt32 == []} - assertJSONDecodeSucceeds("{\"packedInt32\":[\"1\"]}") {$0.packedInt32 == [1]} - assertJSONDecodeSucceeds("{\"packedInt32\":[\"1\",\"2\"]}") {$0.packedInt32 == [1, 2]} - assertJSONDecodeSucceeds(" { \"packedInt32\" : [ \"1\" , \"2\" ] } ") {$0.packedInt32 == [1, 2]} + assertJSONDecodeSucceeds("{\"packedInt32\":null}") { $0.packedInt32 == [] } + assertJSONDecodeSucceeds("{\"packedInt32\":[]}") { $0.packedInt32 == [] } + assertJSONDecodeSucceeds("{\"packedInt32\":[\"1\"]}") { $0.packedInt32 == [1] } + assertJSONDecodeSucceeds("{\"packedInt32\":[\"1\",\"2\"]}") { $0.packedInt32 == [1, 2] } + assertJSONDecodeSucceeds(" { \"packedInt32\" : [ \"1\" , \"2\" ] } ") { $0.packedInt32 == [1, 2] } } func testPackedInt64() { - assertJSONEncode("{\"packedInt64\":[\"1\"]}") {(o: inout MessageTestType) in + assertJSONEncode("{\"packedInt64\":[\"1\"]}") { (o: inout MessageTestType) in o.packedInt64 = [1] } assertJSONEncode("{\"packedInt64\":[\"9223372036854775807\",\"-9223372036854775808\"]}") { (o: inout MessageTestType) in o.packedInt64 = [Int64.max, Int64.min] } - assertJSONDecodeSucceeds("{\"packedInt64\":null}") {$0.packedInt64 == []} - assertJSONDecodeSucceeds("{\"packedInt64\":[]}") {$0.packedInt64 == []} - assertJSONDecodeSucceeds("{\"packedInt64\":[1]}") {$0.packedInt64 == [1]} - assertJSONDecodeSucceeds("{\"packedInt64\":[1,2]}") {$0.packedInt64 == [1, 2]} + assertJSONDecodeSucceeds("{\"packedInt64\":null}") { $0.packedInt64 == [] } + assertJSONDecodeSucceeds("{\"packedInt64\":[]}") { $0.packedInt64 == [] } + assertJSONDecodeSucceeds("{\"packedInt64\":[1]}") { $0.packedInt64 == [1] } + assertJSONDecodeSucceeds("{\"packedInt64\":[1,2]}") { $0.packedInt64 == [1, 2] } assertJSONDecodeFails("{\"packedInt64\":[null]}") } func testPackedUInt32() { - assertJSONEncode("{\"packedUint32\":[1]}") {(o: inout MessageTestType) in + assertJSONEncode("{\"packedUint32\":[1]}") { (o: inout MessageTestType) in o.packedUint32 = [1] } - assertJSONEncode("{\"packedUint32\":[0,4294967295]}") {(o: inout MessageTestType) in + assertJSONEncode("{\"packedUint32\":[0,4294967295]}") { (o: inout MessageTestType) in o.packedUint32 = [UInt32.min, UInt32.max] } - assertJSONDecodeSucceeds("{\"packedUint32\":null}") {$0.packedUint32 == []} - assertJSONDecodeSucceeds("{\"packedUint32\":[]}") {$0.packedUint32 == []} - assertJSONDecodeSucceeds("{\"packedUint32\":[1]}") {$0.packedUint32 == [1]} - assertJSONDecodeSucceeds("{\"packedUint32\":[1,2]}") {$0.packedUint32 == [1, 2]} + assertJSONDecodeSucceeds("{\"packedUint32\":null}") { $0.packedUint32 == [] } + assertJSONDecodeSucceeds("{\"packedUint32\":[]}") { $0.packedUint32 == [] } + assertJSONDecodeSucceeds("{\"packedUint32\":[1]}") { $0.packedUint32 == [1] } + assertJSONDecodeSucceeds("{\"packedUint32\":[1,2]}") { $0.packedUint32 == [1, 2] } assertJSONDecodeFails("{\"packedUint32\":[null]}") assertJSONDecodeFails("{\"packedUint32\":[-1]}") assertJSONDecodeFails("{\"packedUint32\":[1.2]}") } func testPackedUInt64() { - assertJSONEncode("{\"packedUint64\":[\"1\"]}") {(o: inout MessageTestType) in + assertJSONEncode("{\"packedUint64\":[\"1\"]}") { (o: inout MessageTestType) in o.packedUint64 = [1] } assertJSONEncode("{\"packedUint64\":[\"0\",\"18446744073709551615\"]}") { (o: inout MessageTestType) in o.packedUint64 = [UInt64.min, UInt64.max] } - assertJSONDecodeSucceeds("{\"packedUint64\":null}") {$0.packedUint64 == []} - assertJSONDecodeSucceeds("{\"packedUint64\":[]}") {$0.packedUint64 == []} - assertJSONDecodeSucceeds("{\"packedUint64\":[1]}") {$0.packedUint64 == [1]} - assertJSONDecodeSucceeds("{\"packedUint64\":[1,2]}") {$0.packedUint64 == [1, 2]} + assertJSONDecodeSucceeds("{\"packedUint64\":null}") { $0.packedUint64 == [] } + assertJSONDecodeSucceeds("{\"packedUint64\":[]}") { $0.packedUint64 == [] } + assertJSONDecodeSucceeds("{\"packedUint64\":[1]}") { $0.packedUint64 == [1] } + assertJSONDecodeSucceeds("{\"packedUint64\":[1,2]}") { $0.packedUint64 == [1, 2] } assertJSONDecodeFails("{\"packedUint64\":[null]}") assertJSONDecodeFails("{\"packedUint64\":[-1]}") assertJSONDecodeFails("{\"packedUint64\":[1.2]}") } func testPackedSInt32() { - assertJSONEncode("{\"packedSint32\":[1]}") {(o: inout MessageTestType) in + assertJSONEncode("{\"packedSint32\":[1]}") { (o: inout MessageTestType) in o.packedSint32 = [1] } - assertJSONEncode("{\"packedSint32\":[-2147483648,2147483647]}") {(o: inout MessageTestType) in + assertJSONEncode("{\"packedSint32\":[-2147483648,2147483647]}") { (o: inout MessageTestType) in o.packedSint32 = [Int32.min, Int32.max] } - assertJSONDecodeSucceeds("{\"packedSint32\":null}") {$0.packedSint32 == []} - assertJSONDecodeSucceeds("{\"packedSint32\":[]}") {$0.packedSint32 == []} - assertJSONDecodeSucceeds("{\"packedSint32\":[1]}") {$0.packedSint32 == [1]} - assertJSONDecodeSucceeds("{\"packedSint32\":[1,2]}") {$0.packedSint32 == [1, 2]} + assertJSONDecodeSucceeds("{\"packedSint32\":null}") { $0.packedSint32 == [] } + assertJSONDecodeSucceeds("{\"packedSint32\":[]}") { $0.packedSint32 == [] } + assertJSONDecodeSucceeds("{\"packedSint32\":[1]}") { $0.packedSint32 == [1] } + assertJSONDecodeSucceeds("{\"packedSint32\":[1,2]}") { $0.packedSint32 == [1, 2] } assertJSONDecodeFails("{\"packedSint32\":[null]}") assertJSONDecodeFails("{\"packedSint32\":[1.2]}") } func testPackedSInt64() { - assertJSONEncode("{\"packedSint64\":[\"1\"]}") {(o: inout MessageTestType) in + assertJSONEncode("{\"packedSint64\":[\"1\"]}") { (o: inout MessageTestType) in o.packedSint64 = [1] } assertJSONEncode("{\"packedSint64\":[\"-9223372036854775808\",\"9223372036854775807\"]}") { (o: inout MessageTestType) in o.packedSint64 = [Int64.min, Int64.max] } - assertJSONDecodeSucceeds("{\"packedSint64\":null}") {$0.packedSint64 == []} - assertJSONDecodeSucceeds("{\"packedSint64\":[]}") {$0.packedSint64 == []} - assertJSONDecodeSucceeds("{\"packedSint64\":[1]}") {$0.packedSint64 == [1]} - assertJSONDecodeSucceeds("{\"packedSint64\":[1,2]}") {$0.packedSint64 == [1, 2]} + assertJSONDecodeSucceeds("{\"packedSint64\":null}") { $0.packedSint64 == [] } + assertJSONDecodeSucceeds("{\"packedSint64\":[]}") { $0.packedSint64 == [] } + assertJSONDecodeSucceeds("{\"packedSint64\":[1]}") { $0.packedSint64 == [1] } + assertJSONDecodeSucceeds("{\"packedSint64\":[1,2]}") { $0.packedSint64 == [1, 2] } assertJSONDecodeFails("{\"packedSint64\":[null]}") assertJSONDecodeFails("{\"packedSint64\":[1.2]}") } func testPackedFixed32() { - assertJSONEncode("{\"packedFixed32\":[1]}") {(o: inout MessageTestType) in + assertJSONEncode("{\"packedFixed32\":[1]}") { (o: inout MessageTestType) in o.packedFixed32 = [1] } - assertJSONEncode("{\"packedFixed32\":[0,4294967295]}") {(o: inout MessageTestType) in + assertJSONEncode("{\"packedFixed32\":[0,4294967295]}") { (o: inout MessageTestType) in o.packedFixed32 = [UInt32.min, UInt32.max] } - assertJSONDecodeSucceeds("{\"packedFixed32\":null}") {$0.packedFixed32 == []} - assertJSONDecodeSucceeds("{\"packedFixed32\":[]}") {$0.packedFixed32 == []} - assertJSONDecodeSucceeds("{\"packedFixed32\":[1]}") {$0.packedFixed32 == [1]} - assertJSONDecodeSucceeds("{\"packedFixed32\":[1,2]}") {$0.packedFixed32 == [1, 2]} + assertJSONDecodeSucceeds("{\"packedFixed32\":null}") { $0.packedFixed32 == [] } + assertJSONDecodeSucceeds("{\"packedFixed32\":[]}") { $0.packedFixed32 == [] } + assertJSONDecodeSucceeds("{\"packedFixed32\":[1]}") { $0.packedFixed32 == [1] } + assertJSONDecodeSucceeds("{\"packedFixed32\":[1,2]}") { $0.packedFixed32 == [1, 2] } assertJSONDecodeFails("{\"packedFixed32\":[null]}") assertJSONDecodeFails("{\"packedFixed32\":[-1]}") assertJSONDecodeFails("{\"packedFixed32\":[1.2]}") } func testPackedFixed64() { - assertJSONEncode("{\"packedFixed64\":[\"1\"]}") {(o: inout MessageTestType) in + assertJSONEncode("{\"packedFixed64\":[\"1\"]}") { (o: inout MessageTestType) in o.packedFixed64 = [1] } assertJSONEncode("{\"packedFixed64\":[\"0\",\"18446744073709551615\"]}") { (o: inout MessageTestType) in o.packedFixed64 = [UInt64.min, UInt64.max] } - assertJSONDecodeSucceeds("{\"packedFixed64\":null}") {$0.packedFixed64 == []} - assertJSONDecodeSucceeds("{\"packedFixed64\":[]}") {$0.packedFixed64 == []} - assertJSONDecodeSucceeds("{\"packedFixed64\":[1]}") {$0.packedFixed64 == [1]} - assertJSONDecodeSucceeds("{\"packedFixed64\":[1,2]}") {$0.packedFixed64 == [1, 2]} + assertJSONDecodeSucceeds("{\"packedFixed64\":null}") { $0.packedFixed64 == [] } + assertJSONDecodeSucceeds("{\"packedFixed64\":[]}") { $0.packedFixed64 == [] } + assertJSONDecodeSucceeds("{\"packedFixed64\":[1]}") { $0.packedFixed64 == [1] } + assertJSONDecodeSucceeds("{\"packedFixed64\":[1,2]}") { $0.packedFixed64 == [1, 2] } assertJSONDecodeFails("{\"packedFixed64\":[null]}") assertJSONDecodeFails("{\"packedFixed64\":[-1]}") assertJSONDecodeFails("{\"packedFixed64\":[1.2]}") } func testPackedSFixed32() { - assertJSONEncode("{\"packedSfixed32\":[1]}") {(o: inout MessageTestType) in + assertJSONEncode("{\"packedSfixed32\":[1]}") { (o: inout MessageTestType) in o.packedSfixed32 = [1] } - assertJSONEncode("{\"packedSfixed32\":[-2147483648,2147483647]}") {(o: inout MessageTestType) in + assertJSONEncode("{\"packedSfixed32\":[-2147483648,2147483647]}") { (o: inout MessageTestType) in o.packedSfixed32 = [Int32.min, Int32.max] } - assertJSONDecodeSucceeds("{\"packedSfixed32\":null}") {$0.packedSfixed32 == []} - assertJSONDecodeSucceeds("{\"packedSfixed32\":[]}") {$0.packedSfixed32 == []} - assertJSONDecodeSucceeds("{\"packedSfixed32\":[1]}") {$0.packedSfixed32 == [1]} - assertJSONDecodeSucceeds("{\"packedSfixed32\":[1,2]}") {$0.packedSfixed32 == [1, 2]} + assertJSONDecodeSucceeds("{\"packedSfixed32\":null}") { $0.packedSfixed32 == [] } + assertJSONDecodeSucceeds("{\"packedSfixed32\":[]}") { $0.packedSfixed32 == [] } + assertJSONDecodeSucceeds("{\"packedSfixed32\":[1]}") { $0.packedSfixed32 == [1] } + assertJSONDecodeSucceeds("{\"packedSfixed32\":[1,2]}") { $0.packedSfixed32 == [1, 2] } assertJSONDecodeFails("{\"packedSfixed32\":[null]}") assertJSONDecodeFails("{\"packedSfixed32\":[1.2]}") } func testPackedSFixed64() { - assertJSONEncode("{\"packedSfixed64\":[\"1\"]}") {(o: inout MessageTestType) in + assertJSONEncode("{\"packedSfixed64\":[\"1\"]}") { (o: inout MessageTestType) in o.packedSfixed64 = [1] } assertJSONEncode("{\"packedSfixed64\":[\"-9223372036854775808\",\"9223372036854775807\"]}") { (o: inout MessageTestType) in o.packedSfixed64 = [Int64.min, Int64.max] } - assertJSONDecodeSucceeds("{\"packedSfixed64\":null}") {$0.packedSfixed64 == []} - assertJSONDecodeSucceeds("{\"packedSfixed64\":[]}") {$0.packedSfixed64 == []} - assertJSONDecodeSucceeds("{\"packedSfixed64\":[1]}") {$0.packedSfixed64 == [1]} - assertJSONDecodeSucceeds("{\"packedSfixed64\":[1,2]}") {$0.packedSfixed64 == [1, 2]} + assertJSONDecodeSucceeds("{\"packedSfixed64\":null}") { $0.packedSfixed64 == [] } + assertJSONDecodeSucceeds("{\"packedSfixed64\":[]}") { $0.packedSfixed64 == [] } + assertJSONDecodeSucceeds("{\"packedSfixed64\":[1]}") { $0.packedSfixed64 == [1] } + assertJSONDecodeSucceeds("{\"packedSfixed64\":[1,2]}") { $0.packedSfixed64 == [1, 2] } assertJSONDecodeFails("{\"packedSfixed64\":[null]}") assertJSONDecodeFails("{\"packedSfixed64\":[1.2]}") } func testPackedBool() { - assertJSONEncode("{\"packedBool\":[true]}") {(o: inout MessageTestType) in + assertJSONEncode("{\"packedBool\":[true]}") { (o: inout MessageTestType) in o.packedBool = [true] } assertJSONEncode("{\"packedBool\":[true,false]}") { (o: inout MessageTestType) in - o.packedBool = [true,false] + o.packedBool = [true, false] } - assertJSONDecodeSucceeds("{\"packedBool\":null}") {$0.packedBool == []} - assertJSONDecodeSucceeds("{\"packedBool\":[]}") {$0.packedBool == []} + assertJSONDecodeSucceeds("{\"packedBool\":null}") { $0.packedBool == [] } + assertJSONDecodeSucceeds("{\"packedBool\":[]}") { $0.packedBool == [] } assertJSONDecodeFails("{\"packedBool\":[null]}") assertJSONDecodeFails("{\"packedBool\":[1,0]}") assertJSONDecodeFails("{\"packedBool\":[\"true\"]}") @@ -1189,19 +1213,19 @@ final class Test_JSONrepeated: XCTestCase, PBTestHelpers { typealias MessageTestType = SwiftProtoTesting_Proto3_TestUnpackedTypes func testPackedInt32() { - assertJSONEncode("{\"repeatedInt32\":[1]}") {(o: inout MessageTestType) in + assertJSONEncode("{\"repeatedInt32\":[1]}") { (o: inout MessageTestType) in o.repeatedInt32 = [1] } - assertJSONEncode("{\"repeatedInt32\":[1,2]}") {(o: inout MessageTestType) in + assertJSONEncode("{\"repeatedInt32\":[1,2]}") { (o: inout MessageTestType) in o.repeatedInt32 = [1, 2] } - assertEncode([8, 1, 8, 2]) {(o: inout MessageTestType) in + assertEncode([8, 1, 8, 2]) { (o: inout MessageTestType) in o.repeatedInt32 = [1, 2] } - assertJSONDecodeSucceeds("{\"repeatedInt32\":null}") {$0.repeatedInt32 == []} - assertJSONDecodeSucceeds("{\"repeatedInt32\":[]}") {$0.repeatedInt32 == []} - assertJSONDecodeSucceeds("{\"repeatedInt32\":[1]}") {$0.repeatedInt32 == [1]} - assertJSONDecodeSucceeds("{\"repeatedInt32\":[1,2]}") {$0.repeatedInt32 == [1, 2]} + assertJSONDecodeSucceeds("{\"repeatedInt32\":null}") { $0.repeatedInt32 == [] } + assertJSONDecodeSucceeds("{\"repeatedInt32\":[]}") { $0.repeatedInt32 == [] } + assertJSONDecodeSucceeds("{\"repeatedInt32\":[1]}") { $0.repeatedInt32 == [1] } + assertJSONDecodeSucceeds("{\"repeatedInt32\":[1,2]}") { $0.repeatedInt32 == [1, 2] } } } diff --git a/Tests/SwiftProtobufTests/Test_JSONDecodingOptions.swift b/Tests/SwiftProtobufTests/Test_JSONDecodingOptions.swift index e365ac04b..47a10387f 100644 --- a/Tests/SwiftProtobufTests/Test_JSONDecodingOptions.swift +++ b/Tests/SwiftProtobufTests/Test_JSONDecodingOptions.swift @@ -13,8 +13,8 @@ // ----------------------------------------------------------------------------- import Foundation -import XCTest import SwiftProtobuf +import XCTest final class Test_JSONDecodingOptions: XCTestCase { @@ -29,11 +29,11 @@ final class Test_JSONDecodingOptions: XCTestCase { let tests: [(Int, Bool)] = [ // Limit, success/failure - ( 10, true ), - ( 4, true ), - ( 3, true ), - ( 2, false ), - ( 1, false ), + (10, true), + (4, true), + (3, true), + (2, false), + (1, false), ] for (i, jsonInput) in jsonInputs.enumerated() { @@ -52,7 +52,7 @@ final class Test_JSONDecodingOptions: XCTestCase { } else { // Nothing, this is what was expected. } - } catch let e { + } catch let e { XCTFail("Decode failed (pass: \(i), limit: \(limit) with unexpected error: \(e)") } } @@ -144,15 +144,21 @@ final class Test_JSONDecodingOptions: XCTestCase { // Ignoring unknown fields do { - let _ = try SwiftProtoTesting_TestEmptyMessage(jsonString: jsonInput, - options:options) - XCTAssertTrue(isValidJSON, - "Input \(i): Should not have been able to parse: \(jsonInput)") + let _ = try SwiftProtoTesting_TestEmptyMessage( + jsonString: jsonInput, + options: options + ) + XCTAssertTrue( + isValidJSON, + "Input \(i): Should not have been able to parse: \(jsonInput)" + ) } catch JSONDecodingError.unknownField(let field) { XCTFail("Input \(i): should not have gotten unknown field \(field), input \(jsonInput)") } catch let e { - XCTAssertFalse(isValidJSON, - "Input \(i): Error \(e): Should have been able to parse: \(jsonInput)") + XCTAssertFalse( + isValidJSON, + "Input \(i): Error \(e): Should have been able to parse: \(jsonInput)" + ) } } } diff --git a/Tests/SwiftProtobufTests/Test_JSONEncodingOptions.swift b/Tests/SwiftProtobufTests/Test_JSONEncodingOptions.swift index e83a0c0d9..a625b4695 100644 --- a/Tests/SwiftProtobufTests/Test_JSONEncodingOptions.swift +++ b/Tests/SwiftProtobufTests/Test_JSONEncodingOptions.swift @@ -13,307 +13,371 @@ // ----------------------------------------------------------------------------- import Foundation -import XCTest import SwiftProtobuf +import XCTest final class Test_JSONEncodingOptions: XCTestCase { - - func testAlwaysPrintInt64sAsNumbers() { - // Use explicit options (the default is false), no reason only others can be pedantic. - var asStrings = JSONEncodingOptions() - asStrings.alwaysPrintInt64sAsNumbers = false - var asNumbers = JSONEncodingOptions() - asNumbers.alwaysPrintInt64sAsNumbers = true - - // Toplevel fields. - let msg1 = SwiftProtoTesting_Message2.with { - $0.optionalInt64 = 1656338459803 - } - XCTAssertEqual(try msg1.jsonString(options: asStrings), "{\"optionalInt64\":\"1656338459803\"}") - XCTAssertEqual(try msg1.jsonString(options: asNumbers), "{\"optionalInt64\":1656338459803}") - - let msg2 = SwiftProtoTesting_Message2.with { - $0.repeatedInt64 = [1656338459802, 1656338459803] - } - XCTAssertEqual(try msg2.jsonString(options: asStrings), "{\"repeatedInt64\":[\"1656338459802\",\"1656338459803\"]}") - XCTAssertEqual(try msg2.jsonString(options: asNumbers), "{\"repeatedInt64\":[1656338459802,1656338459803]}") - - let msg3 = SwiftProtoTesting_Message2.with { - $0.mapInt64Int64[1656338459803] = 1656338459802 - } - XCTAssertEqual(try msg3.jsonString(options: asStrings), "{\"mapInt64Int64\":{\"1656338459803\":\"1656338459802\"}}") - XCTAssertEqual(try msg3.jsonString(options: asNumbers), "{\"mapInt64Int64\":{\"1656338459803\":1656338459802}}") - - // Nested down a level. - let msg4 = SwiftProtoTesting_Message2.with { - $0.optionalMessage.optionalInt64 = 1656338459802 - } - XCTAssertEqual(try msg4.jsonString(options: asStrings), "{\"optionalMessage\":{\"optionalInt64\":\"1656338459802\"}}") - XCTAssertEqual(try msg4.jsonString(options: asNumbers), "{\"optionalMessage\":{\"optionalInt64\":1656338459802}}") - - let msg5 = SwiftProtoTesting_Message2.with { - $0.optionalMessage.repeatedInt64 = [1656338459802, 1656338459803] - } - XCTAssertEqual(try msg5.jsonString(options: asStrings), "{\"optionalMessage\":{\"repeatedInt64\":[\"1656338459802\",\"1656338459803\"]}}") - XCTAssertEqual(try msg5.jsonString(options: asNumbers), "{\"optionalMessage\":{\"repeatedInt64\":[1656338459802,1656338459803]}}") - - let msg6 = SwiftProtoTesting_Message2.with { - $0.optionalMessage.mapInt64Int64[1656338459803] = 1656338459802 - } - XCTAssertEqual(try msg6.jsonString(options: asStrings), "{\"optionalMessage\":{\"mapInt64Int64\":{\"1656338459803\":\"1656338459802\"}}}") - XCTAssertEqual(try msg6.jsonString(options: asNumbers), "{\"optionalMessage\":{\"mapInt64Int64\":{\"1656338459803\":1656338459802}}}") - - // Array additions. - let msgArray = [msg1, msg2, msg3] - XCTAssertEqual(try SwiftProtoTesting_Message2.jsonString(from: msgArray, options: asStrings), - "[" + - "{\"optionalInt64\":\"1656338459803\"}" + "," + - "{\"repeatedInt64\":[\"1656338459802\",\"1656338459803\"]}" + "," + - "{\"mapInt64Int64\":{\"1656338459803\":\"1656338459802\"}}" + - "]") - XCTAssertEqual(try SwiftProtoTesting_Message2.jsonString(from: msgArray, options: asNumbers), - "[" + - "{\"optionalInt64\":1656338459803}" + "," + - "{\"repeatedInt64\":[1656338459802,1656338459803]}" + "," + - "{\"mapInt64Int64\":{\"1656338459803\":1656338459802}}" + - "]") - - // Any. - Google_Protobuf_Any.register(messageType: SwiftProtoTesting_TestAllTypes.self) - let content = SwiftProtoTesting_TestAllTypes.with { - $0.optionalInt64 = 1656338459803 - } - let msg7 = try! Google_Protobuf_Any(message: content) - XCTAssertEqual(try msg7.jsonString(options: asStrings), - "{\"@type\":\"type.googleapis.com/swift_proto_testing.TestAllTypes\",\"optionalInt64\":\"1656338459803\"}") - XCTAssertEqual(try msg7.jsonString(options: asNumbers), - "{\"@type\":\"type.googleapis.com/swift_proto_testing.TestAllTypes\",\"optionalInt64\":1656338459803}") - - // UInt64 - Toplevel fields. - let msg8 = SwiftProtoTesting_Message2.with { - $0.optionalUint64 = 1656338459803 - } - XCTAssertEqual(try msg8.jsonString(options: asStrings), "{\"optionalUint64\":\"1656338459803\"}") - XCTAssertEqual(try msg8.jsonString(options: asNumbers), "{\"optionalUint64\":1656338459803}") - - let msg9 = SwiftProtoTesting_Message2.with { - $0.repeatedUint64 = [1656338459802, 1656338459803] - } - XCTAssertEqual(try msg9.jsonString(options: asStrings), "{\"repeatedUint64\":[\"1656338459802\",\"1656338459803\"]}") - XCTAssertEqual(try msg9.jsonString(options: asNumbers), "{\"repeatedUint64\":[1656338459802,1656338459803]}") - - let msg10 = SwiftProtoTesting_Message2.with { - $0.mapUint64Uint64[1656338459803] = 1656338459802 - } - XCTAssertEqual(try msg10.jsonString(options: asStrings), "{\"mapUint64Uint64\":{\"1656338459803\":\"1656338459802\"}}") - XCTAssertEqual(try msg10.jsonString(options: asNumbers), "{\"mapUint64Uint64\":{\"1656338459803\":1656338459802}}") - } - - func testAlwaysPrintEnumsAsInts() { - // Use explicit options (the default is false), just to be pedantic. - var asStrings = JSONEncodingOptions() - asStrings.alwaysPrintEnumsAsInts = false - var asInts = JSONEncodingOptions() - asInts.alwaysPrintEnumsAsInts = true - - // Toplevel fields - - let msg1 = SwiftProtoTesting_Message3.with { - $0.optionalEnum = .bar - } - XCTAssertEqual(try msg1.jsonString(options: asStrings), "{\"optionalEnum\":\"BAR\"}") - XCTAssertEqual(try msg1.jsonString(options: asInts), "{\"optionalEnum\":1}") - - let msg2 = SwiftProtoTesting_Message3.with { - $0.repeatedEnum = [.bar, .baz] - } - XCTAssertEqual(try msg2.jsonString(options: asStrings), "{\"repeatedEnum\":[\"BAR\",\"BAZ\"]}") - XCTAssertEqual(try msg2.jsonString(options: asInts), "{\"repeatedEnum\":[1,2]}") - - let msg3 = SwiftProtoTesting_Message3.with { - $0.mapInt32Enum[42] = .baz - } - XCTAssertEqual(try msg3.jsonString(options: asStrings), "{\"mapInt32Enum\":{\"42\":\"BAZ\"}}") - XCTAssertEqual(try msg3.jsonString(options: asInts), "{\"mapInt32Enum\":{\"42\":2}}") - - // The enum field nested down a level. - - let msg4 = SwiftProtoTesting_Message3.with { - $0.optionalMessage.optionalEnum = .bar - } - XCTAssertEqual(try msg4.jsonString(options: asStrings), - "{\"optionalMessage\":{\"optionalEnum\":\"BAR\"}}") - XCTAssertEqual(try msg4.jsonString(options: asInts), - "{\"optionalMessage\":{\"optionalEnum\":1}}") - - let msg5 = SwiftProtoTesting_Message3.with { - $0.optionalMessage.repeatedEnum = [.bar, .baz] - } - XCTAssertEqual(try msg5.jsonString(options: asStrings), - "{\"optionalMessage\":{\"repeatedEnum\":[\"BAR\",\"BAZ\"]}}") - XCTAssertEqual(try msg5.jsonString(options: asInts), - "{\"optionalMessage\":{\"repeatedEnum\":[1,2]}}") - - let msg6 = SwiftProtoTesting_Message3.with { - $0.optionalMessage.mapInt32Enum[42] = .baz - } - XCTAssertEqual(try msg6.jsonString(options: asStrings), - "{\"optionalMessage\":{\"mapInt32Enum\":{\"42\":\"BAZ\"}}}") - XCTAssertEqual(try msg6.jsonString(options: asInts), - "{\"optionalMessage\":{\"mapInt32Enum\":{\"42\":2}}}") - - // The array additions - - let msgArray = [msg1, msg2, msg3] - XCTAssertEqual(try SwiftProtoTesting_Message3.jsonString(from: msgArray, options: asStrings), - "[" + - "{\"optionalEnum\":\"BAR\"}" + "," + - "{\"repeatedEnum\":[\"BAR\",\"BAZ\"]}" + "," + - "{\"mapInt32Enum\":{\"42\":\"BAZ\"}}" + - "]") - XCTAssertEqual(try SwiftProtoTesting_Message3.jsonString(from: msgArray, options: asInts), - "[" + - "{\"optionalEnum\":1}" + "," + - "{\"repeatedEnum\":[1,2]}" + "," + - "{\"mapInt32Enum\":{\"42\":2}}" + - "]") - - // Any - - Google_Protobuf_Any.register(messageType: SwiftProtoTesting_TestAllTypes.self) - let content = SwiftProtoTesting_TestAllTypes.with { - $0.optionalNestedEnum = .neg - } - let msg7 = try! Google_Protobuf_Any(message: content) - XCTAssertEqual(try msg7.jsonString(options: asStrings), - "{\"@type\":\"type.googleapis.com/swift_proto_testing.TestAllTypes\",\"optionalNestedEnum\":\"NEG\"}") - XCTAssertEqual(try msg7.jsonString(options: asInts), - "{\"@type\":\"type.googleapis.com/swift_proto_testing.TestAllTypes\",\"optionalNestedEnum\":-1}") - } - - func testPreserveProtoFieldNames() { - var jsonNames = JSONEncodingOptions() - jsonNames.preserveProtoFieldNames = false - var protoNames = JSONEncodingOptions() - protoNames.preserveProtoFieldNames = true - - // Toplevel fields - - let msg1 = SwiftProtoTesting_Message3.with { - $0.optionalEnum = .bar + func testAlwaysPrintInt64sAsNumbers() { + // Use explicit options (the default is false), no reason only others can be pedantic. + var asStrings = JSONEncodingOptions() + asStrings.alwaysPrintInt64sAsNumbers = false + var asNumbers = JSONEncodingOptions() + asNumbers.alwaysPrintInt64sAsNumbers = true + + // Toplevel fields. + let msg1 = SwiftProtoTesting_Message2.with { + $0.optionalInt64 = 1_656_338_459_803 + } + XCTAssertEqual(try msg1.jsonString(options: asStrings), "{\"optionalInt64\":\"1656338459803\"}") + XCTAssertEqual(try msg1.jsonString(options: asNumbers), "{\"optionalInt64\":1656338459803}") + + let msg2 = SwiftProtoTesting_Message2.with { + $0.repeatedInt64 = [1_656_338_459_802, 1_656_338_459_803] + } + XCTAssertEqual( + try msg2.jsonString(options: asStrings), + "{\"repeatedInt64\":[\"1656338459802\",\"1656338459803\"]}" + ) + XCTAssertEqual(try msg2.jsonString(options: asNumbers), "{\"repeatedInt64\":[1656338459802,1656338459803]}") + + let msg3 = SwiftProtoTesting_Message2.with { + $0.mapInt64Int64[1_656_338_459_803] = 1_656_338_459_802 + } + XCTAssertEqual( + try msg3.jsonString(options: asStrings), + "{\"mapInt64Int64\":{\"1656338459803\":\"1656338459802\"}}" + ) + XCTAssertEqual(try msg3.jsonString(options: asNumbers), "{\"mapInt64Int64\":{\"1656338459803\":1656338459802}}") + + // Nested down a level. + let msg4 = SwiftProtoTesting_Message2.with { + $0.optionalMessage.optionalInt64 = 1_656_338_459_802 + } + XCTAssertEqual( + try msg4.jsonString(options: asStrings), + "{\"optionalMessage\":{\"optionalInt64\":\"1656338459802\"}}" + ) + XCTAssertEqual( + try msg4.jsonString(options: asNumbers), + "{\"optionalMessage\":{\"optionalInt64\":1656338459802}}" + ) + + let msg5 = SwiftProtoTesting_Message2.with { + $0.optionalMessage.repeatedInt64 = [1_656_338_459_802, 1_656_338_459_803] + } + XCTAssertEqual( + try msg5.jsonString(options: asStrings), + "{\"optionalMessage\":{\"repeatedInt64\":[\"1656338459802\",\"1656338459803\"]}}" + ) + XCTAssertEqual( + try msg5.jsonString(options: asNumbers), + "{\"optionalMessage\":{\"repeatedInt64\":[1656338459802,1656338459803]}}" + ) + + let msg6 = SwiftProtoTesting_Message2.with { + $0.optionalMessage.mapInt64Int64[1_656_338_459_803] = 1_656_338_459_802 + } + XCTAssertEqual( + try msg6.jsonString(options: asStrings), + "{\"optionalMessage\":{\"mapInt64Int64\":{\"1656338459803\":\"1656338459802\"}}}" + ) + XCTAssertEqual( + try msg6.jsonString(options: asNumbers), + "{\"optionalMessage\":{\"mapInt64Int64\":{\"1656338459803\":1656338459802}}}" + ) + + // Array additions. + let msgArray = [msg1, msg2, msg3] + XCTAssertEqual( + try SwiftProtoTesting_Message2.jsonString(from: msgArray, options: asStrings), + "[" + "{\"optionalInt64\":\"1656338459803\"}" + "," + + "{\"repeatedInt64\":[\"1656338459802\",\"1656338459803\"]}" + "," + + "{\"mapInt64Int64\":{\"1656338459803\":\"1656338459802\"}}" + "]" + ) + XCTAssertEqual( + try SwiftProtoTesting_Message2.jsonString(from: msgArray, options: asNumbers), + "[" + "{\"optionalInt64\":1656338459803}" + "," + "{\"repeatedInt64\":[1656338459802,1656338459803]}" + "," + + "{\"mapInt64Int64\":{\"1656338459803\":1656338459802}}" + "]" + ) + + // Any. + Google_Protobuf_Any.register(messageType: SwiftProtoTesting_TestAllTypes.self) + let content = SwiftProtoTesting_TestAllTypes.with { + $0.optionalInt64 = 1_656_338_459_803 + } + let msg7 = try! Google_Protobuf_Any(message: content) + XCTAssertEqual( + try msg7.jsonString(options: asStrings), + "{\"@type\":\"type.googleapis.com/swift_proto_testing.TestAllTypes\",\"optionalInt64\":\"1656338459803\"}" + ) + XCTAssertEqual( + try msg7.jsonString(options: asNumbers), + "{\"@type\":\"type.googleapis.com/swift_proto_testing.TestAllTypes\",\"optionalInt64\":1656338459803}" + ) + + // UInt64 - Toplevel fields. + let msg8 = SwiftProtoTesting_Message2.with { + $0.optionalUint64 = 1_656_338_459_803 + } + XCTAssertEqual(try msg8.jsonString(options: asStrings), "{\"optionalUint64\":\"1656338459803\"}") + XCTAssertEqual(try msg8.jsonString(options: asNumbers), "{\"optionalUint64\":1656338459803}") + + let msg9 = SwiftProtoTesting_Message2.with { + $0.repeatedUint64 = [1_656_338_459_802, 1_656_338_459_803] + } + XCTAssertEqual( + try msg9.jsonString(options: asStrings), + "{\"repeatedUint64\":[\"1656338459802\",\"1656338459803\"]}" + ) + XCTAssertEqual(try msg9.jsonString(options: asNumbers), "{\"repeatedUint64\":[1656338459802,1656338459803]}") + + let msg10 = SwiftProtoTesting_Message2.with { + $0.mapUint64Uint64[1_656_338_459_803] = 1_656_338_459_802 + } + XCTAssertEqual( + try msg10.jsonString(options: asStrings), + "{\"mapUint64Uint64\":{\"1656338459803\":\"1656338459802\"}}" + ) + XCTAssertEqual( + try msg10.jsonString(options: asNumbers), + "{\"mapUint64Uint64\":{\"1656338459803\":1656338459802}}" + ) } - XCTAssertEqual(try msg1.jsonString(options: jsonNames), "{\"optionalEnum\":\"BAR\"}") - XCTAssertEqual(try msg1.jsonString(options: protoNames), "{\"optional_enum\":\"BAR\"}") - let msg2 = SwiftProtoTesting_Message3.with { - $0.repeatedEnum = [.bar, .baz] - } - XCTAssertEqual(try msg2.jsonString(options: jsonNames), "{\"repeatedEnum\":[\"BAR\",\"BAZ\"]}") - XCTAssertEqual(try msg2.jsonString(options: protoNames), "{\"repeated_enum\":[\"BAR\",\"BAZ\"]}") + func testAlwaysPrintEnumsAsInts() { + // Use explicit options (the default is false), just to be pedantic. + var asStrings = JSONEncodingOptions() + asStrings.alwaysPrintEnumsAsInts = false + var asInts = JSONEncodingOptions() + asInts.alwaysPrintEnumsAsInts = true + + // Toplevel fields + + let msg1 = SwiftProtoTesting_Message3.with { + $0.optionalEnum = .bar + } + XCTAssertEqual(try msg1.jsonString(options: asStrings), "{\"optionalEnum\":\"BAR\"}") + XCTAssertEqual(try msg1.jsonString(options: asInts), "{\"optionalEnum\":1}") + + let msg2 = SwiftProtoTesting_Message3.with { + $0.repeatedEnum = [.bar, .baz] + } + XCTAssertEqual(try msg2.jsonString(options: asStrings), "{\"repeatedEnum\":[\"BAR\",\"BAZ\"]}") + XCTAssertEqual(try msg2.jsonString(options: asInts), "{\"repeatedEnum\":[1,2]}") + + let msg3 = SwiftProtoTesting_Message3.with { + $0.mapInt32Enum[42] = .baz + } + XCTAssertEqual(try msg3.jsonString(options: asStrings), "{\"mapInt32Enum\":{\"42\":\"BAZ\"}}") + XCTAssertEqual(try msg3.jsonString(options: asInts), "{\"mapInt32Enum\":{\"42\":2}}") + + // The enum field nested down a level. + + let msg4 = SwiftProtoTesting_Message3.with { + $0.optionalMessage.optionalEnum = .bar + } + XCTAssertEqual( + try msg4.jsonString(options: asStrings), + "{\"optionalMessage\":{\"optionalEnum\":\"BAR\"}}" + ) + XCTAssertEqual( + try msg4.jsonString(options: asInts), + "{\"optionalMessage\":{\"optionalEnum\":1}}" + ) + + let msg5 = SwiftProtoTesting_Message3.with { + $0.optionalMessage.repeatedEnum = [.bar, .baz] + } + XCTAssertEqual( + try msg5.jsonString(options: asStrings), + "{\"optionalMessage\":{\"repeatedEnum\":[\"BAR\",\"BAZ\"]}}" + ) + XCTAssertEqual( + try msg5.jsonString(options: asInts), + "{\"optionalMessage\":{\"repeatedEnum\":[1,2]}}" + ) + + let msg6 = SwiftProtoTesting_Message3.with { + $0.optionalMessage.mapInt32Enum[42] = .baz + } + XCTAssertEqual( + try msg6.jsonString(options: asStrings), + "{\"optionalMessage\":{\"mapInt32Enum\":{\"42\":\"BAZ\"}}}" + ) + XCTAssertEqual( + try msg6.jsonString(options: asInts), + "{\"optionalMessage\":{\"mapInt32Enum\":{\"42\":2}}}" + ) + + // The array additions + + let msgArray = [msg1, msg2, msg3] + XCTAssertEqual( + try SwiftProtoTesting_Message3.jsonString(from: msgArray, options: asStrings), + "[" + "{\"optionalEnum\":\"BAR\"}" + "," + "{\"repeatedEnum\":[\"BAR\",\"BAZ\"]}" + "," + + "{\"mapInt32Enum\":{\"42\":\"BAZ\"}}" + "]" + ) + XCTAssertEqual( + try SwiftProtoTesting_Message3.jsonString(from: msgArray, options: asInts), + "[" + "{\"optionalEnum\":1}" + "," + "{\"repeatedEnum\":[1,2]}" + "," + "{\"mapInt32Enum\":{\"42\":2}}" + + "]" + ) + + // Any + + Google_Protobuf_Any.register(messageType: SwiftProtoTesting_TestAllTypes.self) + let content = SwiftProtoTesting_TestAllTypes.with { + $0.optionalNestedEnum = .neg + } + let msg7 = try! Google_Protobuf_Any(message: content) + XCTAssertEqual( + try msg7.jsonString(options: asStrings), + "{\"@type\":\"type.googleapis.com/swift_proto_testing.TestAllTypes\",\"optionalNestedEnum\":\"NEG\"}" + ) + XCTAssertEqual( + try msg7.jsonString(options: asInts), + "{\"@type\":\"type.googleapis.com/swift_proto_testing.TestAllTypes\",\"optionalNestedEnum\":-1}" + ) - let msg3 = SwiftProtoTesting_Message3.with { - $0.mapInt32Enum[42] = .baz } - XCTAssertEqual(try msg3.jsonString(options: jsonNames), "{\"mapInt32Enum\":{\"42\":\"BAZ\"}}") - XCTAssertEqual(try msg3.jsonString(options: protoNames), "{\"map_int32_enum\":{\"42\":\"BAZ\"}}") - - // The enum field nested down a level. - let msg4 = SwiftProtoTesting_Message3.with { - $0.optionalMessage.optionalEnum = .bar + func testPreserveProtoFieldNames() { + var jsonNames = JSONEncodingOptions() + jsonNames.preserveProtoFieldNames = false + var protoNames = JSONEncodingOptions() + protoNames.preserveProtoFieldNames = true + + // Toplevel fields + + let msg1 = SwiftProtoTesting_Message3.with { + $0.optionalEnum = .bar + } + XCTAssertEqual(try msg1.jsonString(options: jsonNames), "{\"optionalEnum\":\"BAR\"}") + XCTAssertEqual(try msg1.jsonString(options: protoNames), "{\"optional_enum\":\"BAR\"}") + + let msg2 = SwiftProtoTesting_Message3.with { + $0.repeatedEnum = [.bar, .baz] + } + XCTAssertEqual(try msg2.jsonString(options: jsonNames), "{\"repeatedEnum\":[\"BAR\",\"BAZ\"]}") + XCTAssertEqual(try msg2.jsonString(options: protoNames), "{\"repeated_enum\":[\"BAR\",\"BAZ\"]}") + + let msg3 = SwiftProtoTesting_Message3.with { + $0.mapInt32Enum[42] = .baz + } + XCTAssertEqual(try msg3.jsonString(options: jsonNames), "{\"mapInt32Enum\":{\"42\":\"BAZ\"}}") + XCTAssertEqual(try msg3.jsonString(options: protoNames), "{\"map_int32_enum\":{\"42\":\"BAZ\"}}") + + // The enum field nested down a level. + + let msg4 = SwiftProtoTesting_Message3.with { + $0.optionalMessage.optionalEnum = .bar + } + XCTAssertEqual( + try msg4.jsonString(options: jsonNames), + "{\"optionalMessage\":{\"optionalEnum\":\"BAR\"}}" + ) + XCTAssertEqual( + try msg4.jsonString(options: protoNames), + "{\"optional_message\":{\"optional_enum\":\"BAR\"}}" + ) + + let msg5 = SwiftProtoTesting_Message3.with { + $0.optionalMessage.repeatedEnum = [.bar, .baz] + } + XCTAssertEqual( + try msg5.jsonString(options: jsonNames), + "{\"optionalMessage\":{\"repeatedEnum\":[\"BAR\",\"BAZ\"]}}" + ) + XCTAssertEqual( + try msg5.jsonString(options: protoNames), + "{\"optional_message\":{\"repeated_enum\":[\"BAR\",\"BAZ\"]}}" + ) + + let msg6 = SwiftProtoTesting_Message3.with { + $0.optionalMessage.mapInt32Enum[42] = .baz + } + XCTAssertEqual( + try msg6.jsonString(options: jsonNames), + "{\"optionalMessage\":{\"mapInt32Enum\":{\"42\":\"BAZ\"}}}" + ) + XCTAssertEqual( + try msg6.jsonString(options: protoNames), + "{\"optional_message\":{\"map_int32_enum\":{\"42\":\"BAZ\"}}}" + ) + + // The array additions + + let msgArray = [msg1, msg2, msg3] + XCTAssertEqual( + try SwiftProtoTesting_Message3.jsonString(from: msgArray, options: jsonNames), + "[" + "{\"optionalEnum\":\"BAR\"}" + "," + "{\"repeatedEnum\":[\"BAR\",\"BAZ\"]}" + "," + + "{\"mapInt32Enum\":{\"42\":\"BAZ\"}}" + "]" + ) + XCTAssertEqual( + try SwiftProtoTesting_Message3.jsonString(from: msgArray, options: protoNames), + "[" + "{\"optional_enum\":\"BAR\"}" + "," + "{\"repeated_enum\":[\"BAR\",\"BAZ\"]}" + "," + + "{\"map_int32_enum\":{\"42\":\"BAZ\"}}" + "]" + ) + + // Any + + Google_Protobuf_Any.register(messageType: SwiftProtoTesting_TestAllTypes.self) + let content = SwiftProtoTesting_TestAllTypes.with { + $0.optionalNestedEnum = .neg + } + let msg7 = try! Google_Protobuf_Any(message: content) + XCTAssertEqual( + try msg7.jsonString(options: jsonNames), + "{\"@type\":\"type.googleapis.com/swift_proto_testing.TestAllTypes\",\"optionalNestedEnum\":\"NEG\"}" + ) + XCTAssertEqual( + try msg7.jsonString(options: protoNames), + "{\"@type\":\"type.googleapis.com/swift_proto_testing.TestAllTypes\",\"optional_nested_enum\":\"NEG\"}" + ) } - XCTAssertEqual(try msg4.jsonString(options: jsonNames), - "{\"optionalMessage\":{\"optionalEnum\":\"BAR\"}}") - XCTAssertEqual(try msg4.jsonString(options: protoNames), - "{\"optional_message\":{\"optional_enum\":\"BAR\"}}") - let msg5 = SwiftProtoTesting_Message3.with { - $0.optionalMessage.repeatedEnum = [.bar, .baz] - } - XCTAssertEqual(try msg5.jsonString(options: jsonNames), - "{\"optionalMessage\":{\"repeatedEnum\":[\"BAR\",\"BAZ\"]}}") - XCTAssertEqual(try msg5.jsonString(options: protoNames), - "{\"optional_message\":{\"repeated_enum\":[\"BAR\",\"BAZ\"]}}") - - let msg6 = SwiftProtoTesting_Message3.with { - $0.optionalMessage.mapInt32Enum[42] = .baz - } - XCTAssertEqual(try msg6.jsonString(options: jsonNames), - "{\"optionalMessage\":{\"mapInt32Enum\":{\"42\":\"BAZ\"}}}") - XCTAssertEqual(try msg6.jsonString(options: protoNames), - "{\"optional_message\":{\"map_int32_enum\":{\"42\":\"BAZ\"}}}") - - // The array additions - - let msgArray = [msg1, msg2, msg3] - XCTAssertEqual(try SwiftProtoTesting_Message3.jsonString(from: msgArray, options: jsonNames), - "[" + - "{\"optionalEnum\":\"BAR\"}" + "," + - "{\"repeatedEnum\":[\"BAR\",\"BAZ\"]}" + "," + - "{\"mapInt32Enum\":{\"42\":\"BAZ\"}}" + - "]") - XCTAssertEqual(try SwiftProtoTesting_Message3.jsonString(from: msgArray, options: protoNames), - "[" + - "{\"optional_enum\":\"BAR\"}" + "," + - "{\"repeated_enum\":[\"BAR\",\"BAZ\"]}" + "," + - "{\"map_int32_enum\":{\"42\":\"BAZ\"}}" + - "]") - - // Any - - Google_Protobuf_Any.register(messageType: SwiftProtoTesting_TestAllTypes.self) - let content = SwiftProtoTesting_TestAllTypes.with { - $0.optionalNestedEnum = .neg - } - let msg7 = try! Google_Protobuf_Any(message: content) - XCTAssertEqual(try msg7.jsonString(options: jsonNames), - "{\"@type\":\"type.googleapis.com/swift_proto_testing.TestAllTypes\",\"optionalNestedEnum\":\"NEG\"}") - XCTAssertEqual(try msg7.jsonString(options: protoNames), - "{\"@type\":\"type.googleapis.com/swift_proto_testing.TestAllTypes\",\"optional_nested_enum\":\"NEG\"}") - } - - func testUseDeterministicOrdering() { - var options = JSONEncodingOptions() - options.useDeterministicOrdering = true - - let stringMap = SwiftProtoTesting_Message3.with { - $0.mapStringString = [ - "b": "B", - "a": "A", - "0": "0", - "UPPER": "v", - "x": "X", - ] - } - XCTAssertEqual( - try stringMap.jsonString(options: options), - "{\"mapStringString\":{\"0\":\"0\",\"UPPER\":\"v\",\"a\":\"A\",\"b\":\"B\",\"x\":\"X\"}}" - ) - - let messageMap = SwiftProtoTesting_Message3.with { - $0.mapInt32Message = [ - 5: .with { $0.optionalSint32 = 5 }, - 1: .with { $0.optionalSint32 = 1 }, - 3: .with { $0.optionalSint32 = 3 }, - ] - } - XCTAssertEqual( - try messageMap.jsonString(options: options), - "{\"mapInt32Message\":{\"1\":{\"optionalSint32\":1},\"3\":{\"optionalSint32\":3},\"5\":{\"optionalSint32\":5}}}" - ) - - let enumMap = SwiftProtoTesting_Message3.with { - $0.mapInt32Enum = [ - 5: .foo, - 3: .bar, - 0: .baz, - 1: .extra3, - ] + func testUseDeterministicOrdering() { + var options = JSONEncodingOptions() + options.useDeterministicOrdering = true + + let stringMap = SwiftProtoTesting_Message3.with { + $0.mapStringString = [ + "b": "B", + "a": "A", + "0": "0", + "UPPER": "v", + "x": "X", + ] + } + XCTAssertEqual( + try stringMap.jsonString(options: options), + "{\"mapStringString\":{\"0\":\"0\",\"UPPER\":\"v\",\"a\":\"A\",\"b\":\"B\",\"x\":\"X\"}}" + ) + + let messageMap = SwiftProtoTesting_Message3.with { + $0.mapInt32Message = [ + 5: .with { $0.optionalSint32 = 5 }, + 1: .with { $0.optionalSint32 = 1 }, + 3: .with { $0.optionalSint32 = 3 }, + ] + } + XCTAssertEqual( + try messageMap.jsonString(options: options), + "{\"mapInt32Message\":{\"1\":{\"optionalSint32\":1},\"3\":{\"optionalSint32\":3},\"5\":{\"optionalSint32\":5}}}" + ) + + let enumMap = SwiftProtoTesting_Message3.with { + $0.mapInt32Enum = [ + 5: .foo, + 3: .bar, + 0: .baz, + 1: .extra3, + ] + } + XCTAssertEqual( + try enumMap.jsonString(options: options), + "{\"mapInt32Enum\":{\"0\":\"BAZ\",\"1\":\"EXTRA_3\",\"3\":\"BAR\",\"5\":\"FOO\"}}" + ) } - XCTAssertEqual( - try enumMap.jsonString(options: options), - "{\"mapInt32Enum\":{\"0\":\"BAZ\",\"1\":\"EXTRA_3\",\"3\":\"BAR\",\"5\":\"FOO\"}}" - ) - } } diff --git a/Tests/SwiftProtobufTests/Test_JSON_Array.swift b/Tests/SwiftProtobufTests/Test_JSON_Array.swift index fb522cd38..4a1f0ff54 100644 --- a/Tests/SwiftProtobufTests/Test_JSON_Array.swift +++ b/Tests/SwiftProtobufTests/Test_JSON_Array.swift @@ -15,8 +15,8 @@ // ----------------------------------------------------------------------------- import Foundation -import XCTest import SwiftProtobuf +import XCTest final class Test_JSON_Array: XCTestCase, PBTestHelpers { typealias MessageTestType = SwiftProtoTesting_Proto3_TestAllTypes @@ -49,7 +49,7 @@ final class Test_JSON_Array: XCTestCase, PBTestHelpers { o1.optionalImportMessage = importMessage o1.optionalNestedEnum = .baz o1.optionalForeignEnum = .foreignBaz -// o1.optionalImportEnum = .importBaz + // o1.optionalImportEnum = .importBaz var publicImportMessage = SwiftProtoTesting_Import_PublicImportMessage() publicImportMessage.e = -999999 o1.optionalPublicImportMessage = publicImportMessage @@ -88,55 +88,58 @@ final class Test_JSON_Array: XCTestCase, PBTestHelpers { } func testTwoObjectsWithMultipleFields() { - let expected: String = ("[{" - + "\"optionalInt32\":1," - + "\"optionalInt64\":\"2\"," - + "\"optionalUint32\":3," - + "\"optionalUint64\":\"4\"," - + "\"optionalSint32\":5," - + "\"optionalSint64\":\"6\"," - + "\"optionalFixed32\":7," - + "\"optionalFixed64\":\"8\"," - + "\"optionalSfixed32\":9," - + "\"optionalSfixed64\":\"10\"," - + "\"optionalFloat\":11.0," - + "\"optionalDouble\":12.0," - + "\"optionalBool\":true," - + "\"optionalString\":\"abc\"," - + "\"optionalBytes\":\"QUI=\"," - + "\"optionalNestedMessage\":{\"bb\":7}," - + "\"optionalForeignMessage\":{\"c\":88}," - + "\"optionalImportMessage\":{\"d\":-9}," - + "\"optionalNestedEnum\":\"BAZ\"," - + "\"optionalForeignEnum\":\"FOREIGN_BAZ\"," -// + "\"optionalImportEnum\":\"IMPORT_BAZ\"," - + "\"optionalPublicImportMessage\":{\"e\":-999999}," - + "\"repeatedInt32\":[1,2]," - + "\"repeatedInt64\":[\"3\",\"4\"]," - + "\"repeatedUint32\":[5,6]," - + "\"repeatedUint64\":[\"7\",\"8\"]," - + "\"repeatedSint32\":[9,10]," - + "\"repeatedSint64\":[\"11\",\"12\"]," - + "\"repeatedFixed32\":[13,14]," - + "\"repeatedFixed64\":[\"15\",\"16\"]," - + "\"repeatedSfixed32\":[17,18]," - + "\"repeatedSfixed64\":[\"19\",\"20\"]," - + "\"repeatedFloat\":[21.0,22.0]," - + "\"repeatedDouble\":[23.0,24.0]," - + "\"repeatedBool\":[true,false]," - + "\"repeatedString\":[\"abc\",\"def\"]," - + "\"repeatedBytes\":[\"\",\"QUI=\"]," - + "\"repeatedNestedMessage\":[{\"bb\":7},{\"bb\":-7}]," - + "\"repeatedForeignMessage\":[{\"c\":88},{\"c\":-88}]," - + "\"repeatedNestedEnum\":[\"BAR\",\"BAZ\"]," - + "\"repeatedForeignEnum\":[\"FOREIGN_BAR\",\"FOREIGN_BAZ\"]," - + "\"oneofUint32\":99" - + "},{}]") + let expected: String = + ("[{" + + "\"optionalInt32\":1," + + "\"optionalInt64\":\"2\"," + + "\"optionalUint32\":3," + + "\"optionalUint64\":\"4\"," + + "\"optionalSint32\":5," + + "\"optionalSint64\":\"6\"," + + "\"optionalFixed32\":7," + + "\"optionalFixed64\":\"8\"," + + "\"optionalSfixed32\":9," + + "\"optionalSfixed64\":\"10\"," + + "\"optionalFloat\":11.0," + + "\"optionalDouble\":12.0," + + "\"optionalBool\":true," + + "\"optionalString\":\"abc\"," + + "\"optionalBytes\":\"QUI=\"," + + "\"optionalNestedMessage\":{\"bb\":7}," + + "\"optionalForeignMessage\":{\"c\":88}," + + "\"optionalImportMessage\":{\"d\":-9}," + + "\"optionalNestedEnum\":\"BAZ\"," + + "\"optionalForeignEnum\":\"FOREIGN_BAZ\"," + // + "\"optionalImportEnum\":\"IMPORT_BAZ\"," + + "\"optionalPublicImportMessage\":{\"e\":-999999}," + + "\"repeatedInt32\":[1,2]," + + "\"repeatedInt64\":[\"3\",\"4\"]," + + "\"repeatedUint32\":[5,6]," + + "\"repeatedUint64\":[\"7\",\"8\"]," + + "\"repeatedSint32\":[9,10]," + + "\"repeatedSint64\":[\"11\",\"12\"]," + + "\"repeatedFixed32\":[13,14]," + + "\"repeatedFixed64\":[\"15\",\"16\"]," + + "\"repeatedSfixed32\":[17,18]," + + "\"repeatedSfixed64\":[\"19\",\"20\"]," + + "\"repeatedFloat\":[21.0,22.0]," + + "\"repeatedDouble\":[23.0,24.0]," + + "\"repeatedBool\":[true,false]," + + "\"repeatedString\":[\"abc\",\"def\"]," + + "\"repeatedBytes\":[\"\",\"QUI=\"]," + + "\"repeatedNestedMessage\":[{\"bb\":7},{\"bb\":-7}]," + + "\"repeatedForeignMessage\":[{\"c\":88},{\"c\":-88}]," + + "\"repeatedNestedEnum\":[\"BAR\",\"BAZ\"]," + + "\"repeatedForeignEnum\":[\"FOREIGN_BAR\",\"FOREIGN_BAZ\"]," + + "\"oneofUint32\":99" + + "},{}]") assertJSONArrayEncode(expected, configure: configureTwoObjects) } func testRepeatedNestedMessage() { - assertJSONArrayEncode("[{\"repeatedNestedMessage\":[{\"bb\":1}]},{\"repeatedNestedMessage\":[{\"bb\":1},{\"bb\":2}]}]") {(o: inout [MessageTestType]) in + assertJSONArrayEncode( + "[{\"repeatedNestedMessage\":[{\"bb\":1}]},{\"repeatedNestedMessage\":[{\"bb\":1},{\"bb\":2}]}]" + ) { (o: inout [MessageTestType]) in var o1 = MessageTestType() var sub1 = SwiftProtoTesting_Proto3_TestAllTypes.NestedMessage() sub1.bb = 1 diff --git a/Tests/SwiftProtobufTests/Test_JSON_Conformance.swift b/Tests/SwiftProtobufTests/Test_JSON_Conformance.swift index 3c3039618..a35e03e0d 100644 --- a/Tests/SwiftProtobufTests/Test_JSON_Conformance.swift +++ b/Tests/SwiftProtobufTests/Test_JSON_Conformance.swift @@ -14,14 +14,20 @@ // ----------------------------------------------------------------------------- import Foundation -import XCTest import SwiftProtobuf +import XCTest final class Test_JSON_Conformance: XCTestCase { - func assertEmptyDecode(_ json: String, file: XCTestFileArgType = #file, line: UInt = #line) -> () { + func assertEmptyDecode(_ json: String, file: XCTestFileArgType = #file, line: UInt = #line) { do { let decoded = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: json) - XCTAssertEqual(decoded, SwiftProtoTesting_Test3_TestAllTypesProto3(), "Decoded object should be equal to empty object: \(decoded)", file: file, line: line) + XCTAssertEqual( + decoded, + SwiftProtoTesting_Test3_TestAllTypesProto3(), + "Decoded object should be equal to empty object: \(decoded)", + file: file, + line: line + ) let recoded = try decoded.jsonString() XCTAssertEqual(recoded, "{}", file: file, line: line) let protobuf: [UInt8] = try decoded.serializedBytes() @@ -240,7 +246,10 @@ final class Test_JSON_Conformance: XCTestCase { do { decoded = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: repeatedValueWithNull) XCTAssertNotEqual(decoded, SwiftProtoTesting_Test3_TestAllTypesProto3()) - XCTAssertEqual(decoded.repeatedValue, [Google_Protobuf_Value(numberValue:1), nil as Google_Protobuf_Value]) + XCTAssertEqual( + decoded.repeatedValue, + [Google_Protobuf_Value(numberValue: 1), nil as Google_Protobuf_Value] + ) } catch { XCTFail("Decode failed with error \(error): \(repeatedValueWithNull)") return @@ -260,7 +269,8 @@ final class Test_JSON_Conformance: XCTestCase { } func testNullConformance() { - let start = "{\n \"optionalBoolWrapper\": null,\n \"optionalInt32Wrapper\": null,\n \"optionalUint32Wrapper\": null,\n \"optionalInt64Wrapper\": null,\n \"optionalUint64Wrapper\": null,\n \"optionalFloatWrapper\": null,\n \"optionalDoubleWrapper\": null,\n \"optionalStringWrapper\": null,\n \"optionalBytesWrapper\": null,\n \"repeatedBoolWrapper\": null,\n \"repeatedInt32Wrapper\": null,\n \"repeatedUint32Wrapper\": null,\n \"repeatedInt64Wrapper\": null,\n \"repeatedUint64Wrapper\": null,\n \"repeatedFloatWrapper\": null,\n \"repeatedDoubleWrapper\": null,\n \"repeatedStringWrapper\": null,\n \"repeatedBytesWrapper\": null\n }" + let start = + "{\n \"optionalBoolWrapper\": null,\n \"optionalInt32Wrapper\": null,\n \"optionalUint32Wrapper\": null,\n \"optionalInt64Wrapper\": null,\n \"optionalUint64Wrapper\": null,\n \"optionalFloatWrapper\": null,\n \"optionalDoubleWrapper\": null,\n \"optionalStringWrapper\": null,\n \"optionalBytesWrapper\": null,\n \"repeatedBoolWrapper\": null,\n \"repeatedInt32Wrapper\": null,\n \"repeatedUint32Wrapper\": null,\n \"repeatedInt64Wrapper\": null,\n \"repeatedUint64Wrapper\": null,\n \"repeatedFloatWrapper\": null,\n \"repeatedDoubleWrapper\": null,\n \"repeatedStringWrapper\": null,\n \"repeatedBytesWrapper\": null\n }" do { let t = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: start) XCTAssertEqual(try t.jsonString(), "{}") @@ -281,42 +291,43 @@ final class Test_JSON_Conformance: XCTestCase { XCTAssertEqual(try t.jsonString(), start) } - func testValue_DoubleNonFinite() { - XCTAssertThrowsError(try Google_Protobuf_Value(numberValue: .nan).jsonString()) { - XCTAssertEqual( - $0 as? JSONEncodingError, - JSONEncodingError.valueNumberNotFinite, - "Wrong error? - \($0)" - ) - } + func testValue_DoubleNonFinite() { + XCTAssertThrowsError(try Google_Protobuf_Value(numberValue: .nan).jsonString()) { + XCTAssertEqual( + $0 as? JSONEncodingError, + JSONEncodingError.valueNumberNotFinite, + "Wrong error? - \($0)" + ) + } - XCTAssertThrowsError(try Google_Protobuf_Value(numberValue: .infinity).jsonString()) { - XCTAssertEqual( - $0 as? JSONEncodingError, - JSONEncodingError.valueNumberNotFinite, - "Wrong error? - \($0)" - ) - } + XCTAssertThrowsError(try Google_Protobuf_Value(numberValue: .infinity).jsonString()) { + XCTAssertEqual( + $0 as? JSONEncodingError, + JSONEncodingError.valueNumberNotFinite, + "Wrong error? - \($0)" + ) + } - XCTAssertThrowsError(try Google_Protobuf_Value(numberValue: -.infinity).jsonString()) { - XCTAssertEqual( - $0 as? JSONEncodingError, - JSONEncodingError.valueNumberNotFinite, - "Wrong error? - \($0)" - ) + XCTAssertThrowsError(try Google_Protobuf_Value(numberValue: -.infinity).jsonString()) { + XCTAssertEqual( + $0 as? JSONEncodingError, + JSONEncodingError.valueNumberNotFinite, + "Wrong error? - \($0)" + ) + } } - } func testNestedAny() { - let start = ("{\n" - + " \"optionalAny\": {\n" - + " \"@type\": \"type.googleapis.com/google.protobuf.Any\",\n" - + " \"value\": {\n" - + " \"@type\": \"type.googleapis.com/swift_proto_testing.test3.TestAllTypes\",\n" - + " \"optionalInt32\": 12345\n" - + " }\n" - + " }\n" - + " }") + let start = + ("{\n" + + " \"optionalAny\": {\n" + + " \"@type\": \"type.googleapis.com/google.protobuf.Any\",\n" + + " \"value\": {\n" + + " \"@type\": \"type.googleapis.com/swift_proto_testing.test3.TestAllTypes\",\n" + + " \"optionalInt32\": 12345\n" + + " }\n" + + " }\n" + + " }") do { _ = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: start) } catch { diff --git a/Tests/SwiftProtobufTests/Test_JSON_Extensions.swift b/Tests/SwiftProtobufTests/Test_JSON_Extensions.swift index f16aeafe9..891292e60 100644 --- a/Tests/SwiftProtobufTests/Test_JSON_Extensions.swift +++ b/Tests/SwiftProtobufTests/Test_JSON_Extensions.swift @@ -13,8 +13,8 @@ // ----------------------------------------------------------------------------- import Foundation -import XCTest import SwiftProtobuf +import XCTest final class Test_JSON_Extensions: XCTestCase, PBTestHelpers { typealias MessageTestType = SwiftProtoTesting_TestAllExtensions @@ -26,36 +26,51 @@ final class Test_JSON_Extensions: XCTestCase, PBTestHelpers { // Append another file's worth: extensions.formUnion(SwiftProtoTesting_Extend_UnittestSwiftExtension_Extensions) // Append an array of extensions - extensions.insert(contentsOf: - [ - Extensions_RepeatedExtensionGroup, - Extensions_ExtensionGroup - ] + extensions.insert(contentsOf: [ + Extensions_RepeatedExtensionGroup, + Extensions_ExtensionGroup, + ] ) } func test_optionalInt32Extension() throws { - assertJSONEncode("{\"[swift_proto_testing.optional_int32_extension]\":17}", - extensions: extensions) { + assertJSONEncode( + "{\"[swift_proto_testing.optional_int32_extension]\":17}", + extensions: extensions + ) { (o: inout MessageTestType) in o.SwiftProtoTesting_optionalInt32Extension = 17 } - assertJSONDecodeFails("{\"[swift_proto_testing.UNKNOWN_EXTENSION]\":17}", - extensions: extensions) - assertJSONDecodeFails("{\"[UNKNOWN_PACKAGE.optional_int32_extension]\":17}", - extensions: extensions) - assertJSONDecodeFails("{\"[swift_proto_testing.optional_int32_extension\":17}", - extensions: extensions) - assertJSONDecodeFails("{\"swift_proto_testing.optional_int32_extension]\":17}", - extensions: extensions) - assertJSONDecodeFails("{\"[optional_int32_extension\":17}", - extensions: extensions) - assertJSONDecodeFails("{\"swift_proto_testing.optional_int32_extension\":17}", - extensions: extensions) - - assertJSONArrayEncode("[{\"[swift_proto_testing.optional_int32_extension]\":17}]", - extensions: extensions) { + assertJSONDecodeFails( + "{\"[swift_proto_testing.UNKNOWN_EXTENSION]\":17}", + extensions: extensions + ) + assertJSONDecodeFails( + "{\"[UNKNOWN_PACKAGE.optional_int32_extension]\":17}", + extensions: extensions + ) + assertJSONDecodeFails( + "{\"[swift_proto_testing.optional_int32_extension\":17}", + extensions: extensions + ) + assertJSONDecodeFails( + "{\"swift_proto_testing.optional_int32_extension]\":17}", + extensions: extensions + ) + assertJSONDecodeFails( + "{\"[optional_int32_extension\":17}", + extensions: extensions + ) + assertJSONDecodeFails( + "{\"swift_proto_testing.optional_int32_extension\":17}", + extensions: extensions + ) + + assertJSONArrayEncode( + "[{\"[swift_proto_testing.optional_int32_extension]\":17}]", + extensions: extensions + ) { (o: inout [MessageTestType]) in var o1 = MessageTestType() o1.SwiftProtoTesting_optionalInt32Extension = 17 @@ -64,9 +79,10 @@ final class Test_JSON_Extensions: XCTestCase, PBTestHelpers { } func test_optionalMessageExtension() throws { - assertJSONEncode("{\"[swift_proto_testing.optional_nested_message_extension]\":{\"bb\":12}}", - extensions: extensions) - { + assertJSONEncode( + "{\"[swift_proto_testing.optional_nested_message_extension]\":{\"bb\":12}}", + extensions: extensions + ) { (o: inout MessageTestType) in o.SwiftProtoTesting_optionalNestedMessageExtension = SwiftProtoTesting_TestAllTypes.NestedMessage.with { @@ -76,29 +92,31 @@ final class Test_JSON_Extensions: XCTestCase, PBTestHelpers { } func test_repeatedInt32Extension() throws { - assertJSONEncode("{\"[swift_proto_testing.repeated_int32_extension]\":[1,2,3,17]}", - extensions: extensions) { + assertJSONEncode( + "{\"[swift_proto_testing.repeated_int32_extension]\":[1,2,3,17]}", + extensions: extensions + ) { (o: inout MessageTestType) in - o.SwiftProtoTesting_repeatedInt32Extension = [1,2,3,17] + o.SwiftProtoTesting_repeatedInt32Extension = [1, 2, 3, 17] } } func test_repeatedMessageExtension() throws { - assertJSONEncode("{\"[swift_proto_testing.repeated_nested_message_extension]\":[{\"bb\":12},{}]}", - extensions: extensions) - { + assertJSONEncode( + "{\"[swift_proto_testing.repeated_nested_message_extension]\":[{\"bb\":12},{}]}", + extensions: extensions + ) { (o: inout MessageTestType) in o.SwiftProtoTesting_repeatedNestedMessageExtension = [ SwiftProtoTesting_TestAllTypes.NestedMessage.with { $0.bb = 12 }, - SwiftProtoTesting_TestAllTypes.NestedMessage() + SwiftProtoTesting_TestAllTypes.NestedMessage(), ] } } func test_optionalStringExtensionWithDefault() throws { - assertJSONEncode("{\"[swift_proto_testing.default_string_extension]\":\"hi\"}", extensions: extensions) - { + assertJSONEncode("{\"[swift_proto_testing.default_string_extension]\":\"hi\"}", extensions: extensions) { (o: inout MessageTestType) in o.SwiftProtoTesting_defaultStringExtension = "hi" } @@ -111,12 +129,12 @@ final class Test_JSON_Extensions: XCTestCase, PBTestHelpers { func test_ArrayWithExtensions() throws { assertJSONArrayEncode( "[" - + "{\"[swift_proto_testing.optional_int32_extension]\":17}," - + "{}," - + "{\"[swift_proto_testing.optional_double_extension]\":1.23}" - + "]", - extensions: extensions) - { + + "{\"[swift_proto_testing.optional_int32_extension]\":17}," + + "{}," + + "{\"[swift_proto_testing.optional_double_extension]\":1.23}" + + "]", + extensions: extensions + ) { (o: inout [MessageTestType]) in let o1 = MessageTestType.with { $0.SwiftProtoTesting_optionalInt32Extension = 17 @@ -136,13 +154,18 @@ final class Test_JSON_RecursiveNested_Extensions: XCTestCase, PBTestHelpers { let extensions = SwiftProtoTesting_Extend_UnittestSwiftExtension_Extensions func test_nestedMessage() throws { - assertJSONEncode("{\"[swift_proto_testing.extend.a_b]\":12}", - extensions: extensions) { - (o: inout MessageTestType) in - o.SwiftProtoTesting_Extend_aB = 12 + assertJSONEncode( + "{\"[swift_proto_testing.extend.a_b]\":12}", + extensions: extensions + ) { + (o: inout MessageTestType) in + o.SwiftProtoTesting_Extend_aB = 12 } - assertJSONDecodeSucceeds("{\"[swift_proto_testing.extend.m2]\":{\"[swift_proto_testing.extend.aB]\":23}}", extensions: extensions) { + assertJSONDecodeSucceeds( + "{\"[swift_proto_testing.extend.m2]\":{\"[swift_proto_testing.extend.aB]\":23}}", + extensions: extensions + ) { $0.SwiftProtoTesting_Extend_m2.SwiftProtoTesting_Extend_aB == 23 } } diff --git a/Tests/SwiftProtobufTests/Test_JSON_Group.swift b/Tests/SwiftProtobufTests/Test_JSON_Group.swift index 2b1c89c13..eeb981e74 100644 --- a/Tests/SwiftProtobufTests/Test_JSON_Group.swift +++ b/Tests/SwiftProtobufTests/Test_JSON_Group.swift @@ -21,20 +21,20 @@ final class Test_JSON_Group: XCTestCase, PBTestHelpers { typealias MessageTestType = SwiftProtoTesting_TestAllTypes func testOptionalGroup() { - assertJSONEncode("{\"optionalgroup\":{\"a\":3}}") {(o: inout MessageTestType) in - o.optionalGroup.a = 3 + assertJSONEncode("{\"optionalgroup\":{\"a\":3}}") { (o: inout MessageTestType) in + o.optionalGroup.a = 3 } } func testRepeatedGroup() { - assertJSONEncode("{\"repeatedgroup\":[{\"a\":1},{\"a\":2}]}") {(o: inout MessageTestType) in - let one = SwiftProtoTesting_TestAllTypes.RepeatedGroup.with { - $0.a = 1 - } - let two = SwiftProtoTesting_TestAllTypes.RepeatedGroup.with { - $0.a = 2 - } - o.repeatedGroup = [one, two] + assertJSONEncode("{\"repeatedgroup\":[{\"a\":1},{\"a\":2}]}") { (o: inout MessageTestType) in + let one = SwiftProtoTesting_TestAllTypes.RepeatedGroup.with { + $0.a = 1 + } + let two = SwiftProtoTesting_TestAllTypes.RepeatedGroup.with { + $0.a = 2 + } + o.repeatedGroup = [one, two] } } } diff --git a/Tests/SwiftProtobufTests/Test_JSON_Performance.swift b/Tests/SwiftProtobufTests/Test_JSON_Performance.swift index 90122d40a..6f02826e8 100644 --- a/Tests/SwiftProtobufTests/Test_JSON_Performance.swift +++ b/Tests/SwiftProtobufTests/Test_JSON_Performance.swift @@ -12,8 +12,8 @@ /// // ----------------------------------------------------------------------------- -import XCTest import SwiftProtobuf +import XCTest final class Test_JSON_Performance: XCTestCase, PBTestHelpers { typealias MessageTestType = SwiftProtoTesting_Fuzz_Message @@ -24,28 +24,27 @@ final class Test_JSON_Performance: XCTestCase, PBTestHelpers { // may be amiss. func testEncoding_manyIntMapsEncoding_shouldBeUnder1s() { - let rawPadding = Array(repeating: 1000000000, count: 150000) + let rawPadding = [Int32](repeating: 1_000_000_000, count: 150000) let mapRepeats = 60000 - let pad = rawPadding.map({$0.description}).joined(separator: ",") + let pad = rawPadding.map({ $0.description }).joined(separator: ",") let child = "{\"mapFixed64Sint64\":{\"30\":\"4\"}}" - let children = Array(repeating: child, count: mapRepeats).joined(separator: ",") - let expected = ( - "{" - + "\"repeatedInt32\":[" - + pad - + "]," - + "\"repeatedMessage\":[" - + children - + "]}" - ) + let children = [String](repeating: child, count: mapRepeats).joined(separator: ",") + let expected = + ("{" + + "\"repeatedInt32\":[" + + pad + + "]," + + "\"repeatedMessage\":[" + + children + + "]}") let msg = MessageTestType.with { $0.repeatedInt32 = rawPadding let child = MessageTestType.with { - $0.mapFixed64Sint64[30] = 4 + $0.mapFixed64Sint64[30] = 4 } - let array = Array(repeating: child, count: mapRepeats) + let array = [MessageTestType](repeating: child, count: mapRepeats) $0.repeatedMessage = array } @@ -60,28 +59,27 @@ final class Test_JSON_Performance: XCTestCase, PBTestHelpers { } func testEncoding_manyEnumMapsEncoding_shouldBeUnder1s() { - let rawPadding = Array(repeating: 1000000000, count: 150000) + let rawPadding = [Int32](repeating: 1_000_000_000, count: 150000) let mapRepeats = 60000 - let pad = rawPadding.map({$0.description}).joined(separator: ",") + let pad = rawPadding.map({ $0.description }).joined(separator: ",") let child = "{\"mapInt32AnEnum\":{\"30\":\"TWO\"}}" - let children = Array(repeating: child, count: mapRepeats).joined(separator: ",") - let expected = ( - "{" - + "\"repeatedInt32\":[" - + pad - + "]," - + "\"repeatedMessage\":[" - + children - + "]}" - ) + let children = [String](repeating: child, count: mapRepeats).joined(separator: ",") + let expected = + ("{" + + "\"repeatedInt32\":[" + + pad + + "]," + + "\"repeatedMessage\":[" + + children + + "]}") let msg = MessageTestType.with { $0.repeatedInt32 = rawPadding let child = MessageTestType.with { - $0.mapInt32AnEnum[30] = SwiftProtoTesting_Fuzz_AnEnum.two + $0.mapInt32AnEnum[30] = SwiftProtoTesting_Fuzz_AnEnum.two } - let array = Array(repeating: child, count: mapRepeats) + let array = [MessageTestType](repeating: child, count: mapRepeats) $0.repeatedMessage = array } @@ -95,33 +93,31 @@ final class Test_JSON_Performance: XCTestCase, PBTestHelpers { XCTAssertEqual(expected, encoded) } - func testEncoding_manyMessageMapsEncoding_shouldBeUnder1s() { - let rawPadding = Array(repeating: 1000000000, count: 150000) + let rawPadding = [Int32](repeating: 1_000_000_000, count: 150000) let mapRepeats = 50000 - let pad = rawPadding.map({$0.description}).joined(separator: ",") + let pad = rawPadding.map({ $0.description }).joined(separator: ",") let child = "{\"mapInt32Message\":{\"30\":{\"singularInt32\":8}}}" - let children = Array(repeating: child, count: mapRepeats).joined(separator: ",") - let expected = ( - "{" - + "\"repeatedInt32\":[" - + pad - + "]," - + "\"repeatedMessage\":[" - + children - + "]}" - ) + let children = [String](repeating: child, count: mapRepeats).joined(separator: ",") + let expected = + ("{" + + "\"repeatedInt32\":[" + + pad + + "]," + + "\"repeatedMessage\":[" + + children + + "]}") let msg = MessageTestType.with { $0.repeatedInt32 = rawPadding let child = MessageTestType.with { - let grandchild = MessageTestType.with { - $0.singularInt32 = 8 - } - $0.mapInt32Message[30] = grandchild + let grandchild = MessageTestType.with { + $0.singularInt32 = 8 + } + $0.mapInt32Message[30] = grandchild } - let array = Array(repeating: child, count: mapRepeats) + let array = [MessageTestType](repeating: child, count: mapRepeats) $0.repeatedMessage = array } diff --git a/Tests/SwiftProtobufTests/Test_Map.swift b/Tests/SwiftProtobufTests/Test_Map.swift index ea6c6f0a0..6c7d8b41a 100644 --- a/Tests/SwiftProtobufTests/Test_Map.swift +++ b/Tests/SwiftProtobufTests/Test_Map.swift @@ -18,7 +18,12 @@ import XCTest final class Test_Map: XCTestCase, PBTestHelpers { typealias MessageTestType = SwiftProtoTesting_TestMap - func assertMapEncode(_ expectedBlocks: [[UInt8]], file: XCTestFileArgType = #file, line: UInt = #line, configure: (inout MessageTestType) -> Void) { + func assertMapEncode( + _ expectedBlocks: [[UInt8]], + file: XCTestFileArgType = #file, + line: UInt = #line, + configure: (inout MessageTestType) -> Void + ) { let empty = MessageTestType() var configured = empty configure(&configured) @@ -28,28 +33,38 @@ final class Test_Map: XCTestCase, PBTestHelpers { // Reorder the provided blocks to match what we were given var t = encoded[0.. Bool in - m.mapInt32Enum == [1:.foo, 3:.baz] + m.mapInt32Enum == [1: .foo, 3: .baz] } } func testMapInt32Message() { - assertJSONEncode("{\"mapInt32ForeignMessage\":{\"7\":{\"c\":999}}}") {(o: inout MessageTestType) in + assertJSONEncode("{\"mapInt32ForeignMessage\":{\"7\":{\"c\":999}}}") { (o: inout MessageTestType) in var m = SwiftProtoTesting_ForeignMessage() m.c = 999 o.mapInt32ForeignMessage[7] = m @@ -312,7 +323,7 @@ final class Test_Map_JSON: XCTestCase, PBTestHelpers { sub7.c = 7 var sub8 = SwiftProtoTesting_ForeignMessage() sub8.c = 8 - return $0.mapInt32ForeignMessage == [7:sub7, 8:sub8] + return $0.mapInt32ForeignMessage == [7: sub7, 8: sub8] } } } diff --git a/Tests/SwiftProtobufTests/Test_Merge.swift b/Tests/SwiftProtobufTests/Test_Merge.swift index 0988a9ab6..cf3f262df 100644 --- a/Tests/SwiftProtobufTests/Test_Merge.swift +++ b/Tests/SwiftProtobufTests/Test_Merge.swift @@ -9,47 +9,47 @@ // ----------------------------------------------------------------------------- import Foundation -import XCTest import SwiftProtobuf +import XCTest final class Test_Merge: XCTestCase, PBTestHelpers { - typealias MessageTestType = SwiftProtoTesting_Proto3_TestAllTypes - - func testMergeSimple() throws { - var m1 = SwiftProtoTesting_Proto3_TestAllTypes() - m1.optionalInt32 = 100 - - var m2 = SwiftProtoTesting_Proto3_TestAllTypes() - m2.optionalInt64 = 1000 - - do { - try m1.merge(serializedBytes: m2.serializedBytes() as [UInt8]) - XCTAssertEqual(m1.optionalInt32, 100) - XCTAssertEqual(m1.optionalInt64, 1000) - } catch let e { - XCTFail("Merge should not have thrown, but it did: \(e)") + typealias MessageTestType = SwiftProtoTesting_Proto3_TestAllTypes + + func testMergeSimple() throws { + var m1 = SwiftProtoTesting_Proto3_TestAllTypes() + m1.optionalInt32 = 100 + + var m2 = SwiftProtoTesting_Proto3_TestAllTypes() + m2.optionalInt64 = 1000 + + do { + try m1.merge(serializedBytes: m2.serializedBytes() as [UInt8]) + XCTAssertEqual(m1.optionalInt32, 100) + XCTAssertEqual(m1.optionalInt64, 1000) + } catch let e { + XCTFail("Merge should not have thrown, but it did: \(e)") + } } - } - - func testMergePreservesValueSemantics() throws { - var original = SwiftProtoTesting_Proto3_TestAllTypes() - original.optionalInt32 = 100 - let copied = original - - var toMerge = SwiftProtoTesting_Proto3_TestAllTypes() - toMerge.optionalInt64 = 1000 - - do { - try original.merge(serializedBytes: toMerge.serializedBytes() as [UInt8]) - - // The original should have the value from the merged message... - XCTAssertEqual(original.optionalInt32, 100) - XCTAssertEqual(original.optionalInt64, 1000) - // ...but the older copy should not be affected. - XCTAssertEqual(copied.optionalInt32, 100) - XCTAssertEqual(copied.optionalInt64, 0) - } catch let e { - XCTFail("Merge should not have thrown, but it did: \(e)") + + func testMergePreservesValueSemantics() throws { + var original = SwiftProtoTesting_Proto3_TestAllTypes() + original.optionalInt32 = 100 + let copied = original + + var toMerge = SwiftProtoTesting_Proto3_TestAllTypes() + toMerge.optionalInt64 = 1000 + + do { + try original.merge(serializedBytes: toMerge.serializedBytes() as [UInt8]) + + // The original should have the value from the merged message... + XCTAssertEqual(original.optionalInt32, 100) + XCTAssertEqual(original.optionalInt64, 1000) + // ...but the older copy should not be affected. + XCTAssertEqual(copied.optionalInt32, 100) + XCTAssertEqual(copied.optionalInt64, 0) + } catch let e { + XCTFail("Merge should not have thrown, but it did: \(e)") + } } - } } diff --git a/Tests/SwiftProtobufTests/Test_MessageSet.swift b/Tests/SwiftProtobufTests/Test_MessageSet.swift index 803d8928b..2a259574d 100644 --- a/Tests/SwiftProtobufTests/Test_MessageSet.swift +++ b/Tests/SwiftProtobufTests/Test_MessageSet.swift @@ -14,250 +14,276 @@ import Foundation import XCTest + @testable import SwiftProtobuf extension SwiftProtoTesting_RawMessageSet.Item { - fileprivate init(typeID: Int, message: Data) { - self.init() - self.typeID = Int32(typeID) - self.message = message - } + fileprivate init(typeID: Int, message: Data) { + self.init() + self.typeID = Int32(typeID) + self.message = message + } } final class Test_MessageSet: XCTestCase { - // wireformat_unittest.cc: TEST(WireFormatTest, SerializeMessageSet) - func testSerialize() throws { - let msg = SwiftProtoTesting_WireFormat_TestMessageSet.with { - $0.SwiftProtoTesting_TestMessageSetExtension1_messageSetExtension.i = 123 - $0.SwiftProtoTesting_TestMessageSetExtension2_messageSetExtension.str = "foo" - } - - let serialized: [UInt8] - do { - serialized = try msg.serializedBytes() - } catch let e { - XCTFail("Failed to serialize: \(e)") - return - } - - // Read it back in with the RawMessageSet to validate it. - - let raw: SwiftProtoTesting_RawMessageSet - do { - raw = try SwiftProtoTesting_RawMessageSet(serializedBytes: serialized) - } catch let e { - XCTFail("Failed to parse: \(e)") - return + // wireformat_unittest.cc: TEST(WireFormatTest, SerializeMessageSet) + func testSerialize() throws { + let msg = SwiftProtoTesting_WireFormat_TestMessageSet.with { + $0.SwiftProtoTesting_TestMessageSetExtension1_messageSetExtension.i = 123 + $0.SwiftProtoTesting_TestMessageSetExtension2_messageSetExtension.str = "foo" + } + + let serialized: [UInt8] + do { + serialized = try msg.serializedBytes() + } catch let e { + XCTFail("Failed to serialize: \(e)") + return + } + + // Read it back in with the RawMessageSet to validate it. + + let raw: SwiftProtoTesting_RawMessageSet + do { + raw = try SwiftProtoTesting_RawMessageSet(serializedBytes: serialized) + } catch let e { + XCTFail("Failed to parse: \(e)") + return + } + + XCTAssertTrue(raw.unknownFields.data.isEmpty) + + XCTAssertEqual(raw.item.count, 2) + + XCTAssertEqual( + Int(raw.item[0].typeID), + SwiftProtoTesting_TestMessageSetExtension1.Extensions.message_set_extension.fieldNumber + ) + XCTAssertEqual( + Int(raw.item[1].typeID), + SwiftProtoTesting_TestMessageSetExtension2.Extensions.message_set_extension.fieldNumber + ) + + let extMsg1 = try SwiftProtoTesting_TestMessageSetExtension1(serializedBytes: raw.item[0].message) + XCTAssertEqual(extMsg1.i, 123) + XCTAssertTrue(extMsg1.unknownFields.data.isEmpty) + let extMsg2 = try SwiftProtoTesting_TestMessageSetExtension2(serializedBytes: raw.item[1].message) + XCTAssertEqual(extMsg2.str, "foo") + XCTAssertTrue(extMsg2.unknownFields.data.isEmpty) } - XCTAssertTrue(raw.unknownFields.data.isEmpty) - - XCTAssertEqual(raw.item.count, 2) - - XCTAssertEqual(Int(raw.item[0].typeID), - SwiftProtoTesting_TestMessageSetExtension1.Extensions.message_set_extension.fieldNumber) - XCTAssertEqual(Int(raw.item[1].typeID), - SwiftProtoTesting_TestMessageSetExtension2.Extensions.message_set_extension.fieldNumber) - - let extMsg1 = try SwiftProtoTesting_TestMessageSetExtension1(serializedBytes: raw.item[0].message) - XCTAssertEqual(extMsg1.i, 123) - XCTAssertTrue(extMsg1.unknownFields.data.isEmpty) - let extMsg2 = try SwiftProtoTesting_TestMessageSetExtension2(serializedBytes: raw.item[1].message) - XCTAssertEqual(extMsg2.str, "foo") - XCTAssertTrue(extMsg2.unknownFields.data.isEmpty) - } - - // wireformat_unittest.cc: TEST(WireFormatTest, ParseMessageSet) - func testParse() throws { - let msg1 = SwiftProtoTesting_TestMessageSetExtension1.with { $0.i = 123 } - let msg2 = SwiftProtoTesting_TestMessageSetExtension2.with { $0.str = "foo" } - var raw = SwiftProtoTesting_RawMessageSet() - raw.item = [ - // Two known extensions. - SwiftProtoTesting_RawMessageSet.Item( - typeID: SwiftProtoTesting_TestMessageSetExtension1.Extensions.message_set_extension.fieldNumber, - message: try msg1.serializedBytes()), - SwiftProtoTesting_RawMessageSet.Item( - typeID: SwiftProtoTesting_TestMessageSetExtension2.Extensions.message_set_extension.fieldNumber, - message: try msg2.serializedBytes()), - // One unknown extension. - SwiftProtoTesting_RawMessageSet.Item(typeID: 7, message: Data([1, 2, 3])) - ] - // Add some unknown data into one of the groups to ensure it gets stripped when parsing. - raw.item[1].unknownFields.append(protobufData: Data([40, 2])) // Field 5, varint of 2 - - let serialized: Data - do { - serialized = try raw.serializedBytes() - } catch let e { - XCTFail("Failed to serialize: \(e)") - return + // wireformat_unittest.cc: TEST(WireFormatTest, ParseMessageSet) + func testParse() throws { + let msg1 = SwiftProtoTesting_TestMessageSetExtension1.with { $0.i = 123 } + let msg2 = SwiftProtoTesting_TestMessageSetExtension2.with { $0.str = "foo" } + var raw = SwiftProtoTesting_RawMessageSet() + raw.item = [ + // Two known extensions. + SwiftProtoTesting_RawMessageSet.Item( + typeID: SwiftProtoTesting_TestMessageSetExtension1.Extensions.message_set_extension.fieldNumber, + message: try msg1.serializedBytes() + ), + SwiftProtoTesting_RawMessageSet.Item( + typeID: SwiftProtoTesting_TestMessageSetExtension2.Extensions.message_set_extension.fieldNumber, + message: try msg2.serializedBytes() + ), + // One unknown extension. + SwiftProtoTesting_RawMessageSet.Item(typeID: 7, message: Data([1, 2, 3])), + ] + // Add some unknown data into one of the groups to ensure it gets stripped when parsing. + raw.item[1].unknownFields.append(protobufData: Data([40, 2])) // Field 5, varint of 2 + + let serialized: Data + do { + serialized = try raw.serializedBytes() + } catch let e { + XCTFail("Failed to serialize: \(e)") + return + } + + let msg: SwiftProtoTesting_WireFormat_TestMessageSet + do { + msg = try SwiftProtoTesting_WireFormat_TestMessageSet( + serializedBytes: serialized, + extensions: SwiftProtoTesting_UnittestMset_Extensions + ) + } catch let e { + XCTFail("Failed to parse: \(e)") + return + } + + // Ensure the extensions showed up, but with nothing extra. + XCTAssertEqual( + msg.SwiftProtoTesting_TestMessageSetExtension1_messageSetExtension.i, + 123 + ) + XCTAssertTrue( + msg.SwiftProtoTesting_TestMessageSetExtension1_messageSetExtension.unknownFields.data.isEmpty + ) + XCTAssertEqual( + msg.SwiftProtoTesting_TestMessageSetExtension2_messageSetExtension.str, + "foo" + ) + XCTAssertTrue( + msg.SwiftProtoTesting_TestMessageSetExtension2_messageSetExtension.unknownFields.data.isEmpty + ) + + // Ensure the unknown shows up as a group. + let expectedUnknowns = Data([ + 11, // Start group + 16, 7, // typeID = 7 + 26, 3, 1, 2, 3, // message data = 3 bytes: 1, 2, 3 + 12, // End Group + ]) + XCTAssertEqual(msg.unknownFields.data, expectedUnknowns) + + var validator = ExtensionValidator() + validator.expectedMessages = [ + (SwiftProtoTesting_TestMessageSetExtension1.Extensions.message_set_extension.fieldNumber, false), + (SwiftProtoTesting_TestMessageSetExtension2.Extensions.message_set_extension.fieldNumber, false), + ] + validator.expectedUnknowns = [expectedUnknowns] + validator.validate(message: msg) } - let msg: SwiftProtoTesting_WireFormat_TestMessageSet - do { - msg = try SwiftProtoTesting_WireFormat_TestMessageSet( - serializedBytes: serialized, - extensions: SwiftProtoTesting_UnittestMset_Extensions) - } catch let e { - XCTFail("Failed to parse: \(e)") - return + static let canonicalTextFormat: String = + ("message_set {\n" + " [swift_proto_testing.TestMessageSetExtension1] {\n" + " i: 23\n" + " }\n" + + " [swift_proto_testing.TestMessageSetExtension2] {\n" + " str: \"foo\"\n" + " }\n" + "}\n") + + func testParseOrder1() throws { + let serialized = Data([ + 11, + 16, 176, 166, 94, // Extension ID + 26, 2, 120, 123, // Payload message + 12, + ]) + + let msg: SwiftProtoTesting_WireFormat_TestMessageSet + do { + msg = try SwiftProtoTesting_WireFormat_TestMessageSet( + serializedBytes: serialized, + extensions: SwiftProtoTesting_UnittestMset_Extensions + ) + } catch let e { + XCTFail("Failed to parse: \(e)") + return + } + XCTAssertEqual( + msg.SwiftProtoTesting_TestMessageSetExtension1_messageSetExtension.i, + 123 + ) } - // Ensure the extensions showed up, but with nothing extra. - XCTAssertEqual( - msg.SwiftProtoTesting_TestMessageSetExtension1_messageSetExtension.i, 123) - XCTAssertTrue( - msg.SwiftProtoTesting_TestMessageSetExtension1_messageSetExtension.unknownFields.data.isEmpty) - XCTAssertEqual( - msg.SwiftProtoTesting_TestMessageSetExtension2_messageSetExtension.str, "foo") - XCTAssertTrue( - msg.SwiftProtoTesting_TestMessageSetExtension2_messageSetExtension.unknownFields.data.isEmpty) - - // Ensure the unknown shows up as a group. - let expectedUnknowns = Data([ - 11, // Start group - 16, 7, // typeID = 7 - 26, 3, 1, 2, 3, // message data = 3 bytes: 1, 2, 3 - 12 // End Group - ]) - XCTAssertEqual(msg.unknownFields.data, expectedUnknowns) - - var validator = ExtensionValidator() - validator.expectedMessages = [ - (SwiftProtoTesting_TestMessageSetExtension1.Extensions.message_set_extension.fieldNumber, false), - (SwiftProtoTesting_TestMessageSetExtension2.Extensions.message_set_extension.fieldNumber, false), - ] - validator.expectedUnknowns = [ expectedUnknowns ] - validator.validate(message: msg) - } - - static let canonicalTextFormat: String = ( - "message_set {\n" + - " [swift_proto_testing.TestMessageSetExtension1] {\n" + - " i: 23\n" + - " }\n" + - " [swift_proto_testing.TestMessageSetExtension2] {\n" + - " str: \"foo\"\n" + - " }\n" + - "}\n" - ) - - func testParseOrder1() throws { - let serialized = Data([11, - 16, 176, 166, 94, // Extension ID - 26, 2, 120, 123, // Payload message - 12]) - - let msg: SwiftProtoTesting_WireFormat_TestMessageSet - do { - msg = try SwiftProtoTesting_WireFormat_TestMessageSet( - serializedBytes: serialized, - extensions: SwiftProtoTesting_UnittestMset_Extensions) - } catch let e { - XCTFail("Failed to parse: \(e)") - return - } - XCTAssertEqual( - msg.SwiftProtoTesting_TestMessageSetExtension1_messageSetExtension.i, 123) - } - - func testParseOrder2() throws { - let serialized = Data([11, - 26, 2, 120, 123, // Payload message - 16, 176, 166, 94, // Extension ID - 12]) - - let msg: SwiftProtoTesting_WireFormat_TestMessageSet - do { - msg = try SwiftProtoTesting_WireFormat_TestMessageSet( - serializedBytes: serialized, - extensions: SwiftProtoTesting_UnittestMset_Extensions) - } catch let e { - XCTFail("Failed to parse: \(e)") - return - } - XCTAssertEqual( - msg.SwiftProtoTesting_TestMessageSetExtension1_messageSetExtension.i, 123) - } - - // text_format_unittest.cc: TEST_F(TextFormatMessageSetTest, Serialize) - func testTextFormat_Serialize() { - let msg = SwiftProtoTesting_TestMessageSetContainer.with { - $0.messageSet.SwiftProtoTesting_TestMessageSetExtension1_messageSetExtension.i = 23 - $0.messageSet.SwiftProtoTesting_TestMessageSetExtension2_messageSetExtension.str = "foo" + func testParseOrder2() throws { + let serialized = Data([ + 11, + 26, 2, 120, 123, // Payload message + 16, 176, 166, 94, // Extension ID + 12, + ]) + + let msg: SwiftProtoTesting_WireFormat_TestMessageSet + do { + msg = try SwiftProtoTesting_WireFormat_TestMessageSet( + serializedBytes: serialized, + extensions: SwiftProtoTesting_UnittestMset_Extensions + ) + } catch let e { + XCTFail("Failed to parse: \(e)") + return + } + XCTAssertEqual( + msg.SwiftProtoTesting_TestMessageSetExtension1_messageSetExtension.i, + 123 + ) } - XCTAssertEqual(msg.textFormatString(), Test_MessageSet.canonicalTextFormat) - } - - // text_format_unittest.cc: TEST_F(TextFormatMessageSetTest, Deserialize) - func testTextFormat_Parse() { - let msg: SwiftProtoTesting_TestMessageSetContainer - do { - msg = try SwiftProtoTesting_TestMessageSetContainer( - textFormatString: Test_MessageSet.canonicalTextFormat, - extensions: SwiftProtoTesting_UnittestMset_Extensions) - } catch let e { - XCTFail("Shouldn't have failed: \(e)") - return - } + // text_format_unittest.cc: TEST_F(TextFormatMessageSetTest, Serialize) + func testTextFormat_Serialize() { + let msg = SwiftProtoTesting_TestMessageSetContainer.with { + $0.messageSet.SwiftProtoTesting_TestMessageSetExtension1_messageSetExtension.i = 23 + $0.messageSet.SwiftProtoTesting_TestMessageSetExtension2_messageSetExtension.str = "foo" + } - XCTAssertEqual( - msg.messageSet.SwiftProtoTesting_TestMessageSetExtension1_messageSetExtension.i, 23) - XCTAssertEqual( - msg.messageSet.SwiftProtoTesting_TestMessageSetExtension2_messageSetExtension.str, "foo") - - // Ensure nothing else showed up. - XCTAssertTrue(msg.unknownFields.data.isEmpty) - XCTAssertTrue(msg.messageSet.unknownFields.data.isEmpty) - - var validator = ExtensionValidator() - validator.expectedMessages = [ - (1, true), // swift_proto_testing.TestMessageSetContainer.message_set (where the extensions are) - (SwiftProtoTesting_TestMessageSetExtension1.Extensions.message_set_extension.fieldNumber, false), - (SwiftProtoTesting_TestMessageSetExtension2.Extensions.message_set_extension.fieldNumber, false), - ] - validator.validate(message: msg) - } - - fileprivate struct ExtensionValidator: PBTestVisitor { - // Values are field number and if we should recurse. - var expectedMessages = [(Int, Bool)]() - var expectedUnknowns = [Data]() - - mutating func validate(message: M) { - do { - try message.traverse(visitor: &self) - } catch let e { - XCTFail("Error while traversing: \(e)") - } - XCTAssertTrue(expectedMessages.isEmpty, - "Expected more messages: \(expectedMessages)") - XCTAssertTrue(expectedUnknowns.isEmpty, - "Expected more unknowns: \(expectedUnknowns)") + XCTAssertEqual(msg.textFormatString(), Test_MessageSet.canonicalTextFormat) } - mutating func visitSingularMessageField(value: M, fieldNumber: Int) throws { - guard !expectedMessages.isEmpty else { - XCTFail("Unexpected Message: \(fieldNumber) = \(value)") - return - } - let (expected, shouldRecurse) = expectedMessages.removeFirst() - XCTAssertEqual(fieldNumber, expected) - if shouldRecurse && expected == fieldNumber { - try value.traverse(visitor: &self) - } + // text_format_unittest.cc: TEST_F(TextFormatMessageSetTest, Deserialize) + func testTextFormat_Parse() { + let msg: SwiftProtoTesting_TestMessageSetContainer + do { + msg = try SwiftProtoTesting_TestMessageSetContainer( + textFormatString: Test_MessageSet.canonicalTextFormat, + extensions: SwiftProtoTesting_UnittestMset_Extensions + ) + } catch let e { + XCTFail("Shouldn't have failed: \(e)") + return + } + + XCTAssertEqual( + msg.messageSet.SwiftProtoTesting_TestMessageSetExtension1_messageSetExtension.i, + 23 + ) + XCTAssertEqual( + msg.messageSet.SwiftProtoTesting_TestMessageSetExtension2_messageSetExtension.str, + "foo" + ) + + // Ensure nothing else showed up. + XCTAssertTrue(msg.unknownFields.data.isEmpty) + XCTAssertTrue(msg.messageSet.unknownFields.data.isEmpty) + + var validator = ExtensionValidator() + validator.expectedMessages = [ + (1, true), // swift_proto_testing.TestMessageSetContainer.message_set (where the extensions are) + (SwiftProtoTesting_TestMessageSetExtension1.Extensions.message_set_extension.fieldNumber, false), + (SwiftProtoTesting_TestMessageSetExtension2.Extensions.message_set_extension.fieldNumber, false), + ] + validator.validate(message: msg) } - mutating func visitUnknown(bytes: Data) throws { - guard !expectedUnknowns.isEmpty else { - XCTFail("Unexpected Unknown: \(bytes)") - return - } - let expected = expectedUnknowns.removeFirst() - XCTAssertEqual(bytes, expected) + fileprivate struct ExtensionValidator: PBTestVisitor { + // Values are field number and if we should recurse. + var expectedMessages = [(Int, Bool)]() + var expectedUnknowns = [Data]() + + mutating func validate(message: M) { + do { + try message.traverse(visitor: &self) + } catch let e { + XCTFail("Error while traversing: \(e)") + } + XCTAssertTrue( + expectedMessages.isEmpty, + "Expected more messages: \(expectedMessages)" + ) + XCTAssertTrue( + expectedUnknowns.isEmpty, + "Expected more unknowns: \(expectedUnknowns)" + ) + } + + mutating func visitSingularMessageField(value: M, fieldNumber: Int) throws { + guard !expectedMessages.isEmpty else { + XCTFail("Unexpected Message: \(fieldNumber) = \(value)") + return + } + let (expected, shouldRecurse) = expectedMessages.removeFirst() + XCTAssertEqual(fieldNumber, expected) + if shouldRecurse && expected == fieldNumber { + try value.traverse(visitor: &self) + } + } + + mutating func visitUnknown(bytes: Data) throws { + guard !expectedUnknowns.isEmpty else { + XCTFail("Unexpected Unknown: \(bytes)") + return + } + let expected = expectedUnknowns.removeFirst() + XCTAssertEqual(bytes, expected) + } } - } } diff --git a/Tests/SwiftProtobufTests/Test_Naming.swift b/Tests/SwiftProtobufTests/Test_Naming.swift index 127339f8f..9a4974be2 100644 --- a/Tests/SwiftProtobufTests/Test_Naming.swift +++ b/Tests/SwiftProtobufTests/Test_Naming.swift @@ -9,8 +9,8 @@ // ----------------------------------------------------------------------------- import Foundation -import XCTest import SwiftProtobuf +import XCTest // In transforming some of the names in protos to Swift names, we do different // transforms, this test is mainly a compile test in that the code below calls @@ -24,890 +24,890 @@ import SwiftProtobuf // NOTE: If this code fails to compile, make sure the name changes make sense. final class Test_PackageMapping: XCTestCase { - func testPackageStartingWithNumber() { - let _ = _4fun_SwiftProtoTesting_Mumble_MyMessage() - } + func testPackageStartingWithNumber() { + let _ = _4fun_SwiftProtoTesting_Mumble_MyMessage() + } } final class Test_FieldNamingInitials: XCTestCase { - func testHidingFunctions() throws { - // Check that we can access the standard `serializeData`, etc - // methods even on messages that define fields or submessages with - // such names: - let msg = SwiftProtoTesting_Names_FieldNames() - _ = try msg.serializedBytes() as [UInt8] - _ = try msg.jsonUTF8Bytes() as [UInt8] - _ = try msg.jsonString() - - let msg2 = SwiftProtoTesting_Names_MessageNames() - // The submessage is a static type name: - _ = SwiftProtoTesting_Names_MessageNames.serializedData() - // The method is an instance property: - _ = try msg2.serializedBytes() as [UInt8] - _ = try msg2.jsonUTF8Bytes() as [UInt8] - _ = try msg2.jsonString() - } - - func testLowers() { - var msg = SwiftProtoTesting_Names_FieldNamingInitials.Lowers() - - msg.http = 1 - XCTAssertTrue(msg.hasHTTP) - msg.clearHTTP() - - msg.httpRequest = 1 - XCTAssertTrue(msg.hasHTTPRequest) - msg.clearHTTPRequest() - - msg.theHTTPRequest = 1 - XCTAssertTrue(msg.hasTheHTTPRequest) - msg.clearTheHTTPRequest() - - msg.theHTTP = 1 - XCTAssertTrue(msg.hasTheHTTP) - msg.clearTheHTTP() - - msg.https = 1 - XCTAssertTrue(msg.hasHTTPS) - msg.clearHTTPS() - - msg.httpsRequest = 1 - XCTAssertTrue(msg.hasHTTPSRequest) - msg.clearHTTPSRequest() - - msg.theHTTPSRequest = 1 - XCTAssertTrue(msg.hasTheHTTPSRequest) - msg.clearTheHTTPSRequest() - - msg.theHTTPS = 1 - XCTAssertTrue(msg.hasTheHTTPS) - msg.clearTheHTTPS() - - msg.url = 1 - XCTAssertTrue(msg.hasURL) - msg.clearURL() - - msg.urlValue = 1 - XCTAssertTrue(msg.hasURLValue) - msg.clearURLValue() - - msg.theURLValue = 1 - XCTAssertTrue(msg.hasTheURLValue) - msg.clearTheURLValue() - - msg.theURL = 1 - XCTAssertTrue(msg.hasTheURL) - msg.clearTheURL() - - msg.aBC = 1 - XCTAssertTrue(msg.hasABC) - msg.clearABC() - - msg.id = 1 - XCTAssertTrue(msg.hasID) - msg.clearID() - - msg.idNumber = 1 - XCTAssertTrue(msg.hasIDNumber) - msg.clearIDNumber() - - msg.theIDNumber = 1 - XCTAssertTrue(msg.hasTheIDNumber) - msg.clearTheIDNumber() - - msg.requestID = 1 - XCTAssertTrue(msg.hasRequestID) - msg.clearRequestID() - } - - func testUppers() { - var msg = SwiftProtoTesting_Names_FieldNamingInitials.Uppers() - - msg.http = 1 - XCTAssertTrue(msg.hasHTTP) - msg.clearHTTP() - - msg.httpRequest = 1 - XCTAssertTrue(msg.hasHTTPRequest) - msg.clearHTTPRequest() - - msg.theHTTPRequest = 1 - XCTAssertTrue(msg.hasTheHTTPRequest) - msg.clearTheHTTPRequest() - - msg.theHTTP = 1 - XCTAssertTrue(msg.hasTheHTTP) - msg.clearTheHTTP() + func testHidingFunctions() throws { + // Check that we can access the standard `serializeData`, etc + // methods even on messages that define fields or submessages with + // such names: + let msg = SwiftProtoTesting_Names_FieldNames() + _ = try msg.serializedBytes() as [UInt8] + _ = try msg.jsonUTF8Bytes() as [UInt8] + _ = try msg.jsonString() + + let msg2 = SwiftProtoTesting_Names_MessageNames() + // The submessage is a static type name: + _ = SwiftProtoTesting_Names_MessageNames.serializedData() + // The method is an instance property: + _ = try msg2.serializedBytes() as [UInt8] + _ = try msg2.jsonUTF8Bytes() as [UInt8] + _ = try msg2.jsonString() + } + + func testLowers() { + var msg = SwiftProtoTesting_Names_FieldNamingInitials.Lowers() + + msg.http = 1 + XCTAssertTrue(msg.hasHTTP) + msg.clearHTTP() + + msg.httpRequest = 1 + XCTAssertTrue(msg.hasHTTPRequest) + msg.clearHTTPRequest() + + msg.theHTTPRequest = 1 + XCTAssertTrue(msg.hasTheHTTPRequest) + msg.clearTheHTTPRequest() + + msg.theHTTP = 1 + XCTAssertTrue(msg.hasTheHTTP) + msg.clearTheHTTP() + + msg.https = 1 + XCTAssertTrue(msg.hasHTTPS) + msg.clearHTTPS() + + msg.httpsRequest = 1 + XCTAssertTrue(msg.hasHTTPSRequest) + msg.clearHTTPSRequest() + + msg.theHTTPSRequest = 1 + XCTAssertTrue(msg.hasTheHTTPSRequest) + msg.clearTheHTTPSRequest() + + msg.theHTTPS = 1 + XCTAssertTrue(msg.hasTheHTTPS) + msg.clearTheHTTPS() + + msg.url = 1 + XCTAssertTrue(msg.hasURL) + msg.clearURL() + + msg.urlValue = 1 + XCTAssertTrue(msg.hasURLValue) + msg.clearURLValue() + + msg.theURLValue = 1 + XCTAssertTrue(msg.hasTheURLValue) + msg.clearTheURLValue() + + msg.theURL = 1 + XCTAssertTrue(msg.hasTheURL) + msg.clearTheURL() + + msg.aBC = 1 + XCTAssertTrue(msg.hasABC) + msg.clearABC() + + msg.id = 1 + XCTAssertTrue(msg.hasID) + msg.clearID() + + msg.idNumber = 1 + XCTAssertTrue(msg.hasIDNumber) + msg.clearIDNumber() + + msg.theIDNumber = 1 + XCTAssertTrue(msg.hasTheIDNumber) + msg.clearTheIDNumber() + + msg.requestID = 1 + XCTAssertTrue(msg.hasRequestID) + msg.clearRequestID() + } + + func testUppers() { + var msg = SwiftProtoTesting_Names_FieldNamingInitials.Uppers() + + msg.http = 1 + XCTAssertTrue(msg.hasHTTP) + msg.clearHTTP() + + msg.httpRequest = 1 + XCTAssertTrue(msg.hasHTTPRequest) + msg.clearHTTPRequest() + + msg.theHTTPRequest = 1 + XCTAssertTrue(msg.hasTheHTTPRequest) + msg.clearTheHTTPRequest() + + msg.theHTTP = 1 + XCTAssertTrue(msg.hasTheHTTP) + msg.clearTheHTTP() - msg.https = 1 - XCTAssertTrue(msg.hasHTTPS) - msg.clearHTTPS() + msg.https = 1 + XCTAssertTrue(msg.hasHTTPS) + msg.clearHTTPS() - msg.httpsRequest = 1 - XCTAssertTrue(msg.hasHTTPSRequest) - msg.clearHTTPSRequest() + msg.httpsRequest = 1 + XCTAssertTrue(msg.hasHTTPSRequest) + msg.clearHTTPSRequest() - msg.theHTTPSRequest = 1 - XCTAssertTrue(msg.hasTheHTTPSRequest) - msg.clearTheHTTPSRequest() + msg.theHTTPSRequest = 1 + XCTAssertTrue(msg.hasTheHTTPSRequest) + msg.clearTheHTTPSRequest() - msg.theHTTPS = 1 - XCTAssertTrue(msg.hasTheHTTPS) - msg.clearTheHTTPS() + msg.theHTTPS = 1 + XCTAssertTrue(msg.hasTheHTTPS) + msg.clearTheHTTPS() - msg.url = 1 - XCTAssertTrue(msg.hasURL) - msg.clearURL() + msg.url = 1 + XCTAssertTrue(msg.hasURL) + msg.clearURL() - msg.urlValue = 1 - XCTAssertTrue(msg.hasURLValue) - msg.clearURLValue() + msg.urlValue = 1 + XCTAssertTrue(msg.hasURLValue) + msg.clearURLValue() - msg.theURLValue = 1 - XCTAssertTrue(msg.hasTheURLValue) - msg.clearTheURLValue() + msg.theURLValue = 1 + XCTAssertTrue(msg.hasTheURLValue) + msg.clearTheURLValue() - msg.theURL = 1 - XCTAssertTrue(msg.hasTheURL) - msg.clearTheURL() + msg.theURL = 1 + XCTAssertTrue(msg.hasTheURL) + msg.clearTheURL() - msg.id = 1 - XCTAssertTrue(msg.hasID) - msg.clearID() + msg.id = 1 + XCTAssertTrue(msg.hasID) + msg.clearID() - msg.idNumber = 1 - XCTAssertTrue(msg.hasIDNumber) - msg.clearIDNumber() + msg.idNumber = 1 + XCTAssertTrue(msg.hasIDNumber) + msg.clearIDNumber() - msg.theIDNumber = 1 - XCTAssertTrue(msg.hasTheIDNumber) - msg.clearTheIDNumber() + msg.theIDNumber = 1 + XCTAssertTrue(msg.hasTheIDNumber) + msg.clearTheIDNumber() - msg.requestID = 1 - XCTAssertTrue(msg.hasRequestID) - msg.clearRequestID() - } - - func testWordCase() { - var msg = SwiftProtoTesting_Names_FieldNamingInitials.WordCase() + msg.requestID = 1 + XCTAssertTrue(msg.hasRequestID) + msg.clearRequestID() + } + + func testWordCase() { + var msg = SwiftProtoTesting_Names_FieldNamingInitials.WordCase() - msg.http = 1 - XCTAssertTrue(msg.hasHTTP) - msg.clearHTTP() + msg.http = 1 + XCTAssertTrue(msg.hasHTTP) + msg.clearHTTP() - msg.httpRequest = 1 - XCTAssertTrue(msg.hasHTTPRequest) - msg.clearHTTPRequest() + msg.httpRequest = 1 + XCTAssertTrue(msg.hasHTTPRequest) + msg.clearHTTPRequest() - msg.theHTTPRequest = 1 - XCTAssertTrue(msg.hasTheHTTPRequest) - msg.clearTheHTTPRequest() + msg.theHTTPRequest = 1 + XCTAssertTrue(msg.hasTheHTTPRequest) + msg.clearTheHTTPRequest() - msg.theHTTP = 1 - XCTAssertTrue(msg.hasTheHTTP) - msg.clearTheHTTP() + msg.theHTTP = 1 + XCTAssertTrue(msg.hasTheHTTP) + msg.clearTheHTTP() - msg.https = 1 - XCTAssertTrue(msg.hasHTTPS) - msg.clearHTTPS() + msg.https = 1 + XCTAssertTrue(msg.hasHTTPS) + msg.clearHTTPS() - msg.httpsRequest = 1 - XCTAssertTrue(msg.hasHTTPSRequest) - msg.clearHTTPSRequest() + msg.httpsRequest = 1 + XCTAssertTrue(msg.hasHTTPSRequest) + msg.clearHTTPSRequest() - msg.theHTTPSRequest = 1 - XCTAssertTrue(msg.hasTheHTTPSRequest) - msg.clearTheHTTPSRequest() + msg.theHTTPSRequest = 1 + XCTAssertTrue(msg.hasTheHTTPSRequest) + msg.clearTheHTTPSRequest() - msg.theHTTPS = 1 - XCTAssertTrue(msg.hasTheHTTPS) - msg.clearTheHTTPS() + msg.theHTTPS = 1 + XCTAssertTrue(msg.hasTheHTTPS) + msg.clearTheHTTPS() - msg.url = 1 - XCTAssertTrue(msg.hasURL) - msg.clearURL() + msg.url = 1 + XCTAssertTrue(msg.hasURL) + msg.clearURL() - msg.urlValue = 1 - XCTAssertTrue(msg.hasURLValue) - msg.clearURLValue() + msg.urlValue = 1 + XCTAssertTrue(msg.hasURLValue) + msg.clearURLValue() - msg.theURLValue = 1 - XCTAssertTrue(msg.hasTheURLValue) - msg.clearTheURLValue() + msg.theURLValue = 1 + XCTAssertTrue(msg.hasTheURLValue) + msg.clearTheURLValue() - msg.theURL = 1 - XCTAssertTrue(msg.hasTheURL) - msg.clearTheURL() + msg.theURL = 1 + XCTAssertTrue(msg.hasTheURL) + msg.clearTheURL() - msg.id = 1 - XCTAssertTrue(msg.hasID) - msg.clearID() + msg.id = 1 + XCTAssertTrue(msg.hasID) + msg.clearID() - msg.idNumber = 1 - XCTAssertTrue(msg.hasIDNumber) - msg.clearIDNumber() + msg.idNumber = 1 + XCTAssertTrue(msg.hasIDNumber) + msg.clearIDNumber() - msg.theIDNumber = 1 - XCTAssertTrue(msg.hasTheIDNumber) - msg.clearTheIDNumber() + msg.theIDNumber = 1 + XCTAssertTrue(msg.hasTheIDNumber) + msg.clearTheIDNumber() - msg.requestID = 1 - XCTAssertTrue(msg.hasRequestID) - msg.clearRequestID() - } + msg.requestID = 1 + XCTAssertTrue(msg.hasRequestID) + msg.clearRequestID() + } } final class Test_ExtensionNamingInitials_MessageScoped: XCTestCase { - func testLowers() { - var msg = SwiftProtoTesting_Names_ExtensionNamingInitials() + func testLowers() { + var msg = SwiftProtoTesting_Names_ExtensionNamingInitials() - msg.SwiftProtoTesting_Names_Lowers_http = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Lowers_http) - msg.clearSwiftProtoTesting_Names_Lowers_http() + msg.SwiftProtoTesting_Names_Lowers_http = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Lowers_http) + msg.clearSwiftProtoTesting_Names_Lowers_http() - msg.SwiftProtoTesting_Names_Lowers_httpRequest = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Lowers_httpRequest) - msg.clearSwiftProtoTesting_Names_Lowers_httpRequest() + msg.SwiftProtoTesting_Names_Lowers_httpRequest = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Lowers_httpRequest) + msg.clearSwiftProtoTesting_Names_Lowers_httpRequest() - msg.SwiftProtoTesting_Names_Lowers_theHTTPRequest = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Lowers_theHTTPRequest) - msg.clearSwiftProtoTesting_Names_Lowers_theHTTPRequest() + msg.SwiftProtoTesting_Names_Lowers_theHTTPRequest = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Lowers_theHTTPRequest) + msg.clearSwiftProtoTesting_Names_Lowers_theHTTPRequest() - msg.SwiftProtoTesting_Names_Lowers_theHTTP = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Lowers_theHTTP) - msg.clearSwiftProtoTesting_Names_Lowers_theHTTP() + msg.SwiftProtoTesting_Names_Lowers_theHTTP = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Lowers_theHTTP) + msg.clearSwiftProtoTesting_Names_Lowers_theHTTP() - msg.SwiftProtoTesting_Names_Lowers_https = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Lowers_https) - msg.clearSwiftProtoTesting_Names_Lowers_https() + msg.SwiftProtoTesting_Names_Lowers_https = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Lowers_https) + msg.clearSwiftProtoTesting_Names_Lowers_https() - msg.SwiftProtoTesting_Names_Lowers_httpsRequest = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Lowers_httpsRequest) - msg.clearSwiftProtoTesting_Names_Lowers_httpsRequest() + msg.SwiftProtoTesting_Names_Lowers_httpsRequest = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Lowers_httpsRequest) + msg.clearSwiftProtoTesting_Names_Lowers_httpsRequest() - msg.SwiftProtoTesting_Names_Lowers_theHTTPSRequest = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Lowers_theHTTPSRequest) - msg.clearSwiftProtoTesting_Names_Lowers_theHTTPSRequest() + msg.SwiftProtoTesting_Names_Lowers_theHTTPSRequest = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Lowers_theHTTPSRequest) + msg.clearSwiftProtoTesting_Names_Lowers_theHTTPSRequest() - msg.SwiftProtoTesting_Names_Lowers_theHTTPS = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Lowers_theHTTPS) - msg.clearSwiftProtoTesting_Names_Lowers_theHTTPS() + msg.SwiftProtoTesting_Names_Lowers_theHTTPS = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Lowers_theHTTPS) + msg.clearSwiftProtoTesting_Names_Lowers_theHTTPS() - msg.SwiftProtoTesting_Names_Lowers_url = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Lowers_url) - msg.clearSwiftProtoTesting_Names_Lowers_url() + msg.SwiftProtoTesting_Names_Lowers_url = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Lowers_url) + msg.clearSwiftProtoTesting_Names_Lowers_url() - msg.SwiftProtoTesting_Names_Lowers_urlValue = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Lowers_urlValue) - msg.clearSwiftProtoTesting_Names_Lowers_urlValue() + msg.SwiftProtoTesting_Names_Lowers_urlValue = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Lowers_urlValue) + msg.clearSwiftProtoTesting_Names_Lowers_urlValue() - msg.SwiftProtoTesting_Names_Lowers_theURLValue = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Lowers_theURLValue) - msg.clearSwiftProtoTesting_Names_Lowers_theURLValue() + msg.SwiftProtoTesting_Names_Lowers_theURLValue = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Lowers_theURLValue) + msg.clearSwiftProtoTesting_Names_Lowers_theURLValue() - msg.SwiftProtoTesting_Names_Lowers_theURL = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Lowers_theURL) - msg.clearSwiftProtoTesting_Names_Lowers_theURL() + msg.SwiftProtoTesting_Names_Lowers_theURL = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Lowers_theURL) + msg.clearSwiftProtoTesting_Names_Lowers_theURL() - msg.SwiftProtoTesting_Names_Lowers_aBC = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Lowers_aBC) - msg.clearSwiftProtoTesting_Names_Lowers_aBC() + msg.SwiftProtoTesting_Names_Lowers_aBC = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Lowers_aBC) + msg.clearSwiftProtoTesting_Names_Lowers_aBC() - msg.SwiftProtoTesting_Names_Lowers_id = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Lowers_id) - msg.clearSwiftProtoTesting_Names_Lowers_id() + msg.SwiftProtoTesting_Names_Lowers_id = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Lowers_id) + msg.clearSwiftProtoTesting_Names_Lowers_id() - msg.SwiftProtoTesting_Names_Lowers_idNumber = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Lowers_idNumber) - msg.clearSwiftProtoTesting_Names_Lowers_idNumber() + msg.SwiftProtoTesting_Names_Lowers_idNumber = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Lowers_idNumber) + msg.clearSwiftProtoTesting_Names_Lowers_idNumber() - msg.SwiftProtoTesting_Names_Lowers_theIDNumber = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Lowers_theIDNumber) - msg.clearSwiftProtoTesting_Names_Lowers_theIDNumber() + msg.SwiftProtoTesting_Names_Lowers_theIDNumber = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Lowers_theIDNumber) + msg.clearSwiftProtoTesting_Names_Lowers_theIDNumber() - msg.SwiftProtoTesting_Names_Lowers_requestID = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Lowers_requestID) - msg.clearSwiftProtoTesting_Names_Lowers_requestID() - } + msg.SwiftProtoTesting_Names_Lowers_requestID = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Lowers_requestID) + msg.clearSwiftProtoTesting_Names_Lowers_requestID() + } - func testUppers() { - var msg = SwiftProtoTesting_Names_ExtensionNamingInitials() + func testUppers() { + var msg = SwiftProtoTesting_Names_ExtensionNamingInitials() - msg.SwiftProtoTesting_Names_Uppers_http = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Uppers_http) - msg.clearSwiftProtoTesting_Names_Uppers_http() + msg.SwiftProtoTesting_Names_Uppers_http = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Uppers_http) + msg.clearSwiftProtoTesting_Names_Uppers_http() - msg.SwiftProtoTesting_Names_Uppers_httpRequest = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Uppers_httpRequest) - msg.clearSwiftProtoTesting_Names_Uppers_httpRequest() + msg.SwiftProtoTesting_Names_Uppers_httpRequest = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Uppers_httpRequest) + msg.clearSwiftProtoTesting_Names_Uppers_httpRequest() - msg.SwiftProtoTesting_Names_Uppers_theHTTPRequest = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Uppers_theHTTPRequest) - msg.clearSwiftProtoTesting_Names_Uppers_theHTTPRequest() + msg.SwiftProtoTesting_Names_Uppers_theHTTPRequest = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Uppers_theHTTPRequest) + msg.clearSwiftProtoTesting_Names_Uppers_theHTTPRequest() - msg.SwiftProtoTesting_Names_Uppers_theHTTP = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Uppers_theHTTP) - msg.clearSwiftProtoTesting_Names_Uppers_theHTTP() + msg.SwiftProtoTesting_Names_Uppers_theHTTP = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Uppers_theHTTP) + msg.clearSwiftProtoTesting_Names_Uppers_theHTTP() - msg.SwiftProtoTesting_Names_Uppers_https = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Uppers_https) - msg.clearSwiftProtoTesting_Names_Uppers_https() + msg.SwiftProtoTesting_Names_Uppers_https = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Uppers_https) + msg.clearSwiftProtoTesting_Names_Uppers_https() - msg.SwiftProtoTesting_Names_Uppers_httpsRequest = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Uppers_httpsRequest) - msg.clearSwiftProtoTesting_Names_Uppers_httpsRequest() + msg.SwiftProtoTesting_Names_Uppers_httpsRequest = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Uppers_httpsRequest) + msg.clearSwiftProtoTesting_Names_Uppers_httpsRequest() - msg.SwiftProtoTesting_Names_Uppers_theHTTPSRequest = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Uppers_theHTTPSRequest) - msg.clearSwiftProtoTesting_Names_Uppers_theHTTPSRequest() + msg.SwiftProtoTesting_Names_Uppers_theHTTPSRequest = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Uppers_theHTTPSRequest) + msg.clearSwiftProtoTesting_Names_Uppers_theHTTPSRequest() - msg.SwiftProtoTesting_Names_Uppers_theHTTPS = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Uppers_theHTTPS) - msg.clearSwiftProtoTesting_Names_Uppers_theHTTPS() + msg.SwiftProtoTesting_Names_Uppers_theHTTPS = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Uppers_theHTTPS) + msg.clearSwiftProtoTesting_Names_Uppers_theHTTPS() - msg.SwiftProtoTesting_Names_Uppers_url = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Uppers_url) - msg.clearSwiftProtoTesting_Names_Uppers_url() + msg.SwiftProtoTesting_Names_Uppers_url = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Uppers_url) + msg.clearSwiftProtoTesting_Names_Uppers_url() - msg.SwiftProtoTesting_Names_Uppers_urlValue = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Uppers_urlValue) - msg.clearSwiftProtoTesting_Names_Uppers_urlValue() + msg.SwiftProtoTesting_Names_Uppers_urlValue = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Uppers_urlValue) + msg.clearSwiftProtoTesting_Names_Uppers_urlValue() - msg.SwiftProtoTesting_Names_Uppers_theURLValue = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Uppers_theURLValue) - msg.clearSwiftProtoTesting_Names_Uppers_theURLValue() + msg.SwiftProtoTesting_Names_Uppers_theURLValue = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Uppers_theURLValue) + msg.clearSwiftProtoTesting_Names_Uppers_theURLValue() - msg.SwiftProtoTesting_Names_Uppers_theURL = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Uppers_theURL) - msg.clearSwiftProtoTesting_Names_Uppers_theURL() + msg.SwiftProtoTesting_Names_Uppers_theURL = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Uppers_theURL) + msg.clearSwiftProtoTesting_Names_Uppers_theURL() - msg.SwiftProtoTesting_Names_Uppers_id = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Uppers_id) - msg.clearSwiftProtoTesting_Names_Uppers_id() + msg.SwiftProtoTesting_Names_Uppers_id = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Uppers_id) + msg.clearSwiftProtoTesting_Names_Uppers_id() - msg.SwiftProtoTesting_Names_Uppers_idNumber = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Uppers_idNumber) - msg.clearSwiftProtoTesting_Names_Uppers_idNumber() + msg.SwiftProtoTesting_Names_Uppers_idNumber = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Uppers_idNumber) + msg.clearSwiftProtoTesting_Names_Uppers_idNumber() - msg.SwiftProtoTesting_Names_Uppers_theIDNumber = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Uppers_theIDNumber) - msg.clearSwiftProtoTesting_Names_Uppers_theIDNumber() + msg.SwiftProtoTesting_Names_Uppers_theIDNumber = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Uppers_theIDNumber) + msg.clearSwiftProtoTesting_Names_Uppers_theIDNumber() - msg.SwiftProtoTesting_Names_Uppers_requestID = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Uppers_requestID) - msg.clearSwiftProtoTesting_Names_Uppers_requestID() - } - - func testWordCase() { - var msg = SwiftProtoTesting_Names_ExtensionNamingInitials() + msg.SwiftProtoTesting_Names_Uppers_requestID = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_Uppers_requestID) + msg.clearSwiftProtoTesting_Names_Uppers_requestID() + } + + func testWordCase() { + var msg = SwiftProtoTesting_Names_ExtensionNamingInitials() - msg.SwiftProtoTesting_Names_WordCase_http = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_WordCase_http) - msg.clearSwiftProtoTesting_Names_WordCase_http() + msg.SwiftProtoTesting_Names_WordCase_http = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_WordCase_http) + msg.clearSwiftProtoTesting_Names_WordCase_http() - msg.SwiftProtoTesting_Names_WordCase_httpRequest = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_WordCase_httpRequest) - msg.clearSwiftProtoTesting_Names_WordCase_httpRequest() + msg.SwiftProtoTesting_Names_WordCase_httpRequest = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_WordCase_httpRequest) + msg.clearSwiftProtoTesting_Names_WordCase_httpRequest() - msg.SwiftProtoTesting_Names_WordCase_theHTTPRequest = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_WordCase_theHTTPRequest) - msg.clearSwiftProtoTesting_Names_WordCase_theHTTPRequest() + msg.SwiftProtoTesting_Names_WordCase_theHTTPRequest = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_WordCase_theHTTPRequest) + msg.clearSwiftProtoTesting_Names_WordCase_theHTTPRequest() - msg.SwiftProtoTesting_Names_WordCase_theHTTP = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_WordCase_theHTTP) - msg.clearSwiftProtoTesting_Names_WordCase_theHTTP() + msg.SwiftProtoTesting_Names_WordCase_theHTTP = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_WordCase_theHTTP) + msg.clearSwiftProtoTesting_Names_WordCase_theHTTP() - msg.SwiftProtoTesting_Names_WordCase_https = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_WordCase_https) - msg.clearSwiftProtoTesting_Names_WordCase_https() + msg.SwiftProtoTesting_Names_WordCase_https = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_WordCase_https) + msg.clearSwiftProtoTesting_Names_WordCase_https() - msg.SwiftProtoTesting_Names_WordCase_httpsRequest = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_WordCase_httpsRequest) - msg.clearSwiftProtoTesting_Names_WordCase_httpsRequest() + msg.SwiftProtoTesting_Names_WordCase_httpsRequest = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_WordCase_httpsRequest) + msg.clearSwiftProtoTesting_Names_WordCase_httpsRequest() - msg.SwiftProtoTesting_Names_WordCase_theHTTPSRequest = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_WordCase_theHTTPSRequest) - msg.clearSwiftProtoTesting_Names_WordCase_theHTTPSRequest() + msg.SwiftProtoTesting_Names_WordCase_theHTTPSRequest = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_WordCase_theHTTPSRequest) + msg.clearSwiftProtoTesting_Names_WordCase_theHTTPSRequest() - msg.SwiftProtoTesting_Names_WordCase_theHTTPS = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_WordCase_theHTTPS) - msg.clearSwiftProtoTesting_Names_WordCase_theHTTPS() + msg.SwiftProtoTesting_Names_WordCase_theHTTPS = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_WordCase_theHTTPS) + msg.clearSwiftProtoTesting_Names_WordCase_theHTTPS() - msg.SwiftProtoTesting_Names_WordCase_url = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_WordCase_url) - msg.clearSwiftProtoTesting_Names_WordCase_url() + msg.SwiftProtoTesting_Names_WordCase_url = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_WordCase_url) + msg.clearSwiftProtoTesting_Names_WordCase_url() - msg.SwiftProtoTesting_Names_WordCase_urlValue = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_WordCase_urlValue) - msg.clearSwiftProtoTesting_Names_WordCase_urlValue() + msg.SwiftProtoTesting_Names_WordCase_urlValue = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_WordCase_urlValue) + msg.clearSwiftProtoTesting_Names_WordCase_urlValue() - msg.SwiftProtoTesting_Names_WordCase_theURLValue = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_WordCase_theURLValue) - msg.clearSwiftProtoTesting_Names_WordCase_theURLValue() + msg.SwiftProtoTesting_Names_WordCase_theURLValue = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_WordCase_theURLValue) + msg.clearSwiftProtoTesting_Names_WordCase_theURLValue() - msg.SwiftProtoTesting_Names_WordCase_theURL = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_WordCase_theURL) - msg.clearSwiftProtoTesting_Names_WordCase_theURL() + msg.SwiftProtoTesting_Names_WordCase_theURL = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_WordCase_theURL) + msg.clearSwiftProtoTesting_Names_WordCase_theURL() - msg.SwiftProtoTesting_Names_WordCase_id = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_WordCase_id) - msg.clearSwiftProtoTesting_Names_WordCase_id() + msg.SwiftProtoTesting_Names_WordCase_id = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_WordCase_id) + msg.clearSwiftProtoTesting_Names_WordCase_id() - msg.SwiftProtoTesting_Names_WordCase_idNumber = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_WordCase_idNumber) - msg.clearSwiftProtoTesting_Names_WordCase_idNumber() + msg.SwiftProtoTesting_Names_WordCase_idNumber = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_WordCase_idNumber) + msg.clearSwiftProtoTesting_Names_WordCase_idNumber() - msg.SwiftProtoTesting_Names_WordCase_theIDNumber = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_WordCase_theIDNumber) - msg.clearSwiftProtoTesting_Names_WordCase_theIDNumber() + msg.SwiftProtoTesting_Names_WordCase_theIDNumber = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_WordCase_theIDNumber) + msg.clearSwiftProtoTesting_Names_WordCase_theIDNumber() - msg.SwiftProtoTesting_Names_WordCase_requestID = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_WordCase_requestID) - msg.clearSwiftProtoTesting_Names_WordCase_requestID() - } + msg.SwiftProtoTesting_Names_WordCase_requestID = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_WordCase_requestID) + msg.clearSwiftProtoTesting_Names_WordCase_requestID() + } } final class Test_ExtensionNamingInitials_GlobalScoped: XCTestCase { - func testLowers() { - var msg = SwiftProtoTesting_Names_ExtensionNamingInitialsLowers() + func testLowers() { + var msg = SwiftProtoTesting_Names_ExtensionNamingInitialsLowers() - msg.SwiftProtoTesting_Names_http = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_http) - msg.clearSwiftProtoTesting_Names_http() + msg.SwiftProtoTesting_Names_http = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_http) + msg.clearSwiftProtoTesting_Names_http() - msg.SwiftProtoTesting_Names_httpRequest = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_httpRequest) - msg.clearSwiftProtoTesting_Names_httpRequest() + msg.SwiftProtoTesting_Names_httpRequest = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_httpRequest) + msg.clearSwiftProtoTesting_Names_httpRequest() - msg.SwiftProtoTesting_Names_theHTTPRequest = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theHTTPRequest) - msg.clearSwiftProtoTesting_Names_theHTTPRequest() + msg.SwiftProtoTesting_Names_theHTTPRequest = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theHTTPRequest) + msg.clearSwiftProtoTesting_Names_theHTTPRequest() - msg.SwiftProtoTesting_Names_theHTTP = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theHTTP) - msg.clearSwiftProtoTesting_Names_theHTTP() + msg.SwiftProtoTesting_Names_theHTTP = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theHTTP) + msg.clearSwiftProtoTesting_Names_theHTTP() - msg.SwiftProtoTesting_Names_https = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_https) - msg.clearSwiftProtoTesting_Names_https() + msg.SwiftProtoTesting_Names_https = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_https) + msg.clearSwiftProtoTesting_Names_https() - msg.SwiftProtoTesting_Names_httpsRequest = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_httpsRequest) - msg.clearSwiftProtoTesting_Names_httpsRequest() + msg.SwiftProtoTesting_Names_httpsRequest = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_httpsRequest) + msg.clearSwiftProtoTesting_Names_httpsRequest() - msg.SwiftProtoTesting_Names_theHTTPSRequest = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theHTTPSRequest) - msg.clearSwiftProtoTesting_Names_theHTTPSRequest() + msg.SwiftProtoTesting_Names_theHTTPSRequest = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theHTTPSRequest) + msg.clearSwiftProtoTesting_Names_theHTTPSRequest() - msg.SwiftProtoTesting_Names_theHTTPS = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theHTTPS) - msg.clearSwiftProtoTesting_Names_theHTTPS() + msg.SwiftProtoTesting_Names_theHTTPS = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theHTTPS) + msg.clearSwiftProtoTesting_Names_theHTTPS() - msg.SwiftProtoTesting_Names_url = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_url) - msg.clearSwiftProtoTesting_Names_url() + msg.SwiftProtoTesting_Names_url = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_url) + msg.clearSwiftProtoTesting_Names_url() - msg.SwiftProtoTesting_Names_urlValue = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_urlValue) - msg.clearSwiftProtoTesting_Names_urlValue() + msg.SwiftProtoTesting_Names_urlValue = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_urlValue) + msg.clearSwiftProtoTesting_Names_urlValue() - msg.SwiftProtoTesting_Names_theURLValue = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theURLValue) - msg.clearSwiftProtoTesting_Names_theURLValue() + msg.SwiftProtoTesting_Names_theURLValue = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theURLValue) + msg.clearSwiftProtoTesting_Names_theURLValue() - msg.SwiftProtoTesting_Names_theURL = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theURL) - msg.clearSwiftProtoTesting_Names_theURL() + msg.SwiftProtoTesting_Names_theURL = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theURL) + msg.clearSwiftProtoTesting_Names_theURL() - msg.SwiftProtoTesting_Names_aBC = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_aBC) - msg.clearSwiftProtoTesting_Names_aBC() + msg.SwiftProtoTesting_Names_aBC = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_aBC) + msg.clearSwiftProtoTesting_Names_aBC() - msg.SwiftProtoTesting_Names_id = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_id) - msg.clearSwiftProtoTesting_Names_id() + msg.SwiftProtoTesting_Names_id = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_id) + msg.clearSwiftProtoTesting_Names_id() - msg.SwiftProtoTesting_Names_idNumber = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_idNumber) - msg.clearSwiftProtoTesting_Names_idNumber() + msg.SwiftProtoTesting_Names_idNumber = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_idNumber) + msg.clearSwiftProtoTesting_Names_idNumber() - msg.SwiftProtoTesting_Names_theIDNumber = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theIDNumber) - msg.clearSwiftProtoTesting_Names_theIDNumber() + msg.SwiftProtoTesting_Names_theIDNumber = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theIDNumber) + msg.clearSwiftProtoTesting_Names_theIDNumber() - msg.SwiftProtoTesting_Names_requestID = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_requestID) - msg.clearSwiftProtoTesting_Names_requestID() - } + msg.SwiftProtoTesting_Names_requestID = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_requestID) + msg.clearSwiftProtoTesting_Names_requestID() + } - func testUppers() { - var msg = SwiftProtoTesting_Names_ExtensionNamingInitialsUppers() + func testUppers() { + var msg = SwiftProtoTesting_Names_ExtensionNamingInitialsUppers() - msg.SwiftProtoTesting_Names_http = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_http) - msg.clearSwiftProtoTesting_Names_http() + msg.SwiftProtoTesting_Names_http = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_http) + msg.clearSwiftProtoTesting_Names_http() - msg.SwiftProtoTesting_Names_httpRequest = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_httpRequest) - msg.clearSwiftProtoTesting_Names_httpRequest() + msg.SwiftProtoTesting_Names_httpRequest = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_httpRequest) + msg.clearSwiftProtoTesting_Names_httpRequest() - msg.SwiftProtoTesting_Names_theHTTPRequest = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theHTTPRequest) - msg.clearSwiftProtoTesting_Names_theHTTPRequest() + msg.SwiftProtoTesting_Names_theHTTPRequest = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theHTTPRequest) + msg.clearSwiftProtoTesting_Names_theHTTPRequest() - msg.SwiftProtoTesting_Names_theHTTP = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theHTTP) - msg.clearSwiftProtoTesting_Names_theHTTP() + msg.SwiftProtoTesting_Names_theHTTP = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theHTTP) + msg.clearSwiftProtoTesting_Names_theHTTP() - msg.SwiftProtoTesting_Names_https = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_https) - msg.clearSwiftProtoTesting_Names_https() + msg.SwiftProtoTesting_Names_https = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_https) + msg.clearSwiftProtoTesting_Names_https() - msg.SwiftProtoTesting_Names_httpsRequest = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_httpsRequest) - msg.clearSwiftProtoTesting_Names_httpsRequest() + msg.SwiftProtoTesting_Names_httpsRequest = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_httpsRequest) + msg.clearSwiftProtoTesting_Names_httpsRequest() - msg.SwiftProtoTesting_Names_theHTTPSRequest = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theHTTPSRequest) - msg.clearSwiftProtoTesting_Names_theHTTPSRequest() + msg.SwiftProtoTesting_Names_theHTTPSRequest = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theHTTPSRequest) + msg.clearSwiftProtoTesting_Names_theHTTPSRequest() - msg.SwiftProtoTesting_Names_theHTTPS = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theHTTPS) - msg.clearSwiftProtoTesting_Names_theHTTPS() + msg.SwiftProtoTesting_Names_theHTTPS = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theHTTPS) + msg.clearSwiftProtoTesting_Names_theHTTPS() - msg.SwiftProtoTesting_Names_url = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_url) - msg.clearSwiftProtoTesting_Names_url() + msg.SwiftProtoTesting_Names_url = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_url) + msg.clearSwiftProtoTesting_Names_url() - msg.SwiftProtoTesting_Names_urlValue = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_urlValue) - msg.clearSwiftProtoTesting_Names_urlValue() + msg.SwiftProtoTesting_Names_urlValue = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_urlValue) + msg.clearSwiftProtoTesting_Names_urlValue() - msg.SwiftProtoTesting_Names_theURLValue = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theURLValue) - msg.clearSwiftProtoTesting_Names_theURLValue() + msg.SwiftProtoTesting_Names_theURLValue = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theURLValue) + msg.clearSwiftProtoTesting_Names_theURLValue() - msg.SwiftProtoTesting_Names_theURL = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theURL) - msg.clearSwiftProtoTesting_Names_theURL() + msg.SwiftProtoTesting_Names_theURL = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theURL) + msg.clearSwiftProtoTesting_Names_theURL() - msg.SwiftProtoTesting_Names_idNumber = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_idNumber) - msg.clearSwiftProtoTesting_Names_idNumber() + msg.SwiftProtoTesting_Names_idNumber = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_idNumber) + msg.clearSwiftProtoTesting_Names_idNumber() - msg.SwiftProtoTesting_Names_theIDNumber = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theIDNumber) - msg.clearSwiftProtoTesting_Names_theIDNumber() + msg.SwiftProtoTesting_Names_theIDNumber = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theIDNumber) + msg.clearSwiftProtoTesting_Names_theIDNumber() - msg.SwiftProtoTesting_Names_requestID = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_requestID) - msg.clearSwiftProtoTesting_Names_requestID() - } - - func testWordCase() { - var msg = SwiftProtoTesting_Names_ExtensionNamingInitialsWordCase() + msg.SwiftProtoTesting_Names_requestID = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_requestID) + msg.clearSwiftProtoTesting_Names_requestID() + } + + func testWordCase() { + var msg = SwiftProtoTesting_Names_ExtensionNamingInitialsWordCase() - msg.SwiftProtoTesting_Names_http = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_http) - msg.clearSwiftProtoTesting_Names_http() + msg.SwiftProtoTesting_Names_http = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_http) + msg.clearSwiftProtoTesting_Names_http() - msg.SwiftProtoTesting_Names_httpRequest = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_httpRequest) - msg.clearSwiftProtoTesting_Names_httpRequest() + msg.SwiftProtoTesting_Names_httpRequest = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_httpRequest) + msg.clearSwiftProtoTesting_Names_httpRequest() - msg.SwiftProtoTesting_Names_theHTTPRequest = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theHTTPRequest) - msg.clearSwiftProtoTesting_Names_theHTTPRequest() + msg.SwiftProtoTesting_Names_theHTTPRequest = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theHTTPRequest) + msg.clearSwiftProtoTesting_Names_theHTTPRequest() - msg.SwiftProtoTesting_Names_theHTTP = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theHTTP) - msg.clearSwiftProtoTesting_Names_theHTTP() + msg.SwiftProtoTesting_Names_theHTTP = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theHTTP) + msg.clearSwiftProtoTesting_Names_theHTTP() - msg.SwiftProtoTesting_Names_https = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_https) - msg.clearSwiftProtoTesting_Names_https() + msg.SwiftProtoTesting_Names_https = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_https) + msg.clearSwiftProtoTesting_Names_https() - msg.SwiftProtoTesting_Names_httpsRequest = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_httpsRequest) - msg.clearSwiftProtoTesting_Names_httpsRequest() + msg.SwiftProtoTesting_Names_httpsRequest = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_httpsRequest) + msg.clearSwiftProtoTesting_Names_httpsRequest() - msg.SwiftProtoTesting_Names_theHTTPSRequest = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theHTTPSRequest) - msg.clearSwiftProtoTesting_Names_theHTTPSRequest() + msg.SwiftProtoTesting_Names_theHTTPSRequest = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theHTTPSRequest) + msg.clearSwiftProtoTesting_Names_theHTTPSRequest() - msg.SwiftProtoTesting_Names_theHTTPS = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theHTTPS) - msg.clearSwiftProtoTesting_Names_theHTTPS() + msg.SwiftProtoTesting_Names_theHTTPS = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theHTTPS) + msg.clearSwiftProtoTesting_Names_theHTTPS() - msg.SwiftProtoTesting_Names_url = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_url) - msg.clearSwiftProtoTesting_Names_url() + msg.SwiftProtoTesting_Names_url = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_url) + msg.clearSwiftProtoTesting_Names_url() - msg.SwiftProtoTesting_Names_urlValue = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_urlValue) - msg.clearSwiftProtoTesting_Names_urlValue() + msg.SwiftProtoTesting_Names_urlValue = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_urlValue) + msg.clearSwiftProtoTesting_Names_urlValue() - msg.SwiftProtoTesting_Names_theURLValue = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theURLValue) - msg.clearSwiftProtoTesting_Names_theURLValue() + msg.SwiftProtoTesting_Names_theURLValue = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theURLValue) + msg.clearSwiftProtoTesting_Names_theURLValue() - msg.SwiftProtoTesting_Names_theURL = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theURL) - msg.clearSwiftProtoTesting_Names_theURL() + msg.SwiftProtoTesting_Names_theURL = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theURL) + msg.clearSwiftProtoTesting_Names_theURL() - msg.SwiftProtoTesting_Names_idNumber = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_idNumber) - msg.clearSwiftProtoTesting_Names_idNumber() + msg.SwiftProtoTesting_Names_idNumber = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_idNumber) + msg.clearSwiftProtoTesting_Names_idNumber() - msg.SwiftProtoTesting_Names_theIDNumber = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theIDNumber) - msg.clearSwiftProtoTesting_Names_theIDNumber() + msg.SwiftProtoTesting_Names_theIDNumber = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_theIDNumber) + msg.clearSwiftProtoTesting_Names_theIDNumber() - msg.SwiftProtoTesting_Names_requestID = 1 - XCTAssertTrue(msg.hasSwiftProtoTesting_Names_requestID) - msg.clearSwiftProtoTesting_Names_requestID() - } + msg.SwiftProtoTesting_Names_requestID = 1 + XCTAssertTrue(msg.hasSwiftProtoTesting_Names_requestID) + msg.clearSwiftProtoTesting_Names_requestID() + } } final class Test_ExtensionNamingInitials_GlobalScoped_NoPrefix: XCTestCase { - func testLowers() { - var msg = SwiftProtoTesting_Names_ExtensionNamingInitialsLowers() + func testLowers() { + var msg = SwiftProtoTesting_Names_ExtensionNamingInitialsLowers() - msg.http = 1 - XCTAssertTrue(msg.hasHTTP) - msg.clearHTTP() + msg.http = 1 + XCTAssertTrue(msg.hasHTTP) + msg.clearHTTP() - msg.httpRequest = 1 - XCTAssertTrue(msg.hasHTTPRequest) - msg.clearHTTPRequest() + msg.httpRequest = 1 + XCTAssertTrue(msg.hasHTTPRequest) + msg.clearHTTPRequest() - msg.theHTTPRequest = 1 - XCTAssertTrue(msg.hasTheHTTPRequest) - msg.clearTheHTTPRequest() + msg.theHTTPRequest = 1 + XCTAssertTrue(msg.hasTheHTTPRequest) + msg.clearTheHTTPRequest() - msg.theHTTP = 1 - XCTAssertTrue(msg.hasTheHTTP) - msg.clearTheHTTP() + msg.theHTTP = 1 + XCTAssertTrue(msg.hasTheHTTP) + msg.clearTheHTTP() - msg.https = 1 - XCTAssertTrue(msg.hasHTTPS) - msg.clearHTTPS() + msg.https = 1 + XCTAssertTrue(msg.hasHTTPS) + msg.clearHTTPS() - msg.httpsRequest = 1 - XCTAssertTrue(msg.hasHTTPSRequest) - msg.clearHTTPSRequest() + msg.httpsRequest = 1 + XCTAssertTrue(msg.hasHTTPSRequest) + msg.clearHTTPSRequest() - msg.theHTTPSRequest = 1 - XCTAssertTrue(msg.hasTheHTTPSRequest) - msg.clearTheHTTPSRequest() + msg.theHTTPSRequest = 1 + XCTAssertTrue(msg.hasTheHTTPSRequest) + msg.clearTheHTTPSRequest() - msg.theHTTPS = 1 - XCTAssertTrue(msg.hasTheHTTPS) - msg.clearTheHTTPS() + msg.theHTTPS = 1 + XCTAssertTrue(msg.hasTheHTTPS) + msg.clearTheHTTPS() - msg.url = 1 - XCTAssertTrue(msg.hasURL) - msg.clearURL() + msg.url = 1 + XCTAssertTrue(msg.hasURL) + msg.clearURL() - msg.urlValue = 1 - XCTAssertTrue(msg.hasURLValue) - msg.clearURLValue() + msg.urlValue = 1 + XCTAssertTrue(msg.hasURLValue) + msg.clearURLValue() - msg.theURLValue = 1 - XCTAssertTrue(msg.hasTheURLValue) - msg.clearTheURLValue() + msg.theURLValue = 1 + XCTAssertTrue(msg.hasTheURLValue) + msg.clearTheURLValue() - msg.theURL = 1 - XCTAssertTrue(msg.hasTheURL) - msg.clearTheURL() + msg.theURL = 1 + XCTAssertTrue(msg.hasTheURL) + msg.clearTheURL() - msg.aBC = 1 - XCTAssertTrue(msg.hasABC) - msg.clearABC() + msg.aBC = 1 + XCTAssertTrue(msg.hasABC) + msg.clearABC() - msg.id = 1 - XCTAssertTrue(msg.hasID) - msg.clearID() + msg.id = 1 + XCTAssertTrue(msg.hasID) + msg.clearID() - msg.idNumber = 1 - XCTAssertTrue(msg.hasIDNumber) - msg.clearIDNumber() + msg.idNumber = 1 + XCTAssertTrue(msg.hasIDNumber) + msg.clearIDNumber() - msg.theIDNumber = 1 - XCTAssertTrue(msg.hasTheIDNumber) - msg.clearTheIDNumber() + msg.theIDNumber = 1 + XCTAssertTrue(msg.hasTheIDNumber) + msg.clearTheIDNumber() - msg.requestID = 1 - XCTAssertTrue(msg.hasRequestID) - msg.clearRequestID() - } + msg.requestID = 1 + XCTAssertTrue(msg.hasRequestID) + msg.clearRequestID() + } - func testUppers() { - var msg = SwiftProtoTesting_Names_ExtensionNamingInitialsUppers() + func testUppers() { + var msg = SwiftProtoTesting_Names_ExtensionNamingInitialsUppers() - msg.http = 1 - XCTAssertTrue(msg.hasHTTP) - msg.clearHTTP() + msg.http = 1 + XCTAssertTrue(msg.hasHTTP) + msg.clearHTTP() - msg.httpRequest = 1 - XCTAssertTrue(msg.hasHTTPRequest) - msg.clearHTTPRequest() + msg.httpRequest = 1 + XCTAssertTrue(msg.hasHTTPRequest) + msg.clearHTTPRequest() - msg.theHTTPRequest = 1 - XCTAssertTrue(msg.hasTheHTTPRequest) - msg.clearTheHTTPRequest() + msg.theHTTPRequest = 1 + XCTAssertTrue(msg.hasTheHTTPRequest) + msg.clearTheHTTPRequest() - msg.theHTTP = 1 - XCTAssertTrue(msg.hasTheHTTP) - msg.clearTheHTTP() + msg.theHTTP = 1 + XCTAssertTrue(msg.hasTheHTTP) + msg.clearTheHTTP() - msg.https = 1 - XCTAssertTrue(msg.hasHTTPS) - msg.clearHTTPS() + msg.https = 1 + XCTAssertTrue(msg.hasHTTPS) + msg.clearHTTPS() - msg.httpsRequest = 1 - XCTAssertTrue(msg.hasHTTPSRequest) - msg.clearHTTPSRequest() + msg.httpsRequest = 1 + XCTAssertTrue(msg.hasHTTPSRequest) + msg.clearHTTPSRequest() - msg.theHTTPSRequest = 1 - XCTAssertTrue(msg.hasTheHTTPSRequest) - msg.clearTheHTTPSRequest() + msg.theHTTPSRequest = 1 + XCTAssertTrue(msg.hasTheHTTPSRequest) + msg.clearTheHTTPSRequest() - msg.theHTTPS = 1 - XCTAssertTrue(msg.hasTheHTTPS) - msg.clearTheHTTPS() + msg.theHTTPS = 1 + XCTAssertTrue(msg.hasTheHTTPS) + msg.clearTheHTTPS() - msg.url = 1 - XCTAssertTrue(msg.hasURL) - msg.clearURL() + msg.url = 1 + XCTAssertTrue(msg.hasURL) + msg.clearURL() - msg.urlValue = 1 - XCTAssertTrue(msg.hasURLValue) - msg.clearURLValue() + msg.urlValue = 1 + XCTAssertTrue(msg.hasURLValue) + msg.clearURLValue() - msg.theURLValue = 1 - XCTAssertTrue(msg.hasTheURLValue) - msg.clearTheURLValue() + msg.theURLValue = 1 + XCTAssertTrue(msg.hasTheURLValue) + msg.clearTheURLValue() - msg.theURL = 1 - XCTAssertTrue(msg.hasTheURL) - msg.clearTheURL() + msg.theURL = 1 + XCTAssertTrue(msg.hasTheURL) + msg.clearTheURL() - msg.id = 1 - XCTAssertTrue(msg.hasID) - msg.clearID() + msg.id = 1 + XCTAssertTrue(msg.hasID) + msg.clearID() - msg.idNumber = 1 - XCTAssertTrue(msg.hasIDNumber) - msg.clearIDNumber() + msg.idNumber = 1 + XCTAssertTrue(msg.hasIDNumber) + msg.clearIDNumber() - msg.theIDNumber = 1 - XCTAssertTrue(msg.hasTheIDNumber) - msg.clearTheIDNumber() + msg.theIDNumber = 1 + XCTAssertTrue(msg.hasTheIDNumber) + msg.clearTheIDNumber() - msg.requestID = 1 - XCTAssertTrue(msg.hasRequestID) - msg.clearRequestID() - } - - func testWordCase() { - var msg = SwiftProtoTesting_Names_ExtensionNamingInitialsWordCase() + msg.requestID = 1 + XCTAssertTrue(msg.hasRequestID) + msg.clearRequestID() + } + + func testWordCase() { + var msg = SwiftProtoTesting_Names_ExtensionNamingInitialsWordCase() - msg.http = 1 - XCTAssertTrue(msg.hasHTTP) - msg.clearHTTP() + msg.http = 1 + XCTAssertTrue(msg.hasHTTP) + msg.clearHTTP() - msg.httpRequest = 1 - XCTAssertTrue(msg.hasHTTPRequest) - msg.clearHTTPRequest() + msg.httpRequest = 1 + XCTAssertTrue(msg.hasHTTPRequest) + msg.clearHTTPRequest() - msg.theHTTPRequest = 1 - XCTAssertTrue(msg.hasTheHTTPRequest) - msg.clearTheHTTPRequest() + msg.theHTTPRequest = 1 + XCTAssertTrue(msg.hasTheHTTPRequest) + msg.clearTheHTTPRequest() - msg.theHTTP = 1 - XCTAssertTrue(msg.hasTheHTTP) - msg.clearTheHTTP() + msg.theHTTP = 1 + XCTAssertTrue(msg.hasTheHTTP) + msg.clearTheHTTP() - msg.https = 1 - XCTAssertTrue(msg.hasHTTPS) - msg.clearHTTPS() + msg.https = 1 + XCTAssertTrue(msg.hasHTTPS) + msg.clearHTTPS() - msg.httpsRequest = 1 - XCTAssertTrue(msg.hasHTTPSRequest) - msg.clearHTTPSRequest() + msg.httpsRequest = 1 + XCTAssertTrue(msg.hasHTTPSRequest) + msg.clearHTTPSRequest() - msg.theHTTPSRequest = 1 - XCTAssertTrue(msg.hasTheHTTPSRequest) - msg.clearTheHTTPSRequest() + msg.theHTTPSRequest = 1 + XCTAssertTrue(msg.hasTheHTTPSRequest) + msg.clearTheHTTPSRequest() - msg.theHTTPS = 1 - XCTAssertTrue(msg.hasTheHTTPS) - msg.clearTheHTTPS() + msg.theHTTPS = 1 + XCTAssertTrue(msg.hasTheHTTPS) + msg.clearTheHTTPS() - msg.url = 1 - XCTAssertTrue(msg.hasURL) - msg.clearURL() + msg.url = 1 + XCTAssertTrue(msg.hasURL) + msg.clearURL() - msg.urlValue = 1 - XCTAssertTrue(msg.hasURLValue) - msg.clearURLValue() + msg.urlValue = 1 + XCTAssertTrue(msg.hasURLValue) + msg.clearURLValue() - msg.theURLValue = 1 - XCTAssertTrue(msg.hasTheURLValue) - msg.clearTheURLValue() + msg.theURLValue = 1 + XCTAssertTrue(msg.hasTheURLValue) + msg.clearTheURLValue() - msg.theURL = 1 - XCTAssertTrue(msg.hasTheURL) - msg.clearTheURL() + msg.theURL = 1 + XCTAssertTrue(msg.hasTheURL) + msg.clearTheURL() - msg.id = 1 - XCTAssertTrue(msg.hasID) - msg.clearID() + msg.id = 1 + XCTAssertTrue(msg.hasID) + msg.clearID() - msg.idNumber = 1 - XCTAssertTrue(msg.hasIDNumber) - msg.clearIDNumber() + msg.idNumber = 1 + XCTAssertTrue(msg.hasIDNumber) + msg.clearIDNumber() - msg.theIDNumber = 1 - XCTAssertTrue(msg.hasTheIDNumber) - msg.clearTheIDNumber() + msg.theIDNumber = 1 + XCTAssertTrue(msg.hasTheIDNumber) + msg.clearTheIDNumber() - msg.requestID = 1 - XCTAssertTrue(msg.hasRequestID) - msg.clearRequestID() - } + msg.requestID = 1 + XCTAssertTrue(msg.hasRequestID) + msg.clearRequestID() + } } final class Test_ValidIdentifiers: XCTestCase { - func testFieldNames() { - let msg = SwiftProtoTesting_Names_ValidIdentifiers() - XCTAssertEqual(msg._1Field, 0) - XCTAssertFalse(msg.has_1Field) - XCTAssertEqual(msg.field, 0) - XCTAssertFalse(msg.hasField) - XCTAssertEqual(msg._3Field3, 0) - XCTAssertFalse(msg.has_3Field3) - } - - func testOneofNames() { - var msg = SwiftProtoTesting_Names_ValidIdentifiers() - XCTAssertEqual(msg._2Of, nil) - - XCTAssertEqual(msg._4, 0) - XCTAssertEqual(msg._5Field, 0) - - msg._2Of = ._4(20) - - XCTAssertEqual(msg._2Of, SwiftProtoTesting_Names_ValidIdentifiers.OneOf__2Of._4(20)) - XCTAssertEqual(msg._4, 20) - } - - func testEnumCaseNames() { - var msg = SwiftProtoTesting_Names_ValidIdentifiers() - msg.enumField = .testEnum0 - msg.enumField = .first - msg.enumField = ._2 - msg.enumField = ._3Value - } + func testFieldNames() { + let msg = SwiftProtoTesting_Names_ValidIdentifiers() + XCTAssertEqual(msg._1Field, 0) + XCTAssertFalse(msg.has_1Field) + XCTAssertEqual(msg.field, 0) + XCTAssertFalse(msg.hasField) + XCTAssertEqual(msg._3Field3, 0) + XCTAssertFalse(msg.has_3Field3) + } + + func testOneofNames() { + var msg = SwiftProtoTesting_Names_ValidIdentifiers() + XCTAssertEqual(msg._2Of, nil) + + XCTAssertEqual(msg._4, 0) + XCTAssertEqual(msg._5Field, 0) + + msg._2Of = ._4(20) + + XCTAssertEqual(msg._2Of, SwiftProtoTesting_Names_ValidIdentifiers.OneOf__2Of._4(20)) + XCTAssertEqual(msg._4, 20) + } + + func testEnumCaseNames() { + var msg = SwiftProtoTesting_Names_ValidIdentifiers() + msg.enumField = .testEnum0 + msg.enumField = .first + msg.enumField = ._2 + msg.enumField = ._3Value + } } diff --git a/Tests/SwiftProtobufTests/Test_OneofFields_Access_Proto2.swift b/Tests/SwiftProtobufTests/Test_OneofFields_Access_Proto2.swift index 425d029dd..316c5ed0d 100644 --- a/Tests/SwiftProtobufTests/Test_OneofFields_Access_Proto2.swift +++ b/Tests/SwiftProtobufTests/Test_OneofFields_Access_Proto2.swift @@ -12,8 +12,8 @@ /// // ----------------------------------------------------------------------------- -import XCTest import Foundation +import XCTest // NOTE: The generator changes what is generated based on the number/types // of fields (using a nested storage class or not), to be completel, all @@ -22,554 +22,554 @@ import Foundation final class Test_OneofFields_Access_Proto2: XCTestCase { - // Accessing one field. - // - Returns default - // - Accepts/Captures value - // - Resets - // - Accepts/Captures default value - - func testOneofInt32() { - var msg = SwiftProtoTesting_Message2() - XCTAssertEqual(msg.oneofInt32, 100) - XCTAssertNil(msg.o) - msg.oneofInt32 = 51 - XCTAssertEqual(msg.oneofInt32, 51) - XCTAssertEqual(msg.o, .oneofInt32(51)) - msg.o = nil - XCTAssertEqual(msg.oneofInt32, 100) - XCTAssertNil(msg.o) - msg.oneofInt32 = 100 - XCTAssertEqual(msg.oneofInt32, 100) - XCTAssertEqual(msg.o, .oneofInt32(100)) - } - - func testOneofInt64() { - var msg = SwiftProtoTesting_Message2() - XCTAssertEqual(msg.oneofInt64, 101) - XCTAssertNil(msg.o) - msg.oneofInt64 = 52 - XCTAssertEqual(msg.oneofInt64, 52) - XCTAssertEqual(msg.o, .oneofInt64(52)) - msg.o = nil - XCTAssertEqual(msg.oneofInt64, 101) - XCTAssertNil(msg.o) - msg.oneofInt64 = 101 - XCTAssertEqual(msg.oneofInt64, 101) - XCTAssertEqual(msg.o, .oneofInt64(101)) - } + // Accessing one field. + // - Returns default + // - Accepts/Captures value + // - Resets + // - Accepts/Captures default value - func testOneofUint32() { - var msg = SwiftProtoTesting_Message2() - XCTAssertEqual(msg.oneofUint32, 102) - XCTAssertNil(msg.o) - msg.oneofUint32 = 53 - XCTAssertEqual(msg.oneofUint32, 53) - XCTAssertEqual(msg.o, .oneofUint32(53)) - msg.o = nil - XCTAssertEqual(msg.oneofUint32, 102) - XCTAssertNil(msg.o) - msg.oneofUint32 = 102 - XCTAssertEqual(msg.oneofUint32, 102) - XCTAssertEqual(msg.o, .oneofUint32(102)) - } - - func testOneofUint64() { - var msg = SwiftProtoTesting_Message2() - XCTAssertEqual(msg.oneofUint64, 103) - XCTAssertNil(msg.o) - msg.oneofUint64 = 54 - XCTAssertEqual(msg.oneofUint64, 54) - XCTAssertEqual(msg.o, .oneofUint64(54)) - msg.o = nil - XCTAssertEqual(msg.oneofUint64, 103) - XCTAssertNil(msg.o) - msg.oneofUint64 = 103 - XCTAssertEqual(msg.oneofUint64, 103) - XCTAssertEqual(msg.o, .oneofUint64(103)) - } + func testOneofInt32() { + var msg = SwiftProtoTesting_Message2() + XCTAssertEqual(msg.oneofInt32, 100) + XCTAssertNil(msg.o) + msg.oneofInt32 = 51 + XCTAssertEqual(msg.oneofInt32, 51) + XCTAssertEqual(msg.o, .oneofInt32(51)) + msg.o = nil + XCTAssertEqual(msg.oneofInt32, 100) + XCTAssertNil(msg.o) + msg.oneofInt32 = 100 + XCTAssertEqual(msg.oneofInt32, 100) + XCTAssertEqual(msg.o, .oneofInt32(100)) + } - func testOneofSint32() { - var msg = SwiftProtoTesting_Message2() - XCTAssertEqual(msg.oneofSint32, 104) - XCTAssertNil(msg.o) - msg.oneofSint32 = 55 - XCTAssertEqual(msg.oneofSint32, 55) - XCTAssertEqual(msg.o, .oneofSint32(55)) - msg.o = nil - XCTAssertEqual(msg.oneofSint32, 104) - XCTAssertNil(msg.o) - msg.oneofSint32 = 104 - XCTAssertEqual(msg.oneofSint32, 104) - XCTAssertEqual(msg.o, .oneofSint32(104)) - } + func testOneofInt64() { + var msg = SwiftProtoTesting_Message2() + XCTAssertEqual(msg.oneofInt64, 101) + XCTAssertNil(msg.o) + msg.oneofInt64 = 52 + XCTAssertEqual(msg.oneofInt64, 52) + XCTAssertEqual(msg.o, .oneofInt64(52)) + msg.o = nil + XCTAssertEqual(msg.oneofInt64, 101) + XCTAssertNil(msg.o) + msg.oneofInt64 = 101 + XCTAssertEqual(msg.oneofInt64, 101) + XCTAssertEqual(msg.o, .oneofInt64(101)) + } - func testOneofSint64() { - var msg = SwiftProtoTesting_Message2() - XCTAssertEqual(msg.oneofSint64, 105) - XCTAssertNil(msg.o) - msg.oneofSint64 = 56 - XCTAssertEqual(msg.oneofSint64, 56) - XCTAssertEqual(msg.o, .oneofSint64(56)) - msg.o = nil - XCTAssertEqual(msg.oneofSint64, 105) - XCTAssertNil(msg.o) - msg.oneofSint64 = 105 - XCTAssertEqual(msg.oneofSint64, 105) - XCTAssertEqual(msg.o, .oneofSint64(105)) - } + func testOneofUint32() { + var msg = SwiftProtoTesting_Message2() + XCTAssertEqual(msg.oneofUint32, 102) + XCTAssertNil(msg.o) + msg.oneofUint32 = 53 + XCTAssertEqual(msg.oneofUint32, 53) + XCTAssertEqual(msg.o, .oneofUint32(53)) + msg.o = nil + XCTAssertEqual(msg.oneofUint32, 102) + XCTAssertNil(msg.o) + msg.oneofUint32 = 102 + XCTAssertEqual(msg.oneofUint32, 102) + XCTAssertEqual(msg.o, .oneofUint32(102)) + } - func testOneofFixed32() { - var msg = SwiftProtoTesting_Message2() - XCTAssertEqual(msg.oneofFixed32, 106) - XCTAssertNil(msg.o) - msg.oneofFixed32 = 57 - XCTAssertEqual(msg.oneofFixed32, 57) - XCTAssertEqual(msg.o, .oneofFixed32(57)) - msg.o = nil - XCTAssertEqual(msg.oneofFixed32, 106) - XCTAssertNil(msg.o) - msg.oneofFixed32 = 106 - XCTAssertEqual(msg.oneofFixed32, 106) - XCTAssertEqual(msg.o, .oneofFixed32(106)) - } + func testOneofUint64() { + var msg = SwiftProtoTesting_Message2() + XCTAssertEqual(msg.oneofUint64, 103) + XCTAssertNil(msg.o) + msg.oneofUint64 = 54 + XCTAssertEqual(msg.oneofUint64, 54) + XCTAssertEqual(msg.o, .oneofUint64(54)) + msg.o = nil + XCTAssertEqual(msg.oneofUint64, 103) + XCTAssertNil(msg.o) + msg.oneofUint64 = 103 + XCTAssertEqual(msg.oneofUint64, 103) + XCTAssertEqual(msg.o, .oneofUint64(103)) + } - func testOneofFixed64() { - var msg = SwiftProtoTesting_Message2() - XCTAssertEqual(msg.oneofFixed64, 107) - XCTAssertNil(msg.o) - msg.oneofFixed64 = 58 - XCTAssertEqual(msg.oneofFixed64, 58) - XCTAssertEqual(msg.o, .oneofFixed64(58)) - msg.o = nil - XCTAssertEqual(msg.oneofFixed64, 107) - XCTAssertNil(msg.o) - msg.oneofFixed64 = 107 - XCTAssertEqual(msg.oneofFixed64, 107) - XCTAssertEqual(msg.o, .oneofFixed64(107)) - } + func testOneofSint32() { + var msg = SwiftProtoTesting_Message2() + XCTAssertEqual(msg.oneofSint32, 104) + XCTAssertNil(msg.o) + msg.oneofSint32 = 55 + XCTAssertEqual(msg.oneofSint32, 55) + XCTAssertEqual(msg.o, .oneofSint32(55)) + msg.o = nil + XCTAssertEqual(msg.oneofSint32, 104) + XCTAssertNil(msg.o) + msg.oneofSint32 = 104 + XCTAssertEqual(msg.oneofSint32, 104) + XCTAssertEqual(msg.o, .oneofSint32(104)) + } - func testOneofSfixed32() { - var msg = SwiftProtoTesting_Message2() - XCTAssertEqual(msg.oneofSfixed32, 108) - XCTAssertNil(msg.o) - msg.oneofSfixed32 = 59 - XCTAssertEqual(msg.oneofSfixed32, 59) - XCTAssertEqual(msg.o, .oneofSfixed32(59)) - msg.o = nil - XCTAssertEqual(msg.oneofSfixed32, 108) - XCTAssertNil(msg.o) - msg.oneofSfixed32 = 108 - XCTAssertEqual(msg.oneofSfixed32, 108) - XCTAssertEqual(msg.o, .oneofSfixed32(108)) - } + func testOneofSint64() { + var msg = SwiftProtoTesting_Message2() + XCTAssertEqual(msg.oneofSint64, 105) + XCTAssertNil(msg.o) + msg.oneofSint64 = 56 + XCTAssertEqual(msg.oneofSint64, 56) + XCTAssertEqual(msg.o, .oneofSint64(56)) + msg.o = nil + XCTAssertEqual(msg.oneofSint64, 105) + XCTAssertNil(msg.o) + msg.oneofSint64 = 105 + XCTAssertEqual(msg.oneofSint64, 105) + XCTAssertEqual(msg.o, .oneofSint64(105)) + } - func testOneofSfixed64() { - var msg = SwiftProtoTesting_Message2() - XCTAssertEqual(msg.oneofSfixed64, 109) - XCTAssertNil(msg.o) - msg.oneofSfixed64 = 60 - XCTAssertEqual(msg.oneofSfixed64, 60) - XCTAssertEqual(msg.o, .oneofSfixed64(60)) - msg.o = nil - XCTAssertEqual(msg.oneofSfixed64, 109) - XCTAssertNil(msg.o) - msg.oneofSfixed64 = 109 - XCTAssertEqual(msg.oneofSfixed64, 109) - XCTAssertEqual(msg.o, .oneofSfixed64(109)) - } + func testOneofFixed32() { + var msg = SwiftProtoTesting_Message2() + XCTAssertEqual(msg.oneofFixed32, 106) + XCTAssertNil(msg.o) + msg.oneofFixed32 = 57 + XCTAssertEqual(msg.oneofFixed32, 57) + XCTAssertEqual(msg.o, .oneofFixed32(57)) + msg.o = nil + XCTAssertEqual(msg.oneofFixed32, 106) + XCTAssertNil(msg.o) + msg.oneofFixed32 = 106 + XCTAssertEqual(msg.oneofFixed32, 106) + XCTAssertEqual(msg.o, .oneofFixed32(106)) + } - func testOneofFloat() { - var msg = SwiftProtoTesting_Message2() - XCTAssertEqual(msg.oneofFloat, 110.0) - XCTAssertNil(msg.o) - msg.oneofFloat = 61.0 - XCTAssertEqual(msg.oneofFloat, 61.0) - XCTAssertEqual(msg.o, .oneofFloat(61.0)) - msg.o = nil - XCTAssertEqual(msg.oneofFloat, 110.0) - XCTAssertNil(msg.o) - msg.oneofFloat = 110.0 - XCTAssertEqual(msg.oneofFloat, 110.0) - XCTAssertEqual(msg.o, .oneofFloat(110.0)) - } + func testOneofFixed64() { + var msg = SwiftProtoTesting_Message2() + XCTAssertEqual(msg.oneofFixed64, 107) + XCTAssertNil(msg.o) + msg.oneofFixed64 = 58 + XCTAssertEqual(msg.oneofFixed64, 58) + XCTAssertEqual(msg.o, .oneofFixed64(58)) + msg.o = nil + XCTAssertEqual(msg.oneofFixed64, 107) + XCTAssertNil(msg.o) + msg.oneofFixed64 = 107 + XCTAssertEqual(msg.oneofFixed64, 107) + XCTAssertEqual(msg.o, .oneofFixed64(107)) + } - func testOneofDouble() { - var msg = SwiftProtoTesting_Message2() - XCTAssertEqual(msg.oneofDouble, 111.0) - XCTAssertNil(msg.o) - msg.oneofDouble = 62.0 - XCTAssertEqual(msg.oneofDouble, 62.0) - XCTAssertEqual(msg.o, .oneofDouble(62.0)) - msg.o = nil - XCTAssertEqual(msg.oneofDouble, 111.0) - XCTAssertNil(msg.o) - msg.oneofDouble = 111.0 - XCTAssertEqual(msg.oneofDouble, 111.0) - XCTAssertEqual(msg.o, .oneofDouble(111.0)) - } + func testOneofSfixed32() { + var msg = SwiftProtoTesting_Message2() + XCTAssertEqual(msg.oneofSfixed32, 108) + XCTAssertNil(msg.o) + msg.oneofSfixed32 = 59 + XCTAssertEqual(msg.oneofSfixed32, 59) + XCTAssertEqual(msg.o, .oneofSfixed32(59)) + msg.o = nil + XCTAssertEqual(msg.oneofSfixed32, 108) + XCTAssertNil(msg.o) + msg.oneofSfixed32 = 108 + XCTAssertEqual(msg.oneofSfixed32, 108) + XCTAssertEqual(msg.o, .oneofSfixed32(108)) + } - func testOneofBool() { - var msg = SwiftProtoTesting_Message2() - XCTAssertEqual(msg.oneofBool, true) - XCTAssertNil(msg.o) - msg.oneofBool = false - XCTAssertEqual(msg.oneofBool, false) - XCTAssertEqual(msg.o, .oneofBool(false)) - msg.o = nil - XCTAssertEqual(msg.oneofBool, true) - XCTAssertNil(msg.o) - msg.oneofBool = true - XCTAssertEqual(msg.oneofBool, true) - XCTAssertEqual(msg.o, .oneofBool(true)) - } + func testOneofSfixed64() { + var msg = SwiftProtoTesting_Message2() + XCTAssertEqual(msg.oneofSfixed64, 109) + XCTAssertNil(msg.o) + msg.oneofSfixed64 = 60 + XCTAssertEqual(msg.oneofSfixed64, 60) + XCTAssertEqual(msg.o, .oneofSfixed64(60)) + msg.o = nil + XCTAssertEqual(msg.oneofSfixed64, 109) + XCTAssertNil(msg.o) + msg.oneofSfixed64 = 109 + XCTAssertEqual(msg.oneofSfixed64, 109) + XCTAssertEqual(msg.o, .oneofSfixed64(109)) + } - func testOneofString() { - var msg = SwiftProtoTesting_Message2() - XCTAssertEqual(msg.oneofString, "string") - XCTAssertNil(msg.o) - msg.oneofString = "64" - XCTAssertEqual(msg.oneofString, "64") - XCTAssertEqual(msg.o, .oneofString("64")) - msg.o = nil - XCTAssertEqual(msg.oneofString, "string") - XCTAssertNil(msg.o) - msg.oneofString = "string" - XCTAssertEqual(msg.oneofString, "string") - XCTAssertEqual(msg.o, .oneofString("string")) - } + func testOneofFloat() { + var msg = SwiftProtoTesting_Message2() + XCTAssertEqual(msg.oneofFloat, 110.0) + XCTAssertNil(msg.o) + msg.oneofFloat = 61.0 + XCTAssertEqual(msg.oneofFloat, 61.0) + XCTAssertEqual(msg.o, .oneofFloat(61.0)) + msg.o = nil + XCTAssertEqual(msg.oneofFloat, 110.0) + XCTAssertNil(msg.o) + msg.oneofFloat = 110.0 + XCTAssertEqual(msg.oneofFloat, 110.0) + XCTAssertEqual(msg.o, .oneofFloat(110.0)) + } - func testOneofBytes() { - var msg = SwiftProtoTesting_Message2() - XCTAssertEqual(msg.oneofBytes, "data".data(using: .utf8)) - XCTAssertNil(msg.o) - msg.oneofBytes = Data([65]) - XCTAssertEqual(msg.oneofBytes, Data([65])) - XCTAssertEqual(msg.o, .oneofBytes(Data([65]))) - msg.o = nil - XCTAssertEqual(msg.oneofBytes, "data".data(using: .utf8)) - XCTAssertNil(msg.o) - msg.oneofBytes = "data".data(using: .utf8)! - XCTAssertEqual(msg.oneofBytes, "data".data(using: .utf8)) - XCTAssertEqual(msg.o, .oneofBytes("data".data(using: .utf8)!)) - } + func testOneofDouble() { + var msg = SwiftProtoTesting_Message2() + XCTAssertEqual(msg.oneofDouble, 111.0) + XCTAssertNil(msg.o) + msg.oneofDouble = 62.0 + XCTAssertEqual(msg.oneofDouble, 62.0) + XCTAssertEqual(msg.o, .oneofDouble(62.0)) + msg.o = nil + XCTAssertEqual(msg.oneofDouble, 111.0) + XCTAssertNil(msg.o) + msg.oneofDouble = 111.0 + XCTAssertEqual(msg.oneofDouble, 111.0) + XCTAssertEqual(msg.o, .oneofDouble(111.0)) + } - func testOneofGroup() { - var msg = SwiftProtoTesting_Message2() - XCTAssertEqual(msg.oneofGroup.a, 116) - XCTAssertFalse(msg.oneofGroup.hasA) - XCTAssertNil(msg.o) - var grp = SwiftProtoTesting_Message2.OneofGroup() - grp.a = 66 - msg.oneofGroup = grp - XCTAssertEqual(msg.oneofGroup.a, 66) - XCTAssertTrue(msg.oneofGroup.hasA) - XCTAssertEqual(msg.oneofGroup, grp) - if case .oneofGroup(let v)? = msg.o { - XCTAssertTrue(v.hasA) - XCTAssertEqual(v.a, 66) - XCTAssertEqual(v, grp) - } else { - XCTFail("Wasn't the right case") + func testOneofBool() { + var msg = SwiftProtoTesting_Message2() + XCTAssertEqual(msg.oneofBool, true) + XCTAssertNil(msg.o) + msg.oneofBool = false + XCTAssertEqual(msg.oneofBool, false) + XCTAssertEqual(msg.o, .oneofBool(false)) + msg.o = nil + XCTAssertEqual(msg.oneofBool, true) + XCTAssertNil(msg.o) + msg.oneofBool = true + XCTAssertEqual(msg.oneofBool, true) + XCTAssertEqual(msg.o, .oneofBool(true)) } - msg.o = nil - XCTAssertEqual(msg.oneofGroup.a, 116) - XCTAssertFalse(msg.oneofGroup.hasA) - XCTAssertNil(msg.o) - // Default within the group - var grp2 = SwiftProtoTesting_Message2.OneofGroup() - grp2.a = 116 - msg.oneofGroup = grp2 - XCTAssertEqual(msg.oneofGroup.a, 116) - XCTAssertTrue(msg.oneofGroup.hasA) - XCTAssertEqual(msg.oneofGroup, grp2) - if case .oneofGroup(let v)? = msg.o { - XCTAssertTrue(v.hasA) - XCTAssertEqual(v.a, 116) - XCTAssertEqual(v, grp2) - } else { - XCTFail("Wasn't the right case") + + func testOneofString() { + var msg = SwiftProtoTesting_Message2() + XCTAssertEqual(msg.oneofString, "string") + XCTAssertNil(msg.o) + msg.oneofString = "64" + XCTAssertEqual(msg.oneofString, "64") + XCTAssertEqual(msg.o, .oneofString("64")) + msg.o = nil + XCTAssertEqual(msg.oneofString, "string") + XCTAssertNil(msg.o) + msg.oneofString = "string" + XCTAssertEqual(msg.oneofString, "string") + XCTAssertEqual(msg.o, .oneofString("string")) } - msg.o = nil - // Group with nothing set. - let grp3 = SwiftProtoTesting_Message2.OneofGroup() - msg.oneofGroup = grp3 - XCTAssertEqual(msg.oneofGroup.a, 116) - XCTAssertFalse(msg.oneofGroup.hasA) - XCTAssertEqual(msg.oneofGroup, grp3) - if case .oneofGroup(let v)? = msg.o { - XCTAssertFalse(v.hasA) - XCTAssertEqual(v.a, 116) - XCTAssertEqual(v, grp3) - } else { - XCTFail("Wasn't the right case") + + func testOneofBytes() { + var msg = SwiftProtoTesting_Message2() + XCTAssertEqual(msg.oneofBytes, "data".data(using: .utf8)) + XCTAssertNil(msg.o) + msg.oneofBytes = Data([65]) + XCTAssertEqual(msg.oneofBytes, Data([65])) + XCTAssertEqual(msg.o, .oneofBytes(Data([65]))) + msg.o = nil + XCTAssertEqual(msg.oneofBytes, "data".data(using: .utf8)) + XCTAssertNil(msg.o) + msg.oneofBytes = "data".data(using: .utf8)! + XCTAssertEqual(msg.oneofBytes, "data".data(using: .utf8)) + XCTAssertEqual(msg.o, .oneofBytes("data".data(using: .utf8)!)) } - } - func testOneofMessage() { - var msg = SwiftProtoTesting_Message2() - XCTAssertEqual(msg.oneofMessage.optionalInt32, 0) - XCTAssertNil(msg.o) - var subMsg = SwiftProtoTesting_Message2() - subMsg.optionalInt32 = 66 - msg.oneofMessage = subMsg - XCTAssertEqual(msg.oneofMessage.optionalInt32, 66) - XCTAssertEqual(msg.oneofMessage, subMsg) - if case .oneofMessage(let v)? = msg.o { - XCTAssertEqual(v.optionalInt32, 66) - XCTAssertEqual(v, subMsg) - } else { - XCTFail("Wasn't the right case") + func testOneofGroup() { + var msg = SwiftProtoTesting_Message2() + XCTAssertEqual(msg.oneofGroup.a, 116) + XCTAssertFalse(msg.oneofGroup.hasA) + XCTAssertNil(msg.o) + var grp = SwiftProtoTesting_Message2.OneofGroup() + grp.a = 66 + msg.oneofGroup = grp + XCTAssertEqual(msg.oneofGroup.a, 66) + XCTAssertTrue(msg.oneofGroup.hasA) + XCTAssertEqual(msg.oneofGroup, grp) + if case .oneofGroup(let v)? = msg.o { + XCTAssertTrue(v.hasA) + XCTAssertEqual(v.a, 66) + XCTAssertEqual(v, grp) + } else { + XCTFail("Wasn't the right case") + } + msg.o = nil + XCTAssertEqual(msg.oneofGroup.a, 116) + XCTAssertFalse(msg.oneofGroup.hasA) + XCTAssertNil(msg.o) + // Default within the group + var grp2 = SwiftProtoTesting_Message2.OneofGroup() + grp2.a = 116 + msg.oneofGroup = grp2 + XCTAssertEqual(msg.oneofGroup.a, 116) + XCTAssertTrue(msg.oneofGroup.hasA) + XCTAssertEqual(msg.oneofGroup, grp2) + if case .oneofGroup(let v)? = msg.o { + XCTAssertTrue(v.hasA) + XCTAssertEqual(v.a, 116) + XCTAssertEqual(v, grp2) + } else { + XCTFail("Wasn't the right case") + } + msg.o = nil + // Group with nothing set. + let grp3 = SwiftProtoTesting_Message2.OneofGroup() + msg.oneofGroup = grp3 + XCTAssertEqual(msg.oneofGroup.a, 116) + XCTAssertFalse(msg.oneofGroup.hasA) + XCTAssertEqual(msg.oneofGroup, grp3) + if case .oneofGroup(let v)? = msg.o { + XCTAssertFalse(v.hasA) + XCTAssertEqual(v.a, 116) + XCTAssertEqual(v, grp3) + } else { + XCTFail("Wasn't the right case") + } } - msg.o = nil - XCTAssertEqual(msg.oneofMessage.optionalInt32, 0) - XCTAssertNil(msg.o) - // Default within the message - var subMsg2 = SwiftProtoTesting_Message2() - subMsg2.optionalInt32 = 0 - msg.oneofMessage = subMsg2 - XCTAssertEqual(msg.oneofMessage.optionalInt32, 0) - XCTAssertTrue(msg.oneofMessage.hasOptionalInt32) - XCTAssertEqual(msg.oneofMessage, subMsg2) - if case .oneofMessage(let v)? = msg.o { - XCTAssertTrue(v.hasOptionalInt32) - XCTAssertEqual(v.optionalInt32, 0) - XCTAssertEqual(v, subMsg2) - } else { - XCTFail("Wasn't the right case") + + func testOneofMessage() { + var msg = SwiftProtoTesting_Message2() + XCTAssertEqual(msg.oneofMessage.optionalInt32, 0) + XCTAssertNil(msg.o) + var subMsg = SwiftProtoTesting_Message2() + subMsg.optionalInt32 = 66 + msg.oneofMessage = subMsg + XCTAssertEqual(msg.oneofMessage.optionalInt32, 66) + XCTAssertEqual(msg.oneofMessage, subMsg) + if case .oneofMessage(let v)? = msg.o { + XCTAssertEqual(v.optionalInt32, 66) + XCTAssertEqual(v, subMsg) + } else { + XCTFail("Wasn't the right case") + } + msg.o = nil + XCTAssertEqual(msg.oneofMessage.optionalInt32, 0) + XCTAssertNil(msg.o) + // Default within the message + var subMsg2 = SwiftProtoTesting_Message2() + subMsg2.optionalInt32 = 0 + msg.oneofMessage = subMsg2 + XCTAssertEqual(msg.oneofMessage.optionalInt32, 0) + XCTAssertTrue(msg.oneofMessage.hasOptionalInt32) + XCTAssertEqual(msg.oneofMessage, subMsg2) + if case .oneofMessage(let v)? = msg.o { + XCTAssertTrue(v.hasOptionalInt32) + XCTAssertEqual(v.optionalInt32, 0) + XCTAssertEqual(v, subMsg2) + } else { + XCTFail("Wasn't the right case") + } + msg.o = nil + // Message with nothing set. + let subMsg3 = SwiftProtoTesting_Message2() + msg.oneofMessage = subMsg3 + XCTAssertEqual(msg.oneofMessage.optionalInt32, 0) + XCTAssertFalse(msg.oneofMessage.hasOptionalInt32) + XCTAssertEqual(msg.oneofMessage, subMsg3) + if case .oneofMessage(let v)? = msg.o { + XCTAssertFalse(v.hasOptionalInt32) + XCTAssertEqual(v.optionalInt32, 0) + XCTAssertEqual(v, subMsg3) + } else { + XCTFail("Wasn't the right case") + } } - msg.o = nil - // Message with nothing set. - let subMsg3 = SwiftProtoTesting_Message2() - msg.oneofMessage = subMsg3 - XCTAssertEqual(msg.oneofMessage.optionalInt32, 0) - XCTAssertFalse(msg.oneofMessage.hasOptionalInt32) - XCTAssertEqual(msg.oneofMessage, subMsg3) - if case .oneofMessage(let v)? = msg.o { - XCTAssertFalse(v.hasOptionalInt32) - XCTAssertEqual(v.optionalInt32, 0) - XCTAssertEqual(v, subMsg3) - } else { - XCTFail("Wasn't the right case") + + func testOneofEnum() { + var msg = SwiftProtoTesting_Message2() + XCTAssertEqual(msg.oneofEnum, .baz) + XCTAssertNil(msg.o) + msg.oneofEnum = .bar + XCTAssertEqual(msg.oneofEnum, .bar) + XCTAssertEqual(msg.o, .oneofEnum(.bar)) + msg.o = nil + XCTAssertEqual(msg.oneofEnum, .baz) + XCTAssertNil(msg.o) + msg.oneofEnum = .baz + XCTAssertEqual(msg.oneofEnum, .baz) + XCTAssertEqual(msg.o, .oneofEnum(.baz)) } - } - func testOneofEnum() { - var msg = SwiftProtoTesting_Message2() - XCTAssertEqual(msg.oneofEnum, .baz) - XCTAssertNil(msg.o) - msg.oneofEnum = .bar - XCTAssertEqual(msg.oneofEnum, .bar) - XCTAssertEqual(msg.o, .oneofEnum(.bar)) - msg.o = nil - XCTAssertEqual(msg.oneofEnum, .baz) - XCTAssertNil(msg.o) - msg.oneofEnum = .baz - XCTAssertEqual(msg.oneofEnum, .baz) - XCTAssertEqual(msg.o, .oneofEnum(.baz)) - } + // Chaining. Set each item in the oneof clear the previous one. - // Chaining. Set each item in the oneof clear the previous one. + func testOneofOnlyOneSet() { + var msg = SwiftProtoTesting_Message2() - func testOneofOnlyOneSet() { - var msg = SwiftProtoTesting_Message2() + func assertRightFiledSet(_ i: Int) { + // Make sure the case is correct for the enum based access. + switch msg.o { + case nil: + XCTAssertEqual(i, 0) + case .oneofInt32(let v)?: + XCTAssertEqual(i, 1) + XCTAssertEqual(v, 51) + case .oneofInt64(let v)?: + XCTAssertEqual(i, 2) + XCTAssertEqual(v, 52) + case .oneofUint32(let v)?: + XCTAssertEqual(i, 3) + XCTAssertEqual(v, 53) + case .oneofUint64(let v)?: + XCTAssertEqual(i, 4) + XCTAssertEqual(v, 54) + case .oneofSint32(let v)?: + XCTAssertEqual(i, 5) + XCTAssertEqual(v, 55) + case .oneofSint64(let v)?: + XCTAssertEqual(i, 6) + XCTAssertEqual(v, 56) + case .oneofFixed32(let v)?: + XCTAssertEqual(i, 7) + XCTAssertEqual(v, 57) + case .oneofFixed64(let v)?: + XCTAssertEqual(i, 8) + XCTAssertEqual(v, 58) + case .oneofSfixed32(let v)?: + XCTAssertEqual(i, 9) + XCTAssertEqual(v, 59) + case .oneofSfixed64(let v)?: + XCTAssertEqual(i, 10) + XCTAssertEqual(v, 60) + case .oneofFloat(let v)?: + XCTAssertEqual(i, 11) + XCTAssertEqual(v, 61.0) + case .oneofDouble(let v)?: + XCTAssertEqual(i, 12) + XCTAssertEqual(v, 62.0) + case .oneofBool(let v)?: + XCTAssertEqual(i, 13) + XCTAssertEqual(v, false) + case .oneofString(let v)?: + XCTAssertEqual(i, 14) + XCTAssertEqual(v, "64") + case .oneofBytes(let v)?: + XCTAssertEqual(i, 15) + XCTAssertEqual(v, Data([65])) + case .oneofGroup(let v)?: + XCTAssertEqual(i, 16) + XCTAssertTrue(v.hasA) + XCTAssertEqual(v.a, 66) + case .oneofMessage(let v)?: + XCTAssertEqual(i, 17) + XCTAssertTrue(v.hasOptionalInt32) + XCTAssertEqual(v.optionalInt32, 68) + case .oneofEnum(let v)?: + XCTAssertEqual(i, 18) + XCTAssertEqual(v, .bar) + } - func assertRightFiledSet(_ i: Int) { - // Make sure the case is correct for the enum based access. - switch msg.o { - case nil: - XCTAssertEqual(i, 0) - case .oneofInt32(let v)?: - XCTAssertEqual(i, 1) - XCTAssertEqual(v, 51) - case .oneofInt64(let v)?: - XCTAssertEqual(i, 2) - XCTAssertEqual(v, 52) - case .oneofUint32(let v)?: - XCTAssertEqual(i, 3) - XCTAssertEqual(v, 53) - case .oneofUint64(let v)?: - XCTAssertEqual(i, 4) - XCTAssertEqual(v, 54) - case .oneofSint32(let v)?: - XCTAssertEqual(i, 5) - XCTAssertEqual(v, 55) - case .oneofSint64(let v)?: - XCTAssertEqual(i, 6) - XCTAssertEqual(v, 56) - case .oneofFixed32(let v)?: - XCTAssertEqual(i, 7) - XCTAssertEqual(v, 57) - case .oneofFixed64(let v)?: - XCTAssertEqual(i, 8) - XCTAssertEqual(v, 58) - case .oneofSfixed32(let v)?: - XCTAssertEqual(i, 9) - XCTAssertEqual(v, 59) - case .oneofSfixed64(let v)?: - XCTAssertEqual(i, 10) - XCTAssertEqual(v, 60) - case .oneofFloat(let v)?: - XCTAssertEqual(i, 11) - XCTAssertEqual(v, 61.0) - case .oneofDouble(let v)?: - XCTAssertEqual(i, 12) - XCTAssertEqual(v, 62.0) - case .oneofBool(let v)?: - XCTAssertEqual(i, 13) - XCTAssertEqual(v, false) - case .oneofString(let v)?: - XCTAssertEqual(i, 14) - XCTAssertEqual(v, "64") - case .oneofBytes(let v)?: - XCTAssertEqual(i, 15) - XCTAssertEqual(v, Data([65])) - case .oneofGroup(let v)?: - XCTAssertEqual(i, 16) - XCTAssertTrue(v.hasA) - XCTAssertEqual(v.a, 66) - case .oneofMessage(let v)?: - XCTAssertEqual(i, 17) - XCTAssertTrue(v.hasOptionalInt32) - XCTAssertEqual(v.optionalInt32, 68) - case .oneofEnum(let v)?: - XCTAssertEqual(i, 18) - XCTAssertEqual(v, .bar) - } + // Check direct field access (gets the right value or the default) + if i == 1 { + XCTAssertEqual(msg.oneofInt32, 51) + } else { + XCTAssertEqual(msg.oneofInt32, 100, "i = \(i)") + } + if i == 2 { + XCTAssertEqual(msg.oneofInt64, 52) + } else { + XCTAssertEqual(msg.oneofInt64, 101, "i = \(i)") + } + if i == 3 { + XCTAssertEqual(msg.oneofUint32, 53) + } else { + XCTAssertEqual(msg.oneofUint32, 102, "i = \(i)") + } + if i == 4 { + XCTAssertEqual(msg.oneofUint64, 54) + } else { + XCTAssertEqual(msg.oneofUint64, 103, "i = \(i)") + } + if i == 5 { + XCTAssertEqual(msg.oneofSint32, 55) + } else { + XCTAssertEqual(msg.oneofSint32, 104, "i = \(i)") + } + if i == 6 { + XCTAssertEqual(msg.oneofSint64, 56) + } else { + XCTAssertEqual(msg.oneofSint64, 105, "i = \(i)") + } + if i == 7 { + XCTAssertEqual(msg.oneofFixed32, 57) + } else { + XCTAssertEqual(msg.oneofFixed32, 106, "i = \(i)") + } + if i == 8 { + XCTAssertEqual(msg.oneofFixed64, 58) + } else { + XCTAssertEqual(msg.oneofFixed64, 107, "i = \(i)") + } + if i == 9 { + XCTAssertEqual(msg.oneofSfixed32, 59) + } else { + XCTAssertEqual(msg.oneofSfixed32, 108, "i = \(i)") + } + if i == 10 { + XCTAssertEqual(msg.oneofSfixed64, 60) + } else { + XCTAssertEqual(msg.oneofSfixed64, 109, "i = \(i)") + } + if i == 11 { + XCTAssertEqual(msg.oneofFloat, 61.0) + } else { + XCTAssertEqual(msg.oneofFloat, 110.0, "i = \(i)") + } + if i == 12 { + XCTAssertEqual(msg.oneofDouble, 62.0) + } else { + XCTAssertEqual(msg.oneofDouble, 111.0, "i = \(i)") + } + if i == 13 { + XCTAssertEqual(msg.oneofBool, false) + } else { + XCTAssertEqual(msg.oneofBool, true, "i = \(i)") + } + if i == 14 { + XCTAssertEqual(msg.oneofString, "64") + } else { + XCTAssertEqual(msg.oneofString, "string", "i = \(i)") + } + if i == 15 { + XCTAssertEqual(msg.oneofBytes, Data([65])) + } else { + XCTAssertEqual(msg.oneofBytes, "data".data(using: .utf8), "i = \(i)") + } + if i == 16 { + XCTAssertTrue(msg.oneofGroup.hasA) + XCTAssertEqual(msg.oneofGroup.a, 66) + } else { + XCTAssertFalse(msg.oneofGroup.hasA, "i = \(i)") + XCTAssertEqual(msg.oneofGroup.a, 116, "i = \(i)") + } + if i == 17 { + XCTAssertTrue(msg.oneofMessage.hasOptionalInt32) + XCTAssertEqual(msg.oneofMessage.optionalInt32, 68) + } else { + XCTAssertFalse(msg.oneofMessage.hasOptionalInt32, "i = \(i)") + XCTAssertEqual(msg.oneofMessage.optionalInt32, 0, "i = \(i)") + } + if i == 18 { + XCTAssertEqual(msg.oneofEnum, .bar) + } else { + XCTAssertEqual(msg.oneofEnum, .baz, "i = \(i)") + } + } - // Check direct field access (gets the right value or the default) - if i == 1 { - XCTAssertEqual(msg.oneofInt32, 51) - } else { - XCTAssertEqual(msg.oneofInt32, 100, "i = \(i)") - } - if i == 2 { - XCTAssertEqual(msg.oneofInt64, 52) - } else { - XCTAssertEqual(msg.oneofInt64, 101, "i = \(i)") - } - if i == 3 { - XCTAssertEqual(msg.oneofUint32, 53) - } else { - XCTAssertEqual(msg.oneofUint32, 102, "i = \(i)") - } - if i == 4 { - XCTAssertEqual(msg.oneofUint64, 54) - } else { - XCTAssertEqual(msg.oneofUint64, 103, "i = \(i)") - } - if i == 5 { - XCTAssertEqual(msg.oneofSint32, 55) - } else { - XCTAssertEqual(msg.oneofSint32, 104, "i = \(i)") - } - if i == 6 { - XCTAssertEqual(msg.oneofSint64, 56) - } else { - XCTAssertEqual(msg.oneofSint64, 105, "i = \(i)") - } - if i == 7 { - XCTAssertEqual(msg.oneofFixed32, 57) - } else { - XCTAssertEqual(msg.oneofFixed32, 106, "i = \(i)") - } - if i == 8 { - XCTAssertEqual(msg.oneofFixed64, 58) - } else { - XCTAssertEqual(msg.oneofFixed64, 107, "i = \(i)") - } - if i == 9 { - XCTAssertEqual(msg.oneofSfixed32, 59) - } else { - XCTAssertEqual(msg.oneofSfixed32, 108, "i = \(i)") - } - if i == 10 { - XCTAssertEqual(msg.oneofSfixed64, 60) - } else { - XCTAssertEqual(msg.oneofSfixed64, 109, "i = \(i)") - } - if i == 11 { - XCTAssertEqual(msg.oneofFloat, 61.0) - } else { - XCTAssertEqual(msg.oneofFloat, 110.0, "i = \(i)") - } - if i == 12 { - XCTAssertEqual(msg.oneofDouble, 62.0) - } else { - XCTAssertEqual(msg.oneofDouble, 111.0, "i = \(i)") - } - if i == 13 { - XCTAssertEqual(msg.oneofBool, false) - } else { - XCTAssertEqual(msg.oneofBool, true, "i = \(i)") - } - if i == 14 { - XCTAssertEqual(msg.oneofString, "64") - } else { - XCTAssertEqual(msg.oneofString, "string", "i = \(i)") - } - if i == 15 { - XCTAssertEqual(msg.oneofBytes, Data([65])) - } else { - XCTAssertEqual(msg.oneofBytes, "data".data(using: .utf8), "i = \(i)") - } - if i == 16 { - XCTAssertTrue(msg.oneofGroup.hasA) - XCTAssertEqual(msg.oneofGroup.a, 66) - } else { - XCTAssertFalse(msg.oneofGroup.hasA, "i = \(i)") - XCTAssertEqual(msg.oneofGroup.a, 116, "i = \(i)") - } - if i == 17 { - XCTAssertTrue(msg.oneofMessage.hasOptionalInt32) - XCTAssertEqual(msg.oneofMessage.optionalInt32, 68) - } else { - XCTAssertFalse(msg.oneofMessage.hasOptionalInt32, "i = \(i)") - XCTAssertEqual(msg.oneofMessage.optionalInt32, 0, "i = \(i)") - } - if i == 18 { - XCTAssertEqual(msg.oneofEnum, .bar) - } else { - XCTAssertEqual(msg.oneofEnum, .baz, "i = \(i)") - } + // Now cycle through the cases. + assertRightFiledSet(0) + msg.oneofInt32 = 51 + assertRightFiledSet(1) + msg.oneofInt64 = 52 + assertRightFiledSet(2) + msg.oneofUint32 = 53 + assertRightFiledSet(3) + msg.oneofUint64 = 54 + assertRightFiledSet(4) + msg.oneofSint32 = 55 + assertRightFiledSet(5) + msg.oneofSint64 = 56 + assertRightFiledSet(6) + msg.oneofFixed32 = 57 + assertRightFiledSet(7) + msg.oneofFixed64 = 58 + assertRightFiledSet(8) + msg.oneofSfixed32 = 59 + assertRightFiledSet(9) + msg.oneofSfixed64 = 60 + assertRightFiledSet(10) + msg.oneofFloat = 61.0 + assertRightFiledSet(11) + msg.oneofDouble = 62.0 + assertRightFiledSet(12) + msg.oneofBool = false + assertRightFiledSet(13) + msg.oneofString = "64" + assertRightFiledSet(14) + msg.oneofBytes = Data([65]) + assertRightFiledSet(15) + msg.oneofGroup.a = 66 + assertRightFiledSet(16) + msg.oneofMessage.optionalInt32 = 68 + assertRightFiledSet(17) + msg.oneofEnum = .bar + assertRightFiledSet(18) } - - // Now cycle through the cases. - assertRightFiledSet(0) - msg.oneofInt32 = 51 - assertRightFiledSet(1) - msg.oneofInt64 = 52 - assertRightFiledSet(2) - msg.oneofUint32 = 53 - assertRightFiledSet(3) - msg.oneofUint64 = 54 - assertRightFiledSet(4) - msg.oneofSint32 = 55 - assertRightFiledSet(5) - msg.oneofSint64 = 56 - assertRightFiledSet(6) - msg.oneofFixed32 = 57 - assertRightFiledSet(7) - msg.oneofFixed64 = 58 - assertRightFiledSet(8) - msg.oneofSfixed32 = 59 - assertRightFiledSet(9) - msg.oneofSfixed64 = 60 - assertRightFiledSet(10) - msg.oneofFloat = 61.0 - assertRightFiledSet(11) - msg.oneofDouble = 62.0 - assertRightFiledSet(12) - msg.oneofBool = false - assertRightFiledSet(13) - msg.oneofString = "64" - assertRightFiledSet(14) - msg.oneofBytes = Data([65]) - assertRightFiledSet(15) - msg.oneofGroup.a = 66 - assertRightFiledSet(16) - msg.oneofMessage.optionalInt32 = 68 - assertRightFiledSet(17) - msg.oneofEnum = .bar - assertRightFiledSet(18) - } } diff --git a/Tests/SwiftProtobufTests/Test_OneofFields_Access_Proto3.swift b/Tests/SwiftProtobufTests/Test_OneofFields_Access_Proto3.swift index c5f010012..99f983b33 100644 --- a/Tests/SwiftProtobufTests/Test_OneofFields_Access_Proto3.swift +++ b/Tests/SwiftProtobufTests/Test_OneofFields_Access_Proto3.swift @@ -12,8 +12,8 @@ /// // ----------------------------------------------------------------------------- -import XCTest import Foundation +import XCTest // NOTE: The generator changes what is generated based on the number/types // of fields (using a nested storage class or not), to be completel, all @@ -22,487 +22,487 @@ import Foundation final class Test_OneofFields_Access_Proto3: XCTestCase { - // Accessing one field. - // - Returns default - // - Accepts/Captures value - // - Resets - // - Accepts/Captures default value + // Accessing one field. + // - Returns default + // - Accepts/Captures value + // - Resets + // - Accepts/Captures default value - func testOneofInt32() { - var msg = SwiftProtoTesting_Message3() - XCTAssertEqual(msg.oneofInt32, 0) - XCTAssertNil(msg.o) - msg.oneofInt32 = 51 - XCTAssertEqual(msg.oneofInt32, 51) - XCTAssertEqual(msg.o, .oneofInt32(51)) - msg.o = nil - XCTAssertEqual(msg.oneofInt32, 0) - XCTAssertNil(msg.o) - msg.oneofInt32 = 0 - XCTAssertEqual(msg.oneofInt32, 0) - XCTAssertEqual(msg.o, .oneofInt32(0)) - } + func testOneofInt32() { + var msg = SwiftProtoTesting_Message3() + XCTAssertEqual(msg.oneofInt32, 0) + XCTAssertNil(msg.o) + msg.oneofInt32 = 51 + XCTAssertEqual(msg.oneofInt32, 51) + XCTAssertEqual(msg.o, .oneofInt32(51)) + msg.o = nil + XCTAssertEqual(msg.oneofInt32, 0) + XCTAssertNil(msg.o) + msg.oneofInt32 = 0 + XCTAssertEqual(msg.oneofInt32, 0) + XCTAssertEqual(msg.o, .oneofInt32(0)) + } - func testOneofInt64() { - var msg = SwiftProtoTesting_Message3() - XCTAssertEqual(msg.oneofInt64, 0) - XCTAssertNil(msg.o) - msg.oneofInt64 = 52 - XCTAssertEqual(msg.oneofInt64, 52) - XCTAssertEqual(msg.o, .oneofInt64(52)) - msg.o = nil - XCTAssertEqual(msg.oneofInt64, 0) - XCTAssertNil(msg.o) - msg.oneofInt64 = 0 - XCTAssertEqual(msg.oneofInt64, 0) - XCTAssertEqual(msg.o, .oneofInt64(0)) - } + func testOneofInt64() { + var msg = SwiftProtoTesting_Message3() + XCTAssertEqual(msg.oneofInt64, 0) + XCTAssertNil(msg.o) + msg.oneofInt64 = 52 + XCTAssertEqual(msg.oneofInt64, 52) + XCTAssertEqual(msg.o, .oneofInt64(52)) + msg.o = nil + XCTAssertEqual(msg.oneofInt64, 0) + XCTAssertNil(msg.o) + msg.oneofInt64 = 0 + XCTAssertEqual(msg.oneofInt64, 0) + XCTAssertEqual(msg.o, .oneofInt64(0)) + } - func testOneofUint32() { - var msg = SwiftProtoTesting_Message3() - XCTAssertEqual(msg.oneofUint32, 0) - XCTAssertNil(msg.o) - msg.oneofUint32 = 53 - XCTAssertEqual(msg.oneofUint32, 53) - XCTAssertEqual(msg.o, .oneofUint32(53)) - msg.o = nil - XCTAssertEqual(msg.oneofUint32, 0) - XCTAssertNil(msg.o) - msg.oneofUint32 = 0 - XCTAssertEqual(msg.oneofUint32, 0) - XCTAssertEqual(msg.o, .oneofUint32(0)) - } + func testOneofUint32() { + var msg = SwiftProtoTesting_Message3() + XCTAssertEqual(msg.oneofUint32, 0) + XCTAssertNil(msg.o) + msg.oneofUint32 = 53 + XCTAssertEqual(msg.oneofUint32, 53) + XCTAssertEqual(msg.o, .oneofUint32(53)) + msg.o = nil + XCTAssertEqual(msg.oneofUint32, 0) + XCTAssertNil(msg.o) + msg.oneofUint32 = 0 + XCTAssertEqual(msg.oneofUint32, 0) + XCTAssertEqual(msg.o, .oneofUint32(0)) + } - func testOneofUint64() { - var msg = SwiftProtoTesting_Message3() - XCTAssertEqual(msg.oneofUint64, 0) - XCTAssertNil(msg.o) - msg.oneofUint64 = 54 - XCTAssertEqual(msg.oneofUint64, 54) - XCTAssertEqual(msg.o, .oneofUint64(54)) - msg.o = nil - XCTAssertEqual(msg.oneofUint64, 0) - XCTAssertNil(msg.o) - msg.oneofUint64 = 0 - XCTAssertEqual(msg.oneofUint64, 0) - XCTAssertEqual(msg.o, .oneofUint64(0)) - } + func testOneofUint64() { + var msg = SwiftProtoTesting_Message3() + XCTAssertEqual(msg.oneofUint64, 0) + XCTAssertNil(msg.o) + msg.oneofUint64 = 54 + XCTAssertEqual(msg.oneofUint64, 54) + XCTAssertEqual(msg.o, .oneofUint64(54)) + msg.o = nil + XCTAssertEqual(msg.oneofUint64, 0) + XCTAssertNil(msg.o) + msg.oneofUint64 = 0 + XCTAssertEqual(msg.oneofUint64, 0) + XCTAssertEqual(msg.o, .oneofUint64(0)) + } - func testOneofSint32() { - var msg = SwiftProtoTesting_Message3() - XCTAssertEqual(msg.oneofSint32, 0) - XCTAssertNil(msg.o) - msg.oneofSint32 = 55 - XCTAssertEqual(msg.oneofSint32, 55) - XCTAssertEqual(msg.o, .oneofSint32(55)) - msg.o = nil - XCTAssertEqual(msg.oneofSint32, 0) - XCTAssertNil(msg.o) - msg.oneofSint32 = 0 - XCTAssertEqual(msg.oneofSint32, 0) - XCTAssertEqual(msg.o, .oneofSint32(0)) - } + func testOneofSint32() { + var msg = SwiftProtoTesting_Message3() + XCTAssertEqual(msg.oneofSint32, 0) + XCTAssertNil(msg.o) + msg.oneofSint32 = 55 + XCTAssertEqual(msg.oneofSint32, 55) + XCTAssertEqual(msg.o, .oneofSint32(55)) + msg.o = nil + XCTAssertEqual(msg.oneofSint32, 0) + XCTAssertNil(msg.o) + msg.oneofSint32 = 0 + XCTAssertEqual(msg.oneofSint32, 0) + XCTAssertEqual(msg.o, .oneofSint32(0)) + } - func testOneofSint64() { - var msg = SwiftProtoTesting_Message3() - XCTAssertEqual(msg.oneofSint64, 0) - XCTAssertNil(msg.o) - msg.oneofSint64 = 56 - XCTAssertEqual(msg.oneofSint64, 56) - XCTAssertEqual(msg.o, .oneofSint64(56)) - msg.o = nil - XCTAssertEqual(msg.oneofSint64, 0) - XCTAssertNil(msg.o) - msg.oneofSint64 = 0 - XCTAssertEqual(msg.oneofSint64, 0) - XCTAssertEqual(msg.o, .oneofSint64(0)) - } + func testOneofSint64() { + var msg = SwiftProtoTesting_Message3() + XCTAssertEqual(msg.oneofSint64, 0) + XCTAssertNil(msg.o) + msg.oneofSint64 = 56 + XCTAssertEqual(msg.oneofSint64, 56) + XCTAssertEqual(msg.o, .oneofSint64(56)) + msg.o = nil + XCTAssertEqual(msg.oneofSint64, 0) + XCTAssertNil(msg.o) + msg.oneofSint64 = 0 + XCTAssertEqual(msg.oneofSint64, 0) + XCTAssertEqual(msg.o, .oneofSint64(0)) + } - func testOneofFixed32() { - var msg = SwiftProtoTesting_Message3() - XCTAssertEqual(msg.oneofFixed32, 0) - XCTAssertNil(msg.o) - msg.oneofFixed32 = 57 - XCTAssertEqual(msg.oneofFixed32, 57) - XCTAssertEqual(msg.o, .oneofFixed32(57)) - msg.o = nil - XCTAssertEqual(msg.oneofFixed32, 0) - XCTAssertNil(msg.o) - msg.oneofFixed32 = 0 - XCTAssertEqual(msg.oneofFixed32, 0) - XCTAssertEqual(msg.o, .oneofFixed32(0)) - } + func testOneofFixed32() { + var msg = SwiftProtoTesting_Message3() + XCTAssertEqual(msg.oneofFixed32, 0) + XCTAssertNil(msg.o) + msg.oneofFixed32 = 57 + XCTAssertEqual(msg.oneofFixed32, 57) + XCTAssertEqual(msg.o, .oneofFixed32(57)) + msg.o = nil + XCTAssertEqual(msg.oneofFixed32, 0) + XCTAssertNil(msg.o) + msg.oneofFixed32 = 0 + XCTAssertEqual(msg.oneofFixed32, 0) + XCTAssertEqual(msg.o, .oneofFixed32(0)) + } - func testOneofFixed64() { - var msg = SwiftProtoTesting_Message3() - XCTAssertEqual(msg.oneofFixed64, 0) - XCTAssertNil(msg.o) - msg.oneofFixed64 = 58 - XCTAssertEqual(msg.oneofFixed64, 58) - XCTAssertEqual(msg.o, .oneofFixed64(58)) - msg.o = nil - XCTAssertEqual(msg.oneofFixed64, 0) - XCTAssertNil(msg.o) - msg.oneofFixed64 = 0 - XCTAssertEqual(msg.oneofFixed64, 0) - XCTAssertEqual(msg.o, .oneofFixed64(0)) - } + func testOneofFixed64() { + var msg = SwiftProtoTesting_Message3() + XCTAssertEqual(msg.oneofFixed64, 0) + XCTAssertNil(msg.o) + msg.oneofFixed64 = 58 + XCTAssertEqual(msg.oneofFixed64, 58) + XCTAssertEqual(msg.o, .oneofFixed64(58)) + msg.o = nil + XCTAssertEqual(msg.oneofFixed64, 0) + XCTAssertNil(msg.o) + msg.oneofFixed64 = 0 + XCTAssertEqual(msg.oneofFixed64, 0) + XCTAssertEqual(msg.o, .oneofFixed64(0)) + } - func testOneofSfixed32() { - var msg = SwiftProtoTesting_Message3() - XCTAssertEqual(msg.oneofSfixed32, 0) - XCTAssertNil(msg.o) - msg.oneofSfixed32 = 59 - XCTAssertEqual(msg.oneofSfixed32, 59) - XCTAssertEqual(msg.o, .oneofSfixed32(59)) - msg.o = nil - XCTAssertEqual(msg.oneofSfixed32, 0) - XCTAssertNil(msg.o) - msg.oneofSfixed32 = 0 - XCTAssertEqual(msg.oneofSfixed32, 0) - XCTAssertEqual(msg.o, .oneofSfixed32(0)) - } + func testOneofSfixed32() { + var msg = SwiftProtoTesting_Message3() + XCTAssertEqual(msg.oneofSfixed32, 0) + XCTAssertNil(msg.o) + msg.oneofSfixed32 = 59 + XCTAssertEqual(msg.oneofSfixed32, 59) + XCTAssertEqual(msg.o, .oneofSfixed32(59)) + msg.o = nil + XCTAssertEqual(msg.oneofSfixed32, 0) + XCTAssertNil(msg.o) + msg.oneofSfixed32 = 0 + XCTAssertEqual(msg.oneofSfixed32, 0) + XCTAssertEqual(msg.o, .oneofSfixed32(0)) + } - func testOneofSfixed64() { - var msg = SwiftProtoTesting_Message3() - XCTAssertEqual(msg.oneofSfixed64, 0) - XCTAssertNil(msg.o) - msg.oneofSfixed64 = 60 - XCTAssertEqual(msg.oneofSfixed64, 60) - XCTAssertEqual(msg.o, .oneofSfixed64(60)) - msg.o = nil - XCTAssertEqual(msg.oneofSfixed64, 0) - XCTAssertNil(msg.o) - msg.oneofSfixed64 = 0 - XCTAssertEqual(msg.oneofSfixed64, 0) - XCTAssertEqual(msg.o, .oneofSfixed64(0)) - } + func testOneofSfixed64() { + var msg = SwiftProtoTesting_Message3() + XCTAssertEqual(msg.oneofSfixed64, 0) + XCTAssertNil(msg.o) + msg.oneofSfixed64 = 60 + XCTAssertEqual(msg.oneofSfixed64, 60) + XCTAssertEqual(msg.o, .oneofSfixed64(60)) + msg.o = nil + XCTAssertEqual(msg.oneofSfixed64, 0) + XCTAssertNil(msg.o) + msg.oneofSfixed64 = 0 + XCTAssertEqual(msg.oneofSfixed64, 0) + XCTAssertEqual(msg.o, .oneofSfixed64(0)) + } - func testOneofFloat() { - var msg = SwiftProtoTesting_Message3() - XCTAssertEqual(msg.oneofFloat, 0.0) - XCTAssertNil(msg.o) - msg.oneofFloat = 61.0 - XCTAssertEqual(msg.oneofFloat, 61.0) - XCTAssertEqual(msg.o, .oneofFloat(61.0)) - msg.o = nil - XCTAssertEqual(msg.oneofFloat, 0.0) - XCTAssertNil(msg.o) - msg.oneofFloat = 0.0 - XCTAssertEqual(msg.oneofFloat, 0.0) - XCTAssertEqual(msg.o, .oneofFloat(0.0)) - } + func testOneofFloat() { + var msg = SwiftProtoTesting_Message3() + XCTAssertEqual(msg.oneofFloat, 0.0) + XCTAssertNil(msg.o) + msg.oneofFloat = 61.0 + XCTAssertEqual(msg.oneofFloat, 61.0) + XCTAssertEqual(msg.o, .oneofFloat(61.0)) + msg.o = nil + XCTAssertEqual(msg.oneofFloat, 0.0) + XCTAssertNil(msg.o) + msg.oneofFloat = 0.0 + XCTAssertEqual(msg.oneofFloat, 0.0) + XCTAssertEqual(msg.o, .oneofFloat(0.0)) + } - func testOneofDouble() { - var msg = SwiftProtoTesting_Message3() - XCTAssertEqual(msg.oneofDouble, 0.0) - XCTAssertNil(msg.o) - msg.oneofDouble = 62.0 - XCTAssertEqual(msg.oneofDouble, 62.0) - XCTAssertEqual(msg.o, .oneofDouble(62.0)) - msg.o = nil - XCTAssertEqual(msg.oneofDouble, 0.0) - XCTAssertNil(msg.o) - msg.oneofDouble = 0.0 - XCTAssertEqual(msg.oneofDouble, 0.0) - XCTAssertEqual(msg.o, .oneofDouble(0.0)) - } + func testOneofDouble() { + var msg = SwiftProtoTesting_Message3() + XCTAssertEqual(msg.oneofDouble, 0.0) + XCTAssertNil(msg.o) + msg.oneofDouble = 62.0 + XCTAssertEqual(msg.oneofDouble, 62.0) + XCTAssertEqual(msg.o, .oneofDouble(62.0)) + msg.o = nil + XCTAssertEqual(msg.oneofDouble, 0.0) + XCTAssertNil(msg.o) + msg.oneofDouble = 0.0 + XCTAssertEqual(msg.oneofDouble, 0.0) + XCTAssertEqual(msg.o, .oneofDouble(0.0)) + } - func testOneofBool() { - var msg = SwiftProtoTesting_Message3() - XCTAssertEqual(msg.oneofBool, false) - XCTAssertNil(msg.o) - msg.oneofBool = true - XCTAssertEqual(msg.oneofBool, true) - XCTAssertEqual(msg.o, .oneofBool(true)) - msg.o = nil - XCTAssertEqual(msg.oneofBool, false) - XCTAssertNil(msg.o) - msg.oneofBool = false - XCTAssertEqual(msg.oneofBool, false) - XCTAssertEqual(msg.o, .oneofBool(false)) - } + func testOneofBool() { + var msg = SwiftProtoTesting_Message3() + XCTAssertEqual(msg.oneofBool, false) + XCTAssertNil(msg.o) + msg.oneofBool = true + XCTAssertEqual(msg.oneofBool, true) + XCTAssertEqual(msg.o, .oneofBool(true)) + msg.o = nil + XCTAssertEqual(msg.oneofBool, false) + XCTAssertNil(msg.o) + msg.oneofBool = false + XCTAssertEqual(msg.oneofBool, false) + XCTAssertEqual(msg.o, .oneofBool(false)) + } - func testOneofString() { - var msg = SwiftProtoTesting_Message3() - XCTAssertEqual(msg.oneofString, "") - XCTAssertNil(msg.o) - msg.oneofString = "64" - XCTAssertEqual(msg.oneofString, "64") - XCTAssertEqual(msg.o, .oneofString("64")) - msg.o = nil - XCTAssertEqual(msg.oneofString, "") - XCTAssertNil(msg.o) - msg.oneofString = "" - XCTAssertEqual(msg.oneofString, "") - XCTAssertEqual(msg.o, .oneofString("")) - } + func testOneofString() { + var msg = SwiftProtoTesting_Message3() + XCTAssertEqual(msg.oneofString, "") + XCTAssertNil(msg.o) + msg.oneofString = "64" + XCTAssertEqual(msg.oneofString, "64") + XCTAssertEqual(msg.o, .oneofString("64")) + msg.o = nil + XCTAssertEqual(msg.oneofString, "") + XCTAssertNil(msg.o) + msg.oneofString = "" + XCTAssertEqual(msg.oneofString, "") + XCTAssertEqual(msg.o, .oneofString("")) + } - func testOneofBytes() { - var msg = SwiftProtoTesting_Message3() - XCTAssertEqual(msg.oneofBytes, Data()) - XCTAssertNil(msg.o) - msg.oneofBytes = Data([65]) - XCTAssertEqual(msg.oneofBytes, Data([65])) - XCTAssertEqual(msg.o, .oneofBytes(Data([65]))) - msg.o = nil - XCTAssertEqual(msg.oneofBytes, Data()) - XCTAssertNil(msg.o) - msg.oneofBytes = Data() - XCTAssertEqual(msg.oneofBytes, Data()) - XCTAssertEqual(msg.o, .oneofBytes(Data())) - } + func testOneofBytes() { + var msg = SwiftProtoTesting_Message3() + XCTAssertEqual(msg.oneofBytes, Data()) + XCTAssertNil(msg.o) + msg.oneofBytes = Data([65]) + XCTAssertEqual(msg.oneofBytes, Data([65])) + XCTAssertEqual(msg.o, .oneofBytes(Data([65]))) + msg.o = nil + XCTAssertEqual(msg.oneofBytes, Data()) + XCTAssertNil(msg.o) + msg.oneofBytes = Data() + XCTAssertEqual(msg.oneofBytes, Data()) + XCTAssertEqual(msg.o, .oneofBytes(Data())) + } - // No group. + // No group. - func testOneofMessage() { - var msg = SwiftProtoTesting_Message3() - XCTAssertEqual(msg.oneofMessage.optionalInt32, 0) - XCTAssertNil(msg.o) - var subMsg = SwiftProtoTesting_Message3() - subMsg.optionalInt32 = 66 - msg.oneofMessage = subMsg - XCTAssertEqual(msg.oneofMessage.optionalInt32, 66) - XCTAssertEqual(msg.oneofMessage, subMsg) - if case .oneofMessage(let v)? = msg.o { - XCTAssertEqual(v.optionalInt32, 66) - XCTAssertEqual(v, subMsg) - } else { - XCTFail("Wasn't the right case") - } - msg.o = nil - XCTAssertEqual(msg.oneofMessage.optionalInt32, 0) - XCTAssertNil(msg.o) - // Default within the message - var subMsg2 = SwiftProtoTesting_Message3() - subMsg2.optionalInt32 = 0 - msg.oneofMessage = subMsg2 - XCTAssertEqual(msg.oneofMessage.optionalInt32, 0) - XCTAssertEqual(msg.oneofMessage, subMsg2) - if case .oneofMessage(let v)? = msg.o { - XCTAssertEqual(v.optionalInt32, 0) - XCTAssertEqual(v, subMsg2) - } else { - XCTFail("Wasn't the right case") + func testOneofMessage() { + var msg = SwiftProtoTesting_Message3() + XCTAssertEqual(msg.oneofMessage.optionalInt32, 0) + XCTAssertNil(msg.o) + var subMsg = SwiftProtoTesting_Message3() + subMsg.optionalInt32 = 66 + msg.oneofMessage = subMsg + XCTAssertEqual(msg.oneofMessage.optionalInt32, 66) + XCTAssertEqual(msg.oneofMessage, subMsg) + if case .oneofMessage(let v)? = msg.o { + XCTAssertEqual(v.optionalInt32, 66) + XCTAssertEqual(v, subMsg) + } else { + XCTFail("Wasn't the right case") + } + msg.o = nil + XCTAssertEqual(msg.oneofMessage.optionalInt32, 0) + XCTAssertNil(msg.o) + // Default within the message + var subMsg2 = SwiftProtoTesting_Message3() + subMsg2.optionalInt32 = 0 + msg.oneofMessage = subMsg2 + XCTAssertEqual(msg.oneofMessage.optionalInt32, 0) + XCTAssertEqual(msg.oneofMessage, subMsg2) + if case .oneofMessage(let v)? = msg.o { + XCTAssertEqual(v.optionalInt32, 0) + XCTAssertEqual(v, subMsg2) + } else { + XCTFail("Wasn't the right case") + } + msg.o = nil + // Message with nothing set. + let subMsg3 = SwiftProtoTesting_Message3() + msg.oneofMessage = subMsg3 + XCTAssertEqual(msg.oneofMessage.optionalInt32, 0) + XCTAssertEqual(msg.oneofMessage, subMsg3) + if case .oneofMessage(let v)? = msg.o { + XCTAssertEqual(v.optionalInt32, 0) + XCTAssertEqual(v, subMsg3) + } else { + XCTFail("Wasn't the right case") + } } - msg.o = nil - // Message with nothing set. - let subMsg3 = SwiftProtoTesting_Message3() - msg.oneofMessage = subMsg3 - XCTAssertEqual(msg.oneofMessage.optionalInt32, 0) - XCTAssertEqual(msg.oneofMessage, subMsg3) - if case .oneofMessage(let v)? = msg.o { - XCTAssertEqual(v.optionalInt32, 0) - XCTAssertEqual(v, subMsg3) - } else { - XCTFail("Wasn't the right case") + + func testOneofEnum() { + var msg = SwiftProtoTesting_Message3() + XCTAssertEqual(msg.oneofEnum, .foo) + XCTAssertNil(msg.o) + msg.oneofEnum = .bar + XCTAssertEqual(msg.oneofEnum, .bar) + XCTAssertEqual(msg.o, .oneofEnum(.bar)) + msg.o = nil + XCTAssertEqual(msg.oneofEnum, .foo) + XCTAssertNil(msg.o) + msg.oneofEnum = .foo + XCTAssertEqual(msg.oneofEnum, .foo) + XCTAssertEqual(msg.o, .oneofEnum(.foo)) } - } - func testOneofEnum() { - var msg = SwiftProtoTesting_Message3() - XCTAssertEqual(msg.oneofEnum, .foo) - XCTAssertNil(msg.o) - msg.oneofEnum = .bar - XCTAssertEqual(msg.oneofEnum, .bar) - XCTAssertEqual(msg.o, .oneofEnum(.bar)) - msg.o = nil - XCTAssertEqual(msg.oneofEnum, .foo) - XCTAssertNil(msg.o) - msg.oneofEnum = .foo - XCTAssertEqual(msg.oneofEnum, .foo) - XCTAssertEqual(msg.o, .oneofEnum(.foo)) - } + // Chaining. Set each item in the oneof clear the previous one. - // Chaining. Set each item in the oneof clear the previous one. + func testOneofOnlyOneSet() { + var msg = SwiftProtoTesting_Message3() - func testOneofOnlyOneSet() { - var msg = SwiftProtoTesting_Message3() + func assertRightFiledSet(_ i: Int) { + // Make sure the case is correct for the enum based access. + switch msg.o { + case nil: + XCTAssertEqual(i, 0) + case .oneofInt32(let v)?: + XCTAssertEqual(i, 1) + XCTAssertEqual(v, 51) + case .oneofInt64(let v)?: + XCTAssertEqual(i, 2) + XCTAssertEqual(v, 52) + case .oneofUint32(let v)?: + XCTAssertEqual(i, 3) + XCTAssertEqual(v, 53) + case .oneofUint64(let v)?: + XCTAssertEqual(i, 4) + XCTAssertEqual(v, 54) + case .oneofSint32(let v)?: + XCTAssertEqual(i, 5) + XCTAssertEqual(v, 55) + case .oneofSint64(let v)?: + XCTAssertEqual(i, 6) + XCTAssertEqual(v, 56) + case .oneofFixed32(let v)?: + XCTAssertEqual(i, 7) + XCTAssertEqual(v, 57) + case .oneofFixed64(let v)?: + XCTAssertEqual(i, 8) + XCTAssertEqual(v, 58) + case .oneofSfixed32(let v)?: + XCTAssertEqual(i, 9) + XCTAssertEqual(v, 59) + case .oneofSfixed64(let v)?: + XCTAssertEqual(i, 10) + XCTAssertEqual(v, 60) + case .oneofFloat(let v)?: + XCTAssertEqual(i, 11) + XCTAssertEqual(v, 61.0) + case .oneofDouble(let v)?: + XCTAssertEqual(i, 12) + XCTAssertEqual(v, 62.0) + case .oneofBool(let v)?: + XCTAssertEqual(i, 13) + XCTAssertEqual(v, true) + case .oneofString(let v)?: + XCTAssertEqual(i, 14) + XCTAssertEqual(v, "64") + case .oneofBytes(let v)?: + XCTAssertEqual(i, 15) + XCTAssertEqual(v, Data([65])) + // No group. + case .oneofMessage(let v)?: + XCTAssertEqual(i, 17) + XCTAssertEqual(v.optionalInt32, 68) + case .oneofEnum(let v)?: + XCTAssertEqual(i, 18) + XCTAssertEqual(v, .bar) + } - func assertRightFiledSet(_ i: Int) { - // Make sure the case is correct for the enum based access. - switch msg.o { - case nil: - XCTAssertEqual(i, 0) - case .oneofInt32(let v)?: - XCTAssertEqual(i, 1) - XCTAssertEqual(v, 51) - case .oneofInt64(let v)?: - XCTAssertEqual(i, 2) - XCTAssertEqual(v, 52) - case .oneofUint32(let v)?: - XCTAssertEqual(i, 3) - XCTAssertEqual(v, 53) - case .oneofUint64(let v)?: - XCTAssertEqual(i, 4) - XCTAssertEqual(v, 54) - case .oneofSint32(let v)?: - XCTAssertEqual(i, 5) - XCTAssertEqual(v, 55) - case .oneofSint64(let v)?: - XCTAssertEqual(i, 6) - XCTAssertEqual(v, 56) - case .oneofFixed32(let v)?: - XCTAssertEqual(i, 7) - XCTAssertEqual(v, 57) - case .oneofFixed64(let v)?: - XCTAssertEqual(i, 8) - XCTAssertEqual(v, 58) - case .oneofSfixed32(let v)?: - XCTAssertEqual(i, 9) - XCTAssertEqual(v, 59) - case .oneofSfixed64(let v)?: - XCTAssertEqual(i, 10) - XCTAssertEqual(v, 60) - case .oneofFloat(let v)?: - XCTAssertEqual(i, 11) - XCTAssertEqual(v, 61.0) - case .oneofDouble(let v)?: - XCTAssertEqual(i, 12) - XCTAssertEqual(v, 62.0) - case .oneofBool(let v)?: - XCTAssertEqual(i, 13) - XCTAssertEqual(v, true) - case .oneofString(let v)?: - XCTAssertEqual(i, 14) - XCTAssertEqual(v, "64") - case .oneofBytes(let v)?: - XCTAssertEqual(i, 15) - XCTAssertEqual(v, Data([65])) - // No group. - case .oneofMessage(let v)?: - XCTAssertEqual(i, 17) - XCTAssertEqual(v.optionalInt32, 68) - case .oneofEnum(let v)?: - XCTAssertEqual(i, 18) - XCTAssertEqual(v, .bar) - } + // Check direct field access (gets the right value or the default) + if i == 1 { + XCTAssertEqual(msg.oneofInt32, 51) + } else { + XCTAssertEqual(msg.oneofInt32, 0, "i = \(i)") + } + if i == 2 { + XCTAssertEqual(msg.oneofInt64, 52) + } else { + XCTAssertEqual(msg.oneofInt64, 0, "i = \(i)") + } + if i == 3 { + XCTAssertEqual(msg.oneofUint32, 53) + } else { + XCTAssertEqual(msg.oneofUint32, 0, "i = \(i)") + } + if i == 4 { + XCTAssertEqual(msg.oneofUint64, 54) + } else { + XCTAssertEqual(msg.oneofUint64, 0, "i = \(i)") + } + if i == 5 { + XCTAssertEqual(msg.oneofSint32, 55) + } else { + XCTAssertEqual(msg.oneofSint32, 0, "i = \(i)") + } + if i == 6 { + XCTAssertEqual(msg.oneofSint64, 56) + } else { + XCTAssertEqual(msg.oneofSint64, 0, "i = \(i)") + } + if i == 7 { + XCTAssertEqual(msg.oneofFixed32, 57) + } else { + XCTAssertEqual(msg.oneofFixed32, 0, "i = \(i)") + } + if i == 8 { + XCTAssertEqual(msg.oneofFixed64, 58) + } else { + XCTAssertEqual(msg.oneofFixed64, 0, "i = \(i)") + } + if i == 9 { + XCTAssertEqual(msg.oneofSfixed32, 59) + } else { + XCTAssertEqual(msg.oneofSfixed32, 0, "i = \(i)") + } + if i == 10 { + XCTAssertEqual(msg.oneofSfixed64, 60) + } else { + XCTAssertEqual(msg.oneofSfixed64, 0, "i = \(i)") + } + if i == 11 { + XCTAssertEqual(msg.oneofFloat, 61.0) + } else { + XCTAssertEqual(msg.oneofFloat, 0.0, "i = \(i)") + } + if i == 12 { + XCTAssertEqual(msg.oneofDouble, 62.0) + } else { + XCTAssertEqual(msg.oneofDouble, 0.0, "i = \(i)") + } + if i == 13 { + XCTAssertEqual(msg.oneofBool, true) + } else { + XCTAssertEqual(msg.oneofBool, false, "i = \(i)") + } + if i == 14 { + XCTAssertEqual(msg.oneofString, "64") + } else { + XCTAssertEqual(msg.oneofString, "", "i = \(i)") + } + if i == 15 { + XCTAssertEqual(msg.oneofBytes, Data([65])) + } else { + XCTAssertEqual(msg.oneofBytes, Data(), "i = \(i)") + } + // No group + if i == 17 { + XCTAssertEqual(msg.oneofMessage.optionalInt32, 68) + } else { + XCTAssertEqual(msg.oneofMessage.optionalInt32, 0, "i = \(i)") + } + if i == 18 { + XCTAssertEqual(msg.oneofEnum, .bar) + } else { + XCTAssertEqual(msg.oneofEnum, .foo, "i = \(i)") + } + } - // Check direct field access (gets the right value or the default) - if i == 1 { - XCTAssertEqual(msg.oneofInt32, 51) - } else { - XCTAssertEqual(msg.oneofInt32, 0, "i = \(i)") - } - if i == 2 { - XCTAssertEqual(msg.oneofInt64, 52) - } else { - XCTAssertEqual(msg.oneofInt64, 0, "i = \(i)") - } - if i == 3 { - XCTAssertEqual(msg.oneofUint32, 53) - } else { - XCTAssertEqual(msg.oneofUint32, 0, "i = \(i)") - } - if i == 4 { - XCTAssertEqual(msg.oneofUint64, 54) - } else { - XCTAssertEqual(msg.oneofUint64, 0, "i = \(i)") - } - if i == 5 { - XCTAssertEqual(msg.oneofSint32, 55) - } else { - XCTAssertEqual(msg.oneofSint32, 0, "i = \(i)") - } - if i == 6 { - XCTAssertEqual(msg.oneofSint64, 56) - } else { - XCTAssertEqual(msg.oneofSint64, 0, "i = \(i)") - } - if i == 7 { - XCTAssertEqual(msg.oneofFixed32, 57) - } else { - XCTAssertEqual(msg.oneofFixed32, 0, "i = \(i)") - } - if i == 8 { - XCTAssertEqual(msg.oneofFixed64, 58) - } else { - XCTAssertEqual(msg.oneofFixed64, 0, "i = \(i)") - } - if i == 9 { - XCTAssertEqual(msg.oneofSfixed32, 59) - } else { - XCTAssertEqual(msg.oneofSfixed32, 0, "i = \(i)") - } - if i == 10 { - XCTAssertEqual(msg.oneofSfixed64, 60) - } else { - XCTAssertEqual(msg.oneofSfixed64, 0, "i = \(i)") - } - if i == 11 { - XCTAssertEqual(msg.oneofFloat, 61.0) - } else { - XCTAssertEqual(msg.oneofFloat, 0.0, "i = \(i)") - } - if i == 12 { - XCTAssertEqual(msg.oneofDouble, 62.0) - } else { - XCTAssertEqual(msg.oneofDouble, 0.0, "i = \(i)") - } - if i == 13 { - XCTAssertEqual(msg.oneofBool, true) - } else { - XCTAssertEqual(msg.oneofBool, false, "i = \(i)") - } - if i == 14 { - XCTAssertEqual(msg.oneofString, "64") - } else { - XCTAssertEqual(msg.oneofString, "", "i = \(i)") - } - if i == 15 { - XCTAssertEqual(msg.oneofBytes, Data([65])) - } else { - XCTAssertEqual(msg.oneofBytes, Data(), "i = \(i)") - } - // No group - if i == 17 { - XCTAssertEqual(msg.oneofMessage.optionalInt32, 68) - } else { - XCTAssertEqual(msg.oneofMessage.optionalInt32, 0, "i = \(i)") - } - if i == 18 { - XCTAssertEqual(msg.oneofEnum, .bar) - } else { - XCTAssertEqual(msg.oneofEnum, .foo, "i = \(i)") - } + // Now cycle through the cases. + assertRightFiledSet(0) + msg.oneofInt32 = 51 + assertRightFiledSet(1) + msg.oneofInt64 = 52 + assertRightFiledSet(2) + msg.oneofUint32 = 53 + assertRightFiledSet(3) + msg.oneofUint64 = 54 + assertRightFiledSet(4) + msg.oneofSint32 = 55 + assertRightFiledSet(5) + msg.oneofSint64 = 56 + assertRightFiledSet(6) + msg.oneofFixed32 = 57 + assertRightFiledSet(7) + msg.oneofFixed64 = 58 + assertRightFiledSet(8) + msg.oneofSfixed32 = 59 + assertRightFiledSet(9) + msg.oneofSfixed64 = 60 + assertRightFiledSet(10) + msg.oneofFloat = 61 + assertRightFiledSet(11) + msg.oneofDouble = 62 + assertRightFiledSet(12) + msg.oneofBool = true + assertRightFiledSet(13) + msg.oneofString = "64" + assertRightFiledSet(14) + msg.oneofBytes = Data([65]) + assertRightFiledSet(15) + // No group + msg.oneofMessage.optionalInt32 = 68 + assertRightFiledSet(17) + msg.oneofEnum = .bar + assertRightFiledSet(18) } - - // Now cycle through the cases. - assertRightFiledSet(0) - msg.oneofInt32 = 51 - assertRightFiledSet(1) - msg.oneofInt64 = 52 - assertRightFiledSet(2) - msg.oneofUint32 = 53 - assertRightFiledSet(3) - msg.oneofUint64 = 54 - assertRightFiledSet(4) - msg.oneofSint32 = 55 - assertRightFiledSet(5) - msg.oneofSint64 = 56 - assertRightFiledSet(6) - msg.oneofFixed32 = 57 - assertRightFiledSet(7) - msg.oneofFixed64 = 58 - assertRightFiledSet(8) - msg.oneofSfixed32 = 59 - assertRightFiledSet(9) - msg.oneofSfixed64 = 60 - assertRightFiledSet(10) - msg.oneofFloat = 61 - assertRightFiledSet(11) - msg.oneofDouble = 62 - assertRightFiledSet(12) - msg.oneofBool = true - assertRightFiledSet(13) - msg.oneofString = "64" - assertRightFiledSet(14) - msg.oneofBytes = Data([65]) - assertRightFiledSet(15) - // No group - msg.oneofMessage.optionalInt32 = 68 - assertRightFiledSet(17) - msg.oneofEnum = .bar - assertRightFiledSet(18) - } } diff --git a/Tests/SwiftProtobufTests/Test_Packed.swift b/Tests/SwiftProtobufTests/Test_Packed.swift index c10e97fab..3024157ce 100644 --- a/Tests/SwiftProtobufTests/Test_Packed.swift +++ b/Tests/SwiftProtobufTests/Test_Packed.swift @@ -20,13 +20,17 @@ final class Test_Packed: XCTestCase, PBTestHelpers { typealias MessageTestType = SwiftProtoTesting_TestPackedTypes func testEncoding_packedInt32() { - assertEncode([210, 5, 16, 255, 255, 255, 255, 7, 0, 128, 128, 128, 128, 248, 255, 255, 255, 255, 1]) {(o: inout MessageTestType) in o.packedInt32 = [Int32.max, 0, Int32.min]} - assertDecodeSucceeds([210, 5, 6, 8, 247, 255, 255, 255, 15]) {$0.packedInt32 == [8, -9]} - assertDecodeSucceeds([210, 5, 0]) {$0.packedInt32 == []} - assertDecodeSucceeds([208, 5, 0, 208, 5, 1]) {$0.packedInt32 == [0, 1]} // Also accept non-packed + assertEncode([210, 5, 16, 255, 255, 255, 255, 7, 0, 128, 128, 128, 128, 248, 255, 255, 255, 255, 1]) { + (o: inout MessageTestType) in o.packedInt32 = [Int32.max, 0, Int32.min] + } + assertDecodeSucceeds([210, 5, 6, 8, 247, 255, 255, 255, 15]) { $0.packedInt32 == [8, -9] } + assertDecodeSucceeds([210, 5, 0]) { $0.packedInt32 == [] } + assertDecodeSucceeds([208, 5, 0, 208, 5, 1]) { $0.packedInt32 == [0, 1] } // Also accept non-packed // Truncate 32-bit values that overflow - assertDecodeSucceeds([210, 5, 11, 8, 247, 255, 255, 255, 255, 255, 255, 255, 255, 1]) {$0.packedInt32 == [8, -9]} + assertDecodeSucceeds([210, 5, 11, 8, 247, 255, 255, 255, 255, 255, 255, 255, 255, 1]) { + $0.packedInt32 == [8, -9] + } assertDecodeFails([210, 5, 12, 8, 247, 255, 255, 255, 255, 255, 255, 255, 255, 1]) assertDecodeFails([210, 5, 10, 8, 247, 255, 255, 255, 255, 255, 255, 255, 255]) @@ -39,13 +43,24 @@ final class Test_Packed: XCTestCase, PBTestHelpers { } func testEncoding_packedInt64() { - assertEncode([218, 5, 20, 255, 255, 255, 255, 255, 255, 255, 255, 127, 0, 128, 128, 128, 128, 128, 128, 128, 128, 128, 1]) {(o: inout MessageTestType) in o.packedInt64 = [Int64.max, 0, Int64.min]} - assertDecodeSucceeds([218, 5, 18, 255, 255, 153, 166, 234, 175, 227, 1, 185, 156, 196, 237, 158, 222, 230, 255, 255, 1]) {$0.packedInt64 == [999999999999999, -111111111111111]} - assertDecodeSucceeds([218, 5, 18, 255, 255, 153, 166, 234, 175, 227, 1, 185, 156, 196, 237, 158, 222, 230, 255, 255, 1, 218, 5, 18, 255, 255, 153, 166, 234, 175, 227, 1, 185, 156, 196, 237, 158, 222, 230, 255, 255, 1]) {$0.packedInt64 == [999999999999999, -111111111111111, 999999999999999, -111111111111111]} - assertDecodeSucceeds([218, 5, 0]) {$0.packedInt64 == []} - assertDecodeFails([218, 5, 19, 255, 255, 153, 166, 234, 175, 227, 1, 185, 156, 196, 237, 158, 222, 230, 255, 255, 1]) - assertDecodeFails([218, 5, 17, 255, 255, 153, 166, 234, 175, 227, 1, 185, 156, 196, 237, 158, 222, 230, 255, 255]) - assertDecodeSucceeds([216, 5, 0]) {$0.packedInt64 == [0]} // Accept non-packed encoding + assertEncode([ + 218, 5, 20, 255, 255, 255, 255, 255, 255, 255, 255, 127, 0, 128, 128, 128, 128, 128, 128, 128, 128, 128, 1, + ]) { (o: inout MessageTestType) in o.packedInt64 = [Int64.max, 0, Int64.min] } + assertDecodeSucceeds([ + 218, 5, 18, 255, 255, 153, 166, 234, 175, 227, 1, 185, 156, 196, 237, 158, 222, 230, 255, 255, 1, + ]) { $0.packedInt64 == [999_999_999_999_999, -111_111_111_111_111] } + assertDecodeSucceeds([ + 218, 5, 18, 255, 255, 153, 166, 234, 175, 227, 1, 185, 156, 196, 237, 158, 222, 230, 255, 255, 1, 218, 5, + 18, 255, 255, 153, 166, 234, 175, 227, 1, 185, 156, 196, 237, 158, 222, 230, 255, 255, 1, + ]) { $0.packedInt64 == [999_999_999_999_999, -111_111_111_111_111, 999_999_999_999_999, -111_111_111_111_111] } + assertDecodeSucceeds([218, 5, 0]) { $0.packedInt64 == [] } + assertDecodeFails([ + 218, 5, 19, 255, 255, 153, 166, 234, 175, 227, 1, 185, 156, 196, 237, 158, 222, 230, 255, 255, 1, + ]) + assertDecodeFails([ + 218, 5, 17, 255, 255, 153, 166, 234, 175, 227, 1, 185, 156, 196, 237, 158, 222, 230, 255, 255, + ]) + assertDecodeSucceeds([216, 5, 0]) { $0.packedInt64 == [0] } // Accept non-packed encoding assertDecodeFails([217, 5]) assertDecodeFails([217, 5, 0]) assertDecodeFails([217, 5, 217, 5]) @@ -67,12 +82,18 @@ final class Test_Packed: XCTestCase, PBTestHelpers { } func testEncoding_packedUint32() { - assertEncode([226, 5, 6, 255, 255, 255, 255, 15, 0]) {(o: inout MessageTestType) in o.packedUint32 = [UInt32.max, UInt32.min]} - assertDecodeSucceeds([226, 5, 5, 210, 9, 213, 187, 3]) {$0.packedUint32 == [1234, 56789]} - assertDecodeSucceeds([226, 5, 12, 255, 255, 255, 255, 15, 255, 255, 255, 255, 7, 1, 0]) {$0.packedUint32 == [4294967295, 2147483647, 1, 0]} - assertDecodeSucceeds([224, 5, 1, 224, 5, 2]) {$0.packedUint32 == [1, 2]} + assertEncode([226, 5, 6, 255, 255, 255, 255, 15, 0]) { (o: inout MessageTestType) in + o.packedUint32 = [UInt32.max, UInt32.min] + } + assertDecodeSucceeds([226, 5, 5, 210, 9, 213, 187, 3]) { $0.packedUint32 == [1234, 56789] } + assertDecodeSucceeds([226, 5, 12, 255, 255, 255, 255, 15, 255, 255, 255, 255, 7, 1, 0]) { + $0.packedUint32 == [4_294_967_295, 2_147_483_647, 1, 0] + } + assertDecodeSucceeds([224, 5, 1, 224, 5, 2]) { $0.packedUint32 == [1, 2] } // Truncate on 32-bit overflow - assertDecodeSucceeds([226, 5, 12, 255, 255, 255, 255, 31, 255, 255, 255, 255, 7, 1, 0]) {$0.packedUint32 == [4294967295, 2147483647, 1, 0]} + assertDecodeSucceeds([226, 5, 12, 255, 255, 255, 255, 31, 255, 255, 255, 255, 7, 1, 0]) { + $0.packedUint32 == [4_294_967_295, 2_147_483_647, 1, 0] + } assertDecodeFails([226, 5, 4, 255, 255, 255, 255]) assertDecodeFails([225, 5, 0]) @@ -90,12 +111,16 @@ final class Test_Packed: XCTestCase, PBTestHelpers { } func testEncoding_packedUint64() { - assertEncode([234, 5, 11, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1, 0]) {(o: inout MessageTestType) in o.packedUint64 = [UInt64.max, UInt64.min]} - assertDecodeSucceeds([234, 5, 9, 149, 154, 239, 58, 177, 209, 249, 214, 3]) {$0.packedUint64 == [123456789, 987654321]} - assertDecodeSucceeds([234, 5, 0]) {$0.packedUint64 == []} - assertDecodeSucceeds([234, 5, 1, 1, 232, 5, 2]) {$0.packedUint64 == [1, 2]} - assertDecodeFails([234, 5, 9, 149, 154, 239, 58, 177, 209, 249, 4]) // Truncated body - assertDecodeFails([234, 5, 8, 149, 154, 239, 58, 177, 209, 249, 214]) // Malformed varint + assertEncode([234, 5, 11, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1, 0]) { (o: inout MessageTestType) in + o.packedUint64 = [UInt64.max, UInt64.min] + } + assertDecodeSucceeds([234, 5, 9, 149, 154, 239, 58, 177, 209, 249, 214, 3]) { + $0.packedUint64 == [123_456_789, 987_654_321] + } + assertDecodeSucceeds([234, 5, 0]) { $0.packedUint64 == [] } + assertDecodeSucceeds([234, 5, 1, 1, 232, 5, 2]) { $0.packedUint64 == [1, 2] } + assertDecodeFails([234, 5, 9, 149, 154, 239, 58, 177, 209, 249, 4]) // Truncated body + assertDecodeFails([234, 5, 8, 149, 154, 239, 58, 177, 209, 249, 214]) // Malformed varint assertDecodeFails([233, 5]) assertDecodeFails([233, 5, 0]) assertDecodeFails([235, 5]) @@ -111,17 +136,23 @@ final class Test_Packed: XCTestCase, PBTestHelpers { } func testEncoding_packedSint32() { - assertEncode([242, 5, 10, 254, 255, 255, 255, 15, 255, 255, 255, 255, 15]) {(o: inout MessageTestType) in o.packedSint32 = [Int32.max, Int32.min]} - assertDecodeSucceeds([242, 5, 13, 255, 255, 255, 255, 15, 1, 0, 2, 254, 255, 255, 255, 15]) {$0.packedSint32 == [-2147483648, -1, 0, 1, 2147483647]} - assertDecodeSucceeds([242, 5, 5, 255, 255, 255, 255, 15, 242, 5, 3, 1, 0, 2, 242, 5, 0, 242, 5, 5, 254, 255, 255, 255, 15]) {$0.packedSint32 == [-2147483648, -1, 0, 1, 2147483647]} - assertDecodeSucceeds([242, 5, 0]) {$0.packedSint32 == []} - assertDecodeSucceeds([240, 5, 0]) {$0.packedSint32 == [0]} - assertDecodeSucceeds([242, 5, 0, 240, 5, 0]) {$0.packedSint32 == [0]} + assertEncode([242, 5, 10, 254, 255, 255, 255, 15, 255, 255, 255, 255, 15]) { (o: inout MessageTestType) in + o.packedSint32 = [Int32.max, Int32.min] + } + assertDecodeSucceeds([242, 5, 13, 255, 255, 255, 255, 15, 1, 0, 2, 254, 255, 255, 255, 15]) { + $0.packedSint32 == [-2_147_483_648, -1, 0, 1, 2_147_483_647] + } + assertDecodeSucceeds([ + 242, 5, 5, 255, 255, 255, 255, 15, 242, 5, 3, 1, 0, 2, 242, 5, 0, 242, 5, 5, 254, 255, 255, 255, 15, + ]) { $0.packedSint32 == [-2_147_483_648, -1, 0, 1, 2_147_483_647] } + assertDecodeSucceeds([242, 5, 0]) { $0.packedSint32 == [] } + assertDecodeSucceeds([240, 5, 0]) { $0.packedSint32 == [0] } + assertDecodeSucceeds([242, 5, 0, 240, 5, 0]) { $0.packedSint32 == [0] } // 32-bit overflow truncates - assertDecodeSucceeds([242, 5, 5, 255, 255, 255, 255, 127]) {$0.packedSint32 == [-2147483648]} + assertDecodeSucceeds([242, 5, 5, 255, 255, 255, 255, 127]) { $0.packedSint32 == [-2_147_483_648] } - assertDecodeFails([242, 5, 5, 255, 255, 255, 255]) // truncated body - assertDecodeFails([242, 5, 4, 255, 255, 255, 255]) // malformed varint + assertDecodeFails([242, 5, 5, 255, 255, 255, 255]) // truncated body + assertDecodeFails([242, 5, 4, 255, 255, 255, 255]) // malformed varint assertDecodeFails([241, 5]) assertDecodeFails([241, 5, 0]) assertDecodeFails([243, 5]) @@ -137,11 +168,17 @@ final class Test_Packed: XCTestCase, PBTestHelpers { } func testEncoding_packedSint64() { - assertEncode([250, 5, 20, 254, 255, 255, 255, 255, 255, 255, 255, 255, 1, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1]) {(o: inout MessageTestType) in o.packedSint64 = [Int64.max, Int64.min]} - assertDecodeSucceeds([250, 5, 9, 170, 180, 222, 117, 225, 162, 243, 173, 7]) {$0.packedSint64 == [123456789, -987654321]} - assertDecodeSucceeds([250, 5, 4, 170, 180, 222, 117, 250, 5, 5, 225, 162, 243, 173, 7]) {$0.packedSint64 == [123456789, -987654321]} - assertDecodeSucceeds([248, 5, 0, 250, 5, 2, 1, 2]) {$0.packedSint64 == [0, -1, 1]} - assertDecodeSucceeds([250, 5, 0]) {$0.packedSint64 == []} + assertEncode([ + 250, 5, 20, 254, 255, 255, 255, 255, 255, 255, 255, 255, 1, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1, + ]) { (o: inout MessageTestType) in o.packedSint64 = [Int64.max, Int64.min] } + assertDecodeSucceeds([250, 5, 9, 170, 180, 222, 117, 225, 162, 243, 173, 7]) { + $0.packedSint64 == [123_456_789, -987_654_321] + } + assertDecodeSucceeds([250, 5, 4, 170, 180, 222, 117, 250, 5, 5, 225, 162, 243, 173, 7]) { + $0.packedSint64 == [123_456_789, -987_654_321] + } + assertDecodeSucceeds([248, 5, 0, 250, 5, 2, 1, 2]) { $0.packedSint64 == [0, -1, 1] } + assertDecodeSucceeds([250, 5, 0]) { $0.packedSint64 == [] } assertDecodeFails([250, 5, 9, 170, 180, 222, 117, 225, 162, 243, 7]) assertDecodeFails([250, 5, 8, 170, 180, 222, 117, 225, 162, 243, 173]) assertDecodeFails([249, 5]) @@ -159,11 +196,17 @@ final class Test_Packed: XCTestCase, PBTestHelpers { } func testEncoding_packedFixed32() { - assertEncode([130, 6, 8, 255, 255, 255, 255, 0, 0, 0, 0]) {(o: inout MessageTestType) in o.packedFixed32 = [UInt32.max, UInt32.min]} - assertDecodeSucceeds([130, 6, 8, 255, 255, 255, 127, 255, 255, 255, 255]) {$0.packedFixed32 == [2147483647, 4294967295]} - assertDecodeSucceeds([130, 6, 4, 255, 255, 255, 127, 130, 6, 4, 255, 255, 255, 255]) {$0.packedFixed32 == [2147483647, 4294967295]} - assertDecodeSucceeds([130, 6, 0]) {$0.packedFixed32 == []} - assertDecodeSucceeds([133, 6, 0, 0, 0, 0]) {$0.packedFixed32 == [0]} + assertEncode([130, 6, 8, 255, 255, 255, 255, 0, 0, 0, 0]) { (o: inout MessageTestType) in + o.packedFixed32 = [UInt32.max, UInt32.min] + } + assertDecodeSucceeds([130, 6, 8, 255, 255, 255, 127, 255, 255, 255, 255]) { + $0.packedFixed32 == [2_147_483_647, 4_294_967_295] + } + assertDecodeSucceeds([130, 6, 4, 255, 255, 255, 127, 130, 6, 4, 255, 255, 255, 255]) { + $0.packedFixed32 == [2_147_483_647, 4_294_967_295] + } + assertDecodeSucceeds([130, 6, 0]) { $0.packedFixed32 == [] } + assertDecodeSucceeds([133, 6, 0, 0, 0, 0]) { $0.packedFixed32 == [0] } assertDecodeFails([130, 6, 4, 8, 255, 255, 255, 127, 255, 255, 255]) assertDecodeFails([130, 6, 7, 255, 255, 255, 127, 255, 255, 255]) assertDecodeFails([128, 6]) @@ -187,13 +230,27 @@ final class Test_Packed: XCTestCase, PBTestHelpers { } func testEncoding_packedFixed64() { - assertEncode([138, 6, 16, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0]) {(o: inout MessageTestType) in o.packedFixed64 = [UInt64.max, UInt64.min]} - assertDecodeSucceeds([138, 6, 24, 255, 255, 255, 127, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255]) {$0.packedFixed64 == [2147483647, 4294967295, 18446744073709551615]} - assertDecodeSucceeds([138, 6, 8, 255, 255, 255, 127, 0, 0, 0, 0, 138, 6, 16, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255]) {$0.packedFixed64 == [2147483647, 4294967295, 18446744073709551615]} - assertDecodeSucceeds([138, 6, 0]) {$0.packedFixed64 == []} - assertDecodeSucceeds([137, 6, 0, 0, 0, 0, 0, 0, 0, 0]) {$0.packedFixed64 == [0]} - assertDecodeFails([138, 6, 24, 255, 255, 255, 127, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255]) - assertDecodeFails([138, 6, 23, 255, 255, 255, 127, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255]) + assertEncode([138, 6, 16, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0]) { + (o: inout MessageTestType) in o.packedFixed64 = [UInt64.max, UInt64.min] + } + assertDecodeSucceeds([ + 138, 6, 24, 255, 255, 255, 127, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, + 255, 255, + ]) { $0.packedFixed64 == [2_147_483_647, 4_294_967_295, 18_446_744_073_709_551_615] } + assertDecodeSucceeds([ + 138, 6, 8, 255, 255, 255, 127, 0, 0, 0, 0, 138, 6, 16, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, + 255, 255, 255, 255, + ]) { $0.packedFixed64 == [2_147_483_647, 4_294_967_295, 18_446_744_073_709_551_615] } + assertDecodeSucceeds([138, 6, 0]) { $0.packedFixed64 == [] } + assertDecodeSucceeds([137, 6, 0, 0, 0, 0, 0, 0, 0, 0]) { $0.packedFixed64 == [0] } + assertDecodeFails([ + 138, 6, 24, 255, 255, 255, 127, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, + 255, + ]) + assertDecodeFails([ + 138, 6, 23, 255, 255, 255, 127, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, + 255, + ]) assertDecodeFails([136, 6]) assertDecodesAsUnknownFields([136, 6, 0]) // Wrong wire type (varint), valid as an unknown field assertDecodeFails([136, 6, 0, 0, 0, 0, 0, 0, 0, 0]) @@ -215,12 +272,20 @@ final class Test_Packed: XCTestCase, PBTestHelpers { } func testEncoding_packedSfixed32() { - assertEncode([146, 6, 8, 255, 255, 255, 127, 0, 0, 0, 128]) {(o: inout MessageTestType) in o.packedSfixed32 = [Int32.max, Int32.min]} - assertDecodeSucceeds([146, 6, 12, 0, 0, 0, 128, 1, 0, 0, 0, 255, 255, 255, 127]) {$0.packedSfixed32 == [-2147483648, 1, 2147483647]} - assertDecodeSucceeds([146, 6, 4, 0, 0, 0, 128, 146, 6, 8, 1, 0, 0, 0, 255, 255, 255, 127]) {$0.packedSfixed32 == [-2147483648, 1, 2147483647]} - assertDecodeSucceeds([146, 6, 4, 0, 0, 0, 128, 146, 6, 0, 146, 6, 8, 1, 0, 0, 0, 255, 255, 255, 127]) {$0.packedSfixed32 == [-2147483648, 1, 2147483647]} - assertDecodeSucceeds([149, 6, 1, 0, 0, 0, 146, 6, 4, 7, 0, 0, 0]) {$0.packedSfixed32 == [1, 7]} - assertDecodeSucceeds([146, 6, 0]) {$0.packedSfixed32 == []} + assertEncode([146, 6, 8, 255, 255, 255, 127, 0, 0, 0, 128]) { (o: inout MessageTestType) in + o.packedSfixed32 = [Int32.max, Int32.min] + } + assertDecodeSucceeds([146, 6, 12, 0, 0, 0, 128, 1, 0, 0, 0, 255, 255, 255, 127]) { + $0.packedSfixed32 == [-2_147_483_648, 1, 2_147_483_647] + } + assertDecodeSucceeds([146, 6, 4, 0, 0, 0, 128, 146, 6, 8, 1, 0, 0, 0, 255, 255, 255, 127]) { + $0.packedSfixed32 == [-2_147_483_648, 1, 2_147_483_647] + } + assertDecodeSucceeds([146, 6, 4, 0, 0, 0, 128, 146, 6, 0, 146, 6, 8, 1, 0, 0, 0, 255, 255, 255, 127]) { + $0.packedSfixed32 == [-2_147_483_648, 1, 2_147_483_647] + } + assertDecodeSucceeds([149, 6, 1, 0, 0, 0, 146, 6, 4, 7, 0, 0, 0]) { $0.packedSfixed32 == [1, 7] } + assertDecodeSucceeds([146, 6, 0]) { $0.packedSfixed32 == [] } assertDecodeFails([146, 6, 12, 0, 0, 0, 128, 1, 0, 0, 0, 255, 255, 255]) assertDecodeFails([146, 6, 11, 0, 0, 0, 128, 1, 0, 0, 0, 255, 255, 255]) assertDecodesAsUnknownFields([144, 6, 5]) // Wrong wire type (varint), valid as an unknown field @@ -244,14 +309,35 @@ final class Test_Packed: XCTestCase, PBTestHelpers { } func testEncoding_packedSfixed64() { - assertEncode([154, 6, 16, 255, 255, 255, 255, 255, 255, 255, 127, 0, 0, 0, 0, 0, 0, 0, 128]) {(o: inout MessageTestType) in o.packedSfixed64 = [Int64.max, Int64.min]} - assertDecodeSucceeds([154, 6, 32, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 127, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 127]) {$0.packedSfixed64 == [-9223372036854775808, 2147483647, 4294967295, 9223372036854775807]} - assertDecodeSucceeds([154, 6, 8, 0, 0, 0, 0, 0, 0, 0, 128, 154, 6, 0, 154, 6, 16, 255, 255, 255, 127, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 154, 6, 8, 255, 255, 255, 255, 255, 255, 255, 127]) {$0.packedSfixed64 == [-9223372036854775808, 2147483647, 4294967295, 9223372036854775807]} - assertDecodeSucceeds([154, 6, 0]) {$0.packedSfixed64 == []} - assertDecodeSucceeds([153, 6, 3, 0, 0, 0, 0, 0, 0, 0]) {$0.packedSfixed64 == [3]} - assertDecodeFails([154, 6, 33, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 127, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 127]) - assertDecodeFails([154, 6, 32, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 127, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255]) - assertDecodeFails([154, 6, 31, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 127, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255]) + assertEncode([154, 6, 16, 255, 255, 255, 255, 255, 255, 255, 127, 0, 0, 0, 0, 0, 0, 0, 128]) { + (o: inout MessageTestType) in o.packedSfixed64 = [Int64.max, Int64.min] + } + assertDecodeSucceeds([ + 154, 6, 32, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 127, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, + 255, 255, 255, 255, 255, 255, 127, + ]) { + $0.packedSfixed64 == [-9_223_372_036_854_775_808, 2_147_483_647, 4_294_967_295, 9_223_372_036_854_775_807] + } + assertDecodeSucceeds([ + 154, 6, 8, 0, 0, 0, 0, 0, 0, 0, 128, 154, 6, 0, 154, 6, 16, 255, 255, 255, 127, 0, 0, 0, 0, 255, 255, 255, + 255, 0, 0, 0, 0, 154, 6, 8, 255, 255, 255, 255, 255, 255, 255, 127, + ]) { + $0.packedSfixed64 == [-9_223_372_036_854_775_808, 2_147_483_647, 4_294_967_295, 9_223_372_036_854_775_807] + } + assertDecodeSucceeds([154, 6, 0]) { $0.packedSfixed64 == [] } + assertDecodeSucceeds([153, 6, 3, 0, 0, 0, 0, 0, 0, 0]) { $0.packedSfixed64 == [3] } + assertDecodeFails([ + 154, 6, 33, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 127, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, + 255, 255, 255, 255, 255, 255, 127, + ]) + assertDecodeFails([ + 154, 6, 32, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 127, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, + 255, 255, 255, 255, 255, 255, + ]) + assertDecodeFails([ + 154, 6, 31, 0, 0, 0, 0, 0, 0, 0, 128, 255, 255, 255, 127, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, + 255, 255, 255, 255, 255, 255, + ]) assertDecodeFails([152, 6]) assertDecodesAsUnknownFields([152, 6, 0]) // Wrong wire type (varint), valid as an unknown field assertDecodeFails([152, 6, 0, 0, 0, 0, 0, 0, 0, 0]) @@ -273,34 +359,48 @@ final class Test_Packed: XCTestCase, PBTestHelpers { } func testEncoding_packedFloat() { - assertEncode([162, 6, 8, 0, 0, 0, 63, 0, 0, 128, 62]) {(o: inout MessageTestType) in o.packedFloat = [0.5, 0.25]} - assertDecodeSucceeds([162, 6, 8, 0, 0, 0, 63, 0, 0, 128, 62]) {$0.packedFloat == [0.5, 0.25]} - assertDecodeSucceeds([162, 6, 4, 0, 0, 0, 63, 162, 6, 4, 0, 0, 128, 62]) {$0.packedFloat == [0.5, 0.25]} - assertDecodeSucceeds([165, 6, 0, 0, 0, 63, 162, 6, 4, 0, 0, 128, 62]) {$0.packedFloat == [0.5, 0.25]} - assertDecodeSucceeds([162, 6, 4, 0, 0, 0, 63, 165, 6, 0, 0, 128, 62]) {$0.packedFloat == [0.5, 0.25]} - assertDecodeSucceeds([165, 6, 0, 0, 0, 63, 165, 6, 0, 0, 128, 62]) {$0.packedFloat == [0.5, 0.25]} - assertDecodeSucceeds([162, 6, 0]) {$0.packedFloat == []} + assertEncode([162, 6, 8, 0, 0, 0, 63, 0, 0, 128, 62]) { (o: inout MessageTestType) in + o.packedFloat = [0.5, 0.25] + } + assertDecodeSucceeds([162, 6, 8, 0, 0, 0, 63, 0, 0, 128, 62]) { $0.packedFloat == [0.5, 0.25] } + assertDecodeSucceeds([162, 6, 4, 0, 0, 0, 63, 162, 6, 4, 0, 0, 128, 62]) { $0.packedFloat == [0.5, 0.25] } + assertDecodeSucceeds([165, 6, 0, 0, 0, 63, 162, 6, 4, 0, 0, 128, 62]) { $0.packedFloat == [0.5, 0.25] } + assertDecodeSucceeds([162, 6, 4, 0, 0, 0, 63, 165, 6, 0, 0, 128, 62]) { $0.packedFloat == [0.5, 0.25] } + assertDecodeSucceeds([165, 6, 0, 0, 0, 63, 165, 6, 0, 0, 128, 62]) { $0.packedFloat == [0.5, 0.25] } + assertDecodeSucceeds([162, 6, 0]) { $0.packedFloat == [] } assertDecodeFails([162, 6, 8, 0, 0, 0, 63, 0, 0, 128]) assertDecodeFails([162, 6, 7, 0, 0, 0, 63, 0, 0, 128]) - assertDecodeFails([160, 6]) // Cannot use wire type 0 - assertDecodeFails([161, 6]) // Cannot use wire type 1 - assertDecodeFails([163, 6]) // Cannot use wire type 3 - assertDecodeFails([164, 6]) // Cannot use wire type 4 - assertDecodeFails([166, 6]) // Cannot use wire type 6 - assertDecodeFails([167, 6]) // Cannot use wire type 7 + assertDecodeFails([160, 6]) // Cannot use wire type 0 + assertDecodeFails([161, 6]) // Cannot use wire type 1 + assertDecodeFails([163, 6]) // Cannot use wire type 3 + assertDecodeFails([164, 6]) // Cannot use wire type 4 + assertDecodeFails([166, 6]) // Cannot use wire type 6 + assertDecodeFails([167, 6]) // Cannot use wire type 7 } func testEncoding_packedDouble() { - assertEncode([170, 6, 16, 0, 0, 0, 0, 0, 0, 224, 63, 0, 0, 0, 0, 0, 0, 208, 63]) {(o: inout MessageTestType) in o.packedDouble = [0.5, 0.25]} - assertDecodeSucceeds([170, 6, 16, 0, 0, 0, 0, 0, 0, 224, 63, 0, 0, 0, 0, 0, 0, 208, 63]) {$0.packedDouble == [0.5, 0.25]} - assertDecodeSucceeds([170, 6, 0]) {$0.packedDouble == []} + assertEncode([170, 6, 16, 0, 0, 0, 0, 0, 0, 224, 63, 0, 0, 0, 0, 0, 0, 208, 63]) { (o: inout MessageTestType) in + o.packedDouble = [0.5, 0.25] + } + assertDecodeSucceeds([170, 6, 16, 0, 0, 0, 0, 0, 0, 224, 63, 0, 0, 0, 0, 0, 0, 208, 63]) { + $0.packedDouble == [0.5, 0.25] + } + assertDecodeSucceeds([170, 6, 0]) { $0.packedDouble == [] } assertDecodeFails([170, 6, 16, 0, 0, 0, 0, 0, 0, 224, 63, 0, 0, 0, 0, 0, 0, 208]) assertDecodeFails([170, 6, 15, 0, 0, 0, 0, 0, 0, 224, 63, 0, 0, 0, 0, 0, 0, 208]) assertDecodeFails([170, 6, 16, 0, 0, 0, 0, 0, 0, 224, 63]) - assertDecodeSucceeds([169, 6, 0, 0, 0, 0, 0, 0, 224, 63, 169, 6, 0, 0, 0, 0, 0, 0, 208, 63]) {$0.packedDouble == [0.5, 0.25]} - assertDecodeSucceeds([169, 6, 0, 0, 0, 0, 0, 0, 224, 63, 170, 6, 8, 0, 0, 0, 0, 0, 0, 208, 63]) {$0.packedDouble == [0.5, 0.25]} - assertDecodeSucceeds([170, 6, 8, 0, 0, 0, 0, 0, 0, 224, 63, 169, 6, 0, 0, 0, 0, 0, 0, 208, 63]) {$0.packedDouble == [0.5, 0.25]} - assertDecodeSucceeds([170, 6, 8, 0, 0, 0, 0, 0, 0, 224, 63, 170, 6, 8, 0, 0, 0, 0, 0, 0, 208, 63]) {$0.packedDouble == [0.5, 0.25]} + assertDecodeSucceeds([169, 6, 0, 0, 0, 0, 0, 0, 224, 63, 169, 6, 0, 0, 0, 0, 0, 0, 208, 63]) { + $0.packedDouble == [0.5, 0.25] + } + assertDecodeSucceeds([169, 6, 0, 0, 0, 0, 0, 0, 224, 63, 170, 6, 8, 0, 0, 0, 0, 0, 0, 208, 63]) { + $0.packedDouble == [0.5, 0.25] + } + assertDecodeSucceeds([170, 6, 8, 0, 0, 0, 0, 0, 0, 224, 63, 169, 6, 0, 0, 0, 0, 0, 0, 208, 63]) { + $0.packedDouble == [0.5, 0.25] + } + assertDecodeSucceeds([170, 6, 8, 0, 0, 0, 0, 0, 0, 224, 63, 170, 6, 8, 0, 0, 0, 0, 0, 0, 208, 63]) { + $0.packedDouble == [0.5, 0.25] + } assertDecodeFails([168, 6]) assertDecodesAsUnknownFields([168, 6, 0]) // Wrong wire type (varint), valid as an unknown field assertDecodeFails([168, 6, 0, 0, 0, 0, 0, 0, 0, 0]) @@ -322,15 +422,20 @@ final class Test_Packed: XCTestCase, PBTestHelpers { } func testEncoding_packedBool() { - assertEncode([178, 6, 4, 1, 0, 0, 1]) {(o: inout MessageTestType) in o.packedBool = [true, false, false, true]} - assertDecodeSucceeds([178, 6, 4, 1, 0, 0, 1]) {$0.packedBool == [true, false, false, true]} - assertDecodeSucceeds([178, 6, 5, 255, 1, 0, 0, 1]) {$0.packedBool == [true, false, false, true]} - assertDecodeSucceeds([178, 6, 5, 1, 128, 0, 0, 1]) {$0.packedBool == [true, false, false, true]} - assertDecodeSucceeds([178, 6, 14, 1, 128, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127]) {$0.packedBool == [true, false, false, true]} - assertDecodeSucceeds([178, 6, 1, 1, 178, 6, 0, 178, 6, 3, 0, 0, 1]) {$0.packedBool == [true, false, false, true]} - assertDecodeSucceeds([178, 6, 0]) {$0.packedBool == []} - assertDecodeSucceeds([176, 6, 0]) {$0.packedBool == [false]} - assertDecodeSucceeds([178, 6, 2, 0, 1, 176, 6, 0]) {$0.packedBool == [false, true, false]} + assertEncode([178, 6, 4, 1, 0, 0, 1]) { (o: inout MessageTestType) in o.packedBool = [true, false, false, true] + } + assertDecodeSucceeds([178, 6, 4, 1, 0, 0, 1]) { $0.packedBool == [true, false, false, true] } + assertDecodeSucceeds([178, 6, 5, 255, 1, 0, 0, 1]) { $0.packedBool == [true, false, false, true] } + assertDecodeSucceeds([178, 6, 5, 1, 128, 0, 0, 1]) { $0.packedBool == [true, false, false, true] } + assertDecodeSucceeds([178, 6, 14, 1, 128, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127]) { + $0.packedBool == [true, false, false, true] + } + assertDecodeSucceeds([178, 6, 1, 1, 178, 6, 0, 178, 6, 3, 0, 0, 1]) { + $0.packedBool == [true, false, false, true] + } + assertDecodeSucceeds([178, 6, 0]) { $0.packedBool == [] } + assertDecodeSucceeds([176, 6, 0]) { $0.packedBool == [false] } + assertDecodeSucceeds([178, 6, 2, 0, 1, 176, 6, 0]) { $0.packedBool == [false, true, false] } assertDecodeFails([178, 6, 4, 1, 0, 0]) assertDecodeFails([178, 6, 3, 1, 0, 128]) @@ -350,15 +455,17 @@ final class Test_Packed: XCTestCase, PBTestHelpers { } func testEncoding_packedEnum() throws { - assertEncode([186, 6, 2, 5, 4]) {(o: inout MessageTestType) in o.packedEnum = [.foreignBar, .foreignFoo]} - assertDecodeSucceeds([186, 6, 2, 4, 5]) {$0.packedEnum == [.foreignFoo, .foreignBar]} - assertDecodeSucceeds([186, 6, 0]) {$0.packedEnum == []} - assertDecodeSucceeds([186, 6, 1, 5, 186, 6, 0, 186, 6, 2, 132, 0]) {$0.packedEnum == [.foreignBar, .foreignFoo]} + assertEncode([186, 6, 2, 5, 4]) { (o: inout MessageTestType) in o.packedEnum = [.foreignBar, .foreignFoo] } + assertDecodeSucceeds([186, 6, 2, 4, 5]) { $0.packedEnum == [.foreignFoo, .foreignBar] } + assertDecodeSucceeds([186, 6, 0]) { $0.packedEnum == [] } + assertDecodeSucceeds([186, 6, 1, 5, 186, 6, 0, 186, 6, 2, 132, 0]) { + $0.packedEnum == [.foreignBar, .foreignFoo] + } // Packed enums can be stored as plain repeated - assertDecodeSucceeds([186, 6, 2, 4, 6, 184, 6, 5]) {$0.packedEnum == [.foreignFoo, .foreignBaz, .foreignBar]} + assertDecodeSucceeds([186, 6, 2, 4, 6, 184, 6, 5]) { $0.packedEnum == [.foreignFoo, .foreignBaz, .foreignBar] } // Proto2 converts unrecognized enum values into unknowns - assertDecodeSucceeds([186, 6, 2, 6, 99]) {$0.packedEnum == [.foreignBaz]} - + assertDecodeSucceeds([186, 6, 2, 6, 99]) { $0.packedEnum == [.foreignBaz] } + // Unknown enums within packed become separate unknown entries do { let decoded1 = try SwiftProtoTesting_TestPackedTypes(serializedBytes: [186, 6, 3, 4, 99, 6]) @@ -385,5 +492,3 @@ final class Test_Packed: XCTestCase, PBTestHelpers { assertDecodeFails([191, 6, 0]) } } - - diff --git a/Tests/SwiftProtobufTests/Test_ParsingMerge.swift b/Tests/SwiftProtobufTests/Test_ParsingMerge.swift index 7fc8b970f..2aed5dd37 100644 --- a/Tests/SwiftProtobufTests/Test_ParsingMerge.swift +++ b/Tests/SwiftProtobufTests/Test_ParsingMerge.swift @@ -29,7 +29,7 @@ final class Test_ParsingMerge: XCTestCase { t1.optionalInt32 = 1 t1.optionalString = "abc" var t2 = SwiftProtoTesting_TestAllTypes() - t2.optionalInt32 = 2 // Should override t1.optionalInt32 + t2.optionalInt32 = 2 // Should override t1.optionalInt32 t2.optionalInt64 = 3 m.field1 = [t1, t2] @@ -122,7 +122,7 @@ final class Test_ParsingMerge: XCTestCase { // repeated_message <== field2 without merging XCTAssertEqual(decoded.repeatedMessage, [t1, t2]) - } catch { + } catch { XCTFail("Decoding failed \(encoded)") } } catch let e { @@ -154,14 +154,13 @@ final class Test_ParsingMerge: XCTestCase { // repeated_message <== field2 without merging XCTAssertEqual(decoded.repeatedMessage, [t1, t2, t3]) - } catch { + } catch { XCTFail("Decoding failed \(encoded)") } } catch let e { XCTFail("Encoding failed for \(m) with error \(e)") } - // But, if the oneofs are set to the message field without chaning between, just like // a normal opitional/required message field, the data should be merged. @@ -184,7 +183,7 @@ final class Test_ParsingMerge: XCTestCase { // repeated_message <== field2 without merging XCTAssertEqual(decoded.repeatedMessage, [t1, t3]) - } catch { + } catch { XCTFail("Decoding failed \(encoded)") } } catch let e { diff --git a/Tests/SwiftProtobufTests/Test_Required.swift b/Tests/SwiftProtobufTests/Test_Required.swift index 53629d1ab..024f2dd73 100644 --- a/Tests/SwiftProtobufTests/Test_Required.swift +++ b/Tests/SwiftProtobufTests/Test_Required.swift @@ -28,9 +28,8 @@ // ----------------------------------------------------------------------------- import Foundation -import XCTest - import SwiftProtobuf +import XCTest final class Test_Required: XCTestCase, PBTestHelpers { typealias MessageTestType = SwiftProtoTesting_TestAllRequiredTypes @@ -74,7 +73,6 @@ final class Test_Required: XCTestCase, PBTestHelpers { XCTAssertTrue(msg2.isInitialized) } - func test_NestedInProto2_IsInitialized() { // message declared in proto2 syntax file, with fields that are another message that has // required fields. @@ -153,7 +151,11 @@ final class Test_Required: XCTestCase, PBTestHelpers { } // Helper to assert decoding fails with a not initialized error. - fileprivate func assertDecodeFailsNotInitialized(_ bytes: [UInt8], file: XCTestFileArgType = #file, line: UInt = #line) { + fileprivate func assertDecodeFailsNotInitialized( + _ bytes: [UInt8], + file: XCTestFileArgType = #file, + line: UInt = #line + ) { do { let _ = try MessageTestType(serializedBytes: bytes) XCTFail("Swift decode should have failed: \(bytes)", file: file, line: line) @@ -165,7 +167,12 @@ final class Test_Required: XCTestCase, PBTestHelpers { } // Helper to assert decoding partial succeeds. - fileprivate func assertPartialDecodeSucceeds(_ bytes: [UInt8], _ expectedTextFormat: String, file: XCTestFileArgType = #file, line: UInt = #line) { + fileprivate func assertPartialDecodeSucceeds( + _ bytes: [UInt8], + _ expectedTextFormat: String, + file: XCTestFileArgType = #file, + line: UInt = #line + ) { do { let msg = try MessageTestType(serializedBytes: bytes, partial: true) var expected = "SwiftProtobufTests.SwiftProtoTesting_TestAllRequiredTypes:\n" @@ -241,16 +248,20 @@ final class Test_Required: XCTestCase, PBTestHelpers { var allBytes: [UInt8] = [] var allTextFormattedField = "SwiftProtobufTests.SwiftProtoTesting_TestAllRequiredTypes:\n" for (bytes, textFormattedField) in testInputs { - allBytes.append(contentsOf: bytes) - allTextFormattedField.append(textFormattedField) - allTextFormattedField.append("\n") + allBytes.append(contentsOf: bytes) + allTextFormattedField.append(textFormattedField) + allTextFormattedField.append("\n") } let fullMsg = try SwiftProtoTesting_TestAllRequiredTypes(serializedBytes: allBytes) assertDebugDescription(allTextFormattedField, fullMsg) } // Helper to assert encoding fails with a not initialized error. - fileprivate func assertEncodeFailsNotInitialized(_ message: MessageTestType, file: XCTestFileArgType = #file, line: UInt = #line) { + fileprivate func assertEncodeFailsNotInitialized( + _ message: MessageTestType, + file: XCTestFileArgType = #file, + line: UInt = #line + ) { do { let _: [UInt8] = try message.serializedBytes() XCTFail("Swift encode should have failed: \(message)", file: file, line: line) @@ -262,7 +273,12 @@ final class Test_Required: XCTestCase, PBTestHelpers { } // Helper to assert encoding partial succeeds. - fileprivate func assertPartialEncodeSucceeds(_ message: MessageTestType, _ expectedBytes: [UInt8], file: XCTestFileArgType = #file, line: UInt = #line) { + fileprivate func assertPartialEncodeSucceeds( + _ message: MessageTestType, + _ expectedBytes: [UInt8], + file: XCTestFileArgType = #file, + line: UInt = #line + ) { do { let data: [UInt8] = try message.serializedBytes(partial: true) XCTAssertEqual(data, expectedBytes, "While encoding \(message)", file: file, line: line) @@ -353,7 +369,11 @@ final class Test_SmallRequired: XCTestCase, PBTestHelpers { // Check behavior of a small message (non-heap-stored) with required fields // Helper to assert decoding fails with a not initialized error. - fileprivate func assertDecodeFailsNotInitialized(_ bytes: [UInt8], file: XCTestFileArgType = #file, line: UInt = #line) { + fileprivate func assertDecodeFailsNotInitialized( + _ bytes: [UInt8], + file: XCTestFileArgType = #file, + line: UInt = #line + ) { do { let _ = try MessageTestType(serializedBytes: bytes) XCTFail("Swift decode should have failed: \(bytes)", file: file, line: line) @@ -365,7 +385,12 @@ final class Test_SmallRequired: XCTestCase, PBTestHelpers { } // Helper to assert decoding partial succeeds. - fileprivate func assertPartialDecodeSucceeds(_ bytes: [UInt8], _ expectedTextFormat: String, file: XCTestFileArgType = #file, line: UInt = #line) { + fileprivate func assertPartialDecodeSucceeds( + _ bytes: [UInt8], + _ expectedTextFormat: String, + file: XCTestFileArgType = #file, + line: UInt = #line + ) { do { let msg = try MessageTestType(serializedBytes: bytes, partial: true) var expected = "SwiftProtobufTests.SwiftProtoTesting_TestSomeRequiredTypes:\n" @@ -401,16 +426,20 @@ final class Test_SmallRequired: XCTestCase, PBTestHelpers { var allBytes: [UInt8] = [] var allTextFormattedField = "SwiftProtobufTests.SwiftProtoTesting_TestSomeRequiredTypes:\n" for (bytes, textFormattedField) in testInputs { - allBytes.append(contentsOf: bytes) - allTextFormattedField.append(textFormattedField) - allTextFormattedField.append("\n") + allBytes.append(contentsOf: bytes) + allTextFormattedField.append(textFormattedField) + allTextFormattedField.append("\n") } let fullMsg = try SwiftProtoTesting_TestSomeRequiredTypes(serializedBytes: allBytes) assertDebugDescription(allTextFormattedField, fullMsg) } // Helper to assert encoding fails with a not initialized error. - fileprivate func assertEncodeFailsNotInitialized(_ message: MessageTestType, file: XCTestFileArgType = #file, line: UInt = #line) { + fileprivate func assertEncodeFailsNotInitialized( + _ message: MessageTestType, + file: XCTestFileArgType = #file, + line: UInt = #line + ) { do { let _: [UInt8] = try message.serializedBytes() XCTFail("Swift encode should have failed: \(message)", file: file, line: line) @@ -422,7 +451,12 @@ final class Test_SmallRequired: XCTestCase, PBTestHelpers { } // Helper to assert encoding partial succeeds. - fileprivate func assertPartialEncodeSucceeds(_ message: MessageTestType, _ expectedBytes: [UInt8], file: XCTestFileArgType = #file, line: UInt = #line) { + fileprivate func assertPartialEncodeSucceeds( + _ message: MessageTestType, + _ expectedBytes: [UInt8], + file: XCTestFileArgType = #file, + line: UInt = #line + ) { do { let data: [UInt8] = try message.serializedBytes(partial: true) XCTAssertEqual(data, expectedBytes, "While encoding \(message)", file: file, line: line) diff --git a/Tests/SwiftProtobufTests/Test_Reserved.swift b/Tests/SwiftProtobufTests/Test_Reserved.swift index 359504d7c..11acbb5c8 100644 --- a/Tests/SwiftProtobufTests/Test_Reserved.swift +++ b/Tests/SwiftProtobufTests/Test_Reserved.swift @@ -17,6 +17,7 @@ import Foundation import XCTest + @testable import SwiftProtobuf final class Test_Reserved: XCTestCase { @@ -36,9 +37,18 @@ final class Test_Reserved: XCTestCase { } func testMessageNames() { - XCTAssertEqual(SwiftProtoTesting_SwiftReservedTest.classMessage.protoMessageName, "swift_proto_testing.SwiftReservedTest.class") - XCTAssertEqual(SwiftProtoTesting_SwiftReservedTest.isEqual.protoMessageName, "swift_proto_testing.SwiftReservedTest.isEqual") - XCTAssertEqual(SwiftProtoTesting_SwiftReservedTest.TypeMessage.protoMessageName, "swift_proto_testing.SwiftReservedTest.Type") + XCTAssertEqual( + SwiftProtoTesting_SwiftReservedTest.classMessage.protoMessageName, + "swift_proto_testing.SwiftReservedTest.class" + ) + XCTAssertEqual( + SwiftProtoTesting_SwiftReservedTest.isEqual.protoMessageName, + "swift_proto_testing.SwiftReservedTest.isEqual" + ) + XCTAssertEqual( + SwiftProtoTesting_SwiftReservedTest.TypeMessage.protoMessageName, + "swift_proto_testing.SwiftReservedTest.Type" + ) } func testFieldNamesMatchingMetadata() { @@ -56,7 +66,10 @@ final class Test_Reserved: XCTestCase { msg.hashValue_p = "bar" msg.debugDescription_p = 5 - assertDebugDescription("SwiftProtobufTests.SwiftProtoTesting_SwiftReservedTest:\nproto_message_name: 1\nproto_package_name: 2\nany_type_prefix: 3\nany_type_url: 4\nis_initialized: \"foo\"\nhash_value: \"bar\"\ndebug_description: 5\n", msg) + assertDebugDescription( + "SwiftProtobufTests.SwiftProtoTesting_SwiftReservedTest:\nproto_message_name: 1\nproto_package_name: 2\nany_type_prefix: 3\nany_type_url: 4\nis_initialized: \"foo\"\nhash_value: \"bar\"\ndebug_description: 5\n", + msg + ) msg.clearIsInitialized_p() msg.clearHashValue_p() diff --git a/Tests/SwiftProtobufTests/Test_SimpleExtensionMap.swift b/Tests/SwiftProtobufTests/Test_SimpleExtensionMap.swift index 50f2c2650..499ac1c1d 100644 --- a/Tests/SwiftProtobufTests/Test_SimpleExtensionMap.swift +++ b/Tests/SwiftProtobufTests/Test_SimpleExtensionMap.swift @@ -14,211 +14,223 @@ import Foundation import XCTest + @testable import SwiftProtobuf extension AnyMessageExtension { - // Support equality to simplify testing of getting the correct errors. - func isEqual(_ other: any AnyMessageExtension) -> Bool { - return (fieldNumber == other.fieldNumber && - fieldName == other.fieldName && - messageType == other.messageType) - } + // Support equality to simplify testing of getting the correct errors. + func isEqual(_ other: any AnyMessageExtension) -> Bool { + (fieldNumber == other.fieldNumber && fieldName == other.fieldName && messageType == other.messageType) + } } // Define some extension to use for testing behaviors. let ext1 = MessageExtension, SwiftProtoTesting_TestAllExtensions>( - _protobuf_fieldNumber: 1, - fieldName: "my_ext1" + _protobuf_fieldNumber: 1, + fieldName: "my_ext1" ) let ext2 = MessageExtension, SwiftProtoTesting_TestAllExtensions>( - _protobuf_fieldNumber: 2, - fieldName: "my_ext2" + _protobuf_fieldNumber: 2, + fieldName: "my_ext2" ) // Same field number as ext1, but different class being extended. let ext3 = MessageExtension, SwiftProtoTesting_TestPackedExtensions>( - _protobuf_fieldNumber: 1, - fieldName: "my_ext1b" + _protobuf_fieldNumber: 1, + fieldName: "my_ext1b" ) // Same field number and message type as ext2, so it will replace it in the mapping. let ext4 = MessageExtension, SwiftProtoTesting_TestAllExtensions>( - _protobuf_fieldNumber: 2, - fieldName: "my_ext4" + _protobuf_fieldNumber: 2, + fieldName: "my_ext4" ) - final class Test_SimpleExtensionMap: XCTestCase { - func assert(map: SimpleExtensionMap, contains: [any AnyMessageExtension], line: UInt = #line) { - // Extact what it constaings. - var includes = [any AnyMessageExtension]() - for (_, l) in map.fields { - for e in l { - includes.append(e) - } - } + func assert(map: SimpleExtensionMap, contains: [any AnyMessageExtension], line: UInt = #line) { + // Extact what it constaings. + var includes = [any AnyMessageExtension]() + for (_, l) in map.fields { + for e in l { + includes.append(e) + } + } - // Check that everything the lists match no matter the orders. - for c in contains { - var found = false - for i in includes { - if (c.isEqual(i)) { - found = true - break + // Check that everything the lists match no matter the orders. + for c in contains { + var found = false + for i in includes { + if c.isEqual(i) { + found = true + break + } + } + XCTAssertTrue(found, "Map didn't include \(c)", line: line) } - } - XCTAssertTrue(found, "Map didn't include \(c)", line: line) - } - for i in includes { - var found = false - for c in contains { - if (i.isEqual(c)) { - found = true - break + for i in includes { + var found = false + for c in contains { + if i.isEqual(c) { + found = true + break + } + } + XCTAssertTrue(found, "Map wasn't supposed to include \(i)", line: line) } - } - XCTAssertTrue(found, "Map wasn't supposed to include \(i)", line: line) } - } - - func testInsert() { - var map = SimpleExtensionMap() - XCTAssertEqual(map.fields.count, 0) - - map.insert(ext1) - assert(map: map, contains: [ext1]) - - map.insert(ext2) - assert(map: map, contains: [ext2, ext1]) - - map.insert(ext3) - assert(map: map, contains: [ext3, ext2, ext1]) - - // ext4 has the same message and number as ext2, so should replace it. - map.insert(ext4) - assert(map: map, contains: [ext4, ext1, ext3]) - } - func testInsert_contentsOf() { - var map = SimpleExtensionMap() - XCTAssertEqual(map.fields.count, 0) + func testInsert() { + var map = SimpleExtensionMap() + XCTAssertEqual(map.fields.count, 0) - map.insert(contentsOf: [ext1, ext2]) - assert(map: map, contains: [ext1, ext2]) + map.insert(ext1) + assert(map: map, contains: [ext1]) - // ext4 has the same message and number as ext2, so should replace it. - map.insert(contentsOf: [ext3, ext4]) - assert(map: map, contains: [ext1, ext4, ext3]) - } + map.insert(ext2) + assert(map: map, contains: [ext2, ext1]) - func testInitialize_list() { - let map1: SimpleExtensionMap = [ext1, ext2] - assert(map: map1, contains: [ext1, ext2]) + map.insert(ext3) + assert(map: map, contains: [ext3, ext2, ext1]) - let map2: SimpleExtensionMap = [ext1, ext2, ext3, ext4] - assert(map: map2, contains: [ext1, ext3, ext4]) -} - - func testFormUnion() { - var map1: SimpleExtensionMap = [ext1] - let map2: SimpleExtensionMap = [ext2] - let map3: SimpleExtensionMap = [ext3, ext4] - - map1.formUnion(map2) - assert(map: map1, contains: [ext1, ext2]) + // ext4 has the same message and number as ext2, so should replace it. + map.insert(ext4) + assert(map: map, contains: [ext4, ext1, ext3]) + } - // ext4 has the same message and number as ext2, so should replace it. - map1.formUnion(map3) - assert(map: map1, contains: [ext1, ext3, ext4]) - } + func testInsert_contentsOf() { + var map = SimpleExtensionMap() + XCTAssertEqual(map.fields.count, 0) - func testUnion() { - let map1: SimpleExtensionMap = [ext1] - let map2: SimpleExtensionMap = [ext2] - let map3: SimpleExtensionMap = [ext3, ext4] + map.insert(contentsOf: [ext1, ext2]) + assert(map: map, contains: [ext1, ext2]) - let map4 = map1.union(map2) - assert(map: map4, contains: [ext1, ext2]) + // ext4 has the same message and number as ext2, so should replace it. + map.insert(contentsOf: [ext3, ext4]) + assert(map: map, contains: [ext1, ext4, ext3]) + } - // ext4 has the same message and number as ext2, so should replace it. - let map5 = map4.union(map3) - assert(map: map5, contains: [ext1, ext3, ext4]) - } - - func testInitialize_union() { - let map1: SimpleExtensionMap = [ext1] - let map2: SimpleExtensionMap = [ext2] - let map3: SimpleExtensionMap = [ext3, ext4] + func testInitialize_list() { + let map1: SimpleExtensionMap = [ext1, ext2] + assert(map: map1, contains: [ext1, ext2]) - let map4 = SimpleExtensionMap(map1, map2) - assert(map: map4, contains: [ext1, ext2]) + let map2: SimpleExtensionMap = [ext1, ext2, ext3, ext4] + assert(map: map2, contains: [ext1, ext3, ext4]) + } - // ext4 has the same message and number as ext2, so should replace it. - let map5 = SimpleExtensionMap(map1, map2, map3) - assert(map: map5, contains: [ext1, ext3, ext4]) - } + func testFormUnion() { + var map1: SimpleExtensionMap = [ext1] + let map2: SimpleExtensionMap = [ext2] + let map3: SimpleExtensionMap = [ext3, ext4] - func testSubscript() { - let map1: SimpleExtensionMap = [ext1, ext2] + map1.formUnion(map2) + assert(map: map1, contains: [ext1, ext2]) - let lookup1 = map1[ext1.messageType, ext1.fieldNumber] - XCTAssertTrue(lookup1!.isEqual(ext1)) + // ext4 has the same message and number as ext2, so should replace it. + map1.formUnion(map3) + assert(map: map1, contains: [ext1, ext3, ext4]) + } - let lookup2 = map1[ext2.messageType, ext2.fieldNumber] - XCTAssertTrue(lookup2!.isEqual(ext2)) + func testUnion() { + let map1: SimpleExtensionMap = [ext1] + let map2: SimpleExtensionMap = [ext2] + let map3: SimpleExtensionMap = [ext3, ext4] - let lookup3 = map1[SwiftProtoTesting_TestAllTypes.self, ext1.fieldNumber] - XCTAssertNil(lookup3) + let map4 = map1.union(map2) + assert(map: map4, contains: [ext1, ext2]) - let lookup4 = map1[ext1.messageType, 999] - XCTAssertNil(lookup4) + // ext4 has the same message and number as ext2, so should replace it. + let map5 = map4.union(map3) + assert(map: map5, contains: [ext1, ext3, ext4]) + } - // ext4 will replace ext2 - let map2: SimpleExtensionMap = [ext1, ext2, ext4] + func testInitialize_union() { + let map1: SimpleExtensionMap = [ext1] + let map2: SimpleExtensionMap = [ext2] + let map3: SimpleExtensionMap = [ext3, ext4] - let lookup1b = map2[ext1.messageType, ext1.fieldNumber] - XCTAssertTrue(lookup1b!.isEqual(ext1)) + let map4 = SimpleExtensionMap(map1, map2) + assert(map: map4, contains: [ext1, ext2]) - let lookup2b = map2[ext2.messageType, ext2.fieldNumber] - XCTAssertTrue(lookup2b!.isEqual(ext4)) - XCTAssertTrue(!lookup2b!.isEqual(ext2)) - } + // ext4 has the same message and number as ext2, so should replace it. + let map5 = SimpleExtensionMap(map1, map2, map3) + assert(map: map5, contains: [ext1, ext3, ext4]) + } - func testFieldNumberForProto() { - let map1: SimpleExtensionMap = [ext1, ext2] + func testSubscript() { + let map1: SimpleExtensionMap = [ext1, ext2] - let lookup1 = map1.fieldNumberForProto(messageType: ext1.messageType, - protoFieldName: ext1.fieldName) - XCTAssertEqual(lookup1, ext1.fieldNumber) + let lookup1 = map1[ext1.messageType, ext1.fieldNumber] + XCTAssertTrue(lookup1!.isEqual(ext1)) - let lookup2 = map1.fieldNumberForProto(messageType: ext2.messageType, - protoFieldName: ext2.fieldName) - XCTAssertEqual(lookup2, ext2.fieldNumber) + let lookup2 = map1[ext2.messageType, ext2.fieldNumber] + XCTAssertTrue(lookup2!.isEqual(ext2)) - let lookup3 = map1.fieldNumberForProto(messageType: ext1.messageType, - protoFieldName: "foo_bar_baz") - XCTAssertNil(lookup3) + let lookup3 = map1[SwiftProtoTesting_TestAllTypes.self, ext1.fieldNumber] + XCTAssertNil(lookup3) - let lookup4 = map1.fieldNumberForProto(messageType: SwiftProtoTesting_TestAllTypes.self, - protoFieldName: ext1.fieldName) - XCTAssertNil(lookup4) + let lookup4 = map1[ext1.messageType, 999] + XCTAssertNil(lookup4) - // ext4 will replace ext2 - let map2: SimpleExtensionMap = [ext1, ext2, ext4] + // ext4 will replace ext2 + let map2: SimpleExtensionMap = [ext1, ext2, ext4] - let lookup1b = map2.fieldNumberForProto(messageType: ext1.messageType, - protoFieldName: ext1.fieldName) - XCTAssertEqual(lookup1b, ext1.fieldNumber) + let lookup1b = map2[ext1.messageType, ext1.fieldNumber] + XCTAssertTrue(lookup1b!.isEqual(ext1)) - let lookup2b = map2.fieldNumberForProto(messageType: ext2.messageType, - protoFieldName: ext2.fieldName) - XCTAssertNil(lookup2b) + let lookup2b = map2[ext2.messageType, ext2.fieldNumber] + XCTAssertTrue(lookup2b!.isEqual(ext4)) + XCTAssertTrue(!lookup2b!.isEqual(ext2)) + } - let lookup3b = map2.fieldNumberForProto(messageType: ext4.messageType, - protoFieldName: ext4.fieldName) - XCTAssertEqual(lookup3b, ext4.fieldNumber) - } + func testFieldNumberForProto() { + let map1: SimpleExtensionMap = [ext1, ext2] + + let lookup1 = map1.fieldNumberForProto( + messageType: ext1.messageType, + protoFieldName: ext1.fieldName + ) + XCTAssertEqual(lookup1, ext1.fieldNumber) + + let lookup2 = map1.fieldNumberForProto( + messageType: ext2.messageType, + protoFieldName: ext2.fieldName + ) + XCTAssertEqual(lookup2, ext2.fieldNumber) + + let lookup3 = map1.fieldNumberForProto( + messageType: ext1.messageType, + protoFieldName: "foo_bar_baz" + ) + XCTAssertNil(lookup3) + + let lookup4 = map1.fieldNumberForProto( + messageType: SwiftProtoTesting_TestAllTypes.self, + protoFieldName: ext1.fieldName + ) + XCTAssertNil(lookup4) + + // ext4 will replace ext2 + let map2: SimpleExtensionMap = [ext1, ext2, ext4] + + let lookup1b = map2.fieldNumberForProto( + messageType: ext1.messageType, + protoFieldName: ext1.fieldName + ) + XCTAssertEqual(lookup1b, ext1.fieldNumber) + + let lookup2b = map2.fieldNumberForProto( + messageType: ext2.messageType, + protoFieldName: ext2.fieldName + ) + XCTAssertNil(lookup2b) + + let lookup3b = map2.fieldNumberForProto( + messageType: ext4.messageType, + protoFieldName: ext4.fieldName + ) + XCTAssertEqual(lookup3b, ext4.fieldNumber) + } } diff --git a/Tests/SwiftProtobufTests/Test_Struct.swift b/Tests/SwiftProtobufTests/Test_Struct.swift index 7868b9425..ccc7eddfe 100644 --- a/Tests/SwiftProtobufTests/Test_Struct.swift +++ b/Tests/SwiftProtobufTests/Test_Struct.swift @@ -14,14 +14,14 @@ // ----------------------------------------------------------------------------- import Foundation -import XCTest import SwiftProtobuf +import XCTest final class Test_Struct: XCTestCase, PBTestHelpers { typealias MessageTestType = Google_Protobuf_Struct func testStruct_pbencode() { - assertEncode([10, 12, 10, 3, 102, 111, 111, 18, 5, 26, 3, 98, 97, 114]) {(o: inout MessageTestType) in + assertEncode([10, 12, 10, 3, 102, 111, 111, 18, 5, 26, 3, 98, 97, 114]) { (o: inout MessageTestType) in var v = Google_Protobuf_Value() v.stringValue = "bar" o.fields["foo"] = v @@ -37,7 +37,8 @@ final class Test_Struct: XCTestCase, PBTestHelpers { var different = Google_Protobuf_Struct() different.fields = ["a": vTrue, "b": vNull, "c": vNull] - return (m.fields.count == 2 + return + (m.fields.count == 2 && m.fields["a"] == vTrue && m.fields["a"] != vNull && m.fields["b"] == vNull @@ -48,7 +49,7 @@ final class Test_Struct: XCTestCase, PBTestHelpers { } func test_JSON() { - assertJSONDecodeSucceeds("{}") {$0.fields == [:]} + assertJSONDecodeSucceeds("{}") { $0.fields == [:] } assertJSONDecodeFails("null") assertJSONDecodeFails("false") assertJSONDecodeFails("true") @@ -65,7 +66,7 @@ final class Test_Struct: XCTestCase, PBTestHelpers { // "null" as a field value indicates the field is missing // (Except for Value, where "null" indicates NullValue) do { - let c1 = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString:"{\"optionalStruct\":null}") + let c1 = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: "{\"optionalStruct\":null}") // null here decodes to an empty field. // See github.com/protocolbuffers/protobuf Issue #1327 XCTAssertEqual(try c1.jsonString(), "{}") @@ -74,7 +75,7 @@ final class Test_Struct: XCTestCase, PBTestHelpers { } do { - let c2 = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString:"{\"optionalStruct\":{}}") + let c2 = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: "{\"optionalStruct\":{}}") XCTAssertNotNil(c2.optionalStruct) XCTAssertEqual(c2.optionalStruct.fields, [:]) } catch let e { @@ -110,7 +111,8 @@ final class Test_JSON_ListValue: XCTestCase, PBTestHelpers { // we need to verify all the basic functionality, including // serialization, equality, hash, etc. func testProtobuf() { - assertEncode([10, 9, 17, 0, 0, 0, 0, 0, 0, 240, 63, 10, 5, 26, 3, 97, 98, 99, 10, 2, 32, 1]) { (o: inout MessageTestType) in + assertEncode([10, 9, 17, 0, 0, 0, 0, 0, 0, 240, 63, 10, 5, 26, 3, 97, 98, 99, 10, 2, 32, 1]) { + (o: inout MessageTestType) in o.values.append(Google_Protobuf_Value(numberValue: 1)) o.values.append(Google_Protobuf_Value(stringValue: "abc")) o.values.append(Google_Protobuf_Value(boolValue: true)) @@ -130,7 +132,7 @@ final class Test_JSON_ListValue: XCTestCase, PBTestHelpers { o.values.append(Google_Protobuf_Value(listValue: [1, nil])) o.values.append(Google_Protobuf_Value(listValue: [])) } - assertJSONDecodeSucceeds("[]") {$0.values == []} + assertJSONDecodeSucceeds("[]") { $0.values == [] } assertJSONDecodeFails("") assertJSONDecodeFails("true") assertJSONDecodeFails("false") @@ -140,20 +142,20 @@ final class Test_JSON_ListValue: XCTestCase, PBTestHelpers { assertJSONDecodeFails("[}") assertJSONDecodeFails("[,]") assertJSONDecodeFails("[true,]") - assertJSONDecodeSucceeds("[true]") {$0.values == [Google_Protobuf_Value(boolValue: true)]} + assertJSONDecodeSucceeds("[true]") { $0.values == [Google_Protobuf_Value(boolValue: true)] } } func test_JSON_nested_list() throws { let limit = JSONDecodingOptions().messageDepthLimit let depths = [ // Small lists - 1,2,3,4,5, + 1, 2, 3, 4, 5, // Little less than default messageDepthLimit, should succeed limit - 3, limit - 2, limit - 1, limit, // Little bigger than default messageDepthLimit, should fail limit + 1, limit + 2, limit + 3, limit + 4, // Really big, should fail cleanly (not crash) - 1000,10000,100000,1000000 + 1000, 10000, 100000, 1_000_000, ] for depth in depths { var s = "" @@ -172,7 +174,7 @@ final class Test_JSON_ListValue: XCTestCase, PBTestHelpers { // Recursion limits should cause this to // fail cleanly without crashing if depth <= limit { - assertJSONDecodeSucceeds(s) {_ in true} + assertJSONDecodeSucceeds(s) { _ in true } } else { assertJSONDecodeFails(s) } @@ -193,7 +195,6 @@ final class Test_JSON_ListValue: XCTestCase, PBTestHelpers { } } - final class Test_Value: XCTestCase, PBTestHelpers { typealias MessageTestType = Google_Protobuf_Value @@ -211,7 +212,6 @@ final class Test_Value: XCTestCase, PBTestHelpers { } } - // TODO: Should have convenience initializers on Google_Protobuf_Value final class Test_JSON_Value: XCTestCase, PBTestHelpers { typealias MessageTestType = Google_Protobuf_Value @@ -235,8 +235,8 @@ final class Test_JSON_Value: XCTestCase, PBTestHelpers { XCTAssertEqual([8, 0], try null.serializedBytes()) XCTAssertEqual(nullFromLiteral, null) XCTAssertNotEqual(nullFromLiteral, Google_Protobuf_Value(numberValue: 1)) - assertJSONDecodeSucceeds("null") {$0.nullValue == .nullValue} - assertJSONDecodeSucceeds(" null ") {$0.nullValue == .nullValue} + assertJSONDecodeSucceeds("null") { $0.nullValue == .nullValue } + assertJSONDecodeSucceeds(" null ") { $0.nullValue == .nullValue } assertJSONDecodeFails("numb") do { @@ -258,11 +258,11 @@ final class Test_JSON_Value: XCTestCase, PBTestHelpers { XCTAssertNotEqual(oneFromIntegerLiteral, twoFromFloatLiteral) XCTAssertEqual("1.0", try oneFromIntegerLiteral.jsonString()) XCTAssertEqual([17, 0, 0, 0, 0, 0, 0, 240, 63], try oneFromIntegerLiteral.serializedBytes()) - assertJSONEncode("3.25") {(o: inout MessageTestType) in + assertJSONEncode("3.25") { (o: inout MessageTestType) in o.numberValue = 3.25 } - assertJSONDecodeSucceeds("3.25") {$0.numberValue == 3.25} - assertJSONDecodeSucceeds(" 3.25 ") {$0.numberValue == 3.25} + assertJSONDecodeSucceeds("3.25") { $0.numberValue == 3.25 } + assertJSONDecodeSucceeds(" 3.25 ") { $0.numberValue == 3.25 } assertJSONDecodeFails("3.2.5") assertDebugDescriptionSuffix(".Google_Protobuf_Value:\nnumber_value: 1.0\n", oneFromIntegerLiteral) @@ -276,14 +276,14 @@ final class Test_JSON_Value: XCTestCase, PBTestHelpers { XCTAssertNotEqual(fromStringLiteral, Google_Protobuf_Value()) // JSON serialization - assertJSONEncode("\"abcd\"") {(o: inout MessageTestType) in + assertJSONEncode("\"abcd\"") { (o: inout MessageTestType) in o.stringValue = "abcd" } - assertJSONEncode("\"\"") {(o: inout MessageTestType) in + assertJSONEncode("\"\"") { (o: inout MessageTestType) in o.stringValue = "" } - assertJSONDecodeSucceeds("\"abcd\"") {$0.stringValue == "abcd"} - assertJSONDecodeSucceeds(" \"abcd\" ") {$0.stringValue == "abcd"} + assertJSONDecodeSucceeds("\"abcd\"") { $0.stringValue == "abcd" } + assertJSONDecodeSucceeds(" \"abcd\" ") { $0.stringValue == "abcd" } assertJSONDecodeFails("\"abcd\" XXX") assertJSONDecodeFails("\"abcd") @@ -311,14 +311,14 @@ final class Test_JSON_Value: XCTestCase, PBTestHelpers { XCTAssertEqual(trueFromLiteral, Google_Protobuf_Value(boolValue: true)) XCTAssertEqual(falseFromLiteral, Google_Protobuf_Value(boolValue: false)) XCTAssertNotEqual(falseFromLiteral, trueFromLiteral) - assertJSONEncode("true") {(o: inout MessageTestType) in + assertJSONEncode("true") { (o: inout MessageTestType) in o.boolValue = true } - assertJSONEncode("false") {(o: inout MessageTestType) in + assertJSONEncode("false") { (o: inout MessageTestType) in o.boolValue = false } - assertJSONDecodeSucceeds("true") {$0.boolValue == true} - assertJSONDecodeSucceeds(" false ") {$0.boolValue == false} + assertJSONDecodeSucceeds("true") { $0.boolValue == true } + assertJSONDecodeSucceeds(" false ") { $0.boolValue == false } assertJSONDecodeFails("yes") assertJSONDecodeFails(" true false ") @@ -326,17 +326,23 @@ final class Test_JSON_Value: XCTestCase, PBTestHelpers { } func testValue_struct() throws { - assertJSONEncode("{\"a\":1.0}") {(o: inout MessageTestType) in - o.structValue = Google_Protobuf_Struct(fields:["a": Google_Protobuf_Value(numberValue: 1)]) + assertJSONEncode("{\"a\":1.0}") { (o: inout MessageTestType) in + o.structValue = Google_Protobuf_Struct(fields: ["a": Google_Protobuf_Value(numberValue: 1)]) } let structValue = try Google_Protobuf_Value(jsonString: "{\"a\":1.0}") - assertDebugDescriptionSuffix(".Google_Protobuf_Value:\nstruct_value {\n fields {\n key: \"a\"\n value {\n number_value: 1.0\n }\n }\n}\n", structValue) + assertDebugDescriptionSuffix( + ".Google_Protobuf_Value:\nstruct_value {\n fields {\n key: \"a\"\n value {\n number_value: 1.0\n }\n }\n}\n", + structValue + ) } func testValue_list() throws { let listValue = try Google_Protobuf_Value(jsonString: "[1, true, \"abc\"]") - assertDebugDescriptionSuffix(".Google_Protobuf_Value:\nlist_value {\n values {\n number_value: 1.0\n }\n values {\n bool_value: true\n }\n values {\n string_value: \"abc\"\n }\n}\n", listValue) + assertDebugDescriptionSuffix( + ".Google_Protobuf_Value:\nlist_value {\n values {\n number_value: 1.0\n }\n values {\n bool_value: true\n }\n values {\n string_value: \"abc\"\n }\n}\n", + listValue + ) } func testValue_complex() { @@ -344,7 +350,8 @@ final class Test_JSON_Value: XCTestCase, PBTestHelpers { let outer = $0.structValue.fields let a = outer["a"]?.structValue.fields let c = outer["c"]?.listValue.values - return (a?["b"]?.numberValue == 1.0 + return + (a?["b"]?.numberValue == 1.0 && c?.count == 4 && c?[0].numberValue == 7 && c?[1].boolValue == true @@ -354,19 +361,20 @@ final class Test_JSON_Value: XCTestCase, PBTestHelpers { } func testStruct_conformance() throws { - let json = ("{\n" - + " \"optionalStruct\": {\n" - + " \"nullValue\": null,\n" - + " \"intValue\": 1234,\n" - + " \"boolValue\": true,\n" - + " \"doubleValue\": 1234.5678,\n" - + " \"stringValue\": \"Hello world!\",\n" - + " \"listValue\": [1234, \"5678\"],\n" - + " \"objectValue\": {\n" - + " \"value\": 0\n" - + " }\n" - + " }\n" - + "}\n") + let json = + ("{\n" + + " \"optionalStruct\": {\n" + + " \"nullValue\": null,\n" + + " \"intValue\": 1234,\n" + + " \"boolValue\": true,\n" + + " \"doubleValue\": 1234.5678,\n" + + " \"stringValue\": \"Hello world!\",\n" + + " \"listValue\": [1234, \"5678\"],\n" + + " \"objectValue\": {\n" + + " \"value\": 0\n" + + " }\n" + + " }\n" + + "}\n") let m: SwiftProtoTesting_Test3_TestAllTypesProto3 do { m = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: json) @@ -402,9 +410,13 @@ final class Test_JSON_Value: XCTestCase, PBTestHelpers { } XCTAssertNotNil(s.fields["listValue"]) if let lv = s.fields["listValue"] { - XCTAssertEqual(lv.listValue, - [Google_Protobuf_Value(numberValue: 1234), - Google_Protobuf_Value(stringValue: "5678")]) + XCTAssertEqual( + lv.listValue, + [ + Google_Protobuf_Value(numberValue: 1234), + Google_Protobuf_Value(stringValue: "5678"), + ] + ) } XCTAssertNotNil(s.fields["objectValue"]) if let ov = s.fields["objectValue"] { @@ -417,9 +429,10 @@ final class Test_JSON_Value: XCTestCase, PBTestHelpers { } func testStruct_null() throws { - let json = ("{\n" - + " \"optionalStruct\": null\n" - + "}\n") + let json = + ("{\n" + + " \"optionalStruct\": null\n" + + "}\n") do { let decoded = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: json) let recoded = try decoded.jsonString() diff --git a/Tests/SwiftProtobufTests/Test_TextFormatDecodingOptions.swift b/Tests/SwiftProtobufTests/Test_TextFormatDecodingOptions.swift index b18ea4d28..5096ea0ed 100644 --- a/Tests/SwiftProtobufTests/Test_TextFormatDecodingOptions.swift +++ b/Tests/SwiftProtobufTests/Test_TextFormatDecodingOptions.swift @@ -13,8 +13,8 @@ // ----------------------------------------------------------------------------- import Foundation -import XCTest import SwiftProtobuf +import XCTest final class Test_TextFormatDecodingOptions: XCTestCase { @@ -23,11 +23,11 @@ final class Test_TextFormatDecodingOptions: XCTestCase { let tests: [(Int, Bool)] = [ // Limit, success/failure - ( 10, true ), - ( 4, true ), - ( 3, true ), - ( 2, false ), - ( 1, false ), + (10, true), + (4, true), + (3, true), + (2, false), + (1, false), ] for (limit, expectSuccess) in tests { @@ -44,7 +44,7 @@ final class Test_TextFormatDecodingOptions: XCTestCase { } else { // Nothing, this is what was expected. } - } catch let e { + } catch let e { XCTFail("Decode failed (limit: \(limit) with unexpected error: \(e)") } } @@ -59,7 +59,7 @@ final class Test_TextFormatDecodingOptions: XCTestCase { var options = TextFormatDecodingOptions() options.ignoreUnknownFields = true - let msg = try SwiftProtoTesting_TestAllTypes(textFormatString: textInputField, options: options) // Shouldn't fail + let msg = try SwiftProtoTesting_TestAllTypes(textFormatString: textInputField, options: options) // Shouldn't fail XCTAssertEqual(msg.textFormatString(), "optional_int32: 2\n") do { @@ -84,7 +84,7 @@ final class Test_TextFormatDecodingOptions: XCTestCase { // This is what should have happened. } - let msg = try SwiftProtoTesting_TestAllTypes(textFormatString: textInputExtField, options: options) // Shouldn't fail + let msg = try SwiftProtoTesting_TestAllTypes(textFormatString: textInputExtField, options: options) // Shouldn't fail XCTAssertEqual(msg.textFormatString(), "optional_int32: 2\n") } @@ -95,7 +95,7 @@ final class Test_TextFormatDecodingOptions: XCTestCase { options.ignoreUnknownFields = true options.ignoreUnknownExtensionFields = true - let msg = try SwiftProtoTesting_TestAllTypes(textFormatString: textInput, options: options) // Shouldn't fail + let msg = try SwiftProtoTesting_TestAllTypes(textFormatString: textInput, options: options) // Shouldn't fail XCTAssertEqual(msg.textFormatString(), "optional_int32: 2\n") } @@ -314,7 +314,7 @@ final class Test_TextFormatDecodingOptions: XCTestCase { assertDecodeIgnoringUnknownsFails("bytes", "\"\\x&\"\n") assertDecodeIgnoringUnknownsFails("bytes", "\"\\xg\"\n") assertDecodeIgnoringUnknownsFails("bytes", "\"\\q\"\n") - assertDecodeIgnoringUnknownsFails("bytes", "\"\\777\"\n") // Out-of-range octal + assertDecodeIgnoringUnknownsFails("bytes", "\"\\777\"\n") // Out-of-range octal assertDecodeIgnoringUnknownsFails("bytes", "\"") assertDecodeIgnoringUnknownsFails("bytes", "\"abcde") assertDecodeIgnoringUnknownsFails("bytes", "\"\\") @@ -486,32 +486,32 @@ final class Test_TextFormatDecodingOptions: XCTestCase { // start and end of each scope along the way. let text = """ - unknown_first_outer: "first", - child { - repeated_child { - unknown_first_inner: [0], - payload { - unknown_first_inner_inner: "test", - optional_int32: 1, - unknown_inner_inner: 2f, + unknown_first_outer: "first", + child { + repeated_child { + unknown_first_inner: [0], + payload { + unknown_first_inner_inner: "test", + optional_int32: 1, + unknown_inner_inner: 2f, + }, + unknown_inner: 3.0, }, - unknown_inner: 3.0, + repeated_child { + unknown_first_inner: 0; + payload { + unknown_first_inner_inner: "test"; + optional_int32: 1; + unknown_inner_inner: 2f; + }, + unknown_inner: [3.0]; + }; + unknown: "nope", + unknown: 12; }, - repeated_child { - unknown_first_inner: 0; - payload { - unknown_first_inner_inner: "test"; - optional_int32: 1; - unknown_inner_inner: 2f; - }, - unknown_inner: [3.0]; - }; - unknown: "nope", - unknown: 12; - }, - unknown_outer: [END]; - unknown_outer_final: "last"; - """ + unknown_outer: [END]; + unknown_outer_final: "last"; + """ var options = TextFormatDecodingOptions() options.ignoreUnknownFields = true @@ -539,49 +539,49 @@ final class Test_TextFormatDecodingOptions: XCTestCase { func testIgnoreUnknown_Comments() throws { // Stress test to unknown field parsing deals with comments correctly. let text = """ - does_not_exist: true # comment - something_else { # comment - # comment - optional_string: "still unknown" - } # comment - - optional_int32: 1 # !!! real field - - does_not_exist: true # comment - something_else { # comment - # comment - optional_string: "still unknown" # comment - optional_string: "still unknown" # comment + does_not_exist: true # comment + something_else { # comment # comment - "continued" # comment - # comment - some_int : 0x12 # comment - a_float: #comment - 0.2 # comment - repeat: [ - # comment - -123 # comment - # comment - , # comment - # comment - 0222 # comment - # comment - , # comment - # comment - 012 # comment - # comment - ] # comment - } # comment - - optional_uint32: 2 # !!! real field - - does_not_exist: true # comment - something_else { # comment - # comment - optional_string: "still unknown" - } # comment - - """ + optional_string: "still unknown" + } # comment + + optional_int32: 1 # !!! real field + + does_not_exist: true # comment + something_else { # comment + # comment + optional_string: "still unknown" # comment + optional_string: "still unknown" # comment + # comment + "continued" # comment + # comment + some_int : 0x12 # comment + a_float: #comment + 0.2 # comment + repeat: [ + # comment + -123 # comment + # comment + , # comment + # comment + 0222 # comment + # comment + , # comment + # comment + 012 # comment + # comment + ] # comment + } # comment + + optional_uint32: 2 # !!! real field + + does_not_exist: true # comment + something_else { # comment + # comment + optional_string: "still unknown" + } # comment + + """ let expected = SwiftProtoTesting_TestAllTypes.with { $0.optionalInt32 = 1 @@ -598,38 +598,38 @@ final class Test_TextFormatDecodingOptions: XCTestCase { func testIgnoreUnknown_Whitespace() throws { // Blanket test to unknown field parsing deals with comments correctly. let text = """ - optional_int32: 1 # !!! real field + optional_int32: 1 # !!! real field - does_not_exist - : - 1 + does_not_exist + : + 1 - something_else { + something_else { - optional_string: "still unknown" + optional_string: "still unknown" - " continued value" + " continued value" - repeated: [ - 1 , - 0x1 - , - 3, 012 - ] + repeated: [ + 1 , + 0x1 + , + 3, 012 + ] - } + } - repeated_strs: [ - "ab" "cd" , - "de" - , - "xyz" - ] + repeated_strs: [ + "ab" "cd" , + "de" + , + "xyz" + ] - an_int:1some_bytes:"abc"msg_field:{a:true}repeated:[1]another_int:3 + an_int:1some_bytes:"abc"msg_field:{a:true}repeated:[1]another_int:3 - optional_uint32: 2 # !!! real field - """ + optional_uint32: 2 # !!! real field + """ let expected = SwiftProtoTesting_TestAllTypes.with { $0.optionalInt32 = 1 @@ -656,25 +656,27 @@ final class Test_TextFormatDecodingOptions: XCTestCase { let testCases: [(field: String, parses: Bool)] = [ ("536870911", true), - ("1536870911", false) + ("1536870911", false), ] for testCase in testCases { let text = """ - optional_int32: 1 # !!! real field + optional_int32: 1 # !!! real field - # Unknown field that's a message to test parsing of field numbers - # nested within a unknown message. - does_not_exist { - \(testCase.field): 1 - } + # Unknown field that's a message to test parsing of field numbers + # nested within a unknown message. + does_not_exist { + \(testCase.field): 1 + } - optional_uint32: 2 # !!! real field - """ + optional_uint32: 2 # !!! real field + """ do { - let msg = try SwiftProtoTesting_TestAllTypes(textFormatString: text, - options: options) + let msg = try SwiftProtoTesting_TestAllTypes( + textFormatString: text, + options: options + ) // If we get here, it should be the expected message. XCTAssertTrue(testCase.parses) XCTAssertEqual(msg, expected) @@ -715,8 +717,10 @@ final class Test_TextFormatDecodingOptions: XCTestCase { for testCase in testCases { do { - let _ = try SwiftProtoTesting_TestEmptyMessage(textFormatString: testCase, - options: options) + let _ = try SwiftProtoTesting_TestEmptyMessage( + textFormatString: testCase, + options: options + ) XCTFail("Should have failed - input: \(testCase)") } catch TextFormatDecodingError.malformedText { // Nothing, was the expected error @@ -731,11 +735,11 @@ final class Test_TextFormatDecodingOptions: XCTestCase { let tests: [(Int, Bool)] = [ // Limit, success/failure - ( 10, true ), - ( 4, true ), - ( 3, true ), - ( 2, false ), - ( 1, false ), + (10, true), + (4, true), + (3, true), + (2, false), + (1, false), ] for (limit, expectSuccess) in tests { @@ -753,7 +757,7 @@ final class Test_TextFormatDecodingOptions: XCTestCase { } else { // Nothing, this is what was expected. } - } catch let e { + } catch let e { XCTFail("Decode failed (limit: \(limit) with unexpected error: \(e)") } } diff --git a/Tests/SwiftProtobufTests/Test_TextFormat_Map_proto3.swift b/Tests/SwiftProtobufTests/Test_TextFormat_Map_proto3.swift index 644530e9c..f557ef940 100644 --- a/Tests/SwiftProtobufTests/Test_TextFormat_Map_proto3.swift +++ b/Tests/SwiftProtobufTests/Test_TextFormat_Map_proto3.swift @@ -13,33 +13,35 @@ // ----------------------------------------------------------------------------- import Foundation -import XCTest import SwiftProtobuf +import XCTest final class Test_TextFormat_Map_proto3: XCTestCase, PBTestHelpers { typealias MessageTestType = SwiftProtoTesting_TestMap func test_Int32Int32() { - assertTextFormatEncode("map_int32_int32 {\n key: 1\n value: 2\n}\n") {(o: inout MessageTestType) in - o.mapInt32Int32 = [1:2] + assertTextFormatEncode("map_int32_int32 {\n key: 1\n value: 2\n}\n") { (o: inout MessageTestType) in + o.mapInt32Int32 = [1: 2] } - assertTextFormatDecodeSucceeds("map_int32_int32 {key: 1, value: 2}") {(o: MessageTestType) in - return o.mapInt32Int32 == [1:2] + assertTextFormatDecodeSucceeds("map_int32_int32 {key: 1, value: 2}") { (o: MessageTestType) in + o.mapInt32Int32 == [1: 2] } - assertTextFormatDecodeSucceeds("map_int32_int32 {key: 1; value: 2}") {(o: MessageTestType) in - return o.mapInt32Int32 == [1:2] + assertTextFormatDecodeSucceeds("map_int32_int32 {key: 1; value: 2}") { (o: MessageTestType) in + o.mapInt32Int32 == [1: 2] } - assertTextFormatDecodeSucceeds("map_int32_int32 {key:1 value:2}") {(o: MessageTestType) in - return o.mapInt32Int32 == [1:2] + assertTextFormatDecodeSucceeds("map_int32_int32 {key:1 value:2}") { (o: MessageTestType) in + o.mapInt32Int32 == [1: 2] } - assertTextFormatDecodeSucceeds("map_int32_int32 {key:1 value:2}\nmap_int32_int32 {key:3 value:4}") {(o: MessageTestType) in - return o.mapInt32Int32 == [1:2, 3:4] + assertTextFormatDecodeSucceeds("map_int32_int32 {key:1 value:2}\nmap_int32_int32 {key:3 value:4}") { + (o: MessageTestType) in + o.mapInt32Int32 == [1: 2, 3: 4] } - assertTextFormatDecodeSucceeds("map_int32_int32 [{key:1 value:2}, {key:3 value:4}]") {(o: MessageTestType) in - return o.mapInt32Int32 == [1:2, 3:4] + assertTextFormatDecodeSucceeds("map_int32_int32 [{key:1 value:2}, {key:3 value:4}]") { (o: MessageTestType) in + o.mapInt32Int32 == [1: 2, 3: 4] } - assertTextFormatDecodeSucceeds("map_int32_int32 [{key:1 value:2}];map_int32_int32 {key:3 value:4}") {(o: MessageTestType) in - return o.mapInt32Int32 == [1:2, 3:4] + assertTextFormatDecodeSucceeds("map_int32_int32 [{key:1 value:2}];map_int32_int32 {key:3 value:4}") { + (o: MessageTestType) in + o.mapInt32Int32 == [1: 2, 3: 4] } assertTextFormatDecodeFails("map_int32_int32 [{key:1 value:2},]") assertTextFormatDecodeFails("map_int32_int32 [{key:1 value:2}") @@ -59,26 +61,26 @@ final class Test_TextFormat_Map_proto3: XCTestCase, PBTestHelpers { } func test_Int32Int32_numbers() { - assertTextFormatDecodeSucceeds("1 {\n key: 1\n value: 2\n}\n") {(o: MessageTestType) in - return o.mapInt32Int32 == [1:2] + assertTextFormatDecodeSucceeds("1 {\n key: 1\n value: 2\n}\n") { (o: MessageTestType) in + o.mapInt32Int32 == [1: 2] } - assertTextFormatDecodeSucceeds("1 {key: 1, value: 2}") {(o: MessageTestType) in - return o.mapInt32Int32 == [1:2] + assertTextFormatDecodeSucceeds("1 {key: 1, value: 2}") { (o: MessageTestType) in + o.mapInt32Int32 == [1: 2] } - assertTextFormatDecodeSucceeds("1 {key: 1; value: 2}") {(o: MessageTestType) in - return o.mapInt32Int32 == [1:2] + assertTextFormatDecodeSucceeds("1 {key: 1; value: 2}") { (o: MessageTestType) in + o.mapInt32Int32 == [1: 2] } - assertTextFormatDecodeSucceeds("1 {key:1 value:2}") {(o: MessageTestType) in - return o.mapInt32Int32 == [1:2] + assertTextFormatDecodeSucceeds("1 {key:1 value:2}") { (o: MessageTestType) in + o.mapInt32Int32 == [1: 2] } - assertTextFormatDecodeSucceeds("1 {key:1 value:2}\n1 {key:3 value:4}") {(o: MessageTestType) in - return o.mapInt32Int32 == [1:2, 3:4] + assertTextFormatDecodeSucceeds("1 {key:1 value:2}\n1 {key:3 value:4}") { (o: MessageTestType) in + o.mapInt32Int32 == [1: 2, 3: 4] } - assertTextFormatDecodeSucceeds("1 [{key:1 value:2}, {key:3 value:4}]") {(o: MessageTestType) in - return o.mapInt32Int32 == [1:2, 3:4] + assertTextFormatDecodeSucceeds("1 [{key:1 value:2}, {key:3 value:4}]") { (o: MessageTestType) in + o.mapInt32Int32 == [1: 2, 3: 4] } - assertTextFormatDecodeSucceeds("1 [{key:1 value:2}];1 {key:3 value:4}") {(o: MessageTestType) in - return o.mapInt32Int32 == [1:2, 3:4] + assertTextFormatDecodeSucceeds("1 [{key:1 value:2}];1 {key:3 value:4}") { (o: MessageTestType) in + o.mapInt32Int32 == [1: 2, 3: 4] } assertTextFormatDecodeFails("1 [{key:1 value:2},]") assertTextFormatDecodeFails("1 [{key:1 value:2}") @@ -87,26 +89,26 @@ final class Test_TextFormat_Map_proto3: XCTestCase, PBTestHelpers { // Using numbers for "key" and "value" in the map entries. - assertTextFormatDecodeSucceeds("1 {\n 1: 1\n 2: 2\n}\n") {(o: MessageTestType) in - return o.mapInt32Int32 == [1:2] + assertTextFormatDecodeSucceeds("1 {\n 1: 1\n 2: 2\n}\n") { (o: MessageTestType) in + o.mapInt32Int32 == [1: 2] } - assertTextFormatDecodeSucceeds("1 {1: 1, 2: 2}") {(o: MessageTestType) in - return o.mapInt32Int32 == [1:2] + assertTextFormatDecodeSucceeds("1 {1: 1, 2: 2}") { (o: MessageTestType) in + o.mapInt32Int32 == [1: 2] } - assertTextFormatDecodeSucceeds("1 {1: 1; 2: 2}") {(o: MessageTestType) in - return o.mapInt32Int32 == [1:2] + assertTextFormatDecodeSucceeds("1 {1: 1; 2: 2}") { (o: MessageTestType) in + o.mapInt32Int32 == [1: 2] } - assertTextFormatDecodeSucceeds("1 {1:1 2:2}") {(o: MessageTestType) in - return o.mapInt32Int32 == [1:2] + assertTextFormatDecodeSucceeds("1 {1:1 2:2}") { (o: MessageTestType) in + o.mapInt32Int32 == [1: 2] } - assertTextFormatDecodeSucceeds("1 {1:1 2:2}\n1 {1:3 2:4}") {(o: MessageTestType) in - return o.mapInt32Int32 == [1:2, 3:4] + assertTextFormatDecodeSucceeds("1 {1:1 2:2}\n1 {1:3 2:4}") { (o: MessageTestType) in + o.mapInt32Int32 == [1: 2, 3: 4] } - assertTextFormatDecodeSucceeds("1 [{1:1 2:2}, {1:3 2:4}]") {(o: MessageTestType) in - return o.mapInt32Int32 == [1:2, 3:4] + assertTextFormatDecodeSucceeds("1 [{1:1 2:2}, {1:3 2:4}]") { (o: MessageTestType) in + o.mapInt32Int32 == [1: 2, 3: 4] } - assertTextFormatDecodeSucceeds("1 [{1:1 2:2}];1 {1:3 2:4}") {(o: MessageTestType) in - return o.mapInt32Int32 == [1:2, 3:4] + assertTextFormatDecodeSucceeds("1 [{1:1 2:2}];1 {1:3 2:4}") { (o: MessageTestType) in + o.mapInt32Int32 == [1: 2, 3: 4] } assertTextFormatDecodeFails("1 [{1:1 2:2},]") assertTextFormatDecodeFails("1 [{1:1 2:2}") @@ -116,23 +118,25 @@ final class Test_TextFormat_Map_proto3: XCTestCase, PBTestHelpers { } func test_StringMessage() { - let foo = SwiftProtoTesting_ForeignMessage.with {$0.c = 999} + let foo = SwiftProtoTesting_ForeignMessage.with { $0.c = 999 } - assertTextFormatEncode("map_string_foreign_message {\n key: \"foo\"\n value {\n c: 999\n }\n}\n") {(o: inout MessageTestType) in + assertTextFormatEncode("map_string_foreign_message {\n key: \"foo\"\n value {\n c: 999\n }\n}\n") { + (o: inout MessageTestType) in o.mapStringForeignMessage = ["foo": foo] } } func test_StringMessage_numbers() { - let foo = SwiftProtoTesting_ForeignMessage.with {$0.c = 999} + let foo = SwiftProtoTesting_ForeignMessage.with { $0.c = 999 } - assertTextFormatDecodeSucceeds("18 {\n key: \"foo\"\n value {\n 1: 999\n }\n}\n") {(o: MessageTestType) in + assertTextFormatDecodeSucceeds("18 {\n key: \"foo\"\n value {\n 1: 999\n }\n}\n") { + (o: MessageTestType) in o.mapStringForeignMessage == ["foo": foo] } // Using numbers for "key" and "value" in the map entries. - assertTextFormatDecodeSucceeds("18 {\n 1: \"foo\"\n 2 {\n 1: 999\n }\n}\n") {(o: MessageTestType) in + assertTextFormatDecodeSucceeds("18 {\n 1: \"foo\"\n 2 {\n 1: 999\n }\n}\n") { (o: MessageTestType) in o.mapStringForeignMessage == ["foo": foo] } } @@ -141,11 +145,15 @@ final class Test_TextFormat_Map_proto3: XCTestCase, PBTestHelpers { var options = TextFormatDecodingOptions() options.ignoreUnknownFields = true - assertTextFormatDecodeSucceeds("map_int32_int32 {\n key: 1\n unknown: 6\n value: 2\n}\n", options: options) {(o: MessageTestType) in - return o.mapInt32Int32 == [1:2] + assertTextFormatDecodeSucceeds("map_int32_int32 {\n key: 1\n unknown: 6\n value: 2\n}\n", options: options) { + (o: MessageTestType) in + o.mapInt32Int32 == [1: 2] } do { - let _ = try MessageTestType(textFormatString: "map_int32_int32 {\n key: 1\n [ext]: 7\n value: 2\n}\n", options: options) + let _ = try MessageTestType( + textFormatString: "map_int32_int32 {\n key: 1\n [ext]: 7\n value: 2\n}\n", + options: options + ) XCTFail("Should have failed") } catch TextFormatDecodingError.unknownField { // This is what should have happened. @@ -156,11 +164,15 @@ final class Test_TextFormat_Map_proto3: XCTestCase, PBTestHelpers { options.ignoreUnknownFields = false options.ignoreUnknownExtensionFields = true - assertTextFormatDecodeSucceeds("map_int32_int32 {\n key: 1\n [ext]: 6\n value: 2\n}\n", options: options) {(o: MessageTestType) in - return o.mapInt32Int32 == [1:2] + assertTextFormatDecodeSucceeds("map_int32_int32 {\n key: 1\n [ext]: 6\n value: 2\n}\n", options: options) { + (o: MessageTestType) in + o.mapInt32Int32 == [1: 2] } do { - let _ = try MessageTestType(textFormatString: "map_int32_int32 {\n key: 1\n unknown: 7\n value: 2\n}\n", options: options) + let _ = try MessageTestType( + textFormatString: "map_int32_int32 {\n key: 1\n unknown: 7\n value: 2\n}\n", + options: options + ) XCTFail("Should have failed") } catch TextFormatDecodingError.unknownField { // This is what should have happened. @@ -171,17 +183,23 @@ final class Test_TextFormat_Map_proto3: XCTestCase, PBTestHelpers { options.ignoreUnknownFields = true options.ignoreUnknownExtensionFields = true - assertTextFormatDecodeSucceeds("map_int32_int32 {\n key: 1\n unknown: 6\n [ext]: 7\n value: 2\n}\n", options: options) {(o: MessageTestType) in - return o.mapInt32Int32 == [1:2] + assertTextFormatDecodeSucceeds( + "map_int32_int32 {\n key: 1\n unknown: 6\n [ext]: 7\n value: 2\n}\n", + options: options + ) { (o: MessageTestType) in + o.mapInt32Int32 == [1: 2] } - assertTextFormatDecodeSucceeds("map_int32_int32 {unknown: 6, [ext]: 7, key: 1, value: 2}", options: options) {(o: MessageTestType) in - return o.mapInt32Int32 == [1:2] + assertTextFormatDecodeSucceeds("map_int32_int32 {unknown: 6, [ext]: 7, key: 1, value: 2}", options: options) { + (o: MessageTestType) in + o.mapInt32Int32 == [1: 2] } - assertTextFormatDecodeSucceeds("map_int32_int32 {key: 1; value: 2; unknown: 6; [ext]: 7}", options: options) {(o: MessageTestType) in - return o.mapInt32Int32 == [1:2] + assertTextFormatDecodeSucceeds("map_int32_int32 {key: 1; value: 2; unknown: 6; [ext]: 7}", options: options) { + (o: MessageTestType) in + o.mapInt32Int32 == [1: 2] } - assertTextFormatDecodeSucceeds("map_int32_int32 {key:1 unknown: 6 [ext]: 7 value:2}", options: options) {(o: MessageTestType) in - return o.mapInt32Int32 == [1:2] + assertTextFormatDecodeSucceeds("map_int32_int32 {key:1 unknown: 6 [ext]: 7 value:2}", options: options) { + (o: MessageTestType) in + o.mapInt32Int32 == [1: 2] } } @@ -189,13 +207,19 @@ final class Test_TextFormat_Map_proto3: XCTestCase, PBTestHelpers { var options = TextFormatDecodingOptions() options.ignoreUnknownFields = true - let foo = SwiftProtoTesting_ForeignMessage.with {$0.c = 999} + let foo = SwiftProtoTesting_ForeignMessage.with { $0.c = 999 } - assertTextFormatDecodeSucceeds("map_string_foreign_message {\n key: \"foo\"\n unknown: 6\n value { c: 999 }\n}\n", options: options) {(o: MessageTestType) in + assertTextFormatDecodeSucceeds( + "map_string_foreign_message {\n key: \"foo\"\n unknown: 6\n value { c: 999 }\n}\n", + options: options + ) { (o: MessageTestType) in o.mapStringForeignMessage == ["foo": foo] } do { - let _ = try MessageTestType(textFormatString: "map_string_foreign_message {\n key: \"foo\"\n [ext]: 7\n value { c: 999 }\n}\n", options: options) + let _ = try MessageTestType( + textFormatString: "map_string_foreign_message {\n key: \"foo\"\n [ext]: 7\n value { c: 999 }\n}\n", + options: options + ) XCTFail("Should have failed") } catch TextFormatDecodingError.unknownField { // This is what should have happened. @@ -206,11 +230,18 @@ final class Test_TextFormat_Map_proto3: XCTestCase, PBTestHelpers { options.ignoreUnknownFields = false options.ignoreUnknownExtensionFields = true - assertTextFormatDecodeSucceeds("map_string_foreign_message {\n key: \"foo\"\n [ext]: 7\n value { c: 999 }\n}\n", options: options) {(o: MessageTestType) in + assertTextFormatDecodeSucceeds( + "map_string_foreign_message {\n key: \"foo\"\n [ext]: 7\n value { c: 999 }\n}\n", + options: options + ) { (o: MessageTestType) in o.mapStringForeignMessage == ["foo": foo] } do { - let _ = try MessageTestType(textFormatString: "map_string_foreign_message {\n key: \"foo\"\n unknown: 6\n value { c: 999 }\n}\n", options: options) + let _ = try MessageTestType( + textFormatString: + "map_string_foreign_message {\n key: \"foo\"\n unknown: 6\n value { c: 999 }\n}\n", + options: options + ) XCTFail("Should have failed") } catch TextFormatDecodingError.unknownField { // This is what should have happened. @@ -221,16 +252,28 @@ final class Test_TextFormat_Map_proto3: XCTestCase, PBTestHelpers { options.ignoreUnknownFields = true options.ignoreUnknownExtensionFields = true - assertTextFormatDecodeSucceeds("map_string_foreign_message {\n key: \"foo\"\n unknown: 6\n [ext]: 7\n value { c: 999 }\n}\n", options: options) {(o: MessageTestType) in + assertTextFormatDecodeSucceeds( + "map_string_foreign_message {\n key: \"foo\"\n unknown: 6\n [ext]: 7\n value { c: 999 }\n}\n", + options: options + ) { (o: MessageTestType) in o.mapStringForeignMessage == ["foo": foo] } - assertTextFormatDecodeSucceeds("map_string_foreign_message { unknown: 6, [ext]: 7, key: \"foo\", value { c: 999 } }", options: options) {(o: MessageTestType) in + assertTextFormatDecodeSucceeds( + "map_string_foreign_message { unknown: 6, [ext]: 7, key: \"foo\", value { c: 999 } }", + options: options + ) { (o: MessageTestType) in o.mapStringForeignMessage == ["foo": foo] } - assertTextFormatDecodeSucceeds("map_string_foreign_message { key: \"foo\"; value { c: 999 }; unknown: 6; [ext]: 7 }", options: options) {(o: MessageTestType) in + assertTextFormatDecodeSucceeds( + "map_string_foreign_message { key: \"foo\"; value { c: 999 }; unknown: 6; [ext]: 7 }", + options: options + ) { (o: MessageTestType) in o.mapStringForeignMessage == ["foo": foo] } - assertTextFormatDecodeSucceeds("map_string_foreign_message { key: \"foo\" value { c: 999 } unknown: 6 [ext]: 7 }", options: options) {(o: MessageTestType) in + assertTextFormatDecodeSucceeds( + "map_string_foreign_message { key: \"foo\" value { c: 999 } unknown: 6 [ext]: 7 }", + options: options + ) { (o: MessageTestType) in o.mapStringForeignMessage == ["foo": foo] } } @@ -239,11 +282,17 @@ final class Test_TextFormat_Map_proto3: XCTestCase, PBTestHelpers { var options = TextFormatDecodingOptions() options.ignoreUnknownFields = true - assertTextFormatDecodeSucceeds("map_int32_enum {\n key: 1\n unknown: 6\n\n value: MAP_ENUM_BAR\n}\n", options: options) {(o: MessageTestType) in + assertTextFormatDecodeSucceeds( + "map_int32_enum {\n key: 1\n unknown: 6\n\n value: MAP_ENUM_BAR\n}\n", + options: options + ) { (o: MessageTestType) in o.mapInt32Enum == [1: .bar] } do { - let _ = try MessageTestType(textFormatString: "map_int32_enum {\n key: 1\n [ext]: 7\n value: MAP_ENUM_BAR\n}\n", options: options) + let _ = try MessageTestType( + textFormatString: "map_int32_enum {\n key: 1\n [ext]: 7\n value: MAP_ENUM_BAR\n}\n", + options: options + ) XCTFail("Should have failed") } catch TextFormatDecodingError.unknownField { // This is what should have happened. @@ -254,11 +303,17 @@ final class Test_TextFormat_Map_proto3: XCTestCase, PBTestHelpers { options.ignoreUnknownFields = false options.ignoreUnknownExtensionFields = true - assertTextFormatDecodeSucceeds("map_int32_enum {\n key: 1\n [ext]: 7\n value: MAP_ENUM_BAR\n}\n", options: options) {(o: MessageTestType) in + assertTextFormatDecodeSucceeds( + "map_int32_enum {\n key: 1\n [ext]: 7\n value: MAP_ENUM_BAR\n}\n", + options: options + ) { (o: MessageTestType) in o.mapInt32Enum == [1: .bar] } do { - let _ = try MessageTestType(textFormatString: "map_int32_enum {\n key: 1\n unknown: 6\n value: MAP_ENUM_BAR\n}\n", options: options) + let _ = try MessageTestType( + textFormatString: "map_int32_enum {\n key: 1\n unknown: 6\n value: MAP_ENUM_BAR\n}\n", + options: options + ) XCTFail("Should have failed") } catch TextFormatDecodingError.unknownField { // This is what should have happened. @@ -269,16 +324,28 @@ final class Test_TextFormat_Map_proto3: XCTestCase, PBTestHelpers { options.ignoreUnknownFields = true options.ignoreUnknownExtensionFields = true - assertTextFormatDecodeSucceeds("map_int32_enum {\n key: 1\n unknown: 6\n [ext]: 7\n value: MAP_ENUM_BAR\n}\n", options: options) {(o: MessageTestType) in + assertTextFormatDecodeSucceeds( + "map_int32_enum {\n key: 1\n unknown: 6\n [ext]: 7\n value: MAP_ENUM_BAR\n}\n", + options: options + ) { (o: MessageTestType) in o.mapInt32Enum == [1: .bar] } - assertTextFormatDecodeSucceeds("map_int32_enum { unknown: 6, [ext]: 7, key: 1, value: MAP_ENUM_BAR }", options: options) {(o: MessageTestType) in + assertTextFormatDecodeSucceeds( + "map_int32_enum { unknown: 6, [ext]: 7, key: 1, value: MAP_ENUM_BAR }", + options: options + ) { (o: MessageTestType) in o.mapInt32Enum == [1: .bar] } - assertTextFormatDecodeSucceeds("map_int32_enum { key: 1; value: MAP_ENUM_BAR; unknown: 6; [ext]: 7 }", options: options) {(o: MessageTestType) in + assertTextFormatDecodeSucceeds( + "map_int32_enum { key: 1; value: MAP_ENUM_BAR; unknown: 6; [ext]: 7 }", + options: options + ) { (o: MessageTestType) in o.mapInt32Enum == [1: .bar] } - assertTextFormatDecodeSucceeds("map_int32_enum { key: 1 value: MAP_ENUM_BAR unknown: 6 [ext]: 7 }", options: options) {(o: MessageTestType) in + assertTextFormatDecodeSucceeds( + "map_int32_enum { key: 1 value: MAP_ENUM_BAR unknown: 6 [ext]: 7 }", + options: options + ) { (o: MessageTestType) in o.mapInt32Enum == [1: .bar] } } diff --git a/Tests/SwiftProtobufTests/Test_TextFormat_Performance.swift b/Tests/SwiftProtobufTests/Test_TextFormat_Performance.swift index 4061c6ae1..bc8d381eb 100644 --- a/Tests/SwiftProtobufTests/Test_TextFormat_Performance.swift +++ b/Tests/SwiftProtobufTests/Test_TextFormat_Performance.swift @@ -12,8 +12,8 @@ /// // ----------------------------------------------------------------------------- -import XCTest import SwiftProtobuf +import XCTest final class Test_TextFormat_Performance: XCTestCase, PBTestHelpers { typealias MessageTestType = SwiftProtoTesting_Fuzz_Message @@ -26,31 +26,30 @@ final class Test_TextFormat_Performance: XCTestCase, PBTestHelpers { func testEncoding_manyMapsEncoding_shouldBeUnder1s() { let repeats = 50000 - let child = ( - "repeated_message {\n" - + " map_fixed64_sint64 {\n" - + " key: 20\n" - + " value: 8\n" - + " }\n" - + " map_fixed64_sint64 {\n" - + " key: 30\n" - + " value: 4\n" - + " }\n" - + " map_fixed64_sint64 {\n" - + " key: 40\n" - + " value: 2\n" - + " }\n" - + "}\n" - ) + let child = + ("repeated_message {\n" + + " map_fixed64_sint64 {\n" + + " key: 20\n" + + " value: 8\n" + + " }\n" + + " map_fixed64_sint64 {\n" + + " key: 30\n" + + " value: 4\n" + + " }\n" + + " map_fixed64_sint64 {\n" + + " key: 40\n" + + " value: 2\n" + + " }\n" + + "}\n") let expected = String(repeating: child, count: repeats) let msg = MessageTestType.with { let child = MessageTestType.with { - $0.mapFixed64Sint64[20] = 8 - $0.mapFixed64Sint64[30] = 4 - $0.mapFixed64Sint64[40] = 2 + $0.mapFixed64Sint64[20] = 8 + $0.mapFixed64Sint64[30] = 4 + $0.mapFixed64Sint64[40] = 2 } - let array = Array(repeating: child, count: repeats) + let array = [MessageTestType](repeating: child, count: repeats) $0.repeatedMessage = array } @@ -67,24 +66,23 @@ final class Test_TextFormat_Performance: XCTestCase, PBTestHelpers { func testEncoding_manyAnyEncoding_shouldBeUnder1s() { let repeats = 50000 - let child = ( - "repeated_message {\n" - + " wkt_any {\n" - + " [type.googleapis.com/google.protobuf.Duration] {\n" - + " seconds: 123\n" - + " nanos: 123456789\n" - + " }\n" - + " }\n" - + "}\n" - ) + let child = + ("repeated_message {\n" + + " wkt_any {\n" + + " [type.googleapis.com/google.protobuf.Duration] {\n" + + " seconds: 123\n" + + " nanos: 123456789\n" + + " }\n" + + " }\n" + + "}\n") let expected = String(repeating: child, count: repeats) let msg = MessageTestType.with { let child = MessageTestType.with { - let duration = Google_Protobuf_Duration(seconds: 123, nanos: 123456789) + let duration = Google_Protobuf_Duration(seconds: 123, nanos: 123_456_789) $0.wktAny = try! Google_Protobuf_Any(message: duration) } - let array = Array(repeating: child, count: repeats) + let array = [MessageTestType](repeating: child, count: repeats) $0.repeatedMessage = array } diff --git a/Tests/SwiftProtobufTests/Test_TextFormat_Unknown.swift b/Tests/SwiftProtobufTests/Test_TextFormat_Unknown.swift index 53d905f90..addb0bb07 100644 --- a/Tests/SwiftProtobufTests/Test_TextFormat_Unknown.swift +++ b/Tests/SwiftProtobufTests/Test_TextFormat_Unknown.swift @@ -14,6 +14,7 @@ import Foundation import XCTest + @testable import SwiftProtobuf final class Test_TextFormat_Unknown: XCTestCase, PBTestHelpers { @@ -45,7 +46,7 @@ final class Test_TextFormat_Unknown: XCTestCase, PBTestHelpers { // This is what should have happened. } - let msg2 = try MessageTestType(textFormatString: text, options: decodeIgnoreAllUnknowns) // Shouldn't throw + let msg2 = try MessageTestType(textFormatString: text, options: decodeIgnoreAllUnknowns) // Shouldn't throw XCTAssertEqual(try msg2.serializedBytes(), []) let textWithoutUnknowns = msg.textFormatString(options: encodeWithoutUnknowns) @@ -65,7 +66,7 @@ final class Test_TextFormat_Unknown: XCTestCase, PBTestHelpers { // This is what should have happened. } - let msg2 = try MessageTestType(textFormatString: text, options: decodeIgnoreAllUnknowns) // Shouldn't throw + let msg2 = try MessageTestType(textFormatString: text, options: decodeIgnoreAllUnknowns) // Shouldn't throw XCTAssertEqual(try msg2.serializedBytes(), []) let textWithoutUnknowns = msg.textFormatString(options: encodeWithoutUnknowns) @@ -85,7 +86,7 @@ final class Test_TextFormat_Unknown: XCTestCase, PBTestHelpers { // This is what should have happened. } - let msg2 = try MessageTestType(textFormatString: text, options: decodeIgnoreAllUnknowns) // Shouldn't throw + let msg2 = try MessageTestType(textFormatString: text, options: decodeIgnoreAllUnknowns) // Shouldn't throw XCTAssertEqual(try msg2.serializedBytes(), []) let textWithoutUnknowns = msg.textFormatString(options: encodeWithoutUnknowns) @@ -106,7 +107,7 @@ final class Test_TextFormat_Unknown: XCTestCase, PBTestHelpers { // This is what should have happened. } - let msg2 = try MessageTestType(textFormatString: text, options: decodeIgnoreAllUnknowns) // Shouldn't throw + let msg2 = try MessageTestType(textFormatString: text, options: decodeIgnoreAllUnknowns) // Shouldn't throw XCTAssertEqual(try msg2.serializedBytes(), []) let textWithoutUnknowns = msg.textFormatString(options: encodeWithoutUnknowns) @@ -128,7 +129,7 @@ final class Test_TextFormat_Unknown: XCTestCase, PBTestHelpers { // This is what should have happened. } - let msg2 = try MessageTestType(textFormatString: text, options: decodeIgnoreAllUnknowns) // Shouldn't throw + let msg2 = try MessageTestType(textFormatString: text, options: decodeIgnoreAllUnknowns) // Shouldn't throw XCTAssertEqual(try msg2.serializedBytes(), []) let textWithoutUnknowns = msg.textFormatString(options: encodeWithoutUnknowns) @@ -148,14 +149,13 @@ final class Test_TextFormat_Unknown: XCTestCase, PBTestHelpers { // This is what should have happened. } - let msg2 = try MessageTestType(textFormatString: text, options: decodeIgnoreAllUnknowns) // Shouldn't throw + let msg2 = try MessageTestType(textFormatString: text, options: decodeIgnoreAllUnknowns) // Shouldn't throw XCTAssertEqual(try msg2.serializedBytes(), []) let textWithoutUnknowns = msg.textFormatString(options: encodeWithoutUnknowns) XCTAssertEqual(textWithoutUnknowns, "") } - func test_unknown_lengthDelimited_zero_length() throws { let bytes: [UInt8] = [8, 1, 18, 0] let msg = try MessageTestType(serializedBytes: bytes) @@ -169,14 +169,13 @@ final class Test_TextFormat_Unknown: XCTestCase, PBTestHelpers { // This is what should have happened. } - let msg2 = try MessageTestType(textFormatString: text, options: decodeIgnoreAllUnknowns) // Shouldn't throw + let msg2 = try MessageTestType(textFormatString: text, options: decodeIgnoreAllUnknowns) // Shouldn't throw XCTAssertEqual(try msg2.serializedBytes(), []) let textWithoutUnknowns = msg.textFormatString(options: encodeWithoutUnknowns) XCTAssertEqual(textWithoutUnknowns, "") } - func test_unknown_lengthDelimited_nested_message() throws { let bytes: [UInt8] = [8, 1, 18, 6, 8, 2, 18, 2, 8, 3] let msg = try MessageTestType(serializedBytes: bytes) @@ -190,7 +189,7 @@ final class Test_TextFormat_Unknown: XCTestCase, PBTestHelpers { // This is what should have happened. } - let msg2 = try MessageTestType(textFormatString: text, options: decodeIgnoreAllUnknowns) // Shouldn't throw + let msg2 = try MessageTestType(textFormatString: text, options: decodeIgnoreAllUnknowns) // Shouldn't throw XCTAssertEqual(try msg2.serializedBytes(), []) let textWithoutUnknowns = msg.textFormatString(options: encodeWithoutUnknowns) @@ -210,7 +209,7 @@ final class Test_TextFormat_Unknown: XCTestCase, PBTestHelpers { // This is what should have happened. } - let msg2 = try MessageTestType(textFormatString: text, options: decodeIgnoreAllUnknowns) // Shouldn't throw + let msg2 = try MessageTestType(textFormatString: text, options: decodeIgnoreAllUnknowns) // Shouldn't throw XCTAssertEqual(try msg2.serializedBytes(), []) let textWithoutUnknowns = msg.textFormatString(options: encodeWithoutUnknowns) @@ -227,7 +226,8 @@ final class Test_TextFormat_Unknown: XCTestCase, PBTestHelpers { lengths.append(0) for _ in 0..<(kNestingDepth - 1) { lengths.append( - kTagSize + Int32(Varint.encodedSize(of: lengths.last!)) + lengths.last!) + kTagSize + Int32(Varint.encodedSize(of: lengths.last!)) + lengths.last! + ) } var bytes = Data() @@ -240,8 +240,10 @@ final class Test_TextFormat_Unknown: XCTestCase, PBTestHelpers { let text = msg.textFormatString() // Internally, the limit is 10, so we'll get 10 objects and then a // string for the bytes. - let expectedPrefix = "1 {\n 1 {\n 1 {\n 1 {\n 1 {\n 1 {\n 1 {\n 1 {\n 1 {\n 1 {\n 1: \"" - let expectedSuffix = "\"\n }\n }\n }\n }\n }\n }\n }\n }\n }\n}\n" + let expectedPrefix = + "1 {\n 1 {\n 1 {\n 1 {\n 1 {\n 1 {\n 1 {\n 1 {\n 1 {\n 1 {\n 1: \"" + let expectedSuffix = + "\"\n }\n }\n }\n }\n }\n }\n }\n }\n }\n}\n" XCTAssertTrue(text.hasPrefix(expectedPrefix)) XCTAssertTrue(text.hasSuffix(expectedSuffix)) @@ -254,7 +256,7 @@ final class Test_TextFormat_Unknown: XCTestCase, PBTestHelpers { // Since unknowns are limited to a depth of 10, we should be able to since the inner most // messages are just a string (bytes) blob. - let msg2 = try MessageTestType(textFormatString: text, options: decodeIgnoreAllUnknowns) // Shouldn't throw + let msg2 = try MessageTestType(textFormatString: text, options: decodeIgnoreAllUnknowns) // Shouldn't throw XCTAssertEqual(try msg2.serializedBytes(), []) // Since unknowns are limited to depth of 10, lower the depth limit on to confirm we stop @@ -285,7 +287,7 @@ final class Test_TextFormat_Unknown: XCTestCase, PBTestHelpers { // This is what should have happened. } - let msg2 = try MessageTestType(textFormatString: text, options: decodeIgnoreAllUnknowns) // Shouldn't throw + let msg2 = try MessageTestType(textFormatString: text, options: decodeIgnoreAllUnknowns) // Shouldn't throw XCTAssertEqual(try msg2.serializedBytes(), []) let textWithoutUnknowns = msg.textFormatString(options: encodeWithoutUnknowns) @@ -305,7 +307,7 @@ final class Test_TextFormat_Unknown: XCTestCase, PBTestHelpers { // This is what should have happened. } - let msg2 = try MessageTestType(textFormatString: text, options: decodeIgnoreAllUnknowns) // Shouldn't throw + let msg2 = try MessageTestType(textFormatString: text, options: decodeIgnoreAllUnknowns) // Shouldn't throw XCTAssertEqual(try msg2.serializedBytes(), []) let textWithoutUnknowns = msg.textFormatString(options: encodeWithoutUnknowns) @@ -318,8 +320,10 @@ final class Test_TextFormat_Unknown: XCTestCase, PBTestHelpers { let kTagStart = FieldTag(fieldNumber: kFieldNum, wireFormat: .startGroup) let kTagEnd = FieldTag(fieldNumber: kFieldNum, wireFormat: .endGroup) - var bytes = Data(capacity: kNestingDepth * - (Varint.encodedSize(of: kTagStart.rawValue) + Varint.encodedSize(of: kTagEnd.rawValue))) + var bytes = Data( + capacity: kNestingDepth + * (Varint.encodedSize(of: kTagStart.rawValue) + Varint.encodedSize(of: kTagEnd.rawValue)) + ) for _ in 0..(_ message: M, expected: String, file: XCTestFileArgType = #file, line: UInt = #line) { + func assertAnyTest( + _ message: M, + expected: String, + file: XCTestFileArgType = #file, + line: UInt = #line + ) { let empty = MessageTestType() var configured = empty do { @@ -34,7 +39,12 @@ final class Test_TextFormat_WKT_proto3: XCTestCase, PBTestHelpers { let decoded = try MessageTestType(textFormatString: encoded) let decodedMessage = try M(unpackingAny: decoded.anyField) let r = (message == decodedMessage) - XCTAssert(r, "Encode/decode cycle should generate equal object: \(decoded) != \(configured)", file: file, line: line) + XCTAssert( + r, + "Encode/decode cycle should generate equal object: \(decoded) != \(configured)", + file: file, + line: line + ) } catch { XCTFail("Encode/decode cycle should not throw error, decoding: \(error)", file: file, line: line) } @@ -43,24 +53,34 @@ final class Test_TextFormat_WKT_proto3: XCTestCase, PBTestHelpers { // Any equality is a little tricky, so this directly tests the inner // contained object after unpacking the Any. func testAny() throws { - assertAnyTest(Google_Protobuf_Duration(seconds: 123, nanos: 123456789), - expected: "any_field {\n [type.googleapis.com/google.protobuf.Duration] {\n seconds: 123\n nanos: 123456789\n }\n}\n") - assertAnyTest(Google_Protobuf_Empty(), - expected: "any_field {\n [type.googleapis.com/google.protobuf.Empty] {\n }\n}\n") + assertAnyTest( + Google_Protobuf_Duration(seconds: 123, nanos: 123_456_789), + expected: + "any_field {\n [type.googleapis.com/google.protobuf.Duration] {\n seconds: 123\n nanos: 123456789\n }\n}\n" + ) + assertAnyTest( + Google_Protobuf_Empty(), + expected: "any_field {\n [type.googleapis.com/google.protobuf.Empty] {\n }\n}\n" + ) // Nested any let a = try SwiftProtoTesting_TestWellKnownTypes.with { - $0.anyField = try Google_Protobuf_Any(message: Google_Protobuf_Any(message: Google_Protobuf_Duration(seconds: 123, nanos: 234567890))) + $0.anyField = try Google_Protobuf_Any( + message: Google_Protobuf_Any(message: Google_Protobuf_Duration(seconds: 123, nanos: 234_567_890)) + ) } let a_encoded = a.textFormatString() - XCTAssertEqual(a_encoded, "any_field {\n [type.googleapis.com/google.protobuf.Any] {\n [type.googleapis.com/google.protobuf.Duration] {\n seconds: 123\n nanos: 234567890\n }\n }\n}\n") + XCTAssertEqual( + a_encoded, + "any_field {\n [type.googleapis.com/google.protobuf.Any] {\n [type.googleapis.com/google.protobuf.Duration] {\n seconds: 123\n nanos: 234567890\n }\n }\n}\n" + ) let a_decoded = try SwiftProtoTesting_TestWellKnownTypes(textFormatString: a_encoded) let a_decoded_any = a_decoded.anyField let a_decoded_any_any = try Google_Protobuf_Any(unpackingAny: a_decoded_any) let a_decoded_any_any_duration = try Google_Protobuf_Duration(unpackingAny: a_decoded_any_any) XCTAssertEqual(a_decoded_any_any_duration.seconds, 123) - XCTAssertEqual(a_decoded_any_any_duration.nanos, 234567890) + XCTAssertEqual(a_decoded_any_any_duration.nanos, 234_567_890) } // Any supports a "verbose" text encoding that uses the URL as the key @@ -68,7 +88,10 @@ final class Test_TextFormat_WKT_proto3: XCTestCase, PBTestHelpers { func testAny_verbose() { let a: SwiftProtoTesting_TestWellKnownTypes do { - a = try SwiftProtoTesting_TestWellKnownTypes(textFormatString: "any_field {[type.googleapis.com/google.protobuf.Duration] {seconds:77,nanos:123456789}}") + a = try SwiftProtoTesting_TestWellKnownTypes( + textFormatString: + "any_field {[type.googleapis.com/google.protobuf.Duration] {seconds:77,nanos:123456789}}" + ) } catch let e { XCTFail("Decoding failed: \(e)") return @@ -77,7 +100,7 @@ final class Test_TextFormat_WKT_proto3: XCTestCase, PBTestHelpers { let a_any = a.anyField let a_duration = try Google_Protobuf_Duration(unpackingAny: a_any) XCTAssertEqual(a_duration.seconds, 77) - XCTAssertEqual(a_duration.nanos, 123456789) + XCTAssertEqual(a_duration.nanos, 123_456_789) } catch let e { XCTFail("Any field doesn't hold a duration?: \(e)") } @@ -85,7 +108,10 @@ final class Test_TextFormat_WKT_proto3: XCTestCase, PBTestHelpers { // Nested Any is a particularly tricky decode problem let b: SwiftProtoTesting_TestWellKnownTypes do { - b = try SwiftProtoTesting_TestWellKnownTypes(textFormatString: "any_field {[type.googleapis.com/google.protobuf.Any]{[type.googleapis.com/google.protobuf.Duration] {seconds:88,nanos:987654321}}}") + b = try SwiftProtoTesting_TestWellKnownTypes( + textFormatString: + "any_field {[type.googleapis.com/google.protobuf.Any]{[type.googleapis.com/google.protobuf.Duration] {seconds:88,nanos:987654321}}}" + ) } catch let e { XCTFail("Decoding failed: \(e)") return @@ -100,7 +126,7 @@ final class Test_TextFormat_WKT_proto3: XCTestCase, PBTestHelpers { do { let b_duration = try Google_Protobuf_Duration(unpackingAny: b_any) XCTAssertEqual(b_duration.seconds, 88) - XCTAssertEqual(b_duration.nanos, 987654321) + XCTAssertEqual(b_duration.nanos, 987_654_321) } catch let e { XCTFail("Inner Any field doesn't hold a Duration: \(e)") } @@ -114,7 +140,7 @@ final class Test_TextFormat_WKT_proto3: XCTestCase, PBTestHelpers { "duration_field {\n seconds: 123\n nanos: 123456789\n}\n" ) { (o: inout MessageTestType) in - o.durationField = Google_Protobuf_Duration(seconds: 123, nanos: 123456789) + o.durationField = Google_Protobuf_Duration(seconds: 123, nanos: 123_456_789) } } @@ -147,7 +173,7 @@ final class Test_TextFormat_WKT_proto3: XCTestCase, PBTestHelpers { "timestamp_field {\n seconds: 123\n nanos: 123456789\n}\n" ) { (o: inout MessageTestType) in - o.timestampField = Google_Protobuf_Timestamp(seconds: 123, nanos: 123456789) + o.timestampField = Google_Protobuf_Timestamp(seconds: 123, nanos: 123_456_789) } } diff --git a/Tests/SwiftProtobufTests/Test_TextFormat_proto2.swift b/Tests/SwiftProtobufTests/Test_TextFormat_proto2.swift index 1cc836b4f..5b351ea0d 100644 --- a/Tests/SwiftProtobufTests/Test_TextFormat_proto2.swift +++ b/Tests/SwiftProtobufTests/Test_TextFormat_proto2.swift @@ -13,15 +13,15 @@ // ----------------------------------------------------------------------------- import Foundation -import XCTest import SwiftProtobuf +import XCTest final class Test_TextFormat_proto2: XCTestCase, PBTestHelpers { typealias MessageTestType = SwiftProtoTesting_TestAllTypes func test_group() { - assertTextFormatEncode("OptionalGroup {\n a: 17\n}\n") {(o: inout MessageTestType) in - o.optionalGroup = SwiftProtoTesting_TestAllTypes.OptionalGroup.with {$0.a = 17} + assertTextFormatEncode("OptionalGroup {\n a: 17\n}\n") { (o: inout MessageTestType) in + o.optionalGroup = SwiftProtoTesting_TestAllTypes.OptionalGroup.with { $0.a = 17 } } } @@ -32,31 +32,32 @@ final class Test_TextFormat_proto2: XCTestCase, PBTestHelpers { } func test_group_numbers() { - assertTextFormatDecodeSucceeds("16 {\n 17: 17\n}\n") {(o: MessageTestType) in - o.optionalGroup == SwiftProtoTesting_TestAllTypes.OptionalGroup.with {$0.a = 17} + assertTextFormatDecodeSucceeds("16 {\n 17: 17\n}\n") { (o: MessageTestType) in + o.optionalGroup == SwiftProtoTesting_TestAllTypes.OptionalGroup.with { $0.a = 17 } } } func test_repeatedGroup() { - assertTextFormatEncode("RepeatedGroup {\n a: 17\n}\nRepeatedGroup {\n a: 18\n}\n") {(o: inout MessageTestType) in - let group17 = SwiftProtoTesting_TestAllTypes.RepeatedGroup.with {$0.a = 17} - let group18 = SwiftProtoTesting_TestAllTypes.RepeatedGroup.with {$0.a = 18} + assertTextFormatEncode("RepeatedGroup {\n a: 17\n}\nRepeatedGroup {\n a: 18\n}\n") { + (o: inout MessageTestType) in + let group17 = SwiftProtoTesting_TestAllTypes.RepeatedGroup.with { $0.a = 17 } + let group18 = SwiftProtoTesting_TestAllTypes.RepeatedGroup.with { $0.a = 18 } o.repeatedGroup = [group17, group18] } } func test_repeatedGroup_altName() throws { let msg = try MessageTestType(textFormatString: "repeatedgroup {a: 17}\nrepeatedgroup {a: 18}") - let group17 = SwiftProtoTesting_TestAllTypes.RepeatedGroup.with {$0.a = 17} - let group18 = SwiftProtoTesting_TestAllTypes.RepeatedGroup.with {$0.a = 18} + let group17 = SwiftProtoTesting_TestAllTypes.RepeatedGroup.with { $0.a = 17 } + let group18 = SwiftProtoTesting_TestAllTypes.RepeatedGroup.with { $0.a = 18 } XCTAssertEqual(msg.repeatedGroup, [group17, group18]) XCTAssertEqual(msg.textFormatString(), "RepeatedGroup {\n a: 17\n}\nRepeatedGroup {\n a: 18\n}\n") } func test_repeatedGroup_numbers() { - assertTextFormatDecodeSucceeds("46 {\n 47: 17\n}\n46 {\n 47: 18\n}\n") {(o: MessageTestType) in - let group17 = SwiftProtoTesting_TestAllTypes.RepeatedGroup.with {$0.a = 17} - let group18 = SwiftProtoTesting_TestAllTypes.RepeatedGroup.with {$0.a = 18} + assertTextFormatDecodeSucceeds("46 {\n 47: 17\n}\n46 {\n 47: 18\n}\n") { (o: MessageTestType) in + let group17 = SwiftProtoTesting_TestAllTypes.RepeatedGroup.with { $0.a = 17 } + let group18 = SwiftProtoTesting_TestAllTypes.RepeatedGroup.with { $0.a = 18 } return o.repeatedGroup == [group17, group18] } } diff --git a/Tests/SwiftProtobufTests/Test_TextFormat_proto2_extensions.swift b/Tests/SwiftProtobufTests/Test_TextFormat_proto2_extensions.swift index c9a1fe6fa..9536e20a7 100644 --- a/Tests/SwiftProtobufTests/Test_TextFormat_proto2_extensions.swift +++ b/Tests/SwiftProtobufTests/Test_TextFormat_proto2_extensions.swift @@ -13,23 +13,27 @@ // ----------------------------------------------------------------------------- import Foundation -import XCTest import SwiftProtobuf +import XCTest final class Test_TextFormat_proto2_extensions: XCTestCase, PBTestHelpers { typealias MessageTestType = SwiftProtoTesting_TestAllExtensions func test_file_level_extension() { - assertTextFormatEncode("[swift_proto_testing.optional_int32_extension]: 789\n", - extensions: SwiftProtoTesting_Unittest_Extensions) { + assertTextFormatEncode( + "[swift_proto_testing.optional_int32_extension]: 789\n", + extensions: SwiftProtoTesting_Unittest_Extensions + ) { (o: inout MessageTestType) in o.SwiftProtoTesting_optionalInt32Extension = 789 } // Fails if we don't provide the extensions to the decoder: assertTextFormatDecodeFails("[swift_proto_testing.optional_int32_extension]: 789\n") - assertTextFormatEncode("[swift_proto_testing.optionalgroup_extension] {\n a: 789\n}\n", - extensions: SwiftProtoTesting_Unittest_Extensions) { + assertTextFormatEncode( + "[swift_proto_testing.optionalgroup_extension] {\n a: 789\n}\n", + extensions: SwiftProtoTesting_Unittest_Extensions + ) { (o: inout MessageTestType) in o.SwiftProtoTesting_optionalGroupExtension.a = 789 } @@ -38,8 +42,10 @@ final class Test_TextFormat_proto2_extensions: XCTestCase, PBTestHelpers { } func test_nested_extension() { - assertTextFormatEncode("[swift_proto_testing.TestNestedExtension.test]: \"foo\"\n", - extensions: SwiftProtoTesting_Unittest_Extensions) { + assertTextFormatEncode( + "[swift_proto_testing.TestNestedExtension.test]: \"foo\"\n", + extensions: SwiftProtoTesting_Unittest_Extensions + ) { (o: inout MessageTestType) in o.SwiftProtoTesting_TestNestedExtension_test = "foo" } diff --git a/Tests/SwiftProtobufTests/Test_TextFormat_proto3.swift b/Tests/SwiftProtobufTests/Test_TextFormat_proto3.swift index e2034244c..e8135e028 100644 --- a/Tests/SwiftProtobufTests/Test_TextFormat_proto3.swift +++ b/Tests/SwiftProtobufTests/Test_TextFormat_proto3.swift @@ -13,8 +13,8 @@ // ----------------------------------------------------------------------------- import Foundation -import XCTest import SwiftProtobuf +import XCTest final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { typealias MessageTestType = SwiftProtoTesting_Proto3_TestAllTypes @@ -22,14 +22,14 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { func testDecoding_comments() { assertTextFormatDecodeSucceeds("optional_int32: 41#optional_int32: 42\noptional_int64: 8") { (o: MessageTestType) in - return o.optionalInt32 == 41 && o.optionalInt64 == 8 + o.optionalInt32 == 41 && o.optionalInt64 == 8 } } func testDecoding_comments_numbers() { assertTextFormatDecodeSucceeds("1: 41#optional_int32: 42\n2: 8") { (o: MessageTestType) in - return o.optionalInt32 == 41 && o.optionalInt64 == 8 + o.optionalInt32 == 41 && o.optionalInt64 == 8 } } @@ -43,50 +43,64 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { XCTAssertEqual("optional_int32: 41\n", a.textFormatString()) - assertTextFormatEncode("optional_int32: 1\n") {(o: inout MessageTestType) in o.optionalInt32 = 1 } - assertTextFormatEncode("optional_int32: 12\n") {(o: inout MessageTestType) in o.optionalInt32 = 12 } - assertTextFormatEncode("optional_int32: 123\n") {(o: inout MessageTestType) in o.optionalInt32 = 123 } - assertTextFormatEncode("optional_int32: 1234\n") {(o: inout MessageTestType) in o.optionalInt32 = 1234 } - assertTextFormatEncode("optional_int32: 12345\n") {(o: inout MessageTestType) in o.optionalInt32 = 12345 } - assertTextFormatEncode("optional_int32: 123456\n") {(o: inout MessageTestType) in o.optionalInt32 = 123456 } - assertTextFormatEncode("optional_int32: 1234567\n") {(o: inout MessageTestType) in o.optionalInt32 = 1234567 } - assertTextFormatEncode("optional_int32: 12345678\n") {(o: inout MessageTestType) in o.optionalInt32 = 12345678 } - assertTextFormatEncode("optional_int32: 123456789\n") {(o: inout MessageTestType) in o.optionalInt32 = 123456789 } - assertTextFormatEncode("optional_int32: 1234567890\n") {(o: inout MessageTestType) in o.optionalInt32 = 1234567890 } - - assertTextFormatEncode("optional_int32: 1\n") {(o: inout MessageTestType) in o.optionalInt32 = 1 } - assertTextFormatEncode("optional_int32: 10\n") {(o: inout MessageTestType) in o.optionalInt32 = 10 } - assertTextFormatEncode("optional_int32: 100\n") {(o: inout MessageTestType) in o.optionalInt32 = 100 } - assertTextFormatEncode("optional_int32: 1000\n") {(o: inout MessageTestType) in o.optionalInt32 = 1000 } - assertTextFormatEncode("optional_int32: 10000\n") {(o: inout MessageTestType) in o.optionalInt32 = 10000 } - assertTextFormatEncode("optional_int32: 100000\n") {(o: inout MessageTestType) in o.optionalInt32 = 100000 } - assertTextFormatEncode("optional_int32: 1000000\n") {(o: inout MessageTestType) in o.optionalInt32 = 1000000 } - assertTextFormatEncode("optional_int32: 10000000\n") {(o: inout MessageTestType) in o.optionalInt32 = 10000000 } - assertTextFormatEncode("optional_int32: 100000000\n") {(o: inout MessageTestType) in o.optionalInt32 = 100000000 } - assertTextFormatEncode("optional_int32: 1000000000\n") {(o: inout MessageTestType) in o.optionalInt32 = 1000000000 } - - - assertTextFormatEncode("optional_int32: 41\n") {(o: inout MessageTestType) in - o.optionalInt32 = 41 } - assertTextFormatEncode("optional_int32: 1\n") {(o: inout MessageTestType) in + assertTextFormatEncode("optional_int32: 1\n") { (o: inout MessageTestType) in o.optionalInt32 = 1 } + assertTextFormatEncode("optional_int32: 12\n") { (o: inout MessageTestType) in o.optionalInt32 = 12 } + assertTextFormatEncode("optional_int32: 123\n") { (o: inout MessageTestType) in o.optionalInt32 = 123 } + assertTextFormatEncode("optional_int32: 1234\n") { (o: inout MessageTestType) in o.optionalInt32 = 1234 } + assertTextFormatEncode("optional_int32: 12345\n") { (o: inout MessageTestType) in o.optionalInt32 = 12345 } + assertTextFormatEncode("optional_int32: 123456\n") { (o: inout MessageTestType) in o.optionalInt32 = 123456 } + assertTextFormatEncode("optional_int32: 1234567\n") { (o: inout MessageTestType) in o.optionalInt32 = 1_234_567 + } + assertTextFormatEncode("optional_int32: 12345678\n") { (o: inout MessageTestType) in + o.optionalInt32 = 12_345_678 + } + assertTextFormatEncode("optional_int32: 123456789\n") { (o: inout MessageTestType) in + o.optionalInt32 = 123_456_789 + } + assertTextFormatEncode("optional_int32: 1234567890\n") { (o: inout MessageTestType) in + o.optionalInt32 = 1_234_567_890 + } + + assertTextFormatEncode("optional_int32: 1\n") { (o: inout MessageTestType) in o.optionalInt32 = 1 } + assertTextFormatEncode("optional_int32: 10\n") { (o: inout MessageTestType) in o.optionalInt32 = 10 } + assertTextFormatEncode("optional_int32: 100\n") { (o: inout MessageTestType) in o.optionalInt32 = 100 } + assertTextFormatEncode("optional_int32: 1000\n") { (o: inout MessageTestType) in o.optionalInt32 = 1000 } + assertTextFormatEncode("optional_int32: 10000\n") { (o: inout MessageTestType) in o.optionalInt32 = 10000 } + assertTextFormatEncode("optional_int32: 100000\n") { (o: inout MessageTestType) in o.optionalInt32 = 100000 } + assertTextFormatEncode("optional_int32: 1000000\n") { (o: inout MessageTestType) in o.optionalInt32 = 1_000_000 + } + assertTextFormatEncode("optional_int32: 10000000\n") { (o: inout MessageTestType) in + o.optionalInt32 = 10_000_000 + } + assertTextFormatEncode("optional_int32: 100000000\n") { (o: inout MessageTestType) in + o.optionalInt32 = 100_000_000 + } + assertTextFormatEncode("optional_int32: 1000000000\n") { (o: inout MessageTestType) in + o.optionalInt32 = 1_000_000_000 + } + + assertTextFormatEncode("optional_int32: 41\n") { (o: inout MessageTestType) in + o.optionalInt32 = 41 + } + assertTextFormatEncode("optional_int32: 1\n") { (o: inout MessageTestType) in o.optionalInt32 = 1 } - assertTextFormatEncode("optional_int32: -1\n") {(o: inout MessageTestType) in + assertTextFormatEncode("optional_int32: -1\n") { (o: inout MessageTestType) in o.optionalInt32 = -1 } - assertTextFormatDecodeSucceeds("optional_int32:0x1234") {(o: MessageTestType) in - return o.optionalInt32 == 0x1234 + assertTextFormatDecodeSucceeds("optional_int32:0x1234") { (o: MessageTestType) in + o.optionalInt32 == 0x1234 } - assertTextFormatDecodeSucceeds("optional_int32:41") {(o: MessageTestType) in - return o.optionalInt32 == 41 + assertTextFormatDecodeSucceeds("optional_int32:41") { (o: MessageTestType) in + o.optionalInt32 == 41 } assertTextFormatDecodeSucceeds("optional_int32: 41#optional_int32: 42") { (o: MessageTestType) in - return o.optionalInt32 == 41 + o.optionalInt32 == 41 } assertTextFormatDecodeSucceeds("optional_int32: 41 optional_int32: 42") { (o: MessageTestType) in - return o.optionalInt32 == 42 + o.optionalInt32 == 42 } assertTextFormatDecodeFails("optional_int32: a\n") assertTextFormatDecodeFails("optional_int32: 999999999999999999999999999999999999\n") @@ -96,8 +110,8 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { assertTextFormatDecodeFails("optional_int32: \"hello\"\n") assertTextFormatDecodeFails("optional_int32: true\n") assertTextFormatDecodeFails("optional_int32: 0x80000000\n") - assertTextFormatDecodeSucceeds("optional_int32: -0x80000000\n") {(o: MessageTestType) in - return o.optionalInt32 == -0x80000000 + assertTextFormatDecodeSucceeds("optional_int32: -0x80000000\n") { (o: MessageTestType) in + o.optionalInt32 == -0x8000_0000 } assertTextFormatDecodeFails("optional_int32: -0x80000001\n") } @@ -108,31 +122,45 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { XCTAssertEqual("optional_int64: 2\n", a.textFormatString()) - assertTextFormatEncode("optional_int64: 1\n") {(o: inout MessageTestType) in o.optionalInt64 = 1 } - assertTextFormatEncode("optional_int64: 12\n") {(o: inout MessageTestType) in o.optionalInt64 = 12 } - assertTextFormatEncode("optional_int64: 123\n") {(o: inout MessageTestType) in o.optionalInt64 = 123 } - assertTextFormatEncode("optional_int64: 1234\n") {(o: inout MessageTestType) in o.optionalInt64 = 1234 } - assertTextFormatEncode("optional_int64: 12345\n") {(o: inout MessageTestType) in o.optionalInt64 = 12345 } - assertTextFormatEncode("optional_int64: 123456\n") {(o: inout MessageTestType) in o.optionalInt64 = 123456 } - assertTextFormatEncode("optional_int64: 1234567\n") {(o: inout MessageTestType) in o.optionalInt64 = 1234567 } - assertTextFormatEncode("optional_int64: 12345678\n") {(o: inout MessageTestType) in o.optionalInt64 = 12345678 } - assertTextFormatEncode("optional_int64: 123456789\n") {(o: inout MessageTestType) in o.optionalInt64 = 123456789 } - assertTextFormatEncode("optional_int64: 1234567890\n") {(o: inout MessageTestType) in o.optionalInt64 = 1234567890 } - - assertTextFormatEncode("optional_int64: 1\n") {(o: inout MessageTestType) in o.optionalInt64 = 1 } - assertTextFormatEncode("optional_int64: 10\n") {(o: inout MessageTestType) in o.optionalInt64 = 10 } - assertTextFormatEncode("optional_int64: 100\n") {(o: inout MessageTestType) in o.optionalInt64 = 100 } - assertTextFormatEncode("optional_int64: 1000\n") {(o: inout MessageTestType) in o.optionalInt64 = 1000 } - assertTextFormatEncode("optional_int64: 10000\n") {(o: inout MessageTestType) in o.optionalInt64 = 10000 } - assertTextFormatEncode("optional_int64: 100000\n") {(o: inout MessageTestType) in o.optionalInt64 = 100000 } - assertTextFormatEncode("optional_int64: 1000000\n") {(o: inout MessageTestType) in o.optionalInt64 = 1000000 } - assertTextFormatEncode("optional_int64: 10000000\n") {(o: inout MessageTestType) in o.optionalInt64 = 10000000 } - assertTextFormatEncode("optional_int64: 100000000\n") {(o: inout MessageTestType) in o.optionalInt64 = 100000000 } - assertTextFormatEncode("optional_int64: 1000000000\n") {(o: inout MessageTestType) in o.optionalInt64 = 1000000000 } - - assertTextFormatEncode("optional_int64: -2\n") {(o: inout MessageTestType) in o.optionalInt64 = -2 } - assertTextFormatDecodeSucceeds("optional_int64: 0x1234567812345678\n") {(o: MessageTestType) in - return o.optionalInt64 == 0x1234567812345678 + assertTextFormatEncode("optional_int64: 1\n") { (o: inout MessageTestType) in o.optionalInt64 = 1 } + assertTextFormatEncode("optional_int64: 12\n") { (o: inout MessageTestType) in o.optionalInt64 = 12 } + assertTextFormatEncode("optional_int64: 123\n") { (o: inout MessageTestType) in o.optionalInt64 = 123 } + assertTextFormatEncode("optional_int64: 1234\n") { (o: inout MessageTestType) in o.optionalInt64 = 1234 } + assertTextFormatEncode("optional_int64: 12345\n") { (o: inout MessageTestType) in o.optionalInt64 = 12345 } + assertTextFormatEncode("optional_int64: 123456\n") { (o: inout MessageTestType) in o.optionalInt64 = 123456 } + assertTextFormatEncode("optional_int64: 1234567\n") { (o: inout MessageTestType) in o.optionalInt64 = 1_234_567 + } + assertTextFormatEncode("optional_int64: 12345678\n") { (o: inout MessageTestType) in + o.optionalInt64 = 12_345_678 + } + assertTextFormatEncode("optional_int64: 123456789\n") { (o: inout MessageTestType) in + o.optionalInt64 = 123_456_789 + } + assertTextFormatEncode("optional_int64: 1234567890\n") { (o: inout MessageTestType) in + o.optionalInt64 = 1_234_567_890 + } + + assertTextFormatEncode("optional_int64: 1\n") { (o: inout MessageTestType) in o.optionalInt64 = 1 } + assertTextFormatEncode("optional_int64: 10\n") { (o: inout MessageTestType) in o.optionalInt64 = 10 } + assertTextFormatEncode("optional_int64: 100\n") { (o: inout MessageTestType) in o.optionalInt64 = 100 } + assertTextFormatEncode("optional_int64: 1000\n") { (o: inout MessageTestType) in o.optionalInt64 = 1000 } + assertTextFormatEncode("optional_int64: 10000\n") { (o: inout MessageTestType) in o.optionalInt64 = 10000 } + assertTextFormatEncode("optional_int64: 100000\n") { (o: inout MessageTestType) in o.optionalInt64 = 100000 } + assertTextFormatEncode("optional_int64: 1000000\n") { (o: inout MessageTestType) in o.optionalInt64 = 1_000_000 + } + assertTextFormatEncode("optional_int64: 10000000\n") { (o: inout MessageTestType) in + o.optionalInt64 = 10_000_000 + } + assertTextFormatEncode("optional_int64: 100000000\n") { (o: inout MessageTestType) in + o.optionalInt64 = 100_000_000 + } + assertTextFormatEncode("optional_int64: 1000000000\n") { (o: inout MessageTestType) in + o.optionalInt64 = 1_000_000_000 + } + + assertTextFormatEncode("optional_int64: -2\n") { (o: inout MessageTestType) in o.optionalInt64 = -2 } + assertTextFormatDecodeSucceeds("optional_int64: 0x1234567812345678\n") { (o: MessageTestType) in + o.optionalInt64 == 0x1234_5678_1234_5678 } assertTextFormatDecodeFails("optional_int64: a\n") @@ -146,16 +174,16 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { XCTAssertEqual("optional_uint32: 3\n", a.textFormatString()) - assertTextFormatEncode("optional_uint32: 3\n") {(o: inout MessageTestType) in + assertTextFormatEncode("optional_uint32: 3\n") { (o: inout MessageTestType) in o.optionalUint32 = 3 } assertTextFormatDecodeSucceeds("optional_uint32: 0x3") { (o: MessageTestType) in - return o.optionalUint32 == 3 + o.optionalUint32 == 3 } assertTextFormatDecodeSucceeds("optional_uint32: 03 optional_int32: 1") { (o: MessageTestType) in - return o.optionalUint32 == 3 && o.optionalInt32 == 1 + o.optionalUint32 == 3 && o.optionalInt32 == 1 } assertTextFormatDecodeFails("optional_uint32: -3\n") assertTextFormatDecodeFails("optional_uint32: 3x\n") @@ -173,12 +201,12 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { XCTAssertEqual("optional_uint64: 4\n", a.textFormatString()) - assertTextFormatEncode("optional_uint64: 4\n") {(o: inout MessageTestType) in + assertTextFormatEncode("optional_uint64: 4\n") { (o: inout MessageTestType) in o.optionalUint64 = 4 } - assertTextFormatDecodeSucceeds("optional_uint64: 0xf234567812345678\n") {(o: MessageTestType) in - return o.optionalUint64 == 0xf234567812345678 + assertTextFormatDecodeSucceeds("optional_uint64: 0xf234567812345678\n") { (o: MessageTestType) in + o.optionalUint64 == 0xf234_5678_1234_5678 } assertTextFormatDecodeFails("optional_uint64: a\n") assertTextFormatDecodeFails("optional_uint64: 999999999999999999999999999999999999\n") @@ -192,15 +220,15 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { XCTAssertEqual("optional_sint32: 5\n", a.textFormatString()) - assertTextFormatEncode("optional_sint32: 5\n") {(o: inout MessageTestType) in + assertTextFormatEncode("optional_sint32: 5\n") { (o: inout MessageTestType) in o.optionalSint32 = 5 } - assertTextFormatEncode("optional_sint32: -5\n") {(o: inout MessageTestType) in + assertTextFormatEncode("optional_sint32: -5\n") { (o: inout MessageTestType) in o.optionalSint32 = -5 } assertTextFormatDecodeSucceeds(" optional_sint32:-5 ") { (o: MessageTestType) in - return o.optionalSint32 == -5 + o.optionalSint32 == -5 } assertTextFormatDecodeFails("optional_sint32: a\n") @@ -212,7 +240,7 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { XCTAssertEqual("optional_sint64: 6\n", a.textFormatString()) - assertTextFormatEncode("optional_sint64: 6\n") {(o: inout MessageTestType) in + assertTextFormatEncode("optional_sint64: 6\n") { (o: inout MessageTestType) in o.optionalSint64 = 6 } assertTextFormatDecodeFails("optional_sint64: a\n") @@ -224,7 +252,7 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { XCTAssertEqual("optional_fixed32: 7\n", a.textFormatString()) - assertTextFormatEncode("optional_fixed32: 7\n") {(o: inout MessageTestType) in + assertTextFormatEncode("optional_fixed32: 7\n") { (o: inout MessageTestType) in o.optionalFixed32 = 7 } @@ -237,7 +265,7 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { XCTAssertEqual("optional_fixed64: 8\n", a.textFormatString()) - assertTextFormatEncode("optional_fixed64: 8\n") {(o: inout MessageTestType) in + assertTextFormatEncode("optional_fixed64: 8\n") { (o: inout MessageTestType) in o.optionalFixed64 = 8 } @@ -250,7 +278,7 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { XCTAssertEqual("optional_sfixed32: 9\n", a.textFormatString()) - assertTextFormatEncode("optional_sfixed32: 9\n") {(o: inout MessageTestType) in + assertTextFormatEncode("optional_sfixed32: 9\n") { (o: inout MessageTestType) in o.optionalSfixed32 = 9 } @@ -263,14 +291,18 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { XCTAssertEqual("optional_sfixed64: 10\n", a.textFormatString()) - assertTextFormatEncode("optional_sfixed64: 10\n") {(o: inout MessageTestType) in + assertTextFormatEncode("optional_sfixed64: 10\n") { (o: inout MessageTestType) in o.optionalSfixed64 = 10 } assertTextFormatDecodeFails("optional_sfixed64: a\n") } - private func assertRoundTripText(file: XCTestFileArgType = #file, line: UInt = #line, configure: (inout MessageTestType) -> Void) { + private func assertRoundTripText( + file: XCTestFileArgType = #file, + line: UInt = #line, + configure: (inout MessageTestType) -> Void + ) { var original = MessageTestType() configure(&original) let text = original.textFormatString() @@ -288,109 +320,112 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { XCTAssertEqual("optional_float: 11.0\n", a.textFormatString()) - assertTextFormatEncode("optional_float: 11.0\n") {(o: inout MessageTestType) in + assertTextFormatEncode("optional_float: 11.0\n") { (o: inout MessageTestType) in o.optionalFloat = 11 } assertTextFormatDecodeSucceeds("optional_float: 1.0f") { (o: MessageTestType) in - return o.optionalFloat == 1.0 + o.optionalFloat == 1.0 } assertTextFormatDecodeSucceeds("optional_float: 1.5e3") { (o: MessageTestType) in - return o.optionalFloat == 1.5e3 + o.optionalFloat == 1.5e3 } assertTextFormatDecodeSucceeds("optional_float: -4.75") { (o: MessageTestType) in - return o.optionalFloat == -4.75 + o.optionalFloat == -4.75 } assertTextFormatDecodeSucceeds("optional_float: 1.0f optional_int32: 1") { (o: MessageTestType) in - return o.optionalFloat == 1.0 && o.optionalInt32 == 1 + o.optionalFloat == 1.0 && o.optionalInt32 == 1 } assertTextFormatDecodeSucceeds("optional_float: 1.0 optional_int32: 1") { (o: MessageTestType) in - return o.optionalFloat == 1.0 && o.optionalInt32 == 1 + o.optionalFloat == 1.0 && o.optionalInt32 == 1 } assertTextFormatDecodeSucceeds("optional_float: 1.0f\n") { (o: MessageTestType) in - return o.optionalFloat == 1.0 + o.optionalFloat == 1.0 } assertTextFormatDecodeSucceeds("optional_float: 1.0F\n") { (o: MessageTestType) in - return o.optionalFloat == 1.0 + o.optionalFloat == 1.0 } assertTextFormatDecodeSucceeds("optional_float: 11\n") { (o: MessageTestType) in - return o.optionalFloat == 11.0 + o.optionalFloat == 11.0 } assertTextFormatDecodeSucceeds("optional_float: 11f\n") { (o: MessageTestType) in - return o.optionalFloat == 11.0 + o.optionalFloat == 11.0 } assertTextFormatDecodeSucceeds("optional_float: 11F\n") { (o: MessageTestType) in - return o.optionalFloat == 11.0 + o.optionalFloat == 11.0 } assertTextFormatDecodeSucceeds("optional_float: 0\n") { (o: MessageTestType) in - return o.optionalFloat == 0.0 + o.optionalFloat == 0.0 } assertTextFormatDecodeSucceeds("optional_float: 0f\n") { (o: MessageTestType) in - return o.optionalFloat == 0.0 + o.optionalFloat == 0.0 } assertTextFormatDecodeSucceeds("optional_float: 0F\n") { (o: MessageTestType) in - return o.optionalFloat == 0.0 + o.optionalFloat == 0.0 + } + assertTextFormatEncode("optional_float: inf\n") { (o: inout MessageTestType) in o.optionalFloat = Float.infinity + } + assertTextFormatEncode("optional_float: -inf\n") { (o: inout MessageTestType) in + o.optionalFloat = -Float.infinity } - assertTextFormatEncode("optional_float: inf\n") {(o: inout MessageTestType) in o.optionalFloat = Float.infinity} - assertTextFormatEncode("optional_float: -inf\n") {(o: inout MessageTestType) in o.optionalFloat = -Float.infinity} // protobuf conformance requires too-large floats to round to Infinity assertTextFormatDecodeSucceeds("optional_float: 3.4028235e+39\n") { (o: MessageTestType) in - return o.optionalFloat == Float.infinity + o.optionalFloat == Float.infinity } assertTextFormatDecodeSucceeds("optional_float: -3.4028235e+39\n") { (o: MessageTestType) in - return o.optionalFloat == -Float.infinity + o.optionalFloat == -Float.infinity } // Too-large of values round to infinity - assertTextFormatDecodeSucceeds("optional_float: 1e50\n") {(o: MessageTestType) in - return o.optionalFloat == Float.infinity + assertTextFormatDecodeSucceeds("optional_float: 1e50\n") { (o: MessageTestType) in + o.optionalFloat == Float.infinity } - assertTextFormatDecodeSucceeds("optional_float: -1e50\n") {(o: MessageTestType) in - return o.optionalFloat == -Float.infinity + assertTextFormatDecodeSucceeds("optional_float: -1e50\n") { (o: MessageTestType) in + o.optionalFloat == -Float.infinity } // Too-small values round to zero (not currently checked by conformance) assertTextFormatDecodeSucceeds("optional_float: 1e-50\n") { (o: MessageTestType) in - return o.optionalFloat == 0.0 && o.optionalFloat.sign == .plus + o.optionalFloat == 0.0 && o.optionalFloat.sign == .plus } assertTextFormatDecodeSucceeds("optional_float: -1e-50\n") { (o: MessageTestType) in - return o.optionalFloat == 0.0 && o.optionalFloat.sign == .minus + o.optionalFloat == 0.0 && o.optionalFloat.sign == .minus } // protobuf conformance requires subnormals to be handled assertTextFormatDecodeSucceeds("optional_float: 1.17549e-39\n") { (o: MessageTestType) in - return o.optionalFloat == Float(1.17549e-39) + o.optionalFloat == Float(1.17549e-39) } assertTextFormatDecodeSucceeds("optional_float: -1.17549e-39\n") { (o: MessageTestType) in - return o.optionalFloat == Float(-1.17549e-39) + o.optionalFloat == Float(-1.17549e-39) } // protobuf conformance requires integer forms larger than Int64 to be accepted assertTextFormatDecodeSucceeds("optional_float: 18446744073709551616\n") { (o: MessageTestType) in - return o.optionalFloat == 1.84467441e+19 + o.optionalFloat == 1.84467441e+19 } assertTextFormatDecodeSucceeds("optional_float: -18446744073709551616\n") { (o: MessageTestType) in - return o.optionalFloat == -1.84467441e+19 + o.optionalFloat == -1.84467441e+19 } - let b = SwiftProtoTesting_Proto3_TestAllTypes.with {$0.optionalFloat = Float.nan} + let b = SwiftProtoTesting_Proto3_TestAllTypes.with { $0.optionalFloat = Float.nan } XCTAssertEqual("optional_float: nan\n", b.textFormatString()) do { @@ -409,17 +444,17 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { assertTextFormatDecodeFails("optional_float: nanoptional_int32: 1\n") - assertTextFormatDecodeSucceeds("optional_float: INFINITY\n") {(o: MessageTestType) in - return o.optionalFloat == Float.infinity + assertTextFormatDecodeSucceeds("optional_float: INFINITY\n") { (o: MessageTestType) in + o.optionalFloat == Float.infinity } - assertTextFormatDecodeSucceeds("optional_float: Infinity\n") {(o: MessageTestType) in - return o.optionalFloat == Float.infinity + assertTextFormatDecodeSucceeds("optional_float: Infinity\n") { (o: MessageTestType) in + o.optionalFloat == Float.infinity } - assertTextFormatDecodeSucceeds("optional_float: -INFINITY\n") {(o: MessageTestType) in - return o.optionalFloat == -Float.infinity + assertTextFormatDecodeSucceeds("optional_float: -INFINITY\n") { (o: MessageTestType) in + o.optionalFloat == -Float.infinity } - assertTextFormatDecodeSucceeds("optional_float: -Infinity\n") {(o: MessageTestType) in - return o.optionalFloat == -Float.infinity + assertTextFormatDecodeSucceeds("optional_float: -Infinity\n") { (o: MessageTestType) in + o.optionalFloat == -Float.infinity } assertTextFormatDecodeFails("optional_float: INFINITY_AND_BEYOND\n") assertTextFormatDecodeFails("optional_float: infinityoptional_int32: 1\n") @@ -430,28 +465,28 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { assertTextFormatDecodeFails("optional_float: 012\n") // A wide range of numbers should exactly round-trip - assertRoundTripText {$0.optionalFloat = 0.1} - assertRoundTripText {$0.optionalFloat = 0.01} - assertRoundTripText {$0.optionalFloat = 0.001} - assertRoundTripText {$0.optionalFloat = 0.0001} - assertRoundTripText {$0.optionalFloat = 0.00001} - assertRoundTripText {$0.optionalFloat = 0.000001} - assertRoundTripText {$0.optionalFloat = 1e-10} - assertRoundTripText {$0.optionalFloat = 1e-20} - assertRoundTripText {$0.optionalFloat = 1e-30} - assertRoundTripText {$0.optionalFloat = Float(1e-40)} - assertRoundTripText {$0.optionalFloat = Float(1e-50)} - assertRoundTripText {$0.optionalFloat = Float(1e-60)} - assertRoundTripText {$0.optionalFloat = Float(1e-100)} - assertRoundTripText {$0.optionalFloat = Float(1e-200)} - assertRoundTripText {$0.optionalFloat = Float.pi} - assertRoundTripText {$0.optionalFloat = 123456.789123456789123} - assertRoundTripText {$0.optionalFloat = 1999.9999999999} - assertRoundTripText {$0.optionalFloat = 1999.9} - assertRoundTripText {$0.optionalFloat = 1999.99} - assertRoundTripText {$0.optionalFloat = 1999.999} - assertRoundTripText {$0.optionalFloat = 3.402823567e+38} - assertRoundTripText {$0.optionalFloat = 1.1754944e-38} + assertRoundTripText { $0.optionalFloat = 0.1 } + assertRoundTripText { $0.optionalFloat = 0.01 } + assertRoundTripText { $0.optionalFloat = 0.001 } + assertRoundTripText { $0.optionalFloat = 0.0001 } + assertRoundTripText { $0.optionalFloat = 0.00001 } + assertRoundTripText { $0.optionalFloat = 0.000001 } + assertRoundTripText { $0.optionalFloat = 1e-10 } + assertRoundTripText { $0.optionalFloat = 1e-20 } + assertRoundTripText { $0.optionalFloat = 1e-30 } + assertRoundTripText { $0.optionalFloat = Float(1e-40) } + assertRoundTripText { $0.optionalFloat = Float(1e-50) } + assertRoundTripText { $0.optionalFloat = Float(1e-60) } + assertRoundTripText { $0.optionalFloat = Float(1e-100) } + assertRoundTripText { $0.optionalFloat = Float(1e-200) } + assertRoundTripText { $0.optionalFloat = Float.pi } + assertRoundTripText { $0.optionalFloat = 123456.789123456789123 } + assertRoundTripText { $0.optionalFloat = 1999.9999999999 } + assertRoundTripText { $0.optionalFloat = 1999.9 } + assertRoundTripText { $0.optionalFloat = 1999.99 } + assertRoundTripText { $0.optionalFloat = 1999.999 } + assertRoundTripText { $0.optionalFloat = 3.402823567e+38 } + assertRoundTripText { $0.optionalFloat = 1.1754944e-38 } } func testEncoding_optionalDouble() { @@ -460,69 +495,73 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { XCTAssertEqual("optional_double: 12.0\n", a.textFormatString()) - assertTextFormatEncode("optional_double: 12.0\n") {(o: inout MessageTestType) in o.optionalDouble = 12 } - assertTextFormatEncode("optional_double: inf\n") {(o: inout MessageTestType) in o.optionalDouble = Double.infinity} - assertTextFormatEncode("optional_double: -inf\n") {(o: inout MessageTestType) in o.optionalDouble = -Double.infinity} - let b = SwiftProtoTesting_Proto3_TestAllTypes.with {$0.optionalDouble = Double.nan} + assertTextFormatEncode("optional_double: 12.0\n") { (o: inout MessageTestType) in o.optionalDouble = 12 } + assertTextFormatEncode("optional_double: inf\n") { (o: inout MessageTestType) in + o.optionalDouble = Double.infinity + } + assertTextFormatEncode("optional_double: -inf\n") { (o: inout MessageTestType) in + o.optionalDouble = -Double.infinity + } + let b = SwiftProtoTesting_Proto3_TestAllTypes.with { $0.optionalDouble = Double.nan } XCTAssertEqual("optional_double: nan\n", b.textFormatString()) - assertTextFormatDecodeSucceeds("optional_double: 1.0\n") {(o: MessageTestType) in - return o.optionalDouble == 1.0 + assertTextFormatDecodeSucceeds("optional_double: 1.0\n") { (o: MessageTestType) in + o.optionalDouble == 1.0 } - assertTextFormatDecodeSucceeds("optional_double: 1.0f\n") {(o: MessageTestType) in - return o.optionalDouble == 1.0 + assertTextFormatDecodeSucceeds("optional_double: 1.0f\n") { (o: MessageTestType) in + o.optionalDouble == 1.0 } - assertTextFormatDecodeSucceeds("optional_double: 1.0F\n") {(o: MessageTestType) in - return o.optionalDouble == 1.0 + assertTextFormatDecodeSucceeds("optional_double: 1.0F\n") { (o: MessageTestType) in + o.optionalDouble == 1.0 } - assertTextFormatDecodeSucceeds("optional_double: 1\n") {(o: MessageTestType) in - return o.optionalDouble == 1.0 + assertTextFormatDecodeSucceeds("optional_double: 1\n") { (o: MessageTestType) in + o.optionalDouble == 1.0 } - assertTextFormatDecodeSucceeds("optional_double: 1f\n") {(o: MessageTestType) in - return o.optionalDouble == 1.0 + assertTextFormatDecodeSucceeds("optional_double: 1f\n") { (o: MessageTestType) in + o.optionalDouble == 1.0 } - assertTextFormatDecodeSucceeds("optional_double: 1F\n") {(o: MessageTestType) in - return o.optionalDouble == 1.0 + assertTextFormatDecodeSucceeds("optional_double: 1F\n") { (o: MessageTestType) in + o.optionalDouble == 1.0 } - assertTextFormatDecodeSucceeds("optional_double: 0\n") {(o: MessageTestType) in - return o.optionalDouble == 0.0 + assertTextFormatDecodeSucceeds("optional_double: 0\n") { (o: MessageTestType) in + o.optionalDouble == 0.0 } - assertTextFormatDecodeSucceeds("optional_double: 0f\n") {(o: MessageTestType) in - return o.optionalDouble == 0.0 + assertTextFormatDecodeSucceeds("optional_double: 0f\n") { (o: MessageTestType) in + o.optionalDouble == 0.0 } - assertTextFormatDecodeSucceeds("optional_double: 0F\n") {(o: MessageTestType) in - return o.optionalDouble == 0.0 + assertTextFormatDecodeSucceeds("optional_double: 0F\n") { (o: MessageTestType) in + o.optionalDouble == 0.0 } - assertTextFormatDecodeSucceeds("12: 1.0\n") {(o: MessageTestType) in - return o.optionalDouble == 1.0 + assertTextFormatDecodeSucceeds("12: 1.0\n") { (o: MessageTestType) in + o.optionalDouble == 1.0 } // Too-large of values round to infinity - assertTextFormatDecodeSucceeds("optional_double: 1e9999\n") {(o: MessageTestType) in - return o.optionalDouble == Double.infinity + assertTextFormatDecodeSucceeds("optional_double: 1e9999\n") { (o: MessageTestType) in + o.optionalDouble == Double.infinity } - assertTextFormatDecodeSucceeds("optional_double: -1e9999\n") {(o: MessageTestType) in - return o.optionalDouble == -Double.infinity + assertTextFormatDecodeSucceeds("optional_double: -1e9999\n") { (o: MessageTestType) in + o.optionalDouble == -Double.infinity } // Too-small values round to zero (not currently checked by conformance) assertTextFormatDecodeSucceeds("optional_double: 1e-9999\n") { (o: MessageTestType) in - return o.optionalDouble == 0.0 && o.optionalDouble.sign == .plus + o.optionalDouble == 0.0 && o.optionalDouble.sign == .plus } assertTextFormatDecodeSucceeds("optional_double: -1e-9999\n") { (o: MessageTestType) in - return o.optionalDouble == 0.0 && o.optionalDouble.sign == .minus + o.optionalDouble == 0.0 && o.optionalDouble.sign == .minus } - assertTextFormatDecodeSucceeds("optional_double: INFINITY\n") {(o: MessageTestType) in - return o.optionalDouble == Double.infinity + assertTextFormatDecodeSucceeds("optional_double: INFINITY\n") { (o: MessageTestType) in + o.optionalDouble == Double.infinity } - assertTextFormatDecodeSucceeds("optional_double: Infinity\n") {(o: MessageTestType) in - return o.optionalDouble == Double.infinity + assertTextFormatDecodeSucceeds("optional_double: Infinity\n") { (o: MessageTestType) in + o.optionalDouble == Double.infinity } - assertTextFormatDecodeSucceeds("optional_double: -INFINITY\n") {(o: MessageTestType) in - return o.optionalDouble == -Double.infinity + assertTextFormatDecodeSucceeds("optional_double: -INFINITY\n") { (o: MessageTestType) in + o.optionalDouble == -Double.infinity } - assertTextFormatDecodeSucceeds("optional_double: -Infinity\n") {(o: MessageTestType) in - return o.optionalDouble == -Double.infinity + assertTextFormatDecodeSucceeds("optional_double: -Infinity\n") { (o: MessageTestType) in + o.optionalDouble == -Double.infinity } assertTextFormatDecodeFails("optional_double: INFINITY_AND_BEYOND\n") assertTextFormatDecodeFails("optional_double: INFIN\n") @@ -532,24 +571,24 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { assertTextFormatDecodeFails("optional_double: 0123\n") // A wide range of numbers should exactly round-trip - assertRoundTripText {$0.optionalDouble = 0.1} - assertRoundTripText {$0.optionalDouble = 0.01} - assertRoundTripText {$0.optionalDouble = 0.001} - assertRoundTripText {$0.optionalDouble = 0.0001} - assertRoundTripText {$0.optionalDouble = 0.00001} - assertRoundTripText {$0.optionalDouble = 0.000001} - assertRoundTripText {$0.optionalDouble = 1e-10} - assertRoundTripText {$0.optionalDouble = 1e-20} - assertRoundTripText {$0.optionalDouble = 1e-30} - assertRoundTripText {$0.optionalDouble = 1e-40} - assertRoundTripText {$0.optionalDouble = 1e-50} - assertRoundTripText {$0.optionalDouble = 1e-60} - assertRoundTripText {$0.optionalDouble = 1e-100} - assertRoundTripText {$0.optionalDouble = 1e-200} - assertRoundTripText {$0.optionalDouble = Double.pi} - assertRoundTripText {$0.optionalDouble = 123456.789123456789123} - assertRoundTripText {$0.optionalDouble = 1.7976931348623157e+308} - assertRoundTripText {$0.optionalDouble = 2.22507385850720138309e-308} + assertRoundTripText { $0.optionalDouble = 0.1 } + assertRoundTripText { $0.optionalDouble = 0.01 } + assertRoundTripText { $0.optionalDouble = 0.001 } + assertRoundTripText { $0.optionalDouble = 0.0001 } + assertRoundTripText { $0.optionalDouble = 0.00001 } + assertRoundTripText { $0.optionalDouble = 0.000001 } + assertRoundTripText { $0.optionalDouble = 1e-10 } + assertRoundTripText { $0.optionalDouble = 1e-20 } + assertRoundTripText { $0.optionalDouble = 1e-30 } + assertRoundTripText { $0.optionalDouble = 1e-40 } + assertRoundTripText { $0.optionalDouble = 1e-50 } + assertRoundTripText { $0.optionalDouble = 1e-60 } + assertRoundTripText { $0.optionalDouble = 1e-100 } + assertRoundTripText { $0.optionalDouble = 1e-200 } + assertRoundTripText { $0.optionalDouble = Double.pi } + assertRoundTripText { $0.optionalDouble = 123456.789123456789123 } + assertRoundTripText { $0.optionalDouble = 1.7976931348623157e+308 } + assertRoundTripText { $0.optionalDouble = 2.22507385850720138309e-308 } } func testEncoding_optionalBool() { @@ -560,44 +599,44 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { a.optionalBool = false XCTAssertEqual("", a.textFormatString()) - assertTextFormatEncode("optional_bool: true\n") {(o: inout MessageTestType) in + assertTextFormatEncode("optional_bool: true\n") { (o: inout MessageTestType) in o.optionalBool = true } - assertTextFormatDecodeSucceeds("optional_bool:true") {(o: MessageTestType) in - return o.optionalBool == true + assertTextFormatDecodeSucceeds("optional_bool:true") { (o: MessageTestType) in + o.optionalBool == true } - assertTextFormatDecodeSucceeds("optional_bool:true ") {(o: MessageTestType) in - return o.optionalBool == true + assertTextFormatDecodeSucceeds("optional_bool:true ") { (o: MessageTestType) in + o.optionalBool == true } - assertTextFormatDecodeSucceeds("optional_bool:true\n ") {(o: MessageTestType) in - return o.optionalBool == true + assertTextFormatDecodeSucceeds("optional_bool:true\n ") { (o: MessageTestType) in + o.optionalBool == true } - assertTextFormatDecodeSucceeds("optional_bool:True\n ") {(o: MessageTestType) in - return o.optionalBool == true + assertTextFormatDecodeSucceeds("optional_bool:True\n ") { (o: MessageTestType) in + o.optionalBool == true } - assertTextFormatDecodeSucceeds("optional_bool:t\n ") {(o: MessageTestType) in - return o.optionalBool == true + assertTextFormatDecodeSucceeds("optional_bool:t\n ") { (o: MessageTestType) in + o.optionalBool == true } - assertTextFormatDecodeSucceeds("optional_bool:1\n ") {(o: MessageTestType) in - return o.optionalBool == true + assertTextFormatDecodeSucceeds("optional_bool:1\n ") { (o: MessageTestType) in + o.optionalBool == true } - assertTextFormatDecodeSucceeds("optional_bool:false\n ") {(o: MessageTestType) in - return o.optionalBool == false + assertTextFormatDecodeSucceeds("optional_bool:false\n ") { (o: MessageTestType) in + o.optionalBool == false } - assertTextFormatDecodeSucceeds("optional_bool:False\n ") {(o: MessageTestType) in - return o.optionalBool == false + assertTextFormatDecodeSucceeds("optional_bool:False\n ") { (o: MessageTestType) in + o.optionalBool == false } - assertTextFormatDecodeSucceeds("optional_bool:f\n ") {(o: MessageTestType) in - return o.optionalBool == false + assertTextFormatDecodeSucceeds("optional_bool:f\n ") { (o: MessageTestType) in + o.optionalBool == false } - assertTextFormatDecodeSucceeds("optional_bool:0\n ") {(o: MessageTestType) in - return o.optionalBool == false + assertTextFormatDecodeSucceeds("optional_bool:0\n ") { (o: MessageTestType) in + o.optionalBool == false } - assertTextFormatDecodeSucceeds("13:0\n ") {(o: MessageTestType) in - return o.optionalBool == false + assertTextFormatDecodeSucceeds("13:0\n ") { (o: MessageTestType) in + o.optionalBool == false } - assertTextFormatDecodeSucceeds("13:1\n ") {(o: MessageTestType) in - return o.optionalBool == true + assertTextFormatDecodeSucceeds("13:1\n ") { (o: MessageTestType) in + o.optionalBool == true } assertTextFormatDecodeFails("optional_bool: 10\n") @@ -647,58 +686,57 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { o.optionalString = "\u{f8}\u{f9}\u{fa}\u{fb}\u{fc}\u{fd}\u{fe}\u{ff}" } - // Adjacent quoted strings concatenate, see // google/protobuf/text_format_unittest.cc#L597 assertTextFormatDecodeSucceeds("optional_string: \"abc\"\"def\"") { (o: MessageTestType) in - return o.optionalString == "abcdef" + o.optionalString == "abcdef" } assertTextFormatDecodeSucceeds("optional_string: \"abc\" \"def\"") { (o: MessageTestType) in - return o.optionalString == "abcdef" + o.optionalString == "abcdef" } assertTextFormatDecodeSucceeds("optional_string: \"abc\" \"def\"") { (o: MessageTestType) in - return o.optionalString == "abcdef" + o.optionalString == "abcdef" } // Adjacent quoted strings concatenate across multiple lines assertTextFormatDecodeSucceeds("optional_string: \"abc\"\n\"def\"") { (o: MessageTestType) in - return o.optionalString == "abcdef" + o.optionalString == "abcdef" } assertTextFormatDecodeSucceeds("optional_string: \"abc\"\n \t \"def\"\n\"ghi\"\n") { (o: MessageTestType) in - return o.optionalString == "abcdefghi" + o.optionalString == "abcdefghi" } assertTextFormatDecodeSucceeds("optional_string: \"abc\"\n\'def\'\n\"ghi\"\n") { (o: MessageTestType) in - return o.optionalString == "abcdefghi" + o.optionalString == "abcdefghi" } assertTextFormatDecodeSucceeds("optional_string: \"abcdefghi\"") { (o: MessageTestType) in - return o.optionalString == "abcdefghi" + o.optionalString == "abcdefghi" } // Note: Values 0-127 are same whether viewed as Unicode code // points or UTF-8 bytes. assertTextFormatDecodeSucceeds("optional_string: \"\\a\\b\\f\\n\\r\\t\\v\\\"\\'\\\\\\?\"") { (o: MessageTestType) in - return o.optionalString == "\u{07}\u{08}\u{0C}\u{0A}\u{0D}\u{09}\u{0B}\"'\\?" + o.optionalString == "\u{07}\u{08}\u{0C}\u{0A}\u{0D}\u{09}\u{0B}\"'\\?" } assertTextFormatDecodeFails("optional_string: \"\\z\"") assertTextFormatDecodeSucceeds("optional_string: \"\\001\\01\\1\\0011\\010\\289\"") { (o: MessageTestType) in - return o.optionalString == "\u{01}\u{01}\u{01}\u{01}\u{31}\u{08}\u{02}89" + o.optionalString == "\u{01}\u{01}\u{01}\u{01}\u{31}\u{08}\u{02}89" } assertTextFormatDecodeSucceeds("optional_string: \"\\x1\\x12\\x123\\x1234\"") { (o: MessageTestType) in - return o.optionalString == "\u{01}\u{12}\u{12}3\u{12}34" + o.optionalString == "\u{01}\u{12}\u{12}3\u{12}34" } assertTextFormatDecodeSucceeds("optional_string: \"\\x0f\\x3g\"") { (o: MessageTestType) in - return o.optionalString == "\u{0f}\u{03}g" + o.optionalString == "\u{0f}\u{03}g" } - assertTextFormatEncode("optional_string: \"abc\"\n") {(o: inout MessageTestType) in + assertTextFormatEncode("optional_string: \"abc\"\n") { (o: inout MessageTestType) in o.optionalString = "abc" } assertTextFormatDecodeFails("optional_string:hello") @@ -729,16 +767,16 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { func testEncoding_optionalString_UTF8() throws { // We encode to/from a string, not a sequence of bytes, so valid // Unicode characters just get preserved on both encode and decode: - assertTextFormatEncode("optional_string: \"☞\"\n") {(o: inout MessageTestType) in + assertTextFormatEncode("optional_string: \"☞\"\n") { (o: inout MessageTestType) in o.optionalString = "☞" } // Other encoders write each byte of a UTF-8 sequence, maybe in hex: - assertTextFormatDecodeSucceeds("optional_string: \"\\xE2\\x98\\x9E\"") {(o: MessageTestType) in - return o.optionalString == "☞" + assertTextFormatDecodeSucceeds("optional_string: \"\\xE2\\x98\\x9E\"") { (o: MessageTestType) in + o.optionalString == "☞" } // Or maybe in octal: - assertTextFormatDecodeSucceeds("optional_string: \"\\342\\230\\236\"") {(o: MessageTestType) in - return o.optionalString == "☞" + assertTextFormatDecodeSucceeds("optional_string: \"\\342\\230\\236\"") { (o: MessageTestType) in + o.optionalString == "☞" } // Each string piece is decoded separately, broken UTF-8 is an error assertTextFormatDecodeFails("optional_string: \"\\342\\230\" \"\\236\"") @@ -748,34 +786,35 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { let o = SwiftProtoTesting_Proto3_TestAllTypes.with { $0.optionalBytes = Data() } XCTAssertEqual("", o.textFormatString()) - assertTextFormatEncode("optional_bytes: \"AB\"\n") {(o: inout MessageTestType) in + assertTextFormatEncode("optional_bytes: \"AB\"\n") { (o: inout MessageTestType) in o.optionalBytes = Data([65, 66]) } - assertTextFormatEncode("optional_bytes: \"\\000\\001AB\\177\\200\\377\"\n") {(o: inout MessageTestType) in + assertTextFormatEncode("optional_bytes: \"\\000\\001AB\\177\\200\\377\"\n") { (o: inout MessageTestType) in o.optionalBytes = Data([0, 1, 65, 66, 127, 128, 255]) } - assertTextFormatEncode("optional_bytes: \"\\b\\t\\n\\v\\f\\r\\\"'?\\\\\"\n") {(o: inout MessageTestType) in + assertTextFormatEncode("optional_bytes: \"\\b\\t\\n\\v\\f\\r\\\"'?\\\\\"\n") { (o: inout MessageTestType) in o.optionalBytes = Data([8, 9, 10, 11, 12, 13, 34, 39, 63, 92]) } - assertTextFormatDecodeSucceeds("optional_bytes: \"A\" \"B\"\n") {(o: MessageTestType) in - return o.optionalBytes == Data([65, 66]) + assertTextFormatDecodeSucceeds("optional_bytes: \"A\" \"B\"\n") { (o: MessageTestType) in + o.optionalBytes == Data([65, 66]) } - assertTextFormatDecodeSucceeds("optional_bytes: \"\\0\\1AB\\178\\189\\x61\\xdq\\x123456789\"\n") {(o: MessageTestType) in - return o.optionalBytes == Data([0, 1, 65, 66, 15, 56, 1, 56, 57, 97, 13, 113, 18, 51, 52, 53, 54, 55, 56, 57]) + assertTextFormatDecodeSucceeds("optional_bytes: \"\\0\\1AB\\178\\189\\x61\\xdq\\x123456789\"\n") { + (o: MessageTestType) in + o.optionalBytes == Data([0, 1, 65, 66, 15, 56, 1, 56, 57, 97, 13, 113, 18, 51, 52, 53, 54, 55, 56, 57]) } // "\1" followed by "2", not "\12" - assertTextFormatDecodeSucceeds("optional_bytes: \"\\1\" \"2\"") {(o: MessageTestType) in - return o.optionalBytes == Data([1, 50]) // Not [10] + assertTextFormatDecodeSucceeds("optional_bytes: \"\\1\" \"2\"") { (o: MessageTestType) in + o.optionalBytes == Data([1, 50]) // Not [10]// Not [10] } // "\x6" followed by "2", not "\x62" - assertTextFormatDecodeSucceeds("optional_bytes: \"\\x6\" \"2\"") {(o: MessageTestType) in - return o.optionalBytes == Data([6, 50]) // Not [98] + assertTextFormatDecodeSucceeds("optional_bytes: \"\\x6\" \"2\"") { (o: MessageTestType) in + o.optionalBytes == Data([6, 50]) // Not [98]// Not [98] } - assertTextFormatDecodeSucceeds("optional_bytes: \"\"\n") {(o: MessageTestType) in - return o.optionalBytes == Data() + assertTextFormatDecodeSucceeds("optional_bytes: \"\"\n") { (o: MessageTestType) in + o.optionalBytes == Data() } - assertTextFormatDecodeSucceeds("optional_bytes: \"\\b\\t\\n\\v\\f\\r\\\"\\'\\?'\"\n") {(o: MessageTestType) in - return o.optionalBytes == Data([8, 9, 10, 11, 12, 13, 34, 39, 63, 39]) + assertTextFormatDecodeSucceeds("optional_bytes: \"\\b\\t\\n\\v\\f\\r\\\"\\'\\?'\"\n") { (o: MessageTestType) in + o.optionalBytes == Data([8, 9, 10, 11, 12, 13, 34, 39, 63, 39]) } assertTextFormatDecodeFails("optional_bytes: 10\n") @@ -784,7 +823,7 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { assertTextFormatDecodeFails("optional_bytes: \"\\x&\"\n") assertTextFormatDecodeFails("optional_bytes: \"\\xg\"\n") assertTextFormatDecodeFails("optional_bytes: \"\\q\"\n") - assertTextFormatDecodeFails("optional_bytes: \"\\777\"\n") // Out-of-range octal + assertTextFormatDecodeFails("optional_bytes: \"\\777\"\n") // Out-of-range octal assertTextFormatDecodeFails("optional_bytes: \"") assertTextFormatDecodeFails("optional_bytes: \"abcde") assertTextFormatDecodeFails("optional_bytes: \"\\") @@ -817,20 +856,20 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { XCTAssertEqual("optional_nested_message {\n bb: 7\n}\n", a.textFormatString()) - assertTextFormatEncode("optional_nested_message {\n bb: 7\n}\n") {(o: inout MessageTestType) in + assertTextFormatEncode("optional_nested_message {\n bb: 7\n}\n") { (o: inout MessageTestType) in o.optionalNestedMessage = nested } // Google permits reading a message field with or without the separating ':' - assertTextFormatDecodeSucceeds("optional_nested_message: {bb:7}") {(o: MessageTestType) in - return o.optionalNestedMessage.bb == 7 + assertTextFormatDecodeSucceeds("optional_nested_message: {bb:7}") { (o: MessageTestType) in + o.optionalNestedMessage.bb == 7 } // Messages can be wrapped in {...} or <...> - assertTextFormatDecodeSucceeds("optional_nested_message ") {(o: MessageTestType) in - return o.optionalNestedMessage.bb == 7 + assertTextFormatDecodeSucceeds("optional_nested_message ") { (o: MessageTestType) in + o.optionalNestedMessage.bb == 7 } // Google permits reading a message field with or without the separating ':' - assertTextFormatDecodeSucceeds("optional_nested_message: ") {(o: MessageTestType) in - return o.optionalNestedMessage.bb == 7 + assertTextFormatDecodeSucceeds("optional_nested_message: ") { (o: MessageTestType) in + o.optionalNestedMessage.bb == 7 } assertTextFormatDecodeFails("optional_nested_message: a\n") @@ -845,10 +884,12 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { XCTAssertEqual("optional_foreign_message {\n c: 88\n}\n", a.textFormatString()) - assertTextFormatEncode("optional_foreign_message {\n c: 88\n}\n") {(o: inout MessageTestType) in o.optionalForeignMessage = foreign } + assertTextFormatEncode("optional_foreign_message {\n c: 88\n}\n") { (o: inout MessageTestType) in + o.optionalForeignMessage = foreign + } do { - let message = try MessageTestType(textFormatString:"optional_foreign_message: {\n c: 88\n}\n") + let message = try MessageTestType(textFormatString: "optional_foreign_message: {\n c: 88\n}\n") XCTAssertEqual(message.optionalForeignMessage.c, 88) } catch { XCTFail("Presented error: \(error)") @@ -866,10 +907,12 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { XCTAssertEqual("optional_import_message {\n d: -9\n}\n", a.textFormatString()) - assertTextFormatEncode("optional_import_message {\n d: -9\n}\n") {(o: inout MessageTestType) in o.optionalImportMessage = importMessage } + assertTextFormatEncode("optional_import_message {\n d: -9\n}\n") { (o: inout MessageTestType) in + o.optionalImportMessage = importMessage + } do { - let message = try MessageTestType(textFormatString:"optional_import_message: {\n d: -9\n}\n") + let message = try MessageTestType(textFormatString: "optional_import_message: {\n d: -9\n}\n") XCTAssertEqual(message.optionalImportMessage.d, -9) } catch { XCTFail("Presented error: \(error)") @@ -884,17 +927,17 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { XCTAssertEqual("optional_nested_enum: BAZ\n", a.textFormatString()) - assertTextFormatEncode("optional_nested_enum: BAZ\n") {(o: inout MessageTestType) in + assertTextFormatEncode("optional_nested_enum: BAZ\n") { (o: inout MessageTestType) in o.optionalNestedEnum = .baz } - assertTextFormatDecodeSucceeds("optional_nested_enum:BAZ"){(o: MessageTestType) in - return o.optionalNestedEnum == .baz + assertTextFormatDecodeSucceeds("optional_nested_enum:BAZ") { (o: MessageTestType) in + o.optionalNestedEnum == .baz } - assertTextFormatDecodeSucceeds("optional_nested_enum:1"){(o: MessageTestType) in - return o.optionalNestedEnum == .foo + assertTextFormatDecodeSucceeds("optional_nested_enum:1") { (o: MessageTestType) in + o.optionalNestedEnum == .foo } - assertTextFormatDecodeSucceeds("optional_nested_enum:2"){(o: MessageTestType) in - return o.optionalNestedEnum == .bar + assertTextFormatDecodeSucceeds("optional_nested_enum:2") { (o: MessageTestType) in + o.optionalNestedEnum == .bar } assertTextFormatDecodeFails("optional_nested_enum: a\n") assertTextFormatDecodeFails("optional_nested_enum: FOOBAR") @@ -913,10 +956,16 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { XCTAssertEqual("optional_foreign_enum: FOREIGN_BAZ\n", a.textFormatString()) - assertTextFormatEncode("optional_foreign_enum: FOREIGN_BAZ\n") {(o: inout MessageTestType) in o.optionalForeignEnum = .foreignBaz } - assertTextFormatDecodeSucceeds("optional_foreign_enum: 6\n") {(o: MessageTestType) in o.optionalForeignEnum == .foreignBaz } + assertTextFormatEncode("optional_foreign_enum: FOREIGN_BAZ\n") { (o: inout MessageTestType) in + o.optionalForeignEnum = .foreignBaz + } + assertTextFormatDecodeSucceeds("optional_foreign_enum: 6\n") { (o: MessageTestType) in + o.optionalForeignEnum == .foreignBaz + } - assertTextFormatEncode("optional_foreign_enum: 99\n") {(o: inout MessageTestType) in o.optionalForeignEnum = .UNRECOGNIZED(99) } + assertTextFormatEncode("optional_foreign_enum: 99\n") { (o: inout MessageTestType) in + o.optionalForeignEnum = .UNRECOGNIZED(99) + } assertTextFormatDecodeFails("optional_foreign_enum: a\n") } @@ -930,10 +979,12 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { XCTAssertEqual("optional_public_import_message {\n e: -999999\n}\n", a.textFormatString()) - assertTextFormatEncode("optional_public_import_message {\n e: -999999\n}\n") {(o: inout MessageTestType) in o.optionalPublicImportMessage = publicImportMessage } + assertTextFormatEncode("optional_public_import_message {\n e: -999999\n}\n") { (o: inout MessageTestType) in + o.optionalPublicImportMessage = publicImportMessage + } do { - let message = try MessageTestType(textFormatString:"optional_public_import_message: {\n e: -999999\n}\n") + let message = try MessageTestType(textFormatString: "optional_public_import_message: {\n e: -999999\n}\n") XCTAssertEqual(message.optionalPublicImportMessage.e, -999999) } catch { XCTFail("Presented error: \(error)") @@ -951,33 +1002,33 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { a.repeatedInt32 = [1, 2] XCTAssertEqual("repeated_int32: [1, 2]\n", a.textFormatString()) - assertTextFormatEncode("repeated_int32: [1, 2]\n") {(o: inout MessageTestType) in + assertTextFormatEncode("repeated_int32: [1, 2]\n") { (o: inout MessageTestType) in o.repeatedInt32 = [1, 2] } assertTextFormatDecodeSucceeds("repeated_int32: 1\n repeated_int32: 2\n") { (o: MessageTestType) in - return o.repeatedInt32 == [1, 2] + o.repeatedInt32 == [1, 2] } assertTextFormatDecodeSucceeds("repeated_int32:[1, 2]") { (o: MessageTestType) in - return o.repeatedInt32 == [1, 2] + o.repeatedInt32 == [1, 2] } assertTextFormatDecodeSucceeds("repeated_int32: [1] repeated_int32: 2\n") { (o: MessageTestType) in - return o.repeatedInt32 == [1, 2] + o.repeatedInt32 == [1, 2] } assertTextFormatDecodeSucceeds("repeated_int32: 1 repeated_int32: [2]\n") { (o: MessageTestType) in - return o.repeatedInt32 == [1, 2] + o.repeatedInt32 == [1, 2] } assertTextFormatDecodeSucceeds("repeated_int32:[]\nrepeated_int32: [1, 2]\nrepeated_int32:[]\n") { (o: MessageTestType) in - return o.repeatedInt32 == [1, 2] + o.repeatedInt32 == [1, 2] } assertTextFormatDecodeSucceeds("repeated_int32:1\nrepeated_int32:2\n") { (o: MessageTestType) in - return o.repeatedInt32 == [1, 2] + o.repeatedInt32 == [1, 2] } assertTextFormatDecodeFails("repeated_int32: 1\nrepeated_int32: a\n") @@ -992,18 +1043,18 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { } func testEncoding_repeatedInt64() { - assertTextFormatEncode("repeated_int64: [3, 4]\n") {(o: inout MessageTestType) in + assertTextFormatEncode("repeated_int64: [3, 4]\n") { (o: inout MessageTestType) in o.repeatedInt64 = [3, 4] } - assertTextFormatDecodeSucceeds("repeated_int64: 3\nrepeated_int64: 4\n") {(o: MessageTestType) in - return o.repeatedInt64 == [3, 4] + assertTextFormatDecodeSucceeds("repeated_int64: 3\nrepeated_int64: 4\n") { (o: MessageTestType) in + o.repeatedInt64 == [3, 4] } assertTextFormatDecodeFails("repeated_int64: 3\nrepeated_int64: a\n") } func testEncoding_repeatedUint32() { - assertTextFormatEncode("repeated_uint32: [5, 6]\n") {(o: inout MessageTestType) in + assertTextFormatEncode("repeated_uint32: [5, 6]\n") { (o: inout MessageTestType) in o.repeatedUint32 = [5, 6] } @@ -1011,7 +1062,7 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { } func testEncoding_repeatedUint64() { - assertTextFormatEncode("repeated_uint64: [7, 8]\n") {(o: inout MessageTestType) in + assertTextFormatEncode("repeated_uint64: [7, 8]\n") { (o: inout MessageTestType) in o.repeatedUint64 = [7, 8] } @@ -1023,7 +1074,7 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { } func testEncoding_repeatedSint32() { - assertTextFormatEncode("repeated_sint32: [9, 10]\n") {(o: inout MessageTestType) in + assertTextFormatEncode("repeated_sint32: [9, 10]\n") { (o: inout MessageTestType) in o.repeatedSint32 = [9, 10] } @@ -1031,7 +1082,7 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { } func testEncoding_repeatedSint64() { - assertTextFormatEncode("repeated_sint64: [11, 12]\n") {(o: inout MessageTestType) in + assertTextFormatEncode("repeated_sint64: [11, 12]\n") { (o: inout MessageTestType) in o.repeatedSint64 = [11, 12] } @@ -1039,7 +1090,7 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { } func testEncoding_repeatedFixed32() { - assertTextFormatEncode("repeated_fixed32: [13, 14]\n") {(o: inout MessageTestType) in + assertTextFormatEncode("repeated_fixed32: [13, 14]\n") { (o: inout MessageTestType) in o.repeatedFixed32 = [13, 14] } @@ -1047,7 +1098,7 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { } func testEncoding_repeatedFixed64() { - assertTextFormatEncode("repeated_fixed64: [15, 16]\n") {(o: inout MessageTestType) in + assertTextFormatEncode("repeated_fixed64: [15, 16]\n") { (o: inout MessageTestType) in o.repeatedFixed64 = [15, 16] } @@ -1055,7 +1106,7 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { } func testEncoding_repeatedSfixed32() { - assertTextFormatEncode("repeated_sfixed32: [17, 18]\n") {(o: inout MessageTestType) in + assertTextFormatEncode("repeated_sfixed32: [17, 18]\n") { (o: inout MessageTestType) in o.repeatedSfixed32 = [17, 18] } @@ -1063,7 +1114,7 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { } func testEncoding_repeatedSfixed64() { - assertTextFormatEncode("repeated_sfixed64: [19, 20]\n") {(o: inout MessageTestType) in + assertTextFormatEncode("repeated_sfixed64: [19, 20]\n") { (o: inout MessageTestType) in o.repeatedSfixed64 = [19, 20] } @@ -1071,7 +1122,7 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { } func testEncoding_repeatedFloat() { - assertTextFormatEncode("repeated_float: [21.0, 22.0]\n") {(o: inout MessageTestType) in + assertTextFormatEncode("repeated_float: [21.0, 22.0]\n") { (o: inout MessageTestType) in o.repeatedFloat = [21, 22] } @@ -1079,10 +1130,10 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { } func testEncoding_repeatedDouble() { - assertTextFormatEncode("repeated_double: [23.0, 24.0]\n") {(o: inout MessageTestType) in + assertTextFormatEncode("repeated_double: [23.0, 24.0]\n") { (o: inout MessageTestType) in o.repeatedDouble = [23, 24] } - assertTextFormatEncode("repeated_double: [2.25, 2.5]\n") {(o: inout MessageTestType) in + assertTextFormatEncode("repeated_double: [2.25, 2.5]\n") { (o: inout MessageTestType) in o.repeatedDouble = [2.25, 2.5] } @@ -1090,12 +1141,12 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { } func testEncoding_repeatedBool() { - assertTextFormatEncode("repeated_bool: [true, false]\n") {(o: inout MessageTestType) in + assertTextFormatEncode("repeated_bool: [true, false]\n") { (o: inout MessageTestType) in o.repeatedBool = [true, false] } assertTextFormatDecodeSucceeds("repeated_bool: [true, false, True, False, t, f, 1, 0]") { (o: MessageTestType) in - return o.repeatedBool == [true, false, true, false, true, false, true, false] + o.repeatedBool == [true, false, true, false, true, false, true, false] } assertTextFormatDecodeFails("repeated_bool: true\nrepeated_bool: a\n") @@ -1104,27 +1155,27 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { func testEncoding_repeatedString() { assertTextFormatDecodeSucceeds("repeated_string: \"abc\"\nrepeated_string: \"def\"\n") { (o: MessageTestType) in - return o.repeatedString == ["abc", "def"] + o.repeatedString == ["abc", "def"] } assertTextFormatDecodeSucceeds("repeated_string: \"a\" \"bc\"\nrepeated_string: 'd' \"e\" \"f\"\n") { (o: MessageTestType) in - return o.repeatedString == ["abc", "def"] + o.repeatedString == ["abc", "def"] } assertTextFormatDecodeSucceeds("repeated_string:[\"abc\", \"def\"]") { (o: MessageTestType) in - return o.repeatedString == ["abc", "def"] + o.repeatedString == ["abc", "def"] } assertTextFormatDecodeSucceeds("repeated_string:[\"a\"\"bc\", \"d\" 'e' \"f\"]") { (o: MessageTestType) in - return o.repeatedString == ["abc", "def"] + o.repeatedString == ["abc", "def"] } assertTextFormatDecodeSucceeds("repeated_string:[\"abc\", 'def']") { (o: MessageTestType) in - return o.repeatedString == ["abc", "def"] + o.repeatedString == ["abc", "def"] } assertTextFormatDecodeSucceeds("repeated_string:[\"abc\"] repeated_string: \"def\"") { (o: MessageTestType) in - return o.repeatedString == ["abc", "def"] + o.repeatedString == ["abc", "def"] } assertTextFormatDecodeFails("repeated_string:[\"abc\", \"def\",]") assertTextFormatDecodeFails("repeated_string:[\"abc\"") @@ -1133,7 +1184,9 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { assertTextFormatDecodeFails("repeated_string: \"abc\"]") assertTextFormatDecodeFails("repeated_string: abc") - assertTextFormatEncode("repeated_string: \"abc\"\nrepeated_string: \"def\"\n") {(o: inout MessageTestType) in o.repeatedString = ["abc", "def"] } + assertTextFormatEncode("repeated_string: \"abc\"\nrepeated_string: \"def\"\n") { (o: inout MessageTestType) in + o.repeatedString = ["abc", "def"] + } } func testEncoding_repeatedBytes() { @@ -1141,17 +1194,17 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { a.repeatedBytes = [Data(), Data([65, 66])] XCTAssertEqual("repeated_bytes: \"\"\nrepeated_bytes: \"AB\"\n", a.textFormatString()) - assertTextFormatEncode("repeated_bytes: \"\"\nrepeated_bytes: \"AB\"\n") {(o: inout MessageTestType) in + assertTextFormatEncode("repeated_bytes: \"\"\nrepeated_bytes: \"AB\"\n") { (o: inout MessageTestType) in o.repeatedBytes = [Data(), Data([65, 66])] } - assertTextFormatDecodeSucceeds("repeated_bytes: \"\"\nrepeated_bytes: \"A\" \"B\"\n") {(o: MessageTestType) in - return o.repeatedBytes == [Data(), Data([65, 66])] + assertTextFormatDecodeSucceeds("repeated_bytes: \"\"\nrepeated_bytes: \"A\" \"B\"\n") { (o: MessageTestType) in + o.repeatedBytes == [Data(), Data([65, 66])] } - assertTextFormatDecodeSucceeds("repeated_bytes: [\"\", \"AB\"]\n") {(o: MessageTestType) in - return o.repeatedBytes == [Data(), Data([65, 66])] + assertTextFormatDecodeSucceeds("repeated_bytes: [\"\", \"AB\"]\n") { (o: MessageTestType) in + o.repeatedBytes == [Data(), Data([65, 66])] } - assertTextFormatDecodeSucceeds("repeated_bytes: [\"\", \"A\" \"B\"]\n") {(o: MessageTestType) in - return o.repeatedBytes == [Data(), Data([65, 66])] + assertTextFormatDecodeSucceeds("repeated_bytes: [\"\", \"A\" \"B\"]\n") { (o: MessageTestType) in + o.repeatedBytes == [Data(), Data([65, 66])] } } @@ -1165,22 +1218,29 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { var a = MessageTestType() a.repeatedNestedMessage = [nested, nested2] - XCTAssertEqual("repeated_nested_message {\n bb: 7\n}\nrepeated_nested_message {\n bb: -7\n}\n", a.textFormatString()) + XCTAssertEqual( + "repeated_nested_message {\n bb: 7\n}\nrepeated_nested_message {\n bb: -7\n}\n", + a.textFormatString() + ) - assertTextFormatEncode("repeated_nested_message {\n bb: 7\n}\nrepeated_nested_message {\n bb: -7\n}\n") {(o: inout MessageTestType) in o.repeatedNestedMessage = [nested, nested2] } + assertTextFormatEncode("repeated_nested_message {\n bb: 7\n}\nrepeated_nested_message {\n bb: -7\n}\n") { + (o: inout MessageTestType) in o.repeatedNestedMessage = [nested, nested2] + } - assertTextFormatDecodeSucceeds("repeated_nested_message: {\n bb: 7\n}\nrepeated_nested_message: {\n bb: -7\n}\n") { + assertTextFormatDecodeSucceeds( + "repeated_nested_message: {\n bb: 7\n}\nrepeated_nested_message: {\n bb: -7\n}\n" + ) { (o: MessageTestType) in - return o.repeatedNestedMessage == [ - MessageTestType.NestedMessage.with {$0.bb = 7}, - MessageTestType.NestedMessage.with {$0.bb = -7} + o.repeatedNestedMessage == [ + MessageTestType.NestedMessage.with { $0.bb = 7 }, + MessageTestType.NestedMessage.with { $0.bb = -7 }, ] } assertTextFormatDecodeSucceeds("repeated_nested_message:[{bb: 7}, {bb: -7}]") { (o: MessageTestType) in - return o.repeatedNestedMessage == [ - MessageTestType.NestedMessage.with {$0.bb = 7}, - MessageTestType.NestedMessage.with {$0.bb = -7} + o.repeatedNestedMessage == [ + MessageTestType.NestedMessage.with { $0.bb = 7 }, + MessageTestType.NestedMessage.with { $0.bb = -7 }, ] } @@ -1197,12 +1257,19 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { var a = MessageTestType() a.repeatedForeignMessage = [foreign, foreign2] - XCTAssertEqual("repeated_foreign_message {\n c: 88\n}\nrepeated_foreign_message {\n c: -88\n}\n", a.textFormatString()) + XCTAssertEqual( + "repeated_foreign_message {\n c: 88\n}\nrepeated_foreign_message {\n c: -88\n}\n", + a.textFormatString() + ) - assertTextFormatEncode("repeated_foreign_message {\n c: 88\n}\nrepeated_foreign_message {\n c: -88\n}\n") {(o: inout MessageTestType) in o.repeatedForeignMessage = [foreign, foreign2] } + assertTextFormatEncode("repeated_foreign_message {\n c: 88\n}\nrepeated_foreign_message {\n c: -88\n}\n") { + (o: inout MessageTestType) in o.repeatedForeignMessage = [foreign, foreign2] + } do { - let message = try MessageTestType(textFormatString:"repeated_foreign_message: {\n c: 88\n}\nrepeated_foreign_message: {\n c: -88\n}\n") + let message = try MessageTestType( + textFormatString: "repeated_foreign_message: {\n c: 88\n}\nrepeated_foreign_message: {\n c: -88\n}\n" + ) XCTAssertEqual(message.repeatedForeignMessage[0].c, 88) XCTAssertEqual(message.repeatedForeignMessage[1].c, -88) } catch { @@ -1212,7 +1279,6 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { assertTextFormatDecodeFails("repeated_foreign_message {\n c: 88\n}\nrepeated_foreign_message {\n c: a\n}\n") } - func testEncoding_repeatedImportMessage() { var importMessage = SwiftProtoTesting_Import_ImportMessage() importMessage.d = -9 @@ -1223,12 +1289,19 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { var a = MessageTestType() a.repeatedImportMessage = [importMessage, importMessage2] - XCTAssertEqual("repeated_import_message {\n d: -9\n}\nrepeated_import_message {\n d: 999999\n}\n", a.textFormatString()) + XCTAssertEqual( + "repeated_import_message {\n d: -9\n}\nrepeated_import_message {\n d: 999999\n}\n", + a.textFormatString() + ) - assertTextFormatEncode("repeated_import_message {\n d: -9\n}\nrepeated_import_message {\n d: 999999\n}\n") {(o: inout MessageTestType) in o.repeatedImportMessage = [importMessage, importMessage2] } + assertTextFormatEncode("repeated_import_message {\n d: -9\n}\nrepeated_import_message {\n d: 999999\n}\n") { + (o: inout MessageTestType) in o.repeatedImportMessage = [importMessage, importMessage2] + } do { - let message = try MessageTestType(textFormatString:"repeated_import_message: {\n d: -9\n}\nrepeated_import_message: {\n d: 999999\n}\n") + let message = try MessageTestType( + textFormatString: "repeated_import_message: {\n d: -9\n}\nrepeated_import_message: {\n d: 999999\n}\n" + ) XCTAssertEqual(message.repeatedImportMessage[0].d, -9) XCTAssertEqual(message.repeatedImportMessage[1].d, 999999) } catch { @@ -1239,51 +1312,52 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { } func testEncoding_repeatedNestedEnum() { - assertTextFormatEncode("repeated_nested_enum: [BAR, BAZ]\n") {(o: inout MessageTestType) in + assertTextFormatEncode("repeated_nested_enum: [BAR, BAZ]\n") { (o: inout MessageTestType) in o.repeatedNestedEnum = [.bar, .baz] } assertTextFormatDecodeSucceeds("repeated_nested_enum: BAR repeated_nested_enum: BAZ") { (o: MessageTestType) in - return o.repeatedNestedEnum == [.bar, .baz] + o.repeatedNestedEnum == [.bar, .baz] } assertTextFormatDecodeSucceeds("repeated_nested_enum: [2, BAZ]") { (o: MessageTestType) in - return o.repeatedNestedEnum == [.bar, .baz] + o.repeatedNestedEnum == [.bar, .baz] } - assertTextFormatDecodeSucceeds("repeated_nested_enum: [] repeated_nested_enum: [2] repeated_nested_enum: [BAZ] repeated_nested_enum: []") { + assertTextFormatDecodeSucceeds( + "repeated_nested_enum: [] repeated_nested_enum: [2] repeated_nested_enum: [BAZ] repeated_nested_enum: []" + ) { (o: MessageTestType) in - return o.repeatedNestedEnum == [.bar, .baz] + o.repeatedNestedEnum == [.bar, .baz] } assertTextFormatDecodeFails("repeated_nested_enum: BAR\nrepeated_nested_enum: a\n") } func testEncoding_repeatedForeignEnum() { - assertTextFormatEncode("repeated_foreign_enum: [FOREIGN_BAR, FOREIGN_BAZ]\n") {(o: inout MessageTestType) in + assertTextFormatEncode("repeated_foreign_enum: [FOREIGN_BAR, FOREIGN_BAZ]\n") { (o: inout MessageTestType) in o.repeatedForeignEnum = [.foreignBar, .foreignBaz] } - assertTextFormatDecodeSucceeds("repeated_foreign_enum: [5, 6]\n") {(o: MessageTestType) in + assertTextFormatDecodeSucceeds("repeated_foreign_enum: [5, 6]\n") { (o: MessageTestType) in o.repeatedForeignEnum == [.foreignBar, .foreignBaz] } - assertTextFormatEncode("repeated_foreign_enum: [123, 321]\n") {(o: inout MessageTestType) in + assertTextFormatEncode("repeated_foreign_enum: [123, 321]\n") { (o: inout MessageTestType) in o.repeatedForeignEnum = [.UNRECOGNIZED(123), .UNRECOGNIZED(321)] } assertTextFormatDecodeFails("repeated_foreign_enum: FOREIGN_BAR\nrepeated_foreign_enum: a\n") } - func testEncoding_oneofUint32() { var a = MessageTestType() a.oneofUint32 = 99 XCTAssertEqual("oneof_uint32: 99\n", a.textFormatString()) - assertTextFormatEncode("oneof_uint32: 99\n") {(o: inout MessageTestType) in o.oneofUint32 = 99 } + assertTextFormatEncode("oneof_uint32: 99\n") { (o: inout MessageTestType) in o.oneofUint32 = 99 } assertTextFormatDecodeFails("oneof_uint32: a\n") } @@ -1308,20 +1382,20 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { } func testExplicitDelimiters() { - assertTextFormatDecodeSucceeds("optional_int32:1,optional_int64:3;optional_uint32:4") {(o: MessageTestType) in - return o.optionalInt32 == 1 && o.optionalInt64 == 3 && o.optionalUint32 == 4 + assertTextFormatDecodeSucceeds("optional_int32:1,optional_int64:3;optional_uint32:4") { (o: MessageTestType) in + o.optionalInt32 == 1 && o.optionalInt64 == 3 && o.optionalUint32 == 4 } - assertTextFormatDecodeSucceeds("optional_int32:1,\n") {(o: MessageTestType) in - return o.optionalInt32 == 1 + assertTextFormatDecodeSucceeds("optional_int32:1,\n") { (o: MessageTestType) in + o.optionalInt32 == 1 } - assertTextFormatDecodeSucceeds("optional_int32:1;\n") {(o: MessageTestType) in - return o.optionalInt32 == 1 + assertTextFormatDecodeSucceeds("optional_int32:1;\n") { (o: MessageTestType) in + o.optionalInt32 == 1 } - assertTextFormatDecodeSucceeds("optional_nested_message {bb:3,},") {(o: MessageTestType) in - return o.optionalNestedMessage.bb == 3 + assertTextFormatDecodeSucceeds("optional_nested_message {bb:3,},") { (o: MessageTestType) in + o.optionalNestedMessage.bb == 3 } - assertTextFormatDecodeSucceeds("optional_nested_message {bb:7;};") {(o: MessageTestType) in - return o.optionalNestedMessage.bb == 7 + assertTextFormatDecodeSucceeds("optional_nested_message {bb:7;};") { (o: MessageTestType) in + o.optionalNestedMessage.bb == 7 } // Make sure duplicate separators fail. assertTextFormatDecodeFails("optional_int32:1,,") @@ -1398,148 +1472,150 @@ final class Test_TextFormat_proto3: XCTestCase, PBTestHelpers { } func testMultipleFields() { - let expected: String = ("optional_int32: 1\n" - + "optional_int64: 2\n" - + "optional_uint32: 3\n" - + "optional_uint64: 4\n" - + "optional_sint32: 5\n" - + "optional_sint64: 6\n" - + "optional_fixed32: 7\n" - + "optional_fixed64: 8\n" - + "optional_sfixed32: 9\n" - + "optional_sfixed64: 10\n" - + "optional_float: 11.0\n" - + "optional_double: 12.0\n" - + "optional_bool: true\n" - + "optional_string: \"abc\"\n" - + "optional_bytes: \"AB\"\n" - + "optional_nested_message {\n" - + " bb: 7\n" - + "}\n" - + "optional_foreign_message {\n" - + " c: 88\n" - + "}\n" - + "optional_import_message {\n" - + " d: -9\n" - + "}\n" - + "optional_nested_enum: BAZ\n" - + "optional_foreign_enum: FOREIGN_BAZ\n" - + "optional_public_import_message {\n" - + " e: -999999\n" - + "}\n" - + "repeated_int32: [1, 2]\n" - + "repeated_int64: [3, 4]\n" - + "repeated_uint32: [5, 6]\n" - + "repeated_uint64: [7, 8]\n" - + "repeated_sint32: [9, 10]\n" - + "repeated_sint64: [11, 12]\n" - + "repeated_fixed32: [13, 14]\n" - + "repeated_fixed64: [15, 16]\n" - + "repeated_sfixed32: [17, 18]\n" - + "repeated_sfixed64: [19, 20]\n" - + "repeated_float: [21.0, 22.0]\n" - + "repeated_double: [23.0, 24.0]\n" - + "repeated_bool: [true, false]\n" - + "repeated_string: \"abc\"\n" - + "repeated_string: \"def\"\n" - + "repeated_bytes: \"\"\n" - + "repeated_bytes: \"AB\"\n" - + "repeated_nested_message {\n" - + " bb: 7\n" - + "}\n" - + "repeated_nested_message {\n" - + " bb: -7\n" - + "}\n" - + "repeated_foreign_message {\n" - + " c: 88\n" - + "}\n" - + "repeated_foreign_message {\n" - + " c: -88\n" - + "}\n" - + "repeated_import_message {\n" - + " d: -9\n" - + "}\n" - + "repeated_import_message {\n" - + " d: 999999\n" - + "}\n" - + "repeated_nested_enum: [BAR, BAZ]\n" - + "repeated_foreign_enum: [FOREIGN_BAR, FOREIGN_BAZ]\n" - + "oneof_uint32: 99\n") + let expected: String = + ("optional_int32: 1\n" + + "optional_int64: 2\n" + + "optional_uint32: 3\n" + + "optional_uint64: 4\n" + + "optional_sint32: 5\n" + + "optional_sint64: 6\n" + + "optional_fixed32: 7\n" + + "optional_fixed64: 8\n" + + "optional_sfixed32: 9\n" + + "optional_sfixed64: 10\n" + + "optional_float: 11.0\n" + + "optional_double: 12.0\n" + + "optional_bool: true\n" + + "optional_string: \"abc\"\n" + + "optional_bytes: \"AB\"\n" + + "optional_nested_message {\n" + + " bb: 7\n" + + "}\n" + + "optional_foreign_message {\n" + + " c: 88\n" + + "}\n" + + "optional_import_message {\n" + + " d: -9\n" + + "}\n" + + "optional_nested_enum: BAZ\n" + + "optional_foreign_enum: FOREIGN_BAZ\n" + + "optional_public_import_message {\n" + + " e: -999999\n" + + "}\n" + + "repeated_int32: [1, 2]\n" + + "repeated_int64: [3, 4]\n" + + "repeated_uint32: [5, 6]\n" + + "repeated_uint64: [7, 8]\n" + + "repeated_sint32: [9, 10]\n" + + "repeated_sint64: [11, 12]\n" + + "repeated_fixed32: [13, 14]\n" + + "repeated_fixed64: [15, 16]\n" + + "repeated_sfixed32: [17, 18]\n" + + "repeated_sfixed64: [19, 20]\n" + + "repeated_float: [21.0, 22.0]\n" + + "repeated_double: [23.0, 24.0]\n" + + "repeated_bool: [true, false]\n" + + "repeated_string: \"abc\"\n" + + "repeated_string: \"def\"\n" + + "repeated_bytes: \"\"\n" + + "repeated_bytes: \"AB\"\n" + + "repeated_nested_message {\n" + + " bb: 7\n" + + "}\n" + + "repeated_nested_message {\n" + + " bb: -7\n" + + "}\n" + + "repeated_foreign_message {\n" + + " c: 88\n" + + "}\n" + + "repeated_foreign_message {\n" + + " c: -88\n" + + "}\n" + + "repeated_import_message {\n" + + " d: -9\n" + + "}\n" + + "repeated_import_message {\n" + + " d: 999999\n" + + "}\n" + + "repeated_nested_enum: [BAR, BAZ]\n" + + "repeated_foreign_enum: [FOREIGN_BAR, FOREIGN_BAZ]\n" + + "oneof_uint32: 99\n") assertTextFormatEncode(expected, configure: configureLargeObject) } func testMultipleFields_numbers() { - let text: String = ("1: 1\n" - + "2: 2\n" - + "3: 3\n" - + "4: 4\n" - + "5: 5\n" - + "6: 6\n" - + "7: 7\n" - + "8: 8\n" - + "9: 9\n" - + "10: 10\n" - + "11: 11\n" - + "12: 12\n" - + "13: true\n" - + "14: \"abc\"\n" - + "15: \"AB\"\n" - + "18 {\n" - + " bb: 7\n" - + "}\n" - + "19 {\n" - + " c: 88\n" - + "}\n" - + "20 {\n" - + " d: -9\n" - + "}\n" - + "21: BAZ\n" - + "22: FOREIGN_BAZ\n" - + "26 {\n" - + " e: -999999\n" - + "}\n" - + "31: [1, 2]\n" - + "32: [3, 4]\n" - + "33: [5, 6]\n" - + "34: [7, 8]\n" - + "35: [9, 10]\n" - + "36: [11, 12]\n" - + "37: [13, 14]\n" - + "38: [15, 16]\n" - + "39: [17, 18]\n" - + "40: [19, 20]\n" - + "41: [21, 22]\n" - + "42: [23, 24]\n" - + "43: [true, false]\n" - + "44: \"abc\"\n" - + "44: \"def\"\n" - + "45: \"\"\n" - + "45: \"AB\"\n" - + "48 {\n" - + " bb: 7\n" - + "}\n" - + "48 {\n" - + " bb: -7\n" - + "}\n" - + "49 {\n" - + " c: 88\n" - + "}\n" - + "49 {\n" - + " c: -88\n" - + "}\n" - + "50 {\n" - + " d: -9\n" - + "}\n" - + "50 {\n" - + " d: 999999\n" - + "}\n" - + "51: [BAR, BAZ]\n" - + "52: [FOREIGN_BAR, FOREIGN_BAZ]\n" - + "111: 99\n") + let text: String = + ("1: 1\n" + + "2: 2\n" + + "3: 3\n" + + "4: 4\n" + + "5: 5\n" + + "6: 6\n" + + "7: 7\n" + + "8: 8\n" + + "9: 9\n" + + "10: 10\n" + + "11: 11\n" + + "12: 12\n" + + "13: true\n" + + "14: \"abc\"\n" + + "15: \"AB\"\n" + + "18 {\n" + + " bb: 7\n" + + "}\n" + + "19 {\n" + + " c: 88\n" + + "}\n" + + "20 {\n" + + " d: -9\n" + + "}\n" + + "21: BAZ\n" + + "22: FOREIGN_BAZ\n" + + "26 {\n" + + " e: -999999\n" + + "}\n" + + "31: [1, 2]\n" + + "32: [3, 4]\n" + + "33: [5, 6]\n" + + "34: [7, 8]\n" + + "35: [9, 10]\n" + + "36: [11, 12]\n" + + "37: [13, 14]\n" + + "38: [15, 16]\n" + + "39: [17, 18]\n" + + "40: [19, 20]\n" + + "41: [21, 22]\n" + + "42: [23, 24]\n" + + "43: [true, false]\n" + + "44: \"abc\"\n" + + "44: \"def\"\n" + + "45: \"\"\n" + + "45: \"AB\"\n" + + "48 {\n" + + " bb: 7\n" + + "}\n" + + "48 {\n" + + " bb: -7\n" + + "}\n" + + "49 {\n" + + " c: 88\n" + + "}\n" + + "49 {\n" + + " c: -88\n" + + "}\n" + + "50 {\n" + + " d: -9\n" + + "}\n" + + "50 {\n" + + " d: 999999\n" + + "}\n" + + "51: [BAR, BAZ]\n" + + "52: [FOREIGN_BAR, FOREIGN_BAZ]\n" + + "111: 99\n") let expected = MessageTestType.with { configureLargeObject(&$0) } - assertTextFormatDecodeSucceeds(text) {(o: MessageTestType) in + assertTextFormatDecodeSucceeds(text) { (o: MessageTestType) in o == expected } } diff --git a/Tests/SwiftProtobufTests/Test_Timestamp.swift b/Tests/SwiftProtobufTests/Test_Timestamp.swift index 3bf627e15..3b6c72ffb 100644 --- a/Tests/SwiftProtobufTests/Test_Timestamp.swift +++ b/Tests/SwiftProtobufTests/Test_Timestamp.swift @@ -19,70 +19,71 @@ /// // ----------------------------------------------------------------------------- - import Foundation -import XCTest import SwiftProtobuf +import XCTest final class Test_Timestamp: XCTestCase, PBTestHelpers { typealias MessageTestType = Google_Protobuf_Timestamp func testJSON() throws { - XCTAssertEqual("\"1970-01-01T00:00:00Z\"", - try Google_Protobuf_Timestamp().jsonString()) + XCTAssertEqual( + "\"1970-01-01T00:00:00Z\"", + try Google_Protobuf_Timestamp().jsonString() + ) assertJSONEncode("\"1970-01-01T00:00:01.000000001Z\"") { (o: inout MessageTestType) in - o.seconds = 1 // 1 second + o.seconds = 1 // 1 second o.nanos = 1 } assertJSONEncode("\"1970-01-01T00:01:00.000000010Z\"") { (o: inout MessageTestType) in - o.seconds = 60 // 1 minute + o.seconds = 60 // 1 minute o.nanos = 10 } assertJSONEncode("\"1970-01-01T01:00:00.000000100Z\"") { (o: inout MessageTestType) in - o.seconds = 3600 // 1 hour + o.seconds = 3600 // 1 hour o.nanos = 100 } assertJSONEncode("\"1970-01-02T00:00:00.000001Z\"") { (o: inout MessageTestType) in - o.seconds = 86400 // 1 day + o.seconds = 86400 // 1 day o.nanos = 1000 } assertJSONEncode("\"1970-02-01T00:00:00.000010Z\"") { (o: inout MessageTestType) in - o.seconds = 2678400 // 1 month + o.seconds = 2_678_400 // 1 month o.nanos = 10000 } assertJSONEncode("\"1971-01-01T00:00:00.000100Z\"") { (o: inout MessageTestType) in - o.seconds = 31536000 // 1 year + o.seconds = 31_536_000 // 1 year o.nanos = 100000 } assertJSONEncode("\"1970-01-01T00:00:01.001Z\"") { (o: inout MessageTestType) in o.seconds = 1 - o.nanos = 1000000 + o.nanos = 1_000_000 } assertJSONEncode("\"1970-01-01T00:00:01.010Z\"") { (o: inout MessageTestType) in o.seconds = 1 - o.nanos = 10000000 + o.nanos = 10_000_000 } assertJSONEncode("\"1970-01-01T00:00:01.100Z\"") { (o: inout MessageTestType) in o.seconds = 1 - o.nanos = 100000000 + o.nanos = 100_000_000 } assertJSONEncode("\"1970-01-01T00:00:01Z\"") { @@ -94,28 +95,28 @@ final class Test_Timestamp: XCTestCase, PBTestHelpers { // Largest representable date assertJSONEncode("\"9999-12-31T23:59:59.999999999Z\"") { (o: inout MessageTestType) in - o.seconds = 253402300799 - o.nanos = 999999999 + o.seconds = 253_402_300_799 + o.nanos = 999_999_999 } // 10 billion seconds after Epoch assertJSONEncode("\"2286-11-20T17:46:40Z\"") { (o: inout MessageTestType) in - o.seconds = 10000000000 + o.seconds = 10_000_000_000 o.nanos = 0 } // 1 billion seconds after Epoch assertJSONEncode("\"2001-09-09T01:46:40Z\"") { (o: inout MessageTestType) in - o.seconds = 1000000000 + o.seconds = 1_000_000_000 o.nanos = 0 } // 1 million seconds after Epoch assertJSONEncode("\"1970-01-12T13:46:40Z\"") { (o: inout MessageTestType) in - o.seconds = 1000000 + o.seconds = 1_000_000 o.nanos = 0 } @@ -136,35 +137,35 @@ final class Test_Timestamp: XCTestCase, PBTestHelpers { // 1 million seconds before Epoch assertJSONEncode("\"1969-12-20T10:13:20Z\"") { (o: inout MessageTestType) in - o.seconds = -1000000 + o.seconds = -1_000_000 o.nanos = 0 } // 1 billion seconds before Epoch assertJSONEncode("\"1938-04-24T22:13:20Z\"") { (o: inout MessageTestType) in - o.seconds = -1000000000 + o.seconds = -1_000_000_000 o.nanos = 0 } // 10 billion seconds before Epoch assertJSONEncode("\"1653-02-10T06:13:20Z\"") { (o: inout MessageTestType) in - o.seconds = -10000000000 + o.seconds = -10_000_000_000 o.nanos = 0 } // Earliest leap year assertJSONEncode("\"0004-02-19T02:50:24Z\"") { (o: inout MessageTestType) in - o.seconds = -62036744976 + o.seconds = -62_036_744_976 o.nanos = 0 } // Earliest representable date assertJSONEncode("\"0001-01-01T00:00:00Z\"") { (o: inout MessageTestType) in - o.seconds = -62135596800 + o.seconds = -62_135_596_800 o.nanos = 0 } @@ -181,21 +182,20 @@ final class Test_Timestamp: XCTestCase, PBTestHelpers { assertJSONDecodeFails("\"9999-12-31T00:00:00\"") } - func testJSON_range() throws { // Check that JSON timestamps round-trip correctly over a wide range. // This checks about 15,000 dates scattered over a 10,000 year period // to verify that our JSON encoder and decoder agree with each other. // Combined with the above checks of specific known dates, this gives a // pretty high confidence that our date calculations are correct. - let earliest: Int64 = -62135596800 - let latest: Int64 = 253402300799 + let earliest: Int64 = -62_135_596_800 + let latest: Int64 = 253_402_300_799 // Use a smaller increment to get more exhaustive testing. An // increment of 12345 will test every single day in the entire // 10,000 year range and require about 15 minutes to run. // An increment of 12345678 will pick about one day out of // every 5 months and require only a few seconds to run. - let increment: Int64 = 12345678 + let increment: Int64 = 12_345_678 var t: Int64 = earliest // If things are broken, this test can easily generate >10,000 failures. // That many failures can break a lot of tools (Xcode, for example), so @@ -213,7 +213,11 @@ final class Test_Timestamp: XCTestCase, PBTestHelpers { if decoded.seconds != t { if roundTripFailures == 0 { // Only the first round-trip failure will be reported here - XCTAssertEqual(decoded.seconds, t, "Round-trip failed for \(encoded): \(t) != \(decoded.seconds)") + XCTAssertEqual( + decoded.seconds, + t, + "Round-trip failed for \(encoded): \(t) != \(decoded.seconds)" + ) } roundTripFailures += 1 } @@ -240,31 +244,52 @@ final class Test_Timestamp: XCTestCase, PBTestHelpers { } func testJSON_timezones() { - assertJSONDecodeSucceeds("\"1970-01-01T08:00:00+08:00\"") {$0.seconds == 0} - assertJSONDecodeSucceeds("\"1969-12-31T16:00:00-08:00\"") {$0.seconds == 0} + assertJSONDecodeSucceeds("\"1970-01-01T08:00:00+08:00\"") { $0.seconds == 0 } + assertJSONDecodeSucceeds("\"1969-12-31T16:00:00-08:00\"") { $0.seconds == 0 } assertJSONDecodeFails("\"0001-01-01T00:00:00+23:59\"") assertJSONDecodeFails("\"9999-12-31T23:59:59-23:59\"") } func testJSON_timestampField() throws { do { - let valid = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: "{\"optionalTimestamp\": \"0001-01-01T00:00:00Z\"}") - XCTAssertEqual(valid.optionalTimestamp, Google_Protobuf_Timestamp(seconds: -62135596800)) + let valid = try SwiftProtoTesting_Test3_TestAllTypesProto3( + jsonString: "{\"optionalTimestamp\": \"0001-01-01T00:00:00Z\"}" + ) + XCTAssertEqual(valid.optionalTimestamp, Google_Protobuf_Timestamp(seconds: -62_135_596_800)) } catch { XCTFail("Should have decoded correctly") } - - XCTAssertThrowsError(try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: "{\"optionalTimestamp\": \"10000-01-01T00:00:00Z\"}")) - XCTAssertThrowsError(try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: "{\"optionalTimestamp\": \"0001-01-01T00:00:00\"}")) - XCTAssertThrowsError(try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: "{\"optionalTimestamp\": \"0001-01-01 00:00:00Z\"}")) - XCTAssertThrowsError(try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: "{\"optionalTimestamp\": \"0001-01-01T00:00:00z\"}")) - XCTAssertThrowsError(try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: "{\"optionalTimestamp\": \"0001-01-01t00:00:00Z\"}")) + XCTAssertThrowsError( + try SwiftProtoTesting_Test3_TestAllTypesProto3( + jsonString: "{\"optionalTimestamp\": \"10000-01-01T00:00:00Z\"}" + ) + ) + XCTAssertThrowsError( + try SwiftProtoTesting_Test3_TestAllTypesProto3( + jsonString: "{\"optionalTimestamp\": \"0001-01-01T00:00:00\"}" + ) + ) + XCTAssertThrowsError( + try SwiftProtoTesting_Test3_TestAllTypesProto3( + jsonString: "{\"optionalTimestamp\": \"0001-01-01 00:00:00Z\"}" + ) + ) + XCTAssertThrowsError( + try SwiftProtoTesting_Test3_TestAllTypesProto3( + jsonString: "{\"optionalTimestamp\": \"0001-01-01T00:00:00z\"}" + ) + ) + XCTAssertThrowsError( + try SwiftProtoTesting_Test3_TestAllTypesProto3( + jsonString: "{\"optionalTimestamp\": \"0001-01-01t00:00:00Z\"}" + ) + ) } // A couple more test cases transcribed from conformance test func testJSON_conformance() throws { - let t1 = Google_Protobuf_Timestamp(seconds: 0, nanos: 10000000) + let t1 = Google_Protobuf_Timestamp(seconds: 0, nanos: 10_000_000) var m1 = SwiftProtoTesting_Test3_TestAllTypesProto3() m1.optionalTimestamp = t1 let expected1 = "{\"optionalTimestamp\":\"1970-01-01T00:00:00.010Z\"}" @@ -287,24 +312,26 @@ final class Test_Timestamp: XCTestCase, PBTestHelpers { // Extra spaces around all the tokens. let json3 = " { \"repeatedTimestamp\" : [ \"0001-01-01T00:00:00Z\" , \"9999-12-31T23:59:59.999999999Z\" ] } " let m3 = try SwiftProtoTesting_Test3_TestAllTypesProto3(jsonString: json3) - let expected3 = [Google_Protobuf_Timestamp(seconds: -62135596800), - Google_Protobuf_Timestamp(seconds: 253402300799, nanos: 999999999)] + let expected3 = [ + Google_Protobuf_Timestamp(seconds: -62_135_596_800), + Google_Protobuf_Timestamp(seconds: 253_402_300_799, nanos: 999_999_999), + ] XCTAssertEqual(m3.repeatedTimestamp, expected3) } func testSerializationFailure() throws { - let maxOutOfRange = Google_Protobuf_Timestamp(seconds:-62135596800, nanos: -1) + let maxOutOfRange = Google_Protobuf_Timestamp(seconds: -62_135_596_800, nanos: -1) XCTAssertThrowsError(try maxOutOfRange.jsonString()) - let minInRange = Google_Protobuf_Timestamp(seconds:-62135596800) + let minInRange = Google_Protobuf_Timestamp(seconds: -62_135_596_800) XCTAssertNotNil(try minInRange.jsonString()) - let maxInRange = Google_Protobuf_Timestamp(seconds:253402300799, nanos: 999999999) + let maxInRange = Google_Protobuf_Timestamp(seconds: 253_402_300_799, nanos: 999_999_999) XCTAssertNotNil(try maxInRange.jsonString()) - let minOutOfRange = Google_Protobuf_Timestamp(seconds:253402300800) + let minOutOfRange = Google_Protobuf_Timestamp(seconds: 253_402_300_800) XCTAssertThrowsError(try minOutOfRange.jsonString()) } func testBasicArithmetic() throws { - let tn1_n1 = Google_Protobuf_Timestamp(seconds: -2, nanos: 999999999) + let tn1_n1 = Google_Protobuf_Timestamp(seconds: -2, nanos: 999_999_999) let t0 = Google_Protobuf_Timestamp() let t1_1 = Google_Protobuf_Timestamp(seconds: 1, nanos: 1) let t2_2 = Google_Protobuf_Timestamp(seconds: 2, nanos: 2) @@ -334,35 +361,42 @@ final class Test_Timestamp: XCTestCase, PBTestHelpers { func testArithmeticNormalizes() throws { // Addition normalizes the result - let r1: Google_Protobuf_Timestamp = Google_Protobuf_Timestamp() + Google_Protobuf_Duration(seconds: 0, nanos: 2000000001) + let r1: Google_Protobuf_Timestamp = + Google_Protobuf_Timestamp() + Google_Protobuf_Duration(seconds: 0, nanos: 2_000_000_001) XCTAssertEqual(r1.seconds, 2) XCTAssertEqual(r1.nanos, 1) // Subtraction normalizes the result - let r2: Google_Protobuf_Timestamp = Google_Protobuf_Timestamp() - Google_Protobuf_Duration(seconds: 0, nanos: 2000000001) + let r2: Google_Protobuf_Timestamp = + Google_Protobuf_Timestamp() - Google_Protobuf_Duration(seconds: 0, nanos: 2_000_000_001) XCTAssertEqual(r2.seconds, -3) - XCTAssertEqual(r2.nanos, 999999999) + XCTAssertEqual(r2.nanos, 999_999_999) // Subtraction normalizes the result - let r3: Google_Protobuf_Duration = Google_Protobuf_Timestamp() - Google_Protobuf_Timestamp(seconds: 0, nanos: 2000000001) + let r3: Google_Protobuf_Duration = + Google_Protobuf_Timestamp() - Google_Protobuf_Timestamp(seconds: 0, nanos: 2_000_000_001) XCTAssertEqual(r3.seconds, -2) XCTAssertEqual(r3.nanos, -1) - let r4: Google_Protobuf_Duration = Google_Protobuf_Timestamp(seconds: 1) - Google_Protobuf_Timestamp(nanos: 2000000001) + let r4: Google_Protobuf_Duration = + Google_Protobuf_Timestamp(seconds: 1) - Google_Protobuf_Timestamp(nanos: 2_000_000_001) XCTAssertEqual(r4.seconds, -1) XCTAssertEqual(r4.nanos, -1) - let r5: Google_Protobuf_Duration = Google_Protobuf_Timestamp(seconds: -1) - Google_Protobuf_Timestamp(nanos: -2000000001) + let r5: Google_Protobuf_Duration = + Google_Protobuf_Timestamp(seconds: -1) - Google_Protobuf_Timestamp(nanos: -2_000_000_001) XCTAssertEqual(r5.seconds, 1) XCTAssertEqual(r5.nanos, 1) - let r6: Google_Protobuf_Duration = Google_Protobuf_Timestamp(seconds: -10) - Google_Protobuf_Timestamp(nanos: -2000000001) + let r6: Google_Protobuf_Duration = + Google_Protobuf_Timestamp(seconds: -10) - Google_Protobuf_Timestamp(nanos: -2_000_000_001) XCTAssertEqual(r6.seconds, -7) - XCTAssertEqual(r6.nanos, -999999999) + XCTAssertEqual(r6.nanos, -999_999_999) - let r7: Google_Protobuf_Duration = Google_Protobuf_Timestamp(seconds: 10) - Google_Protobuf_Timestamp(nanos: 2000000001) + let r7: Google_Protobuf_Duration = + Google_Protobuf_Timestamp(seconds: 10) - Google_Protobuf_Timestamp(nanos: 2_000_000_001) XCTAssertEqual(r7.seconds, 7) - XCTAssertEqual(r7.nanos, 999999999) + XCTAssertEqual(r7.nanos, 999_999_999) } // TODO: Should setter correct for out-of-range @@ -372,7 +406,7 @@ final class Test_Timestamp: XCTestCase, PBTestHelpers { // Negative timestamp let t1 = Google_Protobuf_Timestamp(timeIntervalSince1970: -123.456) XCTAssertEqual(t1.seconds, -124) - XCTAssertEqual(t1.nanos, 544000000) + XCTAssertEqual(t1.nanos, 544_000_000) // Full precision let t2 = Google_Protobuf_Timestamp(timeIntervalSince1970: -123.999999999) @@ -396,17 +430,17 @@ final class Test_Timestamp: XCTestCase, PBTestHelpers { // Positive timestamp let t6 = Google_Protobuf_Timestamp(timeIntervalSince1970: 123.456) XCTAssertEqual(t6.seconds, 123) - XCTAssertEqual(t6.nanos, 456000000) + XCTAssertEqual(t6.nanos, 456_000_000) // Full precision let t7 = Google_Protobuf_Timestamp(timeIntervalSince1970: 123.999999999) XCTAssertEqual(t7.seconds, 123) - XCTAssertEqual(t7.nanos, 999999999) + XCTAssertEqual(t7.nanos, 999_999_999) // Round down let t8 = Google_Protobuf_Timestamp(timeIntervalSince1970: 123.9999999994) XCTAssertEqual(t8.seconds, 123) - XCTAssertEqual(t8.nanos, 999999999) + XCTAssertEqual(t8.nanos, 999_999_999) // Round up let t9 = Google_Protobuf_Timestamp(timeIntervalSince1970: 123.9999999996) @@ -416,20 +450,20 @@ final class Test_Timestamp: XCTestCase, PBTestHelpers { func testInitializationByReferenceTimestamp() throws { let t1 = Google_Protobuf_Timestamp(timeIntervalSinceReferenceDate: 123.456) - XCTAssertEqual(t1.seconds, 978307323) - XCTAssertEqual(t1.nanos, 456000000) + XCTAssertEqual(t1.seconds, 978_307_323) + XCTAssertEqual(t1.nanos, 456_000_000) } func testInitializationByDates() throws { let t1 = Google_Protobuf_Timestamp(date: Date(timeIntervalSinceReferenceDate: 123.456)) - XCTAssertEqual(t1.seconds, 978307323) - XCTAssertEqual(t1.nanos, 456000000) + XCTAssertEqual(t1.seconds, 978_307_323) + XCTAssertEqual(t1.nanos, 456_000_000) } func testTimestampGetters() throws { - let t1 = Google_Protobuf_Timestamp(seconds: 12345678, nanos: 12345678) - XCTAssertEqual(t1.seconds, 12345678) - XCTAssertEqual(t1.nanos, 12345678) + let t1 = Google_Protobuf_Timestamp(seconds: 12_345_678, nanos: 12_345_678) + XCTAssertEqual(t1.seconds, 12_345_678) + XCTAssertEqual(t1.nanos, 12_345_678) XCTAssertEqual(t1.timeIntervalSince1970, 12345678.012345678) XCTAssertEqual(t1.timeIntervalSinceReferenceDate, -965961521.987654322) let d = t1.date diff --git a/Tests/SwiftProtobufTests/Test_Type.swift b/Tests/SwiftProtobufTests/Test_Type.swift index 5ad6ee306..fd35832a9 100644 --- a/Tests/SwiftProtobufTests/Test_Type.swift +++ b/Tests/SwiftProtobufTests/Test_Type.swift @@ -13,8 +13,8 @@ // ----------------------------------------------------------------------------- import Foundation -import XCTest import SwiftProtobuf +import XCTest // Since Type is purely compiled (there is no hand-coding // in it) this is a fairly thin test just to ensure that the proto @@ -24,8 +24,10 @@ final class Test_Type: XCTestCase, PBTestHelpers { typealias MessageTestType = Google_Protobuf_Type func testExists() { - assertEncode([18,13,8,1,16,3,24,1,34,3,102,111,111,64,1, - 18,9,8,8,24,2,34,3,98,97,114]) { (o: inout MessageTestType) in + assertEncode([ + 18, 13, 8, 1, 16, 3, 24, 1, 34, 3, 102, 111, 111, 64, 1, + 18, 9, 8, 8, 24, 2, 34, 3, 98, 97, 114, + ]) { (o: inout MessageTestType) in var field1 = Google_Protobuf_Field() field1.kind = .typeDouble field1.cardinality = .repeated diff --git a/Tests/SwiftProtobufTests/Test_Unknown_proto2.swift b/Tests/SwiftProtobufTests/Test_Unknown_proto2.swift index 8826d1d45..3eb7732f2 100644 --- a/Tests/SwiftProtobufTests/Test_Unknown_proto2.swift +++ b/Tests/SwiftProtobufTests/Test_Unknown_proto2.swift @@ -14,8 +14,8 @@ // ----------------------------------------------------------------------------- import Foundation -import XCTest import SwiftProtobuf +import XCTest // Verify that unknown fields are correctly preserved by // proto2 messages. @@ -56,23 +56,27 @@ final class Test_Unknown_proto2: XCTestCase, PBTestHelpers { } } func assertFails(_ protobufBytes: [UInt8], file: XCTestFileArgType = #file, line: UInt = #line) { - XCTAssertThrowsError(try SwiftProtoTesting_TestEmptyMessage(serializedBytes: protobufBytes), file: file, line: line) + XCTAssertThrowsError( + try SwiftProtoTesting_TestEmptyMessage(serializedBytes: protobufBytes), + file: file, + line: line + ) } // Well-formed input should decode/recode as-is; malformed input should fail to decode - assertFails([0]) // Invalid field number + assertFails([0]) // Invalid field number assertFails([0, 0]) - assertFails([1]) // Invalid field number - assertFails([2]) // Invalid field number - assertFails([3]) // Invalid field number - assertFails([4]) // Invalid field number - assertFails([5]) // Invalid field number - assertFails([6]) // Invalid field number - assertFails([7]) // Invalid field number - assertFails([8]) // Varint field #1 but no varint body + assertFails([1]) // Invalid field number + assertFails([2]) // Invalid field number + assertFails([3]) // Invalid field number + assertFails([4]) // Invalid field number + assertFails([5]) // Invalid field number + assertFails([6]) // Invalid field number + assertFails([7]) // Invalid field number + assertFails([8]) // Varint field #1 but no varint body assertRecodes([8, 0]) - assertFails([8, 128]) // Truncated varint + assertFails([8, 128]) // Truncated varint assertRecodes([9, 0, 0, 0, 0, 0, 0, 0, 0]) - assertFails([9, 0, 0, 0, 0, 0, 0, 0]) // Truncated 64-bit field + assertFails([9, 0, 0, 0, 0, 0, 0, 0]) // Truncated 64-bit field assertFails([9, 0, 0, 0, 0, 0, 0]) assertFails([9, 0, 0, 0, 0, 0]) assertFails([9, 0, 0, 0, 0]) @@ -80,14 +84,14 @@ final class Test_Unknown_proto2: XCTestCase, PBTestHelpers { assertFails([9, 0, 0]) assertFails([9, 0]) assertFails([9]) - assertFails([10]) // Length-delimited field but no length - assertRecodes([10, 0]) // Valid 0-length field - assertFails([10, 1]) // Length 1 but truncated - assertRecodes([10, 1, 2]) // Length 1 with 1 byte - assertFails([10, 2, 1]) // Length 2 truncated - assertFails([11]) // Start group #1 but no end group - assertRecodes([11, 12]) // Start/end group #1 - assertFails([12]) // Bare end group + assertFails([10]) // Length-delimited field but no length + assertRecodes([10, 0]) // Valid 0-length field + assertFails([10, 1]) // Length 1 but truncated + assertRecodes([10, 1, 2]) // Length 1 with 1 byte + assertFails([10, 2, 1]) // Length 2 truncated + assertFails([11]) // Start group #1 but no end group + assertRecodes([11, 12]) // Start/end group #1 + assertFails([12]) // Bare end group assertRecodes([13, 0, 0, 0, 0]) assertFails([13, 0, 0, 0]) assertFails([13, 0, 0]) @@ -95,9 +99,9 @@ final class Test_Unknown_proto2: XCTestCase, PBTestHelpers { assertFails([13]) assertFails([14]) assertFails([15]) - assertRecodes([248, 255, 255, 255, 15, 0]) // Maximum field number - assertFails([128, 128, 128, 128, 16, 0]) // Out-of-range field number - assertFails([248, 255, 255, 255, 127, 0]) // Out-of-range field number + assertRecodes([248, 255, 255, 255, 15, 0]) // Maximum field number + assertFails([128, 128, 128, 128, 16, 0]) // Out-of-range field number + assertFails([248, 255, 255, 255, 127, 0]) // Out-of-range field number } // JSON coding drops unknown fields for both proto2 and proto3 @@ -119,7 +123,7 @@ final class Test_Unknown_proto2: XCTestCase, PBTestHelpers { assertJSONIgnores("{\"unknown\": {}}") assertJSONIgnores("{\"unknown\": {\"foo\": 1}}") assertJSONIgnores("{\"unknown\": 7, \"also_unknown\": 8}") - assertJSONIgnores("{\"unknown\": 7, \"unknown\": 8}") // ??? + assertJSONIgnores("{\"unknown\": 7, \"unknown\": 8}") // ??? // Badly formed JSON should fail to decode, even in unknown sections var options = JSONDecodingOptions() @@ -158,7 +162,6 @@ final class Test_Unknown_proto2: XCTestCase, PBTestHelpers { assertJSONDecodeFails("{\"unknown\", \"a\": 1}", options: options) } - func assertUnknownFields(_ message: any Message, _ bytes: [UInt8], line: UInt = #line) { XCTAssertEqual(message.unknownFields.data, Data(bytes), line: line) } @@ -174,7 +177,7 @@ final class Test_Unknown_proto2: XCTestCase, PBTestHelpers { assertUnknownFields(msg2, [24, 1]) assertUnknownFields(msg1, [24, 1]) - try msg2.merge(serializedBytes: [34, 1, 52]) // Field 4, length delimited + try msg2.merge(serializedBytes: [34, 1, 52]) // Field 4, length delimited assertUnknownFields(msg2, [24, 1, 34, 1, 52]) assertUnknownFields(msg1, [24, 1]) @@ -194,7 +197,7 @@ final class Test_Unknown_proto2: XCTestCase, PBTestHelpers { assertUnknownFields(msg2, [24, 1]) assertUnknownFields(msg1, [24, 1]) - try msg2.merge(serializedBytes: [34, 1, 52]) // Field 4, length delimited + try msg2.merge(serializedBytes: [34, 1, 52]) // Field 4, length delimited assertUnknownFields(msg2, [24, 1, 34, 1, 52]) assertUnknownFields(msg1, [24, 1]) diff --git a/Tests/SwiftProtobufTests/Test_Unknown_proto3.swift b/Tests/SwiftProtobufTests/Test_Unknown_proto3.swift index 095e4c527..2b8f0aac2 100644 --- a/Tests/SwiftProtobufTests/Test_Unknown_proto3.swift +++ b/Tests/SwiftProtobufTests/Test_Unknown_proto3.swift @@ -14,8 +14,8 @@ // ----------------------------------------------------------------------------- import Foundation -import XCTest import SwiftProtobuf +import XCTest // Note: This uses the 'Proto3Arena' version of the empty message. // 'Arena' just indicates that this empty proto3 message @@ -61,20 +61,20 @@ final class Test_Unknown_proto3: XCTestCase, PBTestHelpers { XCTAssertThrowsError(try MessageTestType(serializedBytes: protobufBytes), file: file, line: line) } // Well-formed input should decode/recode as-is; malformed input should fail to decode - assertFails([0]) // Invalid field number + assertFails([0]) // Invalid field number assertFails([0, 0]) - assertFails([1]) // Invalid field number - assertFails([2]) // Invalid field number - assertFails([3]) // Invalid field number - assertFails([4]) // Invalid field number - assertFails([5]) // Invalid field number - assertFails([6]) // Invalid field number - assertFails([7]) // Invalid field number - assertFails([8]) // Varint field #1 but no varint body + assertFails([1]) // Invalid field number + assertFails([2]) // Invalid field number + assertFails([3]) // Invalid field number + assertFails([4]) // Invalid field number + assertFails([5]) // Invalid field number + assertFails([6]) // Invalid field number + assertFails([7]) // Invalid field number + assertFails([8]) // Varint field #1 but no varint body assertRecodes([8, 0]) - assertFails([8, 128]) // Truncated varint + assertFails([8, 128]) // Truncated varint assertRecodes([9, 0, 0, 0, 0, 0, 0, 0, 0]) - assertFails([9, 0, 0, 0, 0, 0, 0, 0]) // Truncated 64-bit field + assertFails([9, 0, 0, 0, 0, 0, 0, 0]) // Truncated 64-bit field assertFails([9, 0, 0, 0, 0, 0, 0]) assertFails([9, 0, 0, 0, 0, 0]) assertFails([9, 0, 0, 0, 0]) @@ -82,14 +82,14 @@ final class Test_Unknown_proto3: XCTestCase, PBTestHelpers { assertFails([9, 0, 0]) assertFails([9, 0]) assertFails([9]) - assertFails([10]) // Length-delimited field but no length - assertRecodes([10, 0]) // Valid 0-length field - assertFails([10, 1]) // Length 1 but truncated - assertRecodes([10, 1, 2]) // Length 1 with 1 byte - assertFails([10, 2, 1]) // Length 2 truncated - assertFails([11]) // Start group #1 but no end group - assertRecodes([11, 12]) // Start/end group #1 - assertFails([12]) // Bare end group + assertFails([10]) // Length-delimited field but no length + assertRecodes([10, 0]) // Valid 0-length field + assertFails([10, 1]) // Length 1 but truncated + assertRecodes([10, 1, 2]) // Length 1 with 1 byte + assertFails([10, 2, 1]) // Length 2 truncated + assertFails([11]) // Start group #1 but no end group + assertRecodes([11, 12]) // Start/end group #1 + assertFails([12]) // Bare end group assertRecodes([13, 0, 0, 0, 0]) assertFails([13, 0, 0, 0]) assertFails([13, 0, 0]) @@ -97,9 +97,9 @@ final class Test_Unknown_proto3: XCTestCase, PBTestHelpers { assertFails([13]) assertFails([14]) assertFails([15]) - assertRecodes([248, 255, 255, 255, 15, 0]) // Maximum field number - assertFails([128, 128, 128, 128, 16, 0]) // Out-of-range field number - assertFails([248, 255, 255, 255, 127, 0]) // Out-of-range field number + assertRecodes([248, 255, 255, 255, 15, 0]) // Maximum field number + assertFails([128, 128, 128, 128, 16, 0]) // Out-of-range field number + assertFails([248, 255, 255, 255, 127, 0]) // Out-of-range field number } // JSON coding drops unknown fields for both proto2 and proto3 @@ -121,7 +121,7 @@ final class Test_Unknown_proto3: XCTestCase, PBTestHelpers { assertJSONIgnores("{\"unknown\": {}}") assertJSONIgnores("{\"unknown\": {\"foo\": 1}}") assertJSONIgnores("{\"unknown\": 7, \"also_unknown\": 8}") - assertJSONIgnores("{\"unknown\": 7, \"unknown\": 8}") // ??? + assertJSONIgnores("{\"unknown\": 7, \"unknown\": 8}") // ??? // Badly formed JSON should fail to decode, even in unknown sections var options = JSONDecodingOptions() @@ -160,7 +160,6 @@ final class Test_Unknown_proto3: XCTestCase, PBTestHelpers { assertJSONDecodeFails("{\"unknown\", \"a\": 1}", options: options) } - func assertUnknownFields(_ message: any Message, _ bytes: [UInt8], line: UInt = #line) { XCTAssertEqual(message.unknownFields.data, Data(bytes), line: line) } @@ -176,7 +175,7 @@ final class Test_Unknown_proto3: XCTestCase, PBTestHelpers { assertUnknownFields(msg2, [24, 1]) assertUnknownFields(msg1, [24, 1]) - try msg2.merge(serializedBytes: [34, 1, 52]) // Field 4, length delimited + try msg2.merge(serializedBytes: [34, 1, 52]) // Field 4, length delimited assertUnknownFields(msg2, [24, 1, 34, 1, 52]) assertUnknownFields(msg1, [24, 1]) @@ -196,7 +195,7 @@ final class Test_Unknown_proto3: XCTestCase, PBTestHelpers { assertUnknownFields(msg2, [24, 1]) assertUnknownFields(msg1, [24, 1]) - try msg2.merge(serializedBytes: [34, 1, 52]) // Field 4, length delimited + try msg2.merge(serializedBytes: [34, 1, 52]) // Field 4, length delimited assertUnknownFields(msg2, [24, 1, 34, 1, 52]) assertUnknownFields(msg1, [24, 1]) diff --git a/Tests/SwiftProtobufTests/Test_Wrappers.swift b/Tests/SwiftProtobufTests/Test_Wrappers.swift index 8ff6156e7..0ce02293f 100644 --- a/Tests/SwiftProtobufTests/Test_Wrappers.swift +++ b/Tests/SwiftProtobufTests/Test_Wrappers.swift @@ -14,8 +14,8 @@ // ----------------------------------------------------------------------------- import Foundation -import XCTest import SwiftProtobuf +import XCTest final class Test_Wrappers: XCTestCase { @@ -37,10 +37,11 @@ final class Test_Wrappers: XCTestCase { XCTAssertEqual("0.0", try m.jsonString()) m.value = 1.0 XCTAssertEqual("1.0", try m.jsonString()) - XCTAssertEqual([9,0,0,0,0,0,0,240,63], try m.serializedBytes()) + XCTAssertEqual([9, 0, 0, 0, 0, 0, 0, 240, 63], try m.serializedBytes()) let mw = try SwiftProtoTesting_Test3_TestAllTypesProto3( - jsonString: "{\"optionalDoubleWrapper\":null}") + jsonString: "{\"optionalDoubleWrapper\":null}" + ) XCTAssertFalse(mw.hasOptionalDoubleWrapper) assertJSONDecodeNullFails(for: Google_Protobuf_DoubleValue.self) @@ -57,13 +58,13 @@ final class Test_Wrappers: XCTestCase { XCTAssertEqual(m3.value, 1.0) // Use object equality to verify decode - XCTAssertEqual(m, try Google_Protobuf_DoubleValue(jsonString:"1.0")) - XCTAssertEqual(m2, try Google_Protobuf_DoubleValue(jsonString:"2")) - XCTAssertEqual(m, try Google_Protobuf_DoubleValue(serializedBytes: [9,0,0,0,0,0,0,240,63])) + XCTAssertEqual(m, try Google_Protobuf_DoubleValue(jsonString: "1.0")) + XCTAssertEqual(m2, try Google_Protobuf_DoubleValue(jsonString: "2")) + XCTAssertEqual(m, try Google_Protobuf_DoubleValue(serializedBytes: [9, 0, 0, 0, 0, 0, 0, 240, 63])) // hash - XCTAssertEqual(m.hashValue, try Google_Protobuf_DoubleValue(jsonString:"1.0").hashValue) - XCTAssertNotEqual(m.hashValue, try Google_Protobuf_DoubleValue(jsonString:"1.1").hashValue) + XCTAssertEqual(m.hashValue, try Google_Protobuf_DoubleValue(jsonString: "1.0").hashValue) + XCTAssertNotEqual(m.hashValue, try Google_Protobuf_DoubleValue(jsonString: "1.1").hashValue) // TODO: Google documents that nulls are preserved; what does this mean? // TODO: Is Google_Protobuf_DoubleValue allowed to quote large numbers when serializing? @@ -75,10 +76,11 @@ final class Test_Wrappers: XCTestCase { XCTAssertEqual("0.0", try m.jsonString()) m.value = 1.0 XCTAssertEqual("1.0", try m.jsonString()) - XCTAssertEqual([13,0,0,128,63], try m.serializedBytes()) + XCTAssertEqual([13, 0, 0, 128, 63], try m.serializedBytes()) let mw = try SwiftProtoTesting_Test3_TestAllTypesProto3( - jsonString: "{\"optionalFloatWrapper\":null}") + jsonString: "{\"optionalFloatWrapper\":null}" + ) XCTAssertFalse(mw.hasOptionalFloatWrapper) assertJSONDecodeNullFails(for: Google_Protobuf_FloatValue.self) @@ -93,19 +95,19 @@ final class Test_Wrappers: XCTestCase { XCTAssertEqual(m3.value, 3.0) // Use object equality to verify decode - XCTAssertEqual(m, try Google_Protobuf_FloatValue(jsonString:"1.0")) - XCTAssertEqual(m2, try Google_Protobuf_FloatValue(jsonString:"2")) - XCTAssertEqual(m, try Google_Protobuf_FloatValue(serializedBytes: [13,0,0,128,63])) + XCTAssertEqual(m, try Google_Protobuf_FloatValue(jsonString: "1.0")) + XCTAssertEqual(m2, try Google_Protobuf_FloatValue(jsonString: "2")) + XCTAssertEqual(m, try Google_Protobuf_FloatValue(serializedBytes: [13, 0, 0, 128, 63])) - XCTAssertThrowsError(try Google_Protobuf_FloatValue(jsonString:"-3.502823e+38")) - XCTAssertThrowsError(try Google_Protobuf_FloatValue(jsonString:"3.502823e+38")) + XCTAssertThrowsError(try Google_Protobuf_FloatValue(jsonString: "-3.502823e+38")) + XCTAssertThrowsError(try Google_Protobuf_FloatValue(jsonString: "3.502823e+38")) - XCTAssertEqual(try Google_Protobuf_FloatValue(jsonString:"-3.402823e+38"), -3.402823e+38) - XCTAssertEqual(try Google_Protobuf_FloatValue(jsonString:"3.402823e+38"), 3.402823e+38) + XCTAssertEqual(try Google_Protobuf_FloatValue(jsonString: "-3.402823e+38"), -3.402823e+38) + XCTAssertEqual(try Google_Protobuf_FloatValue(jsonString: "3.402823e+38"), 3.402823e+38) // hash - XCTAssertEqual(m.hashValue, try Google_Protobuf_FloatValue(jsonString:"1.0").hashValue) - XCTAssertNotEqual(m.hashValue, try Google_Protobuf_FloatValue(jsonString:"1.1").hashValue) + XCTAssertEqual(m.hashValue, try Google_Protobuf_FloatValue(jsonString: "1.0").hashValue) + XCTAssertNotEqual(m.hashValue, try Google_Protobuf_FloatValue(jsonString: "1.1").hashValue) // TODO: Google documents that nulls are preserved; what does this mean? // TODO: Is Google_Protobuf_FloatValue allowed to quote large numbers when serializing? @@ -118,18 +120,19 @@ final class Test_Wrappers: XCTestCase { m.value = 777 let j2 = try m.jsonString() XCTAssertEqual("\"777\"", j2) - XCTAssertEqual([8,137,6], try m.serializedBytes()) + XCTAssertEqual([8, 137, 6], try m.serializedBytes()) // TODO: More let mw = try SwiftProtoTesting_Test3_TestAllTypesProto3( - jsonString: "{\"optionalInt64Wrapper\":null}") + jsonString: "{\"optionalInt64Wrapper\":null}" + ) XCTAssertFalse(mw.hasOptionalInt64Wrapper) assertJSONDecodeNullFails(for: Google_Protobuf_Int64Value.self) // hash - XCTAssertEqual(m.hashValue, try Google_Protobuf_Int64Value(jsonString:"777").hashValue) - XCTAssertNotEqual(m.hashValue, try Google_Protobuf_Int64Value(jsonString:"778").hashValue) + XCTAssertEqual(m.hashValue, try Google_Protobuf_Int64Value(jsonString: "777").hashValue) + XCTAssertNotEqual(m.hashValue, try Google_Protobuf_Int64Value(jsonString: "778").hashValue) } func testUInt64Value() throws { @@ -137,18 +140,19 @@ final class Test_Wrappers: XCTestCase { XCTAssertEqual("\"0\"", try m.jsonString()) m.value = 777 XCTAssertEqual("\"777\"", try m.jsonString()) - XCTAssertEqual([8,137,6], try m.serializedBytes()) + XCTAssertEqual([8, 137, 6], try m.serializedBytes()) // TODO: More let mw = try SwiftProtoTesting_Test3_TestAllTypesProto3( - jsonString: "{\"optionalUint64Wrapper\":null}") + jsonString: "{\"optionalUint64Wrapper\":null}" + ) XCTAssertFalse(mw.hasOptionalUint64Wrapper) assertJSONDecodeNullFails(for: Google_Protobuf_UInt64Value.self) // hash - XCTAssertEqual(m.hashValue, try Google_Protobuf_UInt64Value(jsonString:"777").hashValue) - XCTAssertNotEqual(m.hashValue, try Google_Protobuf_UInt64Value(jsonString:"778").hashValue) + XCTAssertEqual(m.hashValue, try Google_Protobuf_UInt64Value(jsonString: "777").hashValue) + XCTAssertNotEqual(m.hashValue, try Google_Protobuf_UInt64Value(jsonString: "778").hashValue) } func testInt32Value() throws { @@ -156,18 +160,19 @@ final class Test_Wrappers: XCTestCase { XCTAssertEqual("0", try m.jsonString()) m.value = 777 XCTAssertEqual("777", try m.jsonString()) - XCTAssertEqual([8,137,6], try m.serializedBytes()) + XCTAssertEqual([8, 137, 6], try m.serializedBytes()) // TODO: More let mw = try SwiftProtoTesting_Test3_TestAllTypesProto3( - jsonString: "{\"optionalInt32Wrapper\":null}") + jsonString: "{\"optionalInt32Wrapper\":null}" + ) XCTAssertFalse(mw.hasOptionalInt32Wrapper) assertJSONDecodeNullFails(for: Google_Protobuf_Int32Value.self) // hash - XCTAssertEqual(m.hashValue, try Google_Protobuf_Int32Value(jsonString:"777").hashValue) - XCTAssertNotEqual(m.hashValue, try Google_Protobuf_Int32Value(jsonString:"778").hashValue) + XCTAssertEqual(m.hashValue, try Google_Protobuf_Int32Value(jsonString: "777").hashValue) + XCTAssertNotEqual(m.hashValue, try Google_Protobuf_Int32Value(jsonString: "778").hashValue) } func testUInt32Value() throws { @@ -175,18 +180,19 @@ final class Test_Wrappers: XCTestCase { XCTAssertEqual("0", try m.jsonString()) m.value = 777 XCTAssertEqual("777", try m.jsonString()) - XCTAssertEqual([8,137,6], try m.serializedBytes()) + XCTAssertEqual([8, 137, 6], try m.serializedBytes()) // TODO: More let mw = try SwiftProtoTesting_Test3_TestAllTypesProto3( - jsonString: "{\"optionalUint32Wrapper\":null}") + jsonString: "{\"optionalUint32Wrapper\":null}" + ) XCTAssertFalse(mw.hasOptionalUint32Wrapper) assertJSONDecodeNullFails(for: Google_Protobuf_UInt32Value.self) // hash - XCTAssertEqual(m.hashValue, try Google_Protobuf_UInt32Value(jsonString:"777").hashValue) - XCTAssertNotEqual(m.hashValue, try Google_Protobuf_UInt32Value(jsonString:"778").hashValue) + XCTAssertEqual(m.hashValue, try Google_Protobuf_UInt32Value(jsonString: "777").hashValue) + XCTAssertNotEqual(m.hashValue, try Google_Protobuf_UInt32Value(jsonString: "778").hashValue) } func testBoolValue() throws { @@ -194,18 +200,19 @@ final class Test_Wrappers: XCTestCase { XCTAssertEqual("false", try m.jsonString()) m.value = true XCTAssertEqual("true", try m.jsonString()) - XCTAssertEqual([8,1], try m.serializedBytes()) + XCTAssertEqual([8, 1], try m.serializedBytes()) // TODO: More let mw = try SwiftProtoTesting_Test3_TestAllTypesProto3( - jsonString: "{\"optionalBoolWrapper\":null}") + jsonString: "{\"optionalBoolWrapper\":null}" + ) XCTAssertFalse(mw.hasOptionalBoolWrapper) assertJSONDecodeNullFails(for: Google_Protobuf_BoolValue.self) // hash - XCTAssertEqual(m.hashValue, try Google_Protobuf_BoolValue(jsonString:"true").hashValue) - XCTAssertNotEqual(m.hashValue, try Google_Protobuf_BoolValue(jsonString:"false").hashValue) + XCTAssertEqual(m.hashValue, try Google_Protobuf_BoolValue(jsonString: "true").hashValue) + XCTAssertNotEqual(m.hashValue, try Google_Protobuf_BoolValue(jsonString: "false").hashValue) } func testStringValue() throws { @@ -213,22 +220,29 @@ final class Test_Wrappers: XCTestCase { XCTAssertEqual("\"\"", try m.jsonString()) m.value = "abc" XCTAssertEqual("\"abc\"", try m.jsonString()) - XCTAssertEqual([10,3,97,98,99], try m.serializedBytes()) + XCTAssertEqual([10, 3, 97, 98, 99], try m.serializedBytes()) // TODO: More let mw = try SwiftProtoTesting_Test3_TestAllTypesProto3( - jsonString: "{\"optionalStringWrapper\":null}") + jsonString: "{\"optionalStringWrapper\":null}" + ) XCTAssertFalse(mw.hasOptionalStringWrapper) assertJSONDecodeNullFails(for: Google_Protobuf_StringValue.self) XCTAssertThrowsError(try Google_Protobuf_StringValue(jsonString: "\"\\UABCD\"")) - XCTAssertEqual(try Google_Protobuf_StringValue(jsonString: "\"\\uABCD\""), Google_Protobuf_StringValue("\u{ABCD}")) - XCTAssertEqual(try Google_Protobuf_StringValue(jsonString: "\"\\\"\\\\\\/\\b\\f\\n\\r\\t\""), Google_Protobuf_StringValue("\"\\/\u{08}\u{0c}\n\r\t")) + XCTAssertEqual( + try Google_Protobuf_StringValue(jsonString: "\"\\uABCD\""), + Google_Protobuf_StringValue("\u{ABCD}") + ) + XCTAssertEqual( + try Google_Protobuf_StringValue(jsonString: "\"\\\"\\\\\\/\\b\\f\\n\\r\\t\""), + Google_Protobuf_StringValue("\"\\/\u{08}\u{0c}\n\r\t") + ) // hash - XCTAssertEqual(m.hashValue, try Google_Protobuf_StringValue(jsonString:"\"abc\"").hashValue) - XCTAssertNotEqual(m.hashValue, try Google_Protobuf_StringValue(jsonString:"\"def\"").hashValue) + XCTAssertEqual(m.hashValue, try Google_Protobuf_StringValue(jsonString: "\"abc\"").hashValue) + XCTAssertNotEqual(m.hashValue, try Google_Protobuf_StringValue(jsonString: "\"def\"").hashValue) } func testBytesValue() throws { @@ -236,17 +250,18 @@ final class Test_Wrappers: XCTestCase { XCTAssertEqual("\"\"", try m.jsonString()) m.value = Data([0, 1, 2]) XCTAssertEqual("\"AAEC\"", try m.jsonString()) - XCTAssertEqual([10,3,0,1,2], try m.serializedBytes()) + XCTAssertEqual([10, 3, 0, 1, 2], try m.serializedBytes()) // TODO: More let mw = try SwiftProtoTesting_Test3_TestAllTypesProto3( - jsonString: "{\"optionalBytesWrapper\":null}") + jsonString: "{\"optionalBytesWrapper\":null}" + ) XCTAssertFalse(mw.hasOptionalBytesWrapper) assertJSONDecodeNullFails(for: Google_Protobuf_BytesValue.self) // hash - XCTAssertEqual(m.hashValue, try Google_Protobuf_BytesValue(jsonString:"\"AAEC\"").hashValue) - XCTAssertNotEqual(m.hashValue, try Google_Protobuf_BytesValue(jsonString:"\"AAED\"").hashValue) + XCTAssertEqual(m.hashValue, try Google_Protobuf_BytesValue(jsonString: "\"AAEC\"").hashValue) + XCTAssertNotEqual(m.hashValue, try Google_Protobuf_BytesValue(jsonString: "\"AAED\"").hashValue) } } diff --git a/Tests/protoc-gen-swiftTests/Test_DescriptorExtensions.swift b/Tests/protoc-gen-swiftTests/Test_DescriptorExtensions.swift index 90898ec13..18e28ca32 100644 --- a/Tests/protoc-gen-swiftTests/Test_DescriptorExtensions.swift +++ b/Tests/protoc-gen-swiftTests/Test_DescriptorExtensions.swift @@ -8,494 +8,511 @@ // // ----------------------------------------------------------------------------- -import XCTest import SwiftProtobuf import SwiftProtobufPluginLibrary +import XCTest + @testable import protoc_gen_swift -fileprivate typealias FileDescriptorProto = Google_Protobuf_FileDescriptorProto +private typealias FileDescriptorProto = Google_Protobuf_FileDescriptorProto final class Test_DescriptorExtensions: XCTestCase { - func testExtensionRanges() throws { - let fileSet = try Google_Protobuf_FileDescriptorSet(serializedBytes: fileDescriptorSetBytes) - - let descriptorSet = DescriptorSet(proto: fileSet) - - let msgDescriptor = descriptorSet.descriptor(named: "swift_descriptor_test.MsgExtensionRangeOrdering")! - // Quick check of what should be in the proto file - XCTAssertEqual(msgDescriptor.messageExtensionRanges.count, 9) - XCTAssertEqual(msgDescriptor.messageExtensionRanges[0].start, 1) - XCTAssertEqual(msgDescriptor.messageExtensionRanges[1].start, 3) - XCTAssertEqual(msgDescriptor.messageExtensionRanges[2].start, 2) - XCTAssertEqual(msgDescriptor.messageExtensionRanges[3].start, 4) - XCTAssertEqual(msgDescriptor.messageExtensionRanges[4].start, 7) - XCTAssertEqual(msgDescriptor.messageExtensionRanges[5].start, 9) - XCTAssertEqual(msgDescriptor.messageExtensionRanges[6].start, 100) - XCTAssertEqual(msgDescriptor.messageExtensionRanges[7].start, 126) - XCTAssertEqual(msgDescriptor.messageExtensionRanges[8].start, 111) - - // Check sorting/merging - XCTAssertEqual(msgDescriptor._normalizedExtensionRanges.count, 5) - XCTAssertEqual(msgDescriptor._normalizedExtensionRanges[0].lowerBound, 1) - XCTAssertEqual(msgDescriptor._normalizedExtensionRanges[0].upperBound, 5) - XCTAssertEqual(msgDescriptor._normalizedExtensionRanges[1].lowerBound, 7) - XCTAssertEqual(msgDescriptor._normalizedExtensionRanges[1].upperBound, 8) - XCTAssertEqual(msgDescriptor._normalizedExtensionRanges[2].lowerBound, 9) - XCTAssertEqual(msgDescriptor._normalizedExtensionRanges[2].upperBound, 10) - XCTAssertEqual(msgDescriptor._normalizedExtensionRanges[3].lowerBound, 100) - XCTAssertEqual(msgDescriptor._normalizedExtensionRanges[3].upperBound, 121) - XCTAssertEqual(msgDescriptor._normalizedExtensionRanges[4].lowerBound, 126) - XCTAssertEqual(msgDescriptor._normalizedExtensionRanges[4].upperBound, 131) - - - // Check the "ambitious" merging. - XCTAssertEqual(msgDescriptor._ambitiousExtensionRanges.count, 1) - XCTAssertEqual(msgDescriptor._ambitiousExtensionRanges[0].lowerBound, 1) - XCTAssertEqual(msgDescriptor._ambitiousExtensionRanges[0].upperBound, 131) - - let msgDescriptor2 = descriptorSet.descriptor(named: "swift_descriptor_test.MsgExtensionRangeOrderingWithFields")! - // Quick check of what should be in the proto file - XCTAssertEqual(msgDescriptor2.messageExtensionRanges.count, 9) - XCTAssertEqual(msgDescriptor2.messageExtensionRanges[0].start, 1) - XCTAssertEqual(msgDescriptor2.messageExtensionRanges[1].start, 3) - XCTAssertEqual(msgDescriptor2.messageExtensionRanges[2].start, 2) - XCTAssertEqual(msgDescriptor2.messageExtensionRanges[3].start, 4) - XCTAssertEqual(msgDescriptor2.messageExtensionRanges[4].start, 7) - XCTAssertEqual(msgDescriptor2.messageExtensionRanges[5].start, 9) - XCTAssertEqual(msgDescriptor2.messageExtensionRanges[6].start, 100) - XCTAssertEqual(msgDescriptor2.messageExtensionRanges[7].start, 126) - XCTAssertEqual(msgDescriptor2.messageExtensionRanges[8].start, 111) - - // Check sorting/merging - XCTAssertEqual(msgDescriptor2._normalizedExtensionRanges.count, 5) - XCTAssertEqual(msgDescriptor2._normalizedExtensionRanges[0].lowerBound, 1) - XCTAssertEqual(msgDescriptor2._normalizedExtensionRanges[0].upperBound, 5) - XCTAssertEqual(msgDescriptor2._normalizedExtensionRanges[1].lowerBound, 7) - XCTAssertEqual(msgDescriptor2._normalizedExtensionRanges[1].upperBound, 8) - XCTAssertEqual(msgDescriptor2._normalizedExtensionRanges[2].lowerBound, 9) - XCTAssertEqual(msgDescriptor2._normalizedExtensionRanges[2].upperBound, 10) - XCTAssertEqual(msgDescriptor2._normalizedExtensionRanges[3].lowerBound, 100) - XCTAssertEqual(msgDescriptor2._normalizedExtensionRanges[3].upperBound, 121) - XCTAssertEqual(msgDescriptor2._normalizedExtensionRanges[4].lowerBound, 126) - XCTAssertEqual(msgDescriptor2._normalizedExtensionRanges[4].upperBound, 131) - - - // Check the "ambitious" merging. - XCTAssertEqual(msgDescriptor2._ambitiousExtensionRanges.count, 3) - XCTAssertEqual(msgDescriptor2._ambitiousExtensionRanges[0].lowerBound, 1) - XCTAssertEqual(msgDescriptor2._ambitiousExtensionRanges[0].upperBound, 5) - XCTAssertEqual(msgDescriptor2._ambitiousExtensionRanges[1].lowerBound, 7) - XCTAssertEqual(msgDescriptor2._ambitiousExtensionRanges[1].upperBound, 121) - XCTAssertEqual(msgDescriptor2._ambitiousExtensionRanges[2].lowerBound, 126) - XCTAssertEqual(msgDescriptor2._ambitiousExtensionRanges[2].upperBound, 131) - - let msgDescriptor3 = descriptorSet.descriptor(named: "swift_descriptor_test.MsgExtensionRangeOrderingNoMerging")! - // Quick check of what should be in the proto file - XCTAssertEqual(msgDescriptor3.messageExtensionRanges.count, 3) - XCTAssertEqual(msgDescriptor3.messageExtensionRanges[0].start, 3) - XCTAssertEqual(msgDescriptor3.messageExtensionRanges[1].start, 7) - XCTAssertEqual(msgDescriptor3.messageExtensionRanges[2].start, 16) - - // Check sorting/merging - XCTAssertEqual(msgDescriptor3._normalizedExtensionRanges.count, 3) - XCTAssertEqual(msgDescriptor3._normalizedExtensionRanges[0].lowerBound, 3) - XCTAssertEqual(msgDescriptor3._normalizedExtensionRanges[0].upperBound, 6) - XCTAssertEqual(msgDescriptor3._normalizedExtensionRanges[1].lowerBound, 7) - XCTAssertEqual(msgDescriptor3._normalizedExtensionRanges[1].upperBound, 13) - XCTAssertEqual(msgDescriptor3._normalizedExtensionRanges[2].lowerBound, 16) - XCTAssertEqual(msgDescriptor3._normalizedExtensionRanges[2].upperBound, 21) - - // Check the "ambitious" merging. - XCTAssertEqual(msgDescriptor3._ambitiousExtensionRanges.count, 3) - XCTAssertEqual(msgDescriptor3._ambitiousExtensionRanges[0].lowerBound, 3) - XCTAssertEqual(msgDescriptor3._ambitiousExtensionRanges[0].upperBound, 6) - XCTAssertEqual(msgDescriptor3._ambitiousExtensionRanges[1].lowerBound, 7) - XCTAssertEqual(msgDescriptor3._ambitiousExtensionRanges[1].upperBound, 13) - XCTAssertEqual(msgDescriptor3._ambitiousExtensionRanges[2].lowerBound, 16) - XCTAssertEqual(msgDescriptor3._ambitiousExtensionRanges[2].upperBound, 21) - } - - func testEnumValueAliasing() throws { - let txt = [ - "name: \"test.proto\"", - "syntax: \"proto2\"", - "enum_type {", - " name: \"TestEnum\"", - " options {", - " allow_alias: true", - " }", - " value {", - " name: \"TEST_ENUM_FOO\"", - " number: 0", // Primary - " }", - " value {", - " name: \"TEST_ENUM_BAR\"", - " number: 1", - " }", - " value {", - " name: \"TESTENUM_FOO\"", - " number: 0", // Alias - " }", - " value {", - " name: \"_FOO\"", - " number: 0", // Alias - " }", - " value {", - " name: \"FOO\"", - " number: 0", // Alias - " }", - " value {", - " name: \"TEST_ENUM_ALIAS\"", - " number: 0", // Alias (unique name) - " }", - "}" - ] - - let fileProto: Google_Protobuf_FileDescriptorProto - do { - fileProto = try Google_Protobuf_FileDescriptorProto(textFormatStrings: txt) - } catch let e { - XCTFail("Error: \(e)") - return + func testExtensionRanges() throws { + let fileSet = try Google_Protobuf_FileDescriptorSet(serializedBytes: fileDescriptorSetBytes) + + let descriptorSet = DescriptorSet(proto: fileSet) + + let msgDescriptor = descriptorSet.descriptor(named: "swift_descriptor_test.MsgExtensionRangeOrdering")! + // Quick check of what should be in the proto file + XCTAssertEqual(msgDescriptor.messageExtensionRanges.count, 9) + XCTAssertEqual(msgDescriptor.messageExtensionRanges[0].start, 1) + XCTAssertEqual(msgDescriptor.messageExtensionRanges[1].start, 3) + XCTAssertEqual(msgDescriptor.messageExtensionRanges[2].start, 2) + XCTAssertEqual(msgDescriptor.messageExtensionRanges[3].start, 4) + XCTAssertEqual(msgDescriptor.messageExtensionRanges[4].start, 7) + XCTAssertEqual(msgDescriptor.messageExtensionRanges[5].start, 9) + XCTAssertEqual(msgDescriptor.messageExtensionRanges[6].start, 100) + XCTAssertEqual(msgDescriptor.messageExtensionRanges[7].start, 126) + XCTAssertEqual(msgDescriptor.messageExtensionRanges[8].start, 111) + + // Check sorting/merging + XCTAssertEqual(msgDescriptor._normalizedExtensionRanges.count, 5) + XCTAssertEqual(msgDescriptor._normalizedExtensionRanges[0].lowerBound, 1) + XCTAssertEqual(msgDescriptor._normalizedExtensionRanges[0].upperBound, 5) + XCTAssertEqual(msgDescriptor._normalizedExtensionRanges[1].lowerBound, 7) + XCTAssertEqual(msgDescriptor._normalizedExtensionRanges[1].upperBound, 8) + XCTAssertEqual(msgDescriptor._normalizedExtensionRanges[2].lowerBound, 9) + XCTAssertEqual(msgDescriptor._normalizedExtensionRanges[2].upperBound, 10) + XCTAssertEqual(msgDescriptor._normalizedExtensionRanges[3].lowerBound, 100) + XCTAssertEqual(msgDescriptor._normalizedExtensionRanges[3].upperBound, 121) + XCTAssertEqual(msgDescriptor._normalizedExtensionRanges[4].lowerBound, 126) + XCTAssertEqual(msgDescriptor._normalizedExtensionRanges[4].upperBound, 131) + + // Check the "ambitious" merging. + XCTAssertEqual(msgDescriptor._ambitiousExtensionRanges.count, 1) + XCTAssertEqual(msgDescriptor._ambitiousExtensionRanges[0].lowerBound, 1) + XCTAssertEqual(msgDescriptor._ambitiousExtensionRanges[0].upperBound, 131) + + let msgDescriptor2 = descriptorSet.descriptor( + named: "swift_descriptor_test.MsgExtensionRangeOrderingWithFields" + )! + // Quick check of what should be in the proto file + XCTAssertEqual(msgDescriptor2.messageExtensionRanges.count, 9) + XCTAssertEqual(msgDescriptor2.messageExtensionRanges[0].start, 1) + XCTAssertEqual(msgDescriptor2.messageExtensionRanges[1].start, 3) + XCTAssertEqual(msgDescriptor2.messageExtensionRanges[2].start, 2) + XCTAssertEqual(msgDescriptor2.messageExtensionRanges[3].start, 4) + XCTAssertEqual(msgDescriptor2.messageExtensionRanges[4].start, 7) + XCTAssertEqual(msgDescriptor2.messageExtensionRanges[5].start, 9) + XCTAssertEqual(msgDescriptor2.messageExtensionRanges[6].start, 100) + XCTAssertEqual(msgDescriptor2.messageExtensionRanges[7].start, 126) + XCTAssertEqual(msgDescriptor2.messageExtensionRanges[8].start, 111) + + // Check sorting/merging + XCTAssertEqual(msgDescriptor2._normalizedExtensionRanges.count, 5) + XCTAssertEqual(msgDescriptor2._normalizedExtensionRanges[0].lowerBound, 1) + XCTAssertEqual(msgDescriptor2._normalizedExtensionRanges[0].upperBound, 5) + XCTAssertEqual(msgDescriptor2._normalizedExtensionRanges[1].lowerBound, 7) + XCTAssertEqual(msgDescriptor2._normalizedExtensionRanges[1].upperBound, 8) + XCTAssertEqual(msgDescriptor2._normalizedExtensionRanges[2].lowerBound, 9) + XCTAssertEqual(msgDescriptor2._normalizedExtensionRanges[2].upperBound, 10) + XCTAssertEqual(msgDescriptor2._normalizedExtensionRanges[3].lowerBound, 100) + XCTAssertEqual(msgDescriptor2._normalizedExtensionRanges[3].upperBound, 121) + XCTAssertEqual(msgDescriptor2._normalizedExtensionRanges[4].lowerBound, 126) + XCTAssertEqual(msgDescriptor2._normalizedExtensionRanges[4].upperBound, 131) + + // Check the "ambitious" merging. + XCTAssertEqual(msgDescriptor2._ambitiousExtensionRanges.count, 3) + XCTAssertEqual(msgDescriptor2._ambitiousExtensionRanges[0].lowerBound, 1) + XCTAssertEqual(msgDescriptor2._ambitiousExtensionRanges[0].upperBound, 5) + XCTAssertEqual(msgDescriptor2._ambitiousExtensionRanges[1].lowerBound, 7) + XCTAssertEqual(msgDescriptor2._ambitiousExtensionRanges[1].upperBound, 121) + XCTAssertEqual(msgDescriptor2._ambitiousExtensionRanges[2].lowerBound, 126) + XCTAssertEqual(msgDescriptor2._ambitiousExtensionRanges[2].upperBound, 131) + + let msgDescriptor3 = descriptorSet.descriptor( + named: "swift_descriptor_test.MsgExtensionRangeOrderingNoMerging" + )! + // Quick check of what should be in the proto file + XCTAssertEqual(msgDescriptor3.messageExtensionRanges.count, 3) + XCTAssertEqual(msgDescriptor3.messageExtensionRanges[0].start, 3) + XCTAssertEqual(msgDescriptor3.messageExtensionRanges[1].start, 7) + XCTAssertEqual(msgDescriptor3.messageExtensionRanges[2].start, 16) + + // Check sorting/merging + XCTAssertEqual(msgDescriptor3._normalizedExtensionRanges.count, 3) + XCTAssertEqual(msgDescriptor3._normalizedExtensionRanges[0].lowerBound, 3) + XCTAssertEqual(msgDescriptor3._normalizedExtensionRanges[0].upperBound, 6) + XCTAssertEqual(msgDescriptor3._normalizedExtensionRanges[1].lowerBound, 7) + XCTAssertEqual(msgDescriptor3._normalizedExtensionRanges[1].upperBound, 13) + XCTAssertEqual(msgDescriptor3._normalizedExtensionRanges[2].lowerBound, 16) + XCTAssertEqual(msgDescriptor3._normalizedExtensionRanges[2].upperBound, 21) + + // Check the "ambitious" merging. + XCTAssertEqual(msgDescriptor3._ambitiousExtensionRanges.count, 3) + XCTAssertEqual(msgDescriptor3._ambitiousExtensionRanges[0].lowerBound, 3) + XCTAssertEqual(msgDescriptor3._ambitiousExtensionRanges[0].upperBound, 6) + XCTAssertEqual(msgDescriptor3._ambitiousExtensionRanges[1].lowerBound, 7) + XCTAssertEqual(msgDescriptor3._ambitiousExtensionRanges[1].upperBound, 13) + XCTAssertEqual(msgDescriptor3._ambitiousExtensionRanges[2].lowerBound, 16) + XCTAssertEqual(msgDescriptor3._ambitiousExtensionRanges[2].upperBound, 21) + } + + func testEnumValueAliasing() throws { + let txt = [ + "name: \"test.proto\"", + "syntax: \"proto2\"", + "enum_type {", + " name: \"TestEnum\"", + " options {", + " allow_alias: true", + " }", + " value {", + " name: \"TEST_ENUM_FOO\"", + " number: 0", // Primary + " }", + " value {", + " name: \"TEST_ENUM_BAR\"", + " number: 1", + " }", + " value {", + " name: \"TESTENUM_FOO\"", + " number: 0", // Alias + " }", + " value {", + " name: \"_FOO\"", + " number: 0", // Alias + " }", + " value {", + " name: \"FOO\"", + " number: 0", // Alias + " }", + " value {", + " name: \"TEST_ENUM_ALIAS\"", + " number: 0", // Alias (unique name) + " }", + "}", + ] + + let fileProto: Google_Protobuf_FileDescriptorProto + do { + fileProto = try Google_Protobuf_FileDescriptorProto(textFormatStrings: txt) + } catch let e { + XCTFail("Error: \(e)") + return + } + + let descriptorSet = DescriptorSet(protos: [fileProto]) + let e = descriptorSet.enumDescriptor(named: "TestEnum")! + let values = e.values + XCTAssertEqual(values.count, 6) + + let aliasInfo = EnumDescriptor.ValueAliasInfo(enumDescriptor: e) + + // Check mainValues + + XCTAssertEqual(aliasInfo.mainValues.count, 2) + XCTAssertIdentical(aliasInfo.mainValues[0], e.values[0]) + XCTAssertIdentical(aliasInfo.mainValues[1], e.values[1]) + + // Check aliases(_:) + + XCTAssertEqual(aliasInfo.aliases(e.values[0])!.count, 4) + XCTAssertIdentical(aliasInfo.aliases(e.values[0])![0], e.values[2]) + XCTAssertIdentical(aliasInfo.aliases(e.values[0])![1], e.values[3]) + XCTAssertIdentical(aliasInfo.aliases(e.values[0])![2], e.values[4]) + XCTAssertIdentical(aliasInfo.aliases(e.values[0])![3], e.values[5]) + XCTAssertNil(aliasInfo.aliases(e.values[1])) // primary with no aliases + XCTAssertNil(aliasInfo.aliases(e.values[2])) // it itself is an alias + XCTAssertNil(aliasInfo.aliases(e.values[3])) // it itself is an alias + XCTAssertNil(aliasInfo.aliases(e.values[4])) // it itself is an alias + XCTAssertNil(aliasInfo.aliases(e.values[5])) // it itself is an alias + + // Check original(of:) + + XCTAssertNil(aliasInfo.original(of: e.values[0])) + XCTAssertNil(aliasInfo.original(of: e.values[1])) + XCTAssertIdentical(aliasInfo.original(of: e.values[2]), e.values[0]) + XCTAssertIdentical(aliasInfo.original(of: e.values[3]), e.values[0]) + XCTAssertIdentical(aliasInfo.original(of: e.values[4]), e.values[0]) + XCTAssertIdentical(aliasInfo.original(of: e.values[5]), e.values[0]) } - let descriptorSet = DescriptorSet(protos: [fileProto]) - let e = descriptorSet.enumDescriptor(named: "TestEnum")! - let values = e.values - XCTAssertEqual(values.count, 6) - - let aliasInfo = EnumDescriptor.ValueAliasInfo(enumDescriptor: e) - - // Check mainValues - - XCTAssertEqual(aliasInfo.mainValues.count, 2) - XCTAssertIdentical(aliasInfo.mainValues[0], e.values[0]) - XCTAssertIdentical(aliasInfo.mainValues[1], e.values[1]) - - // Check aliases(_:) - - XCTAssertEqual(aliasInfo.aliases(e.values[0])!.count, 4) - XCTAssertIdentical(aliasInfo.aliases(e.values[0])![0], e.values[2]) - XCTAssertIdentical(aliasInfo.aliases(e.values[0])![1], e.values[3]) - XCTAssertIdentical(aliasInfo.aliases(e.values[0])![2], e.values[4]) - XCTAssertIdentical(aliasInfo.aliases(e.values[0])![3], e.values[5]) - XCTAssertNil(aliasInfo.aliases(e.values[1])) // primary with no aliases - XCTAssertNil(aliasInfo.aliases(e.values[2])) // it itself is an alias - XCTAssertNil(aliasInfo.aliases(e.values[3])) // it itself is an alias - XCTAssertNil(aliasInfo.aliases(e.values[4])) // it itself is an alias - XCTAssertNil(aliasInfo.aliases(e.values[5])) // it itself is an alias - - // Check original(of:) - - XCTAssertNil(aliasInfo.original(of: e.values[0])) - XCTAssertNil(aliasInfo.original(of: e.values[1])) - XCTAssertIdentical(aliasInfo.original(of: e.values[2]), e.values[0]) - XCTAssertIdentical(aliasInfo.original(of: e.values[3]), e.values[0]) - XCTAssertIdentical(aliasInfo.original(of: e.values[4]), e.values[0]) - XCTAssertIdentical(aliasInfo.original(of: e.values[5]), e.values[0]) - } - - func test_File_computeImports_noImportPublic() { - let configText = """ - mapping { module_name: "foo", proto_file_path: "file" } - mapping { module_name: "bar", proto_file_path: "dir1/file" } - mapping { module_name: "baz", proto_file_path: ["dir2/file","file4"] } - mapping { module_name: "foo", proto_file_path: "file5" } - """ - - let config = try! SwiftProtobuf_GenSwift_ModuleMappings(textFormatString: configText) - let mapper = try! ProtoFileToModuleMappings(moduleMappingsProto: config) - - let fileProtos = [ - FileDescriptorProto(name: "file"), - FileDescriptorProto(name: "google/protobuf/any.proto", package: "google.protobuf"), - FileDescriptorProto(name: "dir1/file", dependencies: ["file"]), - FileDescriptorProto(name: "dir2/file", dependencies: ["google/protobuf/any.proto"]), - FileDescriptorProto(name: "file4", dependencies: ["dir2/file", "dir1/file", "file"]), - FileDescriptorProto(name: "file5", dependencies: ["file"]), - ] - let descSet = DescriptorSet(protos: fileProtos) - - // (filename, symbols) - let tests: [(String, [String])] = [ - ("file", []), - ("dir1/file", ["foo"]), - ("dir2/file", []), - ("file4", ["bar", "foo"]), - ("file5", []), - ] - - for (name, symbols) in tests { - let fileDesc = descSet.files.filter{ $0.name == name }.first! - assertImports( - for: fileDesc, - mappings: mapper, - symbols: symbols, - symbolsPerSection: (symbols, []), - message: "Looking for \(name)" - ) + func test_File_computeImports_noImportPublic() { + let configText = """ + mapping { module_name: "foo", proto_file_path: "file" } + mapping { module_name: "bar", proto_file_path: "dir1/file" } + mapping { module_name: "baz", proto_file_path: ["dir2/file","file4"] } + mapping { module_name: "foo", proto_file_path: "file5" } + """ + + let config = try! SwiftProtobuf_GenSwift_ModuleMappings(textFormatString: configText) + let mapper = try! ProtoFileToModuleMappings(moduleMappingsProto: config) + + let fileProtos = [ + FileDescriptorProto(name: "file"), + FileDescriptorProto(name: "google/protobuf/any.proto", package: "google.protobuf"), + FileDescriptorProto(name: "dir1/file", dependencies: ["file"]), + FileDescriptorProto(name: "dir2/file", dependencies: ["google/protobuf/any.proto"]), + FileDescriptorProto(name: "file4", dependencies: ["dir2/file", "dir1/file", "file"]), + FileDescriptorProto(name: "file5", dependencies: ["file"]), + ] + let descSet = DescriptorSet(protos: fileProtos) + + // (filename, symbols) + let tests: [(String, [String])] = [ + ("file", []), + ("dir1/file", ["foo"]), + ("dir2/file", []), + ("file4", ["bar", "foo"]), + ("file5", []), + ] + + for (name, symbols) in tests { + let fileDesc = descSet.files.filter { $0.name == name }.first! + assertImports( + for: fileDesc, + mappings: mapper, + symbols: symbols, + symbolsPerSection: (symbols, []), + message: "Looking for \(name)" + ) + } } - } - - func test_File_computeImports_PublicImports() { - // See the notes on computeImports(namer:reexportPublicImports:asImplementationOnly:) - // about how public import complicate things. - - // Given: - // - // + File: a.proto - // message A {} - // - // enum E { - // E_UNSET = 0; - // E_A = 1; - // E_B = 2; - // } - // - // + File: imports_a_publicly.proto - // import public "a.proto"; - // - // message ImportsAPublicly { - // optional A a = 1; - // optional E e = 2; - // } - // - // + File: imports_imports_a_publicly.proto - // import public "imports_a_publicly.proto"; - // - // message ImportsImportsAPublicly { - // optional A a = 1; - // } - // - // + File: uses_a_transitively.proto - // import "imports_a_publicly.proto"; - // - // message UsesATransitively { - // optional A a = 1; - // } - // - // + File: uses_a_transitively2.proto - // import "imports_imports_a_publicly.proto"; - // - // message UsesATransitively2 { - // optional A a = 1; - // } - // - // With a mapping file of: - // - // mapping { - // module_name: "A" - // proto_file_path: "a.proto" - // } - // mapping { - // module_name: "ImportsAPublicly" - // proto_file_path: "imports_a_publicly.proto" - // } - // mapping { - // module_name: "ImportsImportsAPublicly" - // proto_file_path: "imports_imports_a_publicly.proto" - // } - - let configText = """ - mapping { module_name: "A", proto_file_path: "a.proto" } - mapping { module_name: "ImportsAPublicly", proto_file_path: "imports_a_publicly.proto" } - mapping { module_name: "ImportsImportsAPublicly", proto_file_path: "imports_imports_a_publicly.proto" } - """ - - let config = try! SwiftProtobuf_GenSwift_ModuleMappings(textFormatString: configText) - let mapper = try! ProtoFileToModuleMappings(moduleMappingsProto: config) - - let fileProtos = [ - try! FileDescriptorProto(textFormatString: """ - name: "a.proto" - message_type { - name: "A" + + func test_File_computeImports_PublicImports() { + // See the notes on computeImports(namer:reexportPublicImports:asImplementationOnly:) + // about how public import complicate things. + + // Given: + // + // + File: a.proto + // message A {} + // + // enum E { + // E_UNSET = 0; + // E_A = 1; + // E_B = 2; + // } + // + // + File: imports_a_publicly.proto + // import public "a.proto"; + // + // message ImportsAPublicly { + // optional A a = 1; + // optional E e = 2; + // } + // + // + File: imports_imports_a_publicly.proto + // import public "imports_a_publicly.proto"; + // + // message ImportsImportsAPublicly { + // optional A a = 1; + // } + // + // + File: uses_a_transitively.proto + // import "imports_a_publicly.proto"; + // + // message UsesATransitively { + // optional A a = 1; + // } + // + // + File: uses_a_transitively2.proto + // import "imports_imports_a_publicly.proto"; + // + // message UsesATransitively2 { + // optional A a = 1; + // } + // + // With a mapping file of: + // + // mapping { + // module_name: "A" + // proto_file_path: "a.proto" + // } + // mapping { + // module_name: "ImportsAPublicly" + // proto_file_path: "imports_a_publicly.proto" + // } + // mapping { + // module_name: "ImportsImportsAPublicly" + // proto_file_path: "imports_imports_a_publicly.proto" + // } + + let configText = """ + mapping { module_name: "A", proto_file_path: "a.proto" } + mapping { module_name: "ImportsAPublicly", proto_file_path: "imports_a_publicly.proto" } + mapping { module_name: "ImportsImportsAPublicly", proto_file_path: "imports_imports_a_publicly.proto" } + """ + + let config = try! SwiftProtobuf_GenSwift_ModuleMappings(textFormatString: configText) + let mapper = try! ProtoFileToModuleMappings(moduleMappingsProto: config) + + let fileProtos = [ + try! FileDescriptorProto( + textFormatString: """ + name: "a.proto" + message_type { + name: "A" + } + enum_type { + name: "E" + value { + name: "E_UNSET" + number: 0 + } + value { + name: "E_A" + number: 1 + } + value { + name: "E_B" + number: 2 + } + } + """ + ), + try! FileDescriptorProto( + textFormatString: """ + name: "imports_a_publicly.proto" + dependency: "a.proto" + message_type { + name: "ImportsAPublicly" + field { + name: "a" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_MESSAGE + type_name: ".A" + json_name: "a" + } + field { + name: "e" + number: 2 + label: LABEL_OPTIONAL + type: TYPE_ENUM + type_name: ".E" + json_name: "e" + } + } + public_dependency: 0 + """ + ), + try! FileDescriptorProto( + textFormatString: """ + name: "imports_imports_a_publicly.proto" + dependency: "imports_a_publicly.proto" + message_type { + name: "ImportsImportsAPublicly" + field { + name: "a" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_MESSAGE + type_name: ".A" + json_name: "a" + } + } + public_dependency: 0 + """ + ), + try! FileDescriptorProto( + textFormatString: """ + name: "uses_a_transitively.proto" + dependency: "imports_a_publicly.proto" + message_type { + name: "UsesATransitively" + field { + name: "a" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_MESSAGE + type_name: ".A" + json_name: "a" + } + } + """ + ), + try! FileDescriptorProto( + textFormatString: """ + name: "uses_a_transitively2.proto" + dependency: "imports_imports_a_publicly.proto" + message_type { + name: "UsesATransitively2" + field { + name: "a" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_MESSAGE + type_name: ".A" + json_name: "a" + } + } + """ + ), + ] + let descSet = DescriptorSet(protos: fileProtos) + + // (filename, symbols, symbolsPerSection) + let tests: [(String, [String], (imported: [String], exported: [String]))] = [ + ("a.proto", [], ([], [])), + ("imports_a_publicly.proto", ["A"], ([], ["enum A.E", "struct A.A"])), + ( + "imports_imports_a_publicly.proto", + ["ImportsAPublicly"], + ([], ["enum A.E", "struct A.A", "struct ImportsAPublicly.ImportsAPublicly"]) + ), + // this reexports A, so we don't directly pull in A. + // just a plain `import`, nothing to re-export. + ("uses_a_transitively.proto", ["ImportsAPublicly"], (["ImportsAPublicly"], [])), + // this chain reexports A, so we don't directly pull in A. + // just a plain `import`, nothing to re-export. + ("uses_a_transitively2.proto", ["ImportsImportsAPublicly"], (["ImportsImportsAPublicly"], [])), + ] + + for (name, symbols, symbolsPerSection) in tests { + let fileDesc = descSet.files.filter { $0.name == name }.first! + assertImports( + for: fileDesc, + mappings: mapper, + symbols: symbols, + symbolsPerSection: symbolsPerSection, + message: "Looking for \(name)" + ) } - enum_type { - name: "E" - value { - name: "E_UNSET" - number: 0 - } - value { - name: "E_A" - number: 1 - } - value { - name: "E_B" - number: 2 - } + } + + private func assertImports( + for fileDesc: FileDescriptor, + mappings: ProtoFileToModuleMappings, + symbols: [String], + symbolsPerSection: (imported: [String], exported: [String]), + message: String, + file: StaticString = #filePath, + line: UInt = #line + ) { + func symbolsSections(importDirective: String, exportedImportDirective: String) -> String { + let expectedExported = + symbolsPerSection.exported.isEmpty + ? "" + : """ + // Use of 'import public' causes re-exports: + \(symbolsPerSection.exported.map { "@_exported \(exportedImportDirective) \($0)" }.joined(separator: "\n")) + """ + var expectedLines = symbolsPerSection.imported.map { "\(importDirective) \($0)" } + if !expectedExported.isEmpty { + expectedLines.append(expectedExported) + } + return expectedLines.joined(separator: "\n") } - """), - try! FileDescriptorProto(textFormatString: """ - name: "imports_a_publicly.proto" - dependency: "a.proto" - message_type { - name: "ImportsAPublicly" - field { - name: "a" - number: 1 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".A" - json_name: "a" - } - field { - name: "e" - number: 2 - label: LABEL_OPTIONAL - type: TYPE_ENUM - type_name: ".E" - json_name: "e" - } + do { + let result = fileDesc.computeImports(mappings: mappings, directive: .plain) + let expected = symbols.map { "import \($0)" }.joined(separator: "\n") + XCTAssertEqual(expected, result, message, file: file, line: line) } - public_dependency: 0 - """), - try! FileDescriptorProto(textFormatString: """ - name: "imports_imports_a_publicly.proto" - dependency: "imports_a_publicly.proto" - message_type { - name: "ImportsImportsAPublicly" - field { - name: "a" - number: 1 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".A" - json_name: "a" - } + do { + let result = fileDesc.computeImports(mappings: mappings, directive: .plain, reexport: true) + let expected = symbolsSections(importDirective: "import", exportedImportDirective: "import") + XCTAssertEqual(expected, result, message, file: file, line: line) } - public_dependency: 0 - """), - try! FileDescriptorProto(textFormatString: """ - name: "uses_a_transitively.proto" - dependency: "imports_a_publicly.proto" - message_type { - name: "UsesATransitively" - field { - name: "a" - number: 1 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".A" - json_name: "a" - } + do { + let result = fileDesc.computeImports(mappings: mappings, directive: .accessLevel(.internal)) + let expected = symbols.map { "internal import \($0)" }.joined(separator: "\n") + XCTAssertEqual(expected, result, message, file: file, line: line) } - """), - try! FileDescriptorProto(textFormatString: """ - name: "uses_a_transitively2.proto" - dependency: "imports_imports_a_publicly.proto" - message_type { - name: "UsesATransitively2" - field { - name: "a" - number: 1 - label: LABEL_OPTIONAL - type: TYPE_MESSAGE - type_name: ".A" - json_name: "a" - } + do { + let result = fileDesc.computeImports(mappings: mappings, directive: .accessLevel(.package), reexport: true) + let expected = symbolsSections(importDirective: "package import", exportedImportDirective: "public import") + XCTAssertEqual(expected, result, message, file: file, line: line) + } + do { + let result = fileDesc.computeImports(mappings: mappings, directive: .accessLevel(.public), reexport: true) + let expected = symbolsSections(importDirective: "public import", exportedImportDirective: "public import") + XCTAssertEqual(expected, result, message, file: file, line: line) + } + do { + let result = fileDesc.computeImports(mappings: mappings, directive: .implementationOnly) + let expected = symbols.map { "@_implementationOnly import \($0)" }.joined(separator: "\n") + XCTAssertEqual(expected, result, message, file: file, line: line) } - """), - ] - let descSet = DescriptorSet(protos: fileProtos) - - // (filename, symbols, symbolsPerSection) - let tests: [(String, [String], (imported: [String], exported: [String]))] = [ - ("a.proto", [], ([], [])), - ("imports_a_publicly.proto", ["A"], ( [], ["enum A.E", "struct A.A"])), - ("imports_imports_a_publicly.proto", - ["ImportsAPublicly"], - ([], ["enum A.E", "struct A.A", "struct ImportsAPublicly.ImportsAPublicly"]) - ), - // this reexports A, so we don't directly pull in A. - // just a plain `import`, nothing to re-export. - ("uses_a_transitively.proto", ["ImportsAPublicly"], (["ImportsAPublicly"], [])), - // this chain reexports A, so we don't directly pull in A. - // just a plain `import`, nothing to re-export. - ("uses_a_transitively2.proto", ["ImportsImportsAPublicly"], (["ImportsImportsAPublicly"], [])), - ] - - for (name, symbols, symbolsPerSection) in tests { - let fileDesc = descSet.files.filter{ $0.name == name }.first! - assertImports( - for: fileDesc, - mappings: mapper, - symbols: symbols, - symbolsPerSection: symbolsPerSection, - message: "Looking for \(name)" - ) - } - } - - private func assertImports( - for fileDesc: FileDescriptor, - mappings: ProtoFileToModuleMappings, - symbols: [String], - symbolsPerSection: (imported: [String], exported: [String]), - message: String, - file: StaticString = #filePath, - line: UInt = #line - ) { - func symbolsSections(importDirective: String, exportedImportDirective: String) -> String { - let expectedExported = symbolsPerSection.exported.isEmpty ? "" : """ - // Use of 'import public' causes re-exports: - \(symbolsPerSection.exported.map { "@_exported \(exportedImportDirective) \($0)" }.joined(separator: "\n")) - """ - var expectedLines = symbolsPerSection.imported.map { "\(importDirective) \($0)" } - if !expectedExported.isEmpty { - expectedLines.append(expectedExported) - } - return expectedLines.joined(separator: "\n") - } - do { - let result = fileDesc.computeImports(mappings: mappings, directive: .plain) - let expected = symbols.map { "import \($0)" }.joined(separator: "\n") - XCTAssertEqual(expected, result, message, file: file, line: line) - } - do { - let result = fileDesc.computeImports(mappings: mappings, directive: .plain, reexport: true) - let expected = symbolsSections(importDirective: "import", exportedImportDirective: "import") - XCTAssertEqual(expected, result, message, file: file, line: line) - } - do { - let result = fileDesc.computeImports(mappings: mappings, directive: .accessLevel(.internal)) - let expected = symbols.map { "internal import \($0)" }.joined(separator: "\n") - XCTAssertEqual(expected, result, message, file: file, line: line) - } - do { - let result = fileDesc.computeImports(mappings: mappings, directive: .accessLevel(.package), reexport: true) - let expected = symbolsSections(importDirective: "package import", exportedImportDirective: "public import") - XCTAssertEqual(expected, result, message, file: file, line: line) - } - do { - let result = fileDesc.computeImports(mappings: mappings, directive: .accessLevel(.public), reexport: true) - let expected = symbolsSections(importDirective: "public import", exportedImportDirective: "public import") - XCTAssertEqual(expected, result, message, file: file, line: line) - } - do { - let result = fileDesc.computeImports(mappings: mappings, directive: .implementationOnly) - let expected = symbols.map { "@_implementationOnly import \($0)" }.joined(separator: "\n") - XCTAssertEqual(expected, result, message, file: file, line: line) } - } } -private extension FileDescriptor { - func computeImports( - mappings: ProtoFileToModuleMappings, - directive: GeneratorOptions.ImportDirective, - reexport: Bool = false - ) -> String { - let namer = SwiftProtobufNamer( - currentFile: self, - protoFileToModuleMappings: mappings - ) - return computeImports( - namer: namer, - directive: directive, - reexportPublicImports: reexport - ) - } +extension FileDescriptor { + fileprivate func computeImports( + mappings: ProtoFileToModuleMappings, + directive: GeneratorOptions.ImportDirective, + reexport: Bool = false + ) -> String { + let namer = SwiftProtobufNamer( + currentFile: self, + protoFileToModuleMappings: mappings + ) + return computeImports( + namer: namer, + directive: directive, + reexportPublicImports: reexport + ) + } } diff --git a/Tests/protoc-gen-swiftTests/Test_SwiftProtobufNamerExtensions.swift b/Tests/protoc-gen-swiftTests/Test_SwiftProtobufNamerExtensions.swift index 872b51ef3..e63d37c3c 100644 --- a/Tests/protoc-gen-swiftTests/Test_SwiftProtobufNamerExtensions.swift +++ b/Tests/protoc-gen-swiftTests/Test_SwiftProtobufNamerExtensions.swift @@ -8,301 +8,310 @@ // // ----------------------------------------------------------------------------- -import XCTest import SwiftProtobuf import SwiftProtobufPluginLibrary import SwiftProtobufTestHelpers +import XCTest + @testable import protoc_gen_swift final class Test_SwiftProtobufNamer: XCTestCase { - func testEnumValueHandling_AliasNameMatches() throws { - let txt = [ - "name: \"test.proto\"", - "syntax: \"proto2\"", - "enum_type {", - " name: \"TestEnum\"", - " options {", - " allow_alias: true", - " }", - " value {", - " name: \"TEST_ENUM_FOO\"", - " number: 0", // Primary - " }", - " value {", - " name: \"TEST_ENUM_BAR\"", - " number: 1", - " }", - " value {", - " name: \"TESTENUM_FOO\"", - " number: 0", // Alias - " }", - " value {", - " name: \"_FOO\"", - " number: 0", // Alias - " }", - " value {", - " name: \"FOO\"", - " number: 0", // Alias - " }", - " value {", - " name: \"TEST_ENUM_ALIAS\"", - " number: 0", // Alias (unique name) - " }", - "}" - ] - - let fileProto: Google_Protobuf_FileDescriptorProto - do { - fileProto = try Google_Protobuf_FileDescriptorProto(textFormatStrings: txt) - } catch let e { - XCTFail("Error: \(e)") - return + func testEnumValueHandling_AliasNameMatches() throws { + let txt = [ + "name: \"test.proto\"", + "syntax: \"proto2\"", + "enum_type {", + " name: \"TestEnum\"", + " options {", + " allow_alias: true", + " }", + " value {", + " name: \"TEST_ENUM_FOO\"", + " number: 0", // Primary + " }", + " value {", + " name: \"TEST_ENUM_BAR\"", + " number: 1", + " }", + " value {", + " name: \"TESTENUM_FOO\"", + " number: 0", // Alias + " }", + " value {", + " name: \"_FOO\"", + " number: 0", // Alias + " }", + " value {", + " name: \"FOO\"", + " number: 0", // Alias + " }", + " value {", + " name: \"TEST_ENUM_ALIAS\"", + " number: 0", // Alias (unique name) + " }", + "}", + ] + + let fileProto: Google_Protobuf_FileDescriptorProto + do { + fileProto = try Google_Protobuf_FileDescriptorProto(textFormatStrings: txt) + } catch let e { + XCTFail("Error: \(e)") + return + } + + let descriptorSet = DescriptorSet(protos: [fileProto]) + let namer = + SwiftProtobufNamer( + currentFile: descriptorSet.fileDescriptor(named: "test.proto")!, + protoFileToModuleMappings: ProtoFileToModuleMappings() + ) + + let e = descriptorSet.enumDescriptor(named: "TestEnum")! + let values = e.values + XCTAssertEqual(values.count, 6) + + // Test uniquelyNamedValues(enum:) + + let aliasInfo = EnumDescriptor.ValueAliasInfo(enumDescriptor: e) + let filtered = namer.uniquelyNamedValues(valueAliasInfo: aliasInfo) + XCTAssertEqual(filtered.count, 3) + + XCTAssertEqual(filtered[0].name, "TEST_ENUM_FOO") + XCTAssertEqual(filtered[1].name, "TEST_ENUM_BAR") + XCTAssertEqual(filtered[2].name, "TEST_ENUM_ALIAS") + XCTAssertEqual(namer.relativeName(enumValue: filtered[0]), "foo") + XCTAssertEqual(namer.relativeName(enumValue: filtered[1]), "bar") + XCTAssertEqual(namer.relativeName(enumValue: filtered[2]), "alias") } - let descriptorSet = DescriptorSet(protos: [fileProto]) - let namer = - SwiftProtobufNamer(currentFile: descriptorSet.fileDescriptor(named: "test.proto")!, - protoFileToModuleMappings: ProtoFileToModuleMappings()) - - let e = descriptorSet.enumDescriptor(named: "TestEnum")! - let values = e.values - XCTAssertEqual(values.count, 6) - - // Test uniquelyNamedValues(enum:) - - let aliasInfo = EnumDescriptor.ValueAliasInfo(enumDescriptor: e) - let filtered = namer.uniquelyNamedValues(valueAliasInfo: aliasInfo) - XCTAssertEqual(filtered.count, 3) - - XCTAssertEqual(filtered[0].name, "TEST_ENUM_FOO") - XCTAssertEqual(filtered[1].name, "TEST_ENUM_BAR") - XCTAssertEqual(filtered[2].name, "TEST_ENUM_ALIAS") - XCTAssertEqual(namer.relativeName(enumValue: filtered[0]), "foo") - XCTAssertEqual(namer.relativeName(enumValue: filtered[1]), "bar") - XCTAssertEqual(namer.relativeName(enumValue: filtered[2]), "alias") - } - - func testEnumValueHandling_NameCollisions() { - let txt = [ - "name: \"test.proto\"", - "syntax: \"proto2\"", - "enum_type {", - " name: \"TestEnum\"", - " value {", - " name: \"TEST_ENUM_FOO\"", - " number: 0", // Collision - " }", - " value {", - " name: \"TEST_ENUM_BAR\"", - " number: 1", - " }", - " value {", - " name: \"TESTENUM_FOO\"", - " number: 2", // Collision - " }", - " value {", - " name: \"_FOO\"", - " number: -1", // Collision - negative value - " }", - "}" - ] - - let fileProto: Google_Protobuf_FileDescriptorProto - do { - fileProto = try Google_Protobuf_FileDescriptorProto(textFormatStrings: txt) - } catch let e { - XCTFail("Error: \(e)") - return + func testEnumValueHandling_NameCollisions() { + let txt = [ + "name: \"test.proto\"", + "syntax: \"proto2\"", + "enum_type {", + " name: \"TestEnum\"", + " value {", + " name: \"TEST_ENUM_FOO\"", + " number: 0", // Collision + " }", + " value {", + " name: \"TEST_ENUM_BAR\"", + " number: 1", + " }", + " value {", + " name: \"TESTENUM_FOO\"", + " number: 2", // Collision + " }", + " value {", + " name: \"_FOO\"", + " number: -1", // Collision - negative value + " }", + "}", + ] + + let fileProto: Google_Protobuf_FileDescriptorProto + do { + fileProto = try Google_Protobuf_FileDescriptorProto(textFormatStrings: txt) + } catch let e { + XCTFail("Error: \(e)") + return + } + + let descriptorSet = DescriptorSet(protos: [fileProto]) + let namer = + SwiftProtobufNamer( + currentFile: descriptorSet.fileDescriptor(named: "test.proto")!, + protoFileToModuleMappings: ProtoFileToModuleMappings() + ) + + let e = descriptorSet.enumDescriptor(named: "TestEnum")! + let values = e.values + XCTAssertEqual(values.count, 4) + + // Test uniquelyNamedValues(enum:) + + let aliasInfo = EnumDescriptor.ValueAliasInfo(enumDescriptor: e) + let filtered = namer.uniquelyNamedValues(valueAliasInfo: aliasInfo) + XCTAssertEqual(filtered.count, 4) + + XCTAssertEqual(filtered[0].name, "TEST_ENUM_FOO") + XCTAssertEqual(filtered[1].name, "TEST_ENUM_BAR") + XCTAssertEqual(filtered[2].name, "TESTENUM_FOO") + XCTAssertEqual(filtered[3].name, "_FOO") + XCTAssertEqual(namer.relativeName(enumValue: filtered[0]), "foo_0") + XCTAssertEqual(namer.relativeName(enumValue: filtered[1]), "bar") + XCTAssertEqual(namer.relativeName(enumValue: filtered[2]), "foo_2") + XCTAssertEqual(namer.relativeName(enumValue: filtered[3]), "foo_n1") } - let descriptorSet = DescriptorSet(protos: [fileProto]) - let namer = - SwiftProtobufNamer(currentFile: descriptorSet.fileDescriptor(named: "test.proto")!, - protoFileToModuleMappings: ProtoFileToModuleMappings()) - - let e = descriptorSet.enumDescriptor(named: "TestEnum")! - let values = e.values - XCTAssertEqual(values.count, 4) - - // Test uniquelyNamedValues(enum:) - - let aliasInfo = EnumDescriptor.ValueAliasInfo(enumDescriptor: e) - let filtered = namer.uniquelyNamedValues(valueAliasInfo: aliasInfo) - XCTAssertEqual(filtered.count, 4) - - XCTAssertEqual(filtered[0].name, "TEST_ENUM_FOO") - XCTAssertEqual(filtered[1].name, "TEST_ENUM_BAR") - XCTAssertEqual(filtered[2].name, "TESTENUM_FOO") - XCTAssertEqual(filtered[3].name, "_FOO") - XCTAssertEqual(namer.relativeName(enumValue: filtered[0]), "foo_0") - XCTAssertEqual(namer.relativeName(enumValue: filtered[1]), "bar") - XCTAssertEqual(namer.relativeName(enumValue: filtered[2]), "foo_2") - XCTAssertEqual(namer.relativeName(enumValue: filtered[3]), "foo_n1") - } - - func testEnumValueHandling_NameCollisionsAndAliasMatches() { - let txt = [ - "name: \"test.proto\"", - "syntax: \"proto2\"", - "enum_type {", - " name: \"TestEnum\"", - " options {", - " allow_alias: true", - " }", - " value {", - " name: \"TEST_ENUM_FOO\"", - " number: 0", // Collision/Primary0 - " }", - " value {", - " name: \"TEST_ENUM_BAR\"", - " number: 1", - " }", - " value {", - " name: \"TESTENUM_FOO\"", - " number: 0", // Alias 0 - " }", - " value {", - " name: \"_FOO\"", - " number: 2", // Collision/Primary2 - " }", - " value {", - " name: \"FOO\"", - " number: 2", // Alias 2 - " }", - " value {", - " name: \"TEST_ENUM_ALIAS\"", - " number: 0", // Alias 0 - Unique name - " }", - " value {", - " name: \"mumble\"", - " number: 1", // Alias 1 - Collision with next alias - " }", - " value {", - " name: \"MUMBLE\"", - " number: 0", // Alias 0 - Collision with previous alias - " }", - "}" - ] - - let fileProto: Google_Protobuf_FileDescriptorProto - do { - fileProto = try Google_Protobuf_FileDescriptorProto(textFormatStrings: txt) - } catch let e { - XCTFail("Error: \(e)") - return + func testEnumValueHandling_NameCollisionsAndAliasMatches() { + let txt = [ + "name: \"test.proto\"", + "syntax: \"proto2\"", + "enum_type {", + " name: \"TestEnum\"", + " options {", + " allow_alias: true", + " }", + " value {", + " name: \"TEST_ENUM_FOO\"", + " number: 0", // Collision/Primary0 + " }", + " value {", + " name: \"TEST_ENUM_BAR\"", + " number: 1", + " }", + " value {", + " name: \"TESTENUM_FOO\"", + " number: 0", // Alias 0 + " }", + " value {", + " name: \"_FOO\"", + " number: 2", // Collision/Primary2 + " }", + " value {", + " name: \"FOO\"", + " number: 2", // Alias 2 + " }", + " value {", + " name: \"TEST_ENUM_ALIAS\"", + " number: 0", // Alias 0 - Unique name + " }", + " value {", + " name: \"mumble\"", + " number: 1", // Alias 1 - Collision with next alias + " }", + " value {", + " name: \"MUMBLE\"", + " number: 0", // Alias 0 - Collision with previous alias + " }", + "}", + ] + + let fileProto: Google_Protobuf_FileDescriptorProto + do { + fileProto = try Google_Protobuf_FileDescriptorProto(textFormatStrings: txt) + } catch let e { + XCTFail("Error: \(e)") + return + } + + let descriptorSet = DescriptorSet(protos: [fileProto]) + let namer = + SwiftProtobufNamer( + currentFile: descriptorSet.fileDescriptor(named: "test.proto")!, + protoFileToModuleMappings: ProtoFileToModuleMappings() + ) + + let e = descriptorSet.enumDescriptor(named: "TestEnum")! + let values = e.values + XCTAssertEqual(values.count, 8) + + // Test uniquelyNamedValues(enum:) + + let aliasInfo = EnumDescriptor.ValueAliasInfo(enumDescriptor: e) + let filtered = namer.uniquelyNamedValues(valueAliasInfo: aliasInfo) + XCTAssertEqual(filtered.count, 6) + + XCTAssertEqual(filtered[0].name, "TEST_ENUM_FOO") + XCTAssertEqual(filtered[1].name, "TEST_ENUM_BAR") + XCTAssertEqual(filtered[2].name, "_FOO") + XCTAssertEqual(filtered[3].name, "TEST_ENUM_ALIAS") + XCTAssertEqual(filtered[4].name, "mumble") + XCTAssertEqual(filtered[5].name, "MUMBLE") + XCTAssertEqual(namer.relativeName(enumValue: filtered[0]), "foo_0") + XCTAssertEqual(namer.relativeName(enumValue: filtered[1]), "bar") + XCTAssertEqual(namer.relativeName(enumValue: filtered[2]), "foo_2") + XCTAssertEqual(namer.relativeName(enumValue: filtered[3]), "alias") + XCTAssertEqual(namer.relativeName(enumValue: filtered[4]), "mumble_1") + XCTAssertEqual(namer.relativeName(enumValue: filtered[5]), "mumble_0") } - let descriptorSet = DescriptorSet(protos: [fileProto]) - let namer = - SwiftProtobufNamer(currentFile: descriptorSet.fileDescriptor(named: "test.proto")!, - protoFileToModuleMappings: ProtoFileToModuleMappings()) - - let e = descriptorSet.enumDescriptor(named: "TestEnum")! - let values = e.values - XCTAssertEqual(values.count, 8) - - // Test uniquelyNamedValues(enum:) - - let aliasInfo = EnumDescriptor.ValueAliasInfo(enumDescriptor: e) - let filtered = namer.uniquelyNamedValues(valueAliasInfo: aliasInfo) - XCTAssertEqual(filtered.count, 6) - - XCTAssertEqual(filtered[0].name, "TEST_ENUM_FOO") - XCTAssertEqual(filtered[1].name, "TEST_ENUM_BAR") - XCTAssertEqual(filtered[2].name, "_FOO") - XCTAssertEqual(filtered[3].name, "TEST_ENUM_ALIAS") - XCTAssertEqual(filtered[4].name, "mumble") - XCTAssertEqual(filtered[5].name, "MUMBLE") - XCTAssertEqual(namer.relativeName(enumValue: filtered[0]), "foo_0") - XCTAssertEqual(namer.relativeName(enumValue: filtered[1]), "bar") - XCTAssertEqual(namer.relativeName(enumValue: filtered[2]), "foo_2") - XCTAssertEqual(namer.relativeName(enumValue: filtered[3]), "alias") - XCTAssertEqual(namer.relativeName(enumValue: filtered[4]), "mumble_1") - XCTAssertEqual(namer.relativeName(enumValue: filtered[5]), "mumble_0") - } - - func testEnumValueHandling_UniqueAliasNameCollisions() { - // Tests were the aliases collided in naming, but not with - // the original. - - let txt = [ - "name: \"test.proto\"", - "syntax: \"proto2\"", - "enum_type {", - " name: \"AliasedEnum\"", - " options {", - " allow_alias: true", - " }", - " value {", - " name: \"ALIAS_FOO\"", - " number: 0", - " }", - " value {", - " name: \"ALIAS_BAR\"", - " number: 1", - " }", - " value {", - " name: \"ALIAS_BAZ\"", - " number: 2", - " }", - " value {", - " name: \"QUX\"", - " number: 2", // short name merged with the next because they have the same value. - " }", - " value {", - " name: \"qux\"", - " number: 2", - " }", - " value {", - " name: \"bAz\"", - " number: 2", - " }", - "}" - ] - - let fileProto: Google_Protobuf_FileDescriptorProto - do { - fileProto = try Google_Protobuf_FileDescriptorProto(textFormatStrings: txt) - } catch let e { - XCTFail("Error: \(e)") - return + func testEnumValueHandling_UniqueAliasNameCollisions() { + // Tests were the aliases collided in naming, but not with + // the original. + + let txt = [ + "name: \"test.proto\"", + "syntax: \"proto2\"", + "enum_type {", + " name: \"AliasedEnum\"", + " options {", + " allow_alias: true", + " }", + " value {", + " name: \"ALIAS_FOO\"", + " number: 0", + " }", + " value {", + " name: \"ALIAS_BAR\"", + " number: 1", + " }", + " value {", + " name: \"ALIAS_BAZ\"", + " number: 2", + " }", + " value {", + " name: \"QUX\"", + " number: 2", // short name merged with the next because they have the same value. + " }", + " value {", + " name: \"qux\"", + " number: 2", + " }", + " value {", + " name: \"bAz\"", + " number: 2", + " }", + "}", + ] + + let fileProto: Google_Protobuf_FileDescriptorProto + do { + fileProto = try Google_Protobuf_FileDescriptorProto(textFormatStrings: txt) + } catch let e { + XCTFail("Error: \(e)") + return + } + + let descriptorSet = DescriptorSet(protos: [fileProto]) + let namer = + SwiftProtobufNamer( + currentFile: descriptorSet.fileDescriptor(named: "test.proto")!, + protoFileToModuleMappings: ProtoFileToModuleMappings() + ) + + let e = descriptorSet.enumDescriptor(named: "AliasedEnum")! + let values = e.values + XCTAssertEqual(values.count, 6) + + XCTAssertEqual(values[0].name, "ALIAS_FOO") + XCTAssertEqual(values[1].name, "ALIAS_BAR") + XCTAssertEqual(values[2].name, "ALIAS_BAZ") + XCTAssertEqual(values[3].name, "QUX") + XCTAssertEqual(values[4].name, "qux") + XCTAssertEqual(values[5].name, "bAz") + + // Test uniquelyNamedValues(enum:) + + // QUX & qux collided, so only one remains. + + let aliasInfo = EnumDescriptor.ValueAliasInfo(enumDescriptor: e) + let filtered = namer.uniquelyNamedValues(valueAliasInfo: aliasInfo) + XCTAssertEqual(filtered.count, 5) + + XCTAssertEqual(filtered[0].name, "ALIAS_FOO") + XCTAssertEqual(filtered[1].name, "ALIAS_BAR") + XCTAssertEqual(filtered[2].name, "ALIAS_BAZ") + XCTAssertEqual(filtered[3].name, "QUX") + XCTAssertEqual(filtered[4].name, "bAz") + XCTAssertEqual(namer.relativeName(enumValue: filtered[0]), "aliasFoo") + XCTAssertEqual(namer.relativeName(enumValue: filtered[1]), "aliasBar") + XCTAssertEqual(namer.relativeName(enumValue: filtered[2]), "aliasBaz") + XCTAssertEqual(namer.relativeName(enumValue: filtered[3]), "qux") + XCTAssertEqual(namer.relativeName(enumValue: filtered[4]), "bAz") } - let descriptorSet = DescriptorSet(protos: [fileProto]) - let namer = - SwiftProtobufNamer(currentFile: descriptorSet.fileDescriptor(named: "test.proto")!, - protoFileToModuleMappings: ProtoFileToModuleMappings()) - - let e = descriptorSet.enumDescriptor(named: "AliasedEnum")! - let values = e.values - XCTAssertEqual(values.count, 6) - - XCTAssertEqual(values[0].name, "ALIAS_FOO") - XCTAssertEqual(values[1].name, "ALIAS_BAR") - XCTAssertEqual(values[2].name, "ALIAS_BAZ") - XCTAssertEqual(values[3].name, "QUX") - XCTAssertEqual(values[4].name, "qux") - XCTAssertEqual(values[5].name, "bAz") - - // Test uniquelyNamedValues(enum:) - - // QUX & qux collided, so only one remains. - - let aliasInfo = EnumDescriptor.ValueAliasInfo(enumDescriptor: e) - let filtered = namer.uniquelyNamedValues(valueAliasInfo: aliasInfo) - XCTAssertEqual(filtered.count, 5) - - XCTAssertEqual(filtered[0].name, "ALIAS_FOO") - XCTAssertEqual(filtered[1].name, "ALIAS_BAR") - XCTAssertEqual(filtered[2].name, "ALIAS_BAZ") - XCTAssertEqual(filtered[3].name, "QUX") - XCTAssertEqual(filtered[4].name, "bAz") - XCTAssertEqual(namer.relativeName(enumValue: filtered[0]), "aliasFoo") - XCTAssertEqual(namer.relativeName(enumValue: filtered[1]), "aliasBar") - XCTAssertEqual(namer.relativeName(enumValue: filtered[2]), "aliasBaz") - XCTAssertEqual(namer.relativeName(enumValue: filtered[3]), "qux") - XCTAssertEqual(namer.relativeName(enumValue: filtered[4]), "bAz") - } - }