diff --git a/docs/supported-types.md b/docs/supported-types.md index 4c031d02..88555a7a 100644 --- a/docs/supported-types.md +++ b/docs/supported-types.md @@ -21,6 +21,7 @@ For types not listed here, register a [custom factory](custom-factories.md). | `Char` | `some()` | | | `Byte` | `some()` | | | `Short` | `some()` | | +| `Number` | `some()` | Resolves to a random concrete numeric type (`Int`, `Long`, `Double`, `Float`, or `Short`) | | **Standard Library** | | | | `kotlin.uuid.Uuid` | `some()` | Requires `@OptIn(ExperimentalUuidApi::class)` | | `java.util.UUID` | `some()` | | diff --git a/src/main/kotlin/dev/appoutlet/some/config/SomeConfig.kt b/src/main/kotlin/dev/appoutlet/some/config/SomeConfig.kt index afb9310c..f8fe267c 100644 --- a/src/main/kotlin/dev/appoutlet/some/config/SomeConfig.kt +++ b/src/main/kotlin/dev/appoutlet/some/config/SomeConfig.kt @@ -28,6 +28,7 @@ import dev.appoutlet.some.resolver.LocalDateTimeResolver import dev.appoutlet.some.resolver.LongResolver import dev.appoutlet.some.resolver.MapResolver import dev.appoutlet.some.resolver.NullableResolver +import dev.appoutlet.some.resolver.NumberResolver import dev.appoutlet.some.resolver.ObjectResolver import dev.appoutlet.some.resolver.SealedClassResolver import dev.appoutlet.some.resolver.SetResolver @@ -109,6 +110,7 @@ data class SomeConfig( CharResolver(random), ByteResolver(random), ShortResolver(random), + NumberResolver(random), KotlinUuidResolver(), KotlinInstantResolver(random), KotlinDurationResolver(random), diff --git a/src/main/kotlin/dev/appoutlet/some/resolver/NumberResolver.kt b/src/main/kotlin/dev/appoutlet/some/resolver/NumberResolver.kt new file mode 100644 index 00000000..308c6a46 --- /dev/null +++ b/src/main/kotlin/dev/appoutlet/some/resolver/NumberResolver.kt @@ -0,0 +1,53 @@ +package dev.appoutlet.some.resolver + +import dev.appoutlet.some.core.ResolverChain +import dev.appoutlet.some.core.TypeResolver +import kotlin.random.Random +import kotlin.reflect.KType +import kotlin.reflect.typeOf + +/** + * The concrete numeric types that can be randomly chosen when resolving a [Number] request. + * + * This list contains [Int], [Long], [Double], [Float], and [Short]. A random entry is picked + * on each resolution and delegated back to the [ResolverChain]. + */ +private val numberTypes = listOf( + typeOf(), + typeOf(), + typeOf(), + typeOf(), + typeOf() +) + +/** + * Resolves the abstract [Number] type by randomly picking one of the supported concrete numeric + * types and delegating its generation back to the [ResolverChain]. + * + * @param random Random source used to select which concrete numeric type is generated. + */ +class NumberResolver( + private val random: Random +) : TypeResolver { + + /** + * Returns `true` when [type] is exactly [Number]. + * + * @param type The type to inspect. + * @return `true` if the type is [Number], `false` otherwise. + */ + override fun canResolve(type: KType): Boolean = type == typeOf() + + /** + * Picks a random concrete numeric type from [numberTypes] and asks [chain] to resolve it. + * + * @param type The requested type. Expected to be [Number], but the actual value is produced + * by delegating to a concrete numeric resolver through [chain]. + * @param chain Resolver chain used to generate the selected concrete numeric type. + * @return A randomly chosen numeric value among [Int], [Long], [Double], [Float], or [Short]. + */ + override fun resolve(type: KType, chain: ResolverChain): Any? { + val selectedType = numberTypes.random(random) + return chain.resolve(selectedType) + } +} diff --git a/src/test/kotlin/dev/appoutlet/some/resolver/NumberResolverTest.kt b/src/test/kotlin/dev/appoutlet/some/resolver/NumberResolverTest.kt new file mode 100644 index 00000000..8517dc97 --- /dev/null +++ b/src/test/kotlin/dev/appoutlet/some/resolver/NumberResolverTest.kt @@ -0,0 +1,36 @@ +package dev.appoutlet.some.resolver + +import dev.appoutlet.some.test.defaultTestChain +import kotlin.random.Random +import kotlin.reflect.typeOf +import kotlin.test.Test +import kotlin.test.assertFalse +import kotlin.test.assertIs +import kotlin.test.assertTrue + +class NumberResolverTest { + @Test + fun `NumberResolver generates a value of type Number`() { + val resolver = NumberResolver(Random.Default) + val result = resolver.resolve(typeOf(), defaultTestChain) + assertIs(result) + } + + @Test + fun `NumberResolver canResolve detects Number type`() { + val resolver = NumberResolver(Random.Default) + assertTrue(resolver.canResolve(typeOf())) + } + + @Test + fun `NumberResolver rejects non-Number types`() { + val resolver = NumberResolver(Random.Default) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + assertFalse(resolver.canResolve(typeOf())) + } +}