-
Notifications
You must be signed in to change notification settings - Fork 455
/
Descriptor.swift
1582 lines (1400 loc) · 64.3 KB
/
Descriptor.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Sources/SwiftProtobufPluginLibrary/Descriptor.swift - Descriptor wrappers
//
// Copyright (c) 2014 - 2017 Apple Inc. and the project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See LICENSE.txt for license information:
// https://github.com/apple/swift-protobuf/blob/main/LICENSE.txt
//
// -----------------------------------------------------------------------------
///
/// This is like Descriptor.{h,cc} in the google/protobuf C++ code, it provides
/// wrappers around the protos to make a more usable object graph for generation
/// and also provides some SwiftProtobuf specific additions that would be useful
/// to anyone generating something that uses SwiftProtobufs (like support the
/// `service` messages). It is *not* the intent for these to eventually be used
/// as part of some reflection or generate message api.
///
/// Unlike the C++ Descriptors, the intent is for these to *only* be used within
/// the context of a protoc plugin, meaning, the
/// `Google_Protobuf_FileDescriptorSet` used to create these will be *always*
/// be well formed by protoc and the guarentees it provides.
///
// -----------------------------------------------------------------------------
import Foundation
import SwiftProtobuf
/// The front interface for building/getting descriptors. The objects
/// vended from the here are different from the raw
/// `Google_Protobuf_*Proto` types in that they have all the cross object
/// references resolved or wired up, making for an easier to use object
/// model.
///
/// 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<Google_Protobuf_Edition> {
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
/// 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.
public init() {}
}
/// Models a .proto file. `FileDescriptor`s are not directly created,
/// instead they are constructed/fetched via the `DescriptorSet` or
/// 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 { _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 { 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.
for e in enums { e.bind(file: self, registry: registry, containingType: nil) }
for m in messages { m.bind(file: self, registry: registry, containingType: nil) }
for e in extensions { e.bind(file: self, registry: registry, containingType: nil) }
for s in services { s.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 { Int($0) }
result[IndexPath(indexes: intList)] = loc
}
return result
}()
}
/// Describes a type of protocol message, or a particular group within a
/// message. `Descriptor`s are not directly created, instead they are
/// 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.
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 .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 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()`.
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: { $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<Int32>]
/// 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 `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 {
featureResolver.resolve($0.options, resolvedParent: resolvedFeatures)
}
self.messageExtensionRanges = proto.extensionRange.enumerated().map {
ExtensionRange(
proto: $0.element,
index: $0.offset,
features: featureResolver.resolve(
$0.element.options,
resolvedParent: resolvedFeatures
)
)
}
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
)
}
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
for e in messageExtensionRanges { e.bind(containingType: self, registry: registry) }
for e in enums { e.bind(file: file, registry: registry, containingType: self) }
for m in messages { m.bind(file: file, registry: registry, containingType: self) }
for f in fields { f.bind(file: file, registry: registry, containingType: self) }
for o in oneofs { o.bind(registry: registry, containingType: self) }
for e in extensions { e.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
/// created, instead they are constructed/fetched via the `DescriptorSet` or
/// 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! { _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<Int32>]
/// 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)
for v in values { v.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! { 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 { "\(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 }