@@ -11,18 +11,33 @@ import kotlinx.rpc.protobuf.model.*
1111
1212private val modelCache = mutableMapOf<Descriptors .GenericDescriptor , Any >()
1313
14- fun CodeGeneratorRequest.toCommonModel (): Model {
14+ /* *
15+ * Converts a [CodeGeneratorRequest] into the protoc plugin [Model] of the protobuf.
16+ *
17+ * @return a [Model] instance containing a list of [FileDeclaration]s that represent
18+ * the converted protobuf files.
19+ */
20+ fun CodeGeneratorRequest.toModel (): Model {
1521 val protoFileMap = protoFileList.associateBy { it.name }
1622 val fileDescriptors = mutableMapOf<String , Descriptors .FileDescriptor >()
1723
1824 val files = protoFileList.map { protoFile -> protoFile.toDescriptor(protoFileMap, fileDescriptors) }
1925
2026 return Model (
21- files = files.map { it.toCommonModel () }
27+ files = files.map { it.toModel () }
2228 )
2329}
2430
2531
32+ /* *
33+ * Converts a [DescriptorProtos.FileDescriptorProto] instance to a [Descriptors.FileDescriptor],
34+ * resolving its dependencies.
35+ *
36+ * @param protoFileMap a map of file names to `FileDescriptorProto` objects which are available for resolution.
37+ * @param cache a mutable map that stores already resolved `FileDescriptor` objects by file name to prevent
38+ * redundant computations.
39+ * @return the resolved `FileDescriptor` instance corresponding to this `FileDescriptorProto`.
40+ */
2641private fun DescriptorProtos.FileDescriptorProto.toDescriptor (
2742 protoFileMap : Map <String , DescriptorProtos .FileDescriptorProto >,
2843 cache : MutableMap <String , Descriptors .FileDescriptor >
@@ -39,6 +54,38 @@ private fun DescriptorProtos.FileDescriptorProto.toDescriptor(
3954 return fileDescriptor
4055}
4156
57+ /* *
58+ * Returns the fully qualified name [FqName] of this descriptor, resolving it to the most specific
59+ * declaration or package name based on its type.
60+ *
61+ * Depending on the type of the descriptor, the fully qualified name is computed recursively,
62+ * using the containing type or file, and appropriately converting names.
63+ *
64+ * @return The fully qualified name represented as an instance of FqName, specific to the descriptor's context.
65+ */
66+ private fun Descriptors.GenericDescriptor.fqName (): FqName {
67+ val nameCapital = name.simpleProtoNameToKotlin(firstLetterUpper = true )
68+ val nameLower = name.simpleProtoNameToKotlin()
69+ return when (this ) {
70+ is Descriptors .FileDescriptor -> FqName .Package .fromString(`package`)
71+ is Descriptors .Descriptor -> FqName .Declaration (nameCapital, containingType?.fqName() ? : file.fqName())
72+ is Descriptors .FieldDescriptor -> {
73+ val usedName = if (realContainingOneof != null ) nameCapital else nameLower
74+ FqName .Declaration (usedName, containingType?.fqName() ? : file.fqName())
75+ }
76+
77+ is Descriptors .EnumValueDescriptor -> FqName .Declaration (name, type.fqName())
78+ is Descriptors .OneofDescriptor -> FqName .Declaration (nameCapital, containingType?.fqName() ? : file.fqName())
79+ is Descriptors .ServiceDescriptor -> FqName .Declaration (nameCapital, file?.fqName() ? : file.fqName())
80+ is Descriptors .MethodDescriptor -> FqName .Declaration (nameLower, service?.fqName() ? : file.fqName())
81+ else -> error(" Unknown generic descriptor: $this " )
82+ }
83+ }
84+
85+ /* *
86+ * Caches the `descriptor.toModel()` result in the [modelCache] to ensure that only a single object
87+ * per descriptor exists.
88+ */
4289private inline fun <D , reified T > D.cached (block : (D ) -> T ): T
4390 where D : Descriptors .GenericDescriptor , T : Any {
4491 if (modelCache.containsKey(this )) {
@@ -49,35 +96,35 @@ private inline fun <D, reified T> D.cached(block: (D) -> T): T
4996 return declaration
5097}
5198
52- private fun Descriptors.FileDescriptor.toCommonModel (): FileDeclaration = cached {
99+ private fun Descriptors.FileDescriptor.toModel (): FileDeclaration = cached {
53100 return FileDeclaration (
54101 name = kotlinFileName(),
55102 packageName = FqName .Package .fromString(`package`),
56- dependencies = dependencies.map { it.toCommonModel () },
57- messageDeclarations = messageTypes.map { it.toCommonModel () },
58- enumDeclarations = enumTypes.map { it.toCommonModel () },
59- serviceDeclarations = services.map { it.toCommonModel () },
103+ dependencies = dependencies.map { it.toModel () },
104+ messageDeclarations = messageTypes.map { it.toModel () },
105+ enumDeclarations = enumTypes.map { it.toModel () },
106+ serviceDeclarations = services.map { it.toModel () },
60107 doc = null ,
61108 dec = this ,
62109 )
63110}
64111
65- private fun Descriptors.Descriptor.toCommonModel (): MessageDeclaration = cached {
66- val regularFields = fields.filter { field -> field.realContainingOneof == null }.map { it.toCommonModel () }
112+ private fun Descriptors.Descriptor.toModel (): MessageDeclaration = cached {
113+ val regularFields = fields.filter { field -> field.realContainingOneof == null }.map { it.toModel () }
67114
68115 return MessageDeclaration (
69116 name = fqName(),
70117 actualFields = regularFields,
71118 // get all oneof declarations that are not created from an optional in proto3 https://github.com/googleapis/api-linter/issues/1323
72- oneOfDeclarations = oneofs.filter { it.fields[0 ].realContainingOneof != null }.map { it.toCommonModel () },
73- enumDeclarations = enumTypes.map { it.toCommonModel () },
74- nestedDeclarations = nestedTypes.map { it.toCommonModel () },
119+ oneOfDeclarations = oneofs.filter { it.fields[0 ].realContainingOneof != null }.map { it.toModel () },
120+ enumDeclarations = enumTypes.map { it.toModel () },
121+ nestedDeclarations = nestedTypes.map { it.toModel () },
75122 doc = null ,
76123 dec = this ,
77124 )
78125}
79126
80- private fun Descriptors.FieldDescriptor.toCommonModel (): FieldDeclaration = cached {
127+ private fun Descriptors.FieldDescriptor.toModel (): FieldDeclaration = cached {
81128 toProto().hasProto3Optional()
82129 return FieldDeclaration (
83130 name = fqName().simpleName,
@@ -88,15 +135,15 @@ private fun Descriptors.FieldDescriptor.toCommonModel(): FieldDeclaration = cach
88135}
89136
90137
91- private fun Descriptors.OneofDescriptor.toCommonModel (): OneOfDeclaration = cached {
138+ private fun Descriptors.OneofDescriptor.toModel (): OneOfDeclaration = cached {
92139 return OneOfDeclaration (
93140 name = fqName(),
94- variants = fields.map { it.toCommonModel () },
141+ variants = fields.map { it.toModel () },
95142 dec = this ,
96143 )
97144}
98145
99- private fun Descriptors.EnumDescriptor.toCommonModel (): EnumDeclaration = cached {
146+ private fun Descriptors.EnumDescriptor.toModel (): EnumDeclaration = cached {
100147 val entriesMap = mutableMapOf<Int , EnumDeclaration .Entry >()
101148 val aliases = mutableListOf<EnumDeclaration .Alias >()
102149
@@ -105,7 +152,7 @@ private fun Descriptors.EnumDescriptor.toCommonModel(): EnumDeclaration = cached
105152 val original = entriesMap.getValue(value.number)
106153 aliases.add(value.toAliasModel(original))
107154 } else {
108- entriesMap[value.number] = value.toCommonModel ()
155+ entriesMap[value.number] = value.toModel ()
109156 }
110157 }
111158
@@ -122,7 +169,7 @@ private fun Descriptors.EnumDescriptor.toCommonModel(): EnumDeclaration = cached
122169 )
123170}
124171
125- private fun Descriptors.EnumValueDescriptor.toCommonModel (): EnumDeclaration .Entry = cached {
172+ private fun Descriptors.EnumValueDescriptor.toModel (): EnumDeclaration .Entry = cached {
126173 return EnumDeclaration .Entry (
127174 name = fqName(),
128175 doc = null ,
@@ -140,19 +187,19 @@ private fun Descriptors.EnumValueDescriptor.toAliasModel(original: EnumDeclarati
140187 )
141188}
142189
143- private fun Descriptors.ServiceDescriptor.toCommonModel (): ServiceDeclaration = cached {
190+ private fun Descriptors.ServiceDescriptor.toModel (): ServiceDeclaration = cached {
144191 return ServiceDeclaration (
145192 name = fqName(),
146- methods = methods.map { it.toCommonModel () },
193+ methods = methods.map { it.toModel () },
147194 dec = this ,
148195 )
149196}
150197
151- private fun Descriptors.MethodDescriptor.toCommonModel (): MethodDeclaration = cached {
198+ private fun Descriptors.MethodDescriptor.toModel (): MethodDeclaration = cached {
152199 return MethodDeclaration (
153200 name = name,
154- inputType = inputType.toCommonModel (),
155- outputType = outputType.toCommonModel (),
201+ inputType = inputType.toModel (),
202+ outputType = outputType.toModel (),
156203 dec = this ,
157204 )
158205}
@@ -176,41 +223,25 @@ private fun Descriptors.FieldDescriptor.modelType(): FieldType {
176223 Descriptors .FieldDescriptor .Type .SFIXED64 -> FieldType .IntegralType .SFIXED64
177224 Descriptors .FieldDescriptor .Type .SINT32 -> FieldType .IntegralType .SINT32
178225 Descriptors .FieldDescriptor .Type .SINT64 -> FieldType .IntegralType .SINT64
179- Descriptors .FieldDescriptor .Type .ENUM -> FieldType .Reference (lazy { enumType!! .toCommonModel ().name })
180- Descriptors .FieldDescriptor .Type .MESSAGE -> FieldType .Reference (lazy { messageType!! .toCommonModel ().name })
226+ Descriptors .FieldDescriptor .Type .ENUM -> FieldType .Reference (lazy { enumType!! .toModel ().name })
227+ Descriptors .FieldDescriptor .Type .MESSAGE -> FieldType .Reference (lazy { messageType!! .toModel ().name })
181228 Descriptors .FieldDescriptor .Type .GROUP -> error(" GROUP type is unsupported" )
182229 }
183230
231+ if (isMapField) {
232+ val keyType = messageType.findFieldByName(" key" ).modelType()
233+ val valType = messageType.findFieldByName(" value" ).modelType()
234+ val mapEntry = FieldType .Map .Entry (keyType, valType)
235+ return FieldType .Map (lazy { mapEntry })
236+ }
237+
184238 if (isRepeated) {
185239 return FieldType .List (baseType)
186240 }
187241
188- // TODO: Handle map type
189-
190242 return baseType
191243}
192244
193- // // GenericDescriptor Extensions ////
194-
195- private fun Descriptors.GenericDescriptor.fqName (): FqName {
196- val nameCapital = name.simpleProtoNameToKotlin(firstLetterUpper = true )
197- val nameLower = name.simpleProtoNameToKotlin()
198- return when (this ) {
199- is Descriptors .FileDescriptor -> FqName .Package .fromString(`package`)
200- is Descriptors .Descriptor -> FqName .Declaration (nameCapital, containingType?.fqName() ? : file.fqName())
201- is Descriptors .FieldDescriptor -> {
202- val usedName = if (realContainingOneof != null ) nameCapital else nameLower
203- FqName .Declaration (usedName, containingType?.fqName() ? : file.fqName())
204- }
205-
206- is Descriptors .EnumValueDescriptor -> FqName .Declaration (name, type.fqName())
207- is Descriptors .OneofDescriptor -> FqName .Declaration (nameCapital, containingType?.fqName() ? : file.fqName())
208- is Descriptors .ServiceDescriptor -> FqName .Declaration (nameCapital, file?.fqName() ? : file.fqName())
209- is Descriptors .MethodDescriptor -> FqName .Declaration (nameLower, service?.fqName() ? : file.fqName())
210- else -> error(" Unknown generic descriptor: $this " )
211- }
212- }
213-
214245// // Utility Extensions ////
215246
216247private fun Descriptors.FileDescriptor.kotlinFileName (): String {
0 commit comments