1515///
1616// -----------------------------------------------------------------------------
1717import Foundation
18- import SwiftProtobufPluginLibrary
1918import SwiftProtobuf
20-
19+ import SwiftProtobufPluginLibrary
2120
2221class FileGenerator {
2322 private let fileDescriptor : FileDescriptor
@@ -39,104 +38,140 @@ class FileGenerator {
3938 }
4039 }
4140
42- init ( fileDescriptor: FileDescriptor ,
43- generatorOptions: GeneratorOptions ) {
41+ init (
42+ fileDescriptor: FileDescriptor ,
43+ generatorOptions: GeneratorOptions
44+ ) {
4445 self . fileDescriptor = fileDescriptor
4546 self . generatorOptions = generatorOptions
46- namer = SwiftProtobufNamer ( currentFile: fileDescriptor,
47- protoFileToModuleMappings: generatorOptions. protoToModuleMappings)
47+ namer = SwiftProtobufNamer (
48+ currentFile: fileDescriptor,
49+ protoFileToModuleMappings: generatorOptions. protoToModuleMappings
50+ )
4851 }
4952
5053 /// Generate, if `errorString` gets filled in, then report error instead of using
5154 /// what written into `printer`.
5255 func generateOutputFile( printer p: inout CodePrinter , errorString: inout String ? ) {
53- guard fileDescriptor. options. swiftPrefix. isEmpty ||
54- isValidSwiftIdentifier ( fileDescriptor. options. swiftPrefix,
55- allowQuoted: false ) else {
56- errorString = " \( fileDescriptor. name) has an 'swift_prefix' that isn't a valid Swift identifier ( \( fileDescriptor. options. swiftPrefix) ). "
57- return
56+ guard
57+ fileDescriptor. options. swiftPrefix. isEmpty
58+ || isValidSwiftIdentifier (
59+ fileDescriptor. options. swiftPrefix,
60+ allowQuoted: false
61+ )
62+ else {
63+ errorString =
64+ " \( fileDescriptor. name) has an 'swift_prefix' that isn't a valid Swift identifier ( \( fileDescriptor. options. swiftPrefix) ). "
65+ return
5866 }
59- p. print ( """
67+ p. print (
68+ """
6069 // DO NOT EDIT.
6170 // swift-format-ignore-file
71+ // swiftlint:disable all
6272 //
6373 // Generated by the Swift generator plugin for the protocol buffer compiler.
6474 // Source: \( fileDescriptor. name)
6575 //
6676 // For information on using the generated types, please see the documentation:
6777 // https://github.com/apple/swift-protobuf/
6878
69- """ )
79+ """
80+ )
7081
7182 // Attempt to bring over the comments at the top of the .proto file as
7283 // they likely contain copyrights/preamble/etc.
7384 //
7485 // The C++ FileDescriptor::GetSourceLocation(), says the location for
7586 // the file is an empty path. That never seems to have comments on it.
7687 // https://github.com/protocolbuffers/protobuf/issues/2249 opened to
77- // figure out the right way to do this since the syntax entry is
78- // optional .
79- var comments = String ( )
88+ // figure out the right way to do this but going forward best bet seems
89+ // to be to look for the "edition" or the "syntax" decl .
90+ let editionPath = IndexPath ( index : Google_Protobuf_FileDescriptorProto . FieldNumbers . edition )
8091 let syntaxPath = IndexPath ( index: Google_Protobuf_FileDescriptorProto . FieldNumbers. syntax)
81- if let syntaxLocation = fileDescriptor. sourceCodeInfoLocation ( path: syntaxPath) {
82- comments = syntaxLocation. asSourceComment ( commentPrefix: " /// " ,
83- leadingDetachedPrefix: " // " )
84- // If the was a leading or tailing comment it won't have a blank
85- // line, after it, so ensure there is one.
86- if !comments. isEmpty && !comments. hasSuffix ( " \n \n " ) {
87- comments. append ( " \n " )
88- }
92+ var commentLocation : Google_Protobuf_SourceCodeInfo . Location ? = nil
93+ if self . generatorOptions. experimentalStripNonfunctionalCodegen {
94+ // Comments are inherently non-functional, and may change subtly on
95+ // transformations.
96+ } else if let location = fileDescriptor. sourceCodeInfoLocation ( path: editionPath) {
97+ commentLocation = location
98+ } else if let location = fileDescriptor. sourceCodeInfoLocation ( path: syntaxPath) {
99+ commentLocation = location
100+ }
101+ if let commentLocation = commentLocation {
102+ let comments = commentLocation. asSourceComment (
103+ commentPrefix: " /// " ,
104+ leadingDetachedPrefix: " // "
105+ )
106+ if !comments. isEmpty {
107+ // If the was a leading or tailing comment it won't have a blank
108+ // line, after it, so ensure there is one.
109+ p. print ( comments, newlines: !comments. hasSuffix ( " \n \n " ) )
110+ }
89111 }
90112
91- p. print ( " \( comments) import Foundation " )
113+ let fileDefinesTypes =
114+ !fileDescriptor. enums. isEmpty || !fileDescriptor. messages. isEmpty || !fileDescriptor. extensions. isEmpty
92115
93- if self . generatorOptions. implementationOnlyImports,
94- self . generatorOptions. visibility == . public {
95- errorString = """
96- Cannot use @_implementationOnly imports when the proto visibility is public.
97- Either change the visibility to internal, or disable @_implementationOnly imports.
98- """
99- return
116+ var hasImports = false
117+ if fileDescriptor. needsFoundationImport {
118+ p. print ( " \( generatorOptions. importDirective. snippet) Foundation " )
119+ hasImports = true
100120 }
101121
102- // Import all other imports as @_implementationOnly if the visiblity is
103- // internal and the option is set, to avoid exposing internal types to users.
104- let visibilityAnnotation : String = {
105- if self . generatorOptions. implementationOnlyImports,
106- self . generatorOptions. visibility == . internal {
107- return " @_implementationOnly "
108- } else {
109- return " "
122+ if fileDescriptor. isBundledProto {
123+ p. print (
124+ " // 'import \( namer. swiftProtobufModuleName) ' suppressed, this proto file is meant to be bundled in the runtime. "
125+ )
126+ hasImports = true
127+ } else if fileDefinesTypes {
128+ p. print ( " \( generatorOptions. importDirective. snippet) \( namer. swiftProtobufModuleName) " )
129+ hasImports = true
130+ }
131+
132+ let neededImports = fileDescriptor. computeImports (
133+ namer: namer,
134+ directive: generatorOptions. importDirective,
135+ reexportPublicImports: generatorOptions. visibility != . internal
136+ )
137+ if !neededImports. isEmpty {
138+ if hasImports {
139+ p. print ( )
110140 }
111- } ( )
112- if !fileDescriptor. isBundledProto {
113- // The well known types ship with the runtime, everything else needs
114- // to import the runtime.
115- p. print ( " \( visibilityAnnotation) import \( namer. swiftProtobufModuleName) " )
141+ p. print ( neededImports)
142+ hasImports = true
116143 }
117- if let neededImports = generatorOptions. protoToModuleMappings. neededModules ( forFile: fileDescriptor) {
118- p. print ( )
119- for i in neededImports {
120- p. print ( " \( visibilityAnnotation) import \( i) " )
144+
145+ // If there is nothing to generate, then just record that and be done (usually means
146+ // there just was one or more services).
147+ guard fileDefinesTypes else {
148+ if hasImports {
149+ p. print ( )
121150 }
151+ p. print ( " // This file contained no messages, enums, or extensions. " )
152+ return
122153 }
123154
124155 let extensionSet =
125- ExtensionSetGenerator ( fileDescriptor: fileDescriptor,
126- generatorOptions: generatorOptions,
127- namer: namer)
156+ ExtensionSetGenerator (
157+ fileDescriptor: fileDescriptor,
158+ generatorOptions: generatorOptions,
159+ namer: namer
160+ )
128161
129162 extensionSet. add ( extensionFields: fileDescriptor. extensions)
130163
131164 let enums = fileDescriptor. enums. map {
132- return EnumGenerator ( descriptor: $0, generatorOptions: generatorOptions, namer: namer)
165+ EnumGenerator ( descriptor: $0, generatorOptions: generatorOptions, namer: namer)
133166 }
134167
135168 let messages = fileDescriptor. messages. map {
136- return MessageGenerator ( descriptor: $0,
137- generatorOptions: generatorOptions,
138- namer: namer,
139- extensionSet: extensionSet)
169+ MessageGenerator (
170+ descriptor: $0,
171+ generatorOptions: generatorOptions,
172+ namer: namer,
173+ extensionSet: extensionSet
174+ )
140175 }
141176
142177 for e in enums {
@@ -147,23 +182,13 @@ class FileGenerator {
147182 m. generateMainStruct ( printer: & p, parent: nil , errorString: & errorString)
148183 }
149184
150- var sendablePrinter = CodePrinter ( p)
151- for m in messages {
152- m. generateSendable ( printer: & sendablePrinter)
153- }
154-
155- if !sendablePrinter. isEmpty {
156- p. print ( " " , " #if swift(>=5.5) && canImport(_Concurrency) " )
157- p. append ( sendablePrinter)
158- p. print ( " #endif // swift(>=5.5) && canImport(_Concurrency) " )
159- }
160-
161185 if !extensionSet. isEmpty {
162186 let pathParts = splitPath ( pathname: fileDescriptor. name)
163187 let filename = pathParts. base + pathParts. suffix
164188 p. print (
165189 " " ,
166- " // MARK: - Extension support defined in \( filename) . " )
190+ " // MARK: - Extension support defined in \( filename) . "
191+ )
167192
168193 // Generate the Swift Extensions on the Messages that provide the api
169194 // for using the protobuf extension.
@@ -185,11 +210,13 @@ class FileGenerator {
185210 if needsProtoPackage || !enums. isEmpty || !messages. isEmpty {
186211 p. print (
187212 " " ,
188- " // MARK: - Code below here is support for the SwiftProtobuf runtime. " )
213+ " // MARK: - Code below here is support for the SwiftProtobuf runtime. "
214+ )
189215 if needsProtoPackage {
190216 p. print (
191217 " " ,
192- " fileprivate let _protobuf_package = \" \( protoPackage) \" " )
218+ " fileprivate let _protobuf_package = \" \( protoPackage) \" "
219+ )
193220 }
194221 for e in enums {
195222 e. generateRuntimeSupport ( printer: & p)
0 commit comments