@@ -45,24 +45,6 @@ class ConfigParserGenerator(private val resolver: Resolver) {
4545 val implClassName = ClassName (packageName, typeName, element.simpleName.asString() + " _Impl" )
4646 if (defaultsType != null ) {
4747 typeBuilder.addType(defaultsType)
48- val defaultImplClassName = ClassName (packageName, typeName, element.simpleName.asString() + " _Defaults" )
49- val property = PropertySpec .builder(" DEFAULTS" , defaultImplClassName, KModifier .PRIVATE , KModifier .FINAL )
50- .initializer(" %T()" , defaultImplClassName)
51- typeBuilder.addProperty(property.build())
52- if (! hasRequiredFields) {
53- val initializer = CodeBlock .builder().add(" %T(" , implClassName).indent()
54- for (i in fields.indices) {
55- if (i > 0 ) {
56- initializer.add(" ," )
57- }
58- if (fields[i].hasDefault) {
59- initializer.add(" \n DEFAULTS.%N()" , fields[i].name)
60- } else {
61- initializer.add(" \n null" )
62- }
63- }
64- initializer.unindent().add(" \n )" )
65- }
6648 } else {
6749 if (fields.isEmpty()) {
6850 typeBuilder.addProperty(
@@ -126,19 +108,7 @@ class ConfigParserGenerator(private val resolver: Resolver) {
126108 .generated(ConfigParserGenerator ::class )
127109 .addOriginatingKSFile(element)
128110 val fields = f.value
129- val hasRequiredFields = fields.stream()
130- .anyMatch { ! it.hasDefault && ! it.isNullable }
131111 val implClassName = element.toClassName()
132- if (! hasRequiredFields) {
133- val initializer = CodeBlock .builder().add(" %T(" , implClassName).indent()
134- for (i in fields.indices) {
135- if (i > 0 ) {
136- initializer.add(" ," )
137- }
138- initializer.add(" \n null" )
139- }
140- initializer.unindent().add(" \n )" )
141- }
142112 val constructor = buildConstructor(typeBuilder, fields)
143113 typeBuilder.primaryConstructor(constructor )
144114 typeBuilder.addFunction(buildExtractMethod(element, targetType.toTypeName(), implClassName, fields))
@@ -268,7 +238,7 @@ class ConfigParserGenerator(private val resolver: Resolver) {
268238
269239 private fun buildDefaultsType (type : KSType , typeDecl : KSClassDeclaration , fields : List <ConfigField >): TypeSpec ? {
270240 var hasDefaults = false
271- val defaults = TypeSpec .classBuilder (typeDecl.simpleName.asString() + " _Defaults" )
241+ val defaults = TypeSpec .objectBuilder (typeDecl.simpleName.asString() + " _Defaults" )
272242 .addModifiers(KModifier .PRIVATE )
273243 .addSuperinterface(type.toTypeName())
274244 for (tp in typeDecl.typeParameters) {
@@ -332,8 +302,36 @@ class ConfigParserGenerator(private val resolver: Resolver) {
332302 rootParse.returns(typeName.copy(true ))
333303 }
334304 }
305+ val isDataClassWithDefaults = typeDecl.classKind == ClassKind .CLASS
306+ && typeDecl.modifiers.contains(Modifier .DATA )
307+ && fields.any { it.hasDefault }
308+ val isPojo = typeDecl.classKind == ClassKind .CLASS && ! typeDecl.modifiers.contains(Modifier .DATA ) && ! typeDecl.isRecord()
309+
310+ // Parse required fields first
335311 for (field in fields) {
336- rootParse.addStatement(" val %N = this.%N(_config)" , field.name, " parse_${field.name} " )
312+ if (! field.hasDefault) {
313+ rootParse.addStatement(" val %N = this.%N(_config)" , field.name, " parse_${field.name} " )
314+ }
315+ }
316+
317+ if (isDataClassWithDefaults) {
318+ // Create local _defaults using parsed required fields (Kotlin fills in default values)
319+ val requiredNamedArgs = fields.filter { ! it.hasDefault }.joinToCode(" ,\n " ) { CodeBlock .of(" %N = %N" , it.name, it.name) }
320+ rootParse.addStatement(" val _defaults = %T(%L)" , implClassName, requiredNamedArgs)
321+ }
322+ if (isPojo) {
323+ // todo generics?
324+ rootParse.addStatement(" val _defaults = %T()" , typeDecl.toTypeName())
325+ }
326+ val defaults = when {
327+ isDataClassWithDefaults || isPojo -> CodeBlock .of(" %N" , " _defaults" )
328+ else -> CodeBlock .of(" %T" , implClassName.peerClass(typeDecl.simpleName.asString() + " _Defaults" ))
329+ }
330+
331+ for (field in fields) {
332+ if (field.hasDefault) {
333+ rootParse.addStatement(" val %N = this.%N(%L, _config)" , field.name, " parse_${field.name} " , defaults)
334+ }
337335 }
338336 if (typeDecl.classKind == ClassKind .CLASS && ! typeDecl.modifiers.contains(Modifier .DATA ) && ! typeDecl.isRecord()) {
339337 rootParse.addStatement(" val _result = %T()" , implClassName)
@@ -364,34 +362,43 @@ class ConfigParserGenerator(private val resolver: Resolver) {
364362 val parse = FunSpec .builder(" parse_" + field.name)
365363 .addModifiers(KModifier .PRIVATE )
366364 .returns(field.typeName)
365+ if (field.hasDefault) {
366+ parse.addParameter(" defaults" , typeDecl.toTypeName())
367+ }
367368 parse.addParameter(" config" , ConfigClassNames .objectValue)
368- parse.addStatement(" var value = config.get(%N)" , " _${field.name} _path" )
369- val isSupportedType = field.mapping == null && supportedTypes.containsKey(field.typeName)
370- parse.controlFlow(" if (value is %T.NullValue)" , ConfigClassNames .configValue) {
371- if (field.isNullable && ! field.hasDefault) {
372- addStatement(" return null" )
373- } else if (field.hasDefault) {
369+ parse.addStatement(" val value = config.get(%N)" , " _${field.name} _path" )
370+
371+ val returnDefaultOrThrow = CodeBlock .builder().apply {
372+ if (field.hasDefault) {
374373 if (typeDecl.classKind == ClassKind .INTERFACE ) {
375- addStatement(" return DEFAULTS .%N()" , field.name)
374+ addStatement(" return defaults .%N()" , field.name)
376375 } else {
377- addStatement(" return DEFAULTS .%N" , field.name)
376+ addStatement(" return defaults .%N" , field.name)
378377 }
379- } else if (isSupportedType) {
378+ } else if (field.isNullable) {
379+ addStatement(" return null" )
380+ } else {
380381 addStatement(" throw %T.missingValue(value)" , ConfigClassNames .configValueExtractionException)
381382 }
382- }
383+ }.build()
383384
385+ val isSupportedType = field.mapping == null && supportedTypes.containsKey(field.typeName)
384386 if (isSupportedType) {
387+ parse.controlFlow(" if (value is %T.NullValue)" , ConfigClassNames .configValue) {
388+ addCode(returnDefaultOrThrow)
389+ }
385390 parse.addStatement(" return %L" , this .parseSupportedType(field.typeName))
386391 } else if (field.isNullable) {
392+ parse.controlFlow(" if (value is %T.NullValue)" , ConfigClassNames .configValue) {
393+ addCode(returnDefaultOrThrow)
394+ }
387395 parse.addStatement(" return %N.extract(value)" , " ${field.name} _parser" )
388396 } else {
389- parse.addStatement(" var parsed = %N.extract(value)" , " ${field.name} _parser" )
397+ parse.addStatement(" val parsed = %N.extract(value)" , " ${field.name} _parser" )
390398 parse.controlFlow(" if (parsed == null)" ) {
391- parse.addStatement(" throw %T.missingValueAfterParse(value)" , ConfigClassNames .configValueExtractionException)
392- parse.nextControlFlow(" else" )
393- parse.addStatement(" return parsed" )
399+ addCode(returnDefaultOrThrow)
394400 }
401+ parse.addStatement(" return parsed" )
395402 }
396403 return parse.build()
397404 }
0 commit comments