Skip to content

Commit

Permalink
Add support for objects in sealed classes (#194)
Browse files Browse the repository at this point in the history
  • Loading branch information
tagantroy authored and sksamuel committed Feb 8, 2021
1 parent d837507 commit d7279ab
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ import com.sksamuel.hoplite.DecoderContext
import com.sksamuel.hoplite.Node
import com.sksamuel.hoplite.ParameterMapper
import com.sksamuel.hoplite.PrimitiveNode
import com.sksamuel.hoplite.StringNode
import com.sksamuel.hoplite.Undefined
import com.sksamuel.hoplite.fp.ValidatedNel
import com.sksamuel.hoplite.fp.flatMap
import com.sksamuel.hoplite.fp.sequence
import com.sksamuel.hoplite.isDefined
import com.sksamuel.hoplite.simpleName
import java.lang.IllegalArgumentException
import java.lang.reflect.InvocationTargetException
import kotlin.reflect.KClass
Expand Down Expand Up @@ -40,8 +42,14 @@ class DataClassDecoder : NullHandlingDecoder<Any> {
): ConfigResult<Any> {

val klass = type.classifier as KClass<*>
if (klass.constructors.isEmpty())
if (klass.constructors.isEmpty()) {
val instance = klass.objectInstance
if (instance != null && node is StringNode && node.value == type.simpleName.substringAfter("$")) {
return instance.valid()
}
return ConfigFailure.DataClassWithoutConstructor(klass).invalid()
}

val constructor = klass.constructors.first()

// we have a special case, which is a data class with a single field with the name 'value'.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ sealed class Database {

sealed class Lonely

sealed class PoolingStrategy {
object ABI : PoolingStrategy()
data class Combo(val combo: List<PoolingStrategy>) : PoolingStrategy()
object Omni: PoolingStrategy()
object OsVersion : PoolingStrategy()
}

class SealedClassDecoderTest : FunSpec({

test("sealed classes decoding") {
Expand Down Expand Up @@ -45,4 +52,27 @@ class SealedClassDecoderTest : FunSpec({
"\n" +
" - 'lonely': Sealed class class com.sksamuel.hoplite.yaml.Lonely does not define any subclasses"
}

test("object inside sealed class decoding"){
data class Config(val poolingStrategy: PoolingStrategy)
val config = ConfigLoader().loadConfigOrThrow<Config>("/sealed_class_with_object.yaml")
config.poolingStrategy shouldBe PoolingStrategy.OsVersion
}

test("list of object inside sealed class decoding"){
data class Config(val poolingStrategy: PoolingStrategy)
val config = ConfigLoader().loadConfigOrThrow<Config>("/sealed_class_with_list_of_objects.yaml")
config.poolingStrategy shouldBe PoolingStrategy.Combo(listOf(PoolingStrategy.Omni, PoolingStrategy.OsVersion))
}

test("should error for invalid value inside sealed class"){
data class Config(val poolingStrategy: PoolingStrategy)
shouldThrow<ConfigException> {
ConfigLoader().loadConfigOrThrow<Config>("/sealed_class_with_object_invalid_value.yaml")
}.message shouldBe "Error loading config because:\n" +
"\n" +
" - Could not instantiate 'com.sksamuel.hoplite.yaml.`SealedClassDecoderTest\$1\$6\$Config`' because:\n" +
"\n" +
" - 'poolingStrategy': Could not find appropriate subclass of class com.sksamuel.hoplite.yaml.PoolingStrategy: Tried com.sksamuel.hoplite.yaml.PoolingStrategy\$ABI, com.sksamuel.hoplite.yaml.PoolingStrategy\$Combo, com.sksamuel.hoplite.yaml.PoolingStrategy\$Omni, com.sksamuel.hoplite.yaml.PoolingStrategy\$OsVersion (/sealed_class_with_object_invalid_value.yaml:0:17)"
}
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
poolingStrategy:
combo: [Omni, OsVersion]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
poolingStrategy: OsVersion
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
poolingStrategy: Random

0 comments on commit d7279ab

Please sign in to comment.