@@ -3,9 +3,13 @@ package com.auritylab.graphql.kotlin.toolkit.codegen.generator
33import com.auritylab.graphql.kotlin.toolkit.codegen.CodegenOptions
44import com.auritylab.graphql.kotlin.toolkit.codegen.mapper.GeneratedMapper
55import com.auritylab.graphql.kotlin.toolkit.codegen.mapper.KotlinTypeMapper
6+ import com.auritylab.graphql.kotlin.toolkit.common.directive.DirectiveFacade
67import com.squareup.kotlinpoet.ClassName
8+ import com.squareup.kotlinpoet.CodeBlock
79import com.squareup.kotlinpoet.FileSpec
810import com.squareup.kotlinpoet.FunSpec
11+ import com.squareup.kotlinpoet.KModifier
12+ import com.squareup.kotlinpoet.MemberName
913import com.squareup.kotlinpoet.PropertySpec
1014import com.squareup.kotlinpoet.TypeSpec
1115import graphql.schema.GraphQLEnumType
@@ -40,16 +44,105 @@ internal class EnumGenerator(
4044 .initializer(" stringValue" )
4145 .build()
4246 )
43- .also {
47+ .also { builder ->
4448 // Go through all enum values and create enum constants within this enum.
4549 enum.values.forEach { enum ->
46- it .addEnumConstant(
50+ builder .addEnumConstant(
4751 enum.name, TypeSpec .anonymousClassBuilder()
4852 .addSuperclassConstructorParameter(" %S" , enum.name)
4953 .build()
5054 )
5155 }
56+
57+ // If the "kRepresentation" directive is set on the enum, we have to add a parser/converter function
58+ DirectiveFacade .representation.getArguments(enum)?.className
59+ ?.let {
60+ // Add the output property/function.
61+ builder.addProperty(buildParserProperty(it))
62+ builder.addFunction(buildPropertyOperatorFunction())
63+
64+ // Add the input functions within a companion, as they are static.
65+ builder.addType(
66+ TypeSpec .companionObjectBuilder()
67+ .addFunction(buildInputParser(it))
68+ .addFunction(buildInputParserOperatorFunction(it))
69+ .build()
70+ )
71+ }
5272 }
5373 .build()
5474 }
75+
76+ /* *
77+ * Will build a parser property which is capable of converting the enum constant into the given
78+ * [representationClass]. The function utilizes the `.valueOf(...)` method on the enum constant.
79+ */
80+ private fun buildParserProperty (representationClass : String ): PropertySpec {
81+ // Resolve the class name and the valueOf function of the class.
82+ val parsedClass = ClassName .bestGuess(representationClass)
83+
84+ // Build the converter code.
85+ val code = CodeBlock .builder()
86+ .beginControlFlow(" return try" )
87+ .addStatement(" %L(name)" , MemberName (parsedClass, " valueOf" ).canonicalName)
88+ .endControlFlow()
89+ .beginControlFlow(" catch(ex: IllegalArgumentException)" )
90+ .addStatement(
91+ " throw NoSuchElementException(%P)" ,
92+ " Enum value '\$ name' could not be found on enum '${parsedClass.canonicalName} '"
93+ )
94+ .endControlFlow()
95+ .build()
96+
97+ return PropertySpec .builder(" representation" , parsedClass)
98+ .getter(FunSpec .getterBuilder().addCode(code).build())
99+ .build()
100+ }
101+
102+ /* *
103+ * Will build a function which overrides the invoke operator and delegates to the "representation" property
104+ * to resolve the representation.
105+ */
106+ private fun buildPropertyOperatorFunction (): FunSpec {
107+ return FunSpec .builder(" invoke" )
108+ .addModifiers(KModifier .OPERATOR )
109+ .addCode(" return representation" )
110+ .build()
111+ }
112+
113+ /* *
114+ * Will build a function which accepts the representation enum as input and returns the matching generated
115+ * enum constant. This also utilizes the #valueOf(...) method of the enum.
116+ */
117+ private fun buildInputParser (representationClass : String ): FunSpec {
118+ val code = CodeBlock .builder()
119+ .beginControlFlow(" return try" )
120+ .addStatement(" valueOf(input.name)" )
121+ .endControlFlow()
122+ .beginControlFlow(" catch(ex: IllegalArgumentException)" )
123+ .addStatement(
124+ " throw NoSuchElementException(%P)" ,
125+ " Enum value '\$ {input.name}' could not be found on enum '\$ {this::class.java.canonicalName}'"
126+ )
127+ .endControlFlow()
128+ .build()
129+
130+ // Build the function with the input and the generated code.
131+ return FunSpec .builder(" of" )
132+ .addParameter(" input" , ClassName .bestGuess(representationClass))
133+ .addCode(code)
134+ .build()
135+ }
136+
137+ /* *
138+ * Will build a invoke function override, which delegates to the #of(...) function to convert the
139+ * representation enum to the generated enum constant.
140+ */
141+ private fun buildInputParserOperatorFunction (representationClass : String ): FunSpec {
142+ return FunSpec .builder(" invoke" )
143+ .addModifiers(KModifier .OPERATOR )
144+ .addParameter(" input" , ClassName .bestGuess(representationClass))
145+ .addCode(" return of(input)" )
146+ .build()
147+ }
55148}
0 commit comments