| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596 |
- // 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 }
- /// 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
- /// field, first get the `Descriptor` for the message in which it is defined,
- /// then find the field. To get a `FieldDescriptor` for an extension, get the
- /// `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! { _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
- /// This should never be called directly. Use isRequired and isRepeated
- /// helper methods instead.
- @available(*, deprecated, message: "Use isRequired or isRepeated instead.")
- public var label: Google_Protobuf_FieldDescriptorProto.Label {
- if isRepeated {
- return .repeated
- } else if isRequired {
- return .required
- } else {
- return .optional
- }
- }
- // Storage for `label`, used by other apis.
- private var _label: Google_Protobuf_FieldDescriptorProto.Label
- /// Whether or not the field is required. For proto2 required fields and
- /// Editions `LEGACY_REQUIRED` fields.
- public var isRequired: Bool {
- // Implementation comes from FieldDescriptor::is_required()
- features.fieldPresence == .legacyRequired
- }
- /// Whether or not the field is repeated/map field.
- public var isRepeated: Bool { _label == .repeated }
- /// Use !isRequired() && !isRepeated() instead.
- @available(*, deprecated, message: "Use !isRequired && !isRepeated instead.")
- public var isOptional: Bool { label == .optional }
- /// Is this field packable.
- public var isPackable: Bool {
- // This logic comes from the C++ FieldDescriptor::is_packable() impl.
- isRepeated && 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.
- package var _hasOptionalKeyword: Bool {
- // This logic comes from the C++ FieldDescriptor::has_optional_keyword()
- // impl.
- proto3Optional || (file.edition == .proto2 && !isRequired && !isRepeated && 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 !isRepeated 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<Descriptor>)
- }
- 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<Descriptor>)
- case `enum`(UnownedBox<EnumDescriptor>)
- }
- 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.
- ///
- /// SwiftProtobuf does *not* generate anything for these (or methods), but
- /// 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! { _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
- for m in methods { m.bind(service: self, registry: registry) }
- }
- }
- /// Describes an individual service method.
- ///
- /// SwiftProtobuf does *not* generate anything for these (or services), but
- /// 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 { "\(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.
- ///
- /// All fullNames are like defined in descriptor.proto, they start with a
- /// leading '.'. This simplifies the string ops when assembling the message
- /// graph.
- 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`.
- private struct UnownedBox<T: AnyObject> {
- unowned let value: T
- }
|