uchuhimo / konf

A type-safe cascading configuration library for Kotlin/Java/Android, supporting most configuration formats
Apache License 2.0
310 stars 28 forks source link

Multi-type items #65

Open DanySK opened 3 years ago

DanySK commented 3 years ago

Hi, I'd like to be able to express the following valid configurations:

multitype: somestring
multitype:
  a: map

I can support either one, but not both:

object FooSpec : ConfigSpec("") {
    val multitype by required<String>() // former case
}
object FooSpec : ConfigSpec("") {
    val multitype by required<Map<String, String>>() // latter case
}

Is there a way to capture both cases? I cannot rename the key.

DanySK commented 3 years ago

I tried this strategy:

class MapOrString private constructor(source: Either<String, Map<String, String>) {
    val string: String? = source.leftOrNull
    val map: Map<String, String>? = source.rightOrNull

    constructor(stringDescriptor: String) : this(Either.left(stringDescriptor))
    constructor(map: Map<String, String>) : this(Either.right(map))
}
object FooSpec : ConfigSpec("") {
    val multitype by required<MapOrString>()
}

but it fails with:

Exception in thread "main" com.uchuhimo.konf.source.LoadException: fail to load multitype
    at com.uchuhimo.konf.source.SourceKt.loadItem(Source.kt:638)
    at com.uchuhimo.konf.source.SourceKt$load$1.invoke(Source.kt:653)
    at com.uchuhimo.konf.source.SourceKt$load$1.invoke(Source.kt)
    at com.uchuhimo.konf.BaseConfig.lock(BaseConfig.kt:66)
    at com.uchuhimo.konf.source.SourceKt.load(Source.kt:651)
    at com.uchuhimo.konf.BaseConfig.withSource(BaseConfig.kt:608)
    at com.uchuhimo.konf.source.Loader.string(Loader.kt:204)
        ...
Caused by: com.uchuhimo.konf.source.ObjectMappingException: unable to map source somestring in [type: YAML, content: "
multitype: somestring
"] to value of type MapOrString
    at com.uchuhimo.konf.source.SourceKt.toValue(Source.kt:952)
    at com.uchuhimo.konf.source.SourceKt.loadItem(Source.kt:631)
    ... 8 more
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `arrow.core.Either` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
 at [Source: UNKNOWN; line: -1, column: -1]

It looks like Konf never tries secondary constructors...

DanySK commented 3 years ago

I found a way! Secondary constructors need to be annotated with @JsonCreator! This should really be documented...

stale[bot] commented 3 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed after 30 days if no further activity occurs, but feel free to re-open a closed issue if needed.