io.decomat.ProductClass.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of decomat-core Show documentation
Show all versions of decomat-core Show documentation
DecoMat - Deconstructive Pattern Matching for Kotlin
package io.decomat
import java.util.WeakHashMap
import kotlin.reflect.KProperty1
import kotlin.reflect.full.declaredMemberProperties
import kotlin.reflect.full.isSubclassOf
import kotlin.reflect.full.memberProperties
import kotlin.reflect.full.primaryConstructor
sealed interface ProductClass {
val value: T
fun isIfHas() =
when(val thisComp = this) {
is HasProductClass -> thisComp.productComponents
else -> this
}
}
private fun fail(msg: String): Nothing = throw IllegalArgumentException(msg)
// Does not seem to work, no annotations are found
//@Suppress("UNCHECKED_CAST")
//interface HasProductClassAuto: HasProductClass {
// override val productComponents: ProductClass get() =
// cache.computeIfAbsent(this) { _doInit() } as ProductClass
//
// fun _doInit(): ProductClass {
// val cls = this::class
// val ctor = cls.primaryConstructor ?: fail("No primary constructor found in the class ${this}")
// println(cls.memberProperties.map { "${it.name} (${it.annotations})" })
// val componentNames = ctor.parameters.filter { it.annotations.any { anno -> anno.annotationClass.qualifiedName == "io.decomat.Component" } }.map { it.name }
// if (componentNames.isEmpty()) fail("No components annotated with @Component found in the class ${this}. Found components: ${ctor.parameters.map { it.name }}.")
// val components: List, *>> =
// cls.memberProperties.filter { componentNames.contains(it.name) }
// if (components.size != componentNames.size)
// fail("Not all the parameters with @Component annoations (${componentNames.joinToString(", ")}) were found to be components (${components.joinToString { "," }})")
//
// fun comp(i: Int) = components[i].getter.call(this)
//
// val productClass =
// when(components.size) {
// 1 -> ProductClass1(this as T, comp(0))
// 2 -> ProductClass2(this as T, comp(0), comp(1))
// 3 -> ProductClass3(this as T, comp(0), comp(1), comp(2))
// else -> fail("Num components needs to be 1, 2, or 3 but was: ${components.size}")
// }
//
// return productClass
// }
//
// // Sigh, need to use a global cache to compute product-classes we've since since you can't assign
// // values directly to varaibles inside of kotlin interfaces
// companion object {
// val cache = WeakHashMap, ProductClass<*>>()
// }
//}
interface HasProductClass: ProductClass {
val productComponents: ProductClass
override val value get() = productComponents.value
}
fun productComponentsOf(host: T) = ProductClass0(host)
fun productComponentsOf(host: T, componentA: A) = ProductClass1(host, componentA)
fun productComponentsOf(host: T, componentA: A, componentB: B) = ProductClass2(host, componentA, componentB)
fun productComponentsOf(host: T, componentA: A, componentB: B, componentC: C) = ProductClass3(host, componentA, componentB, componentC)
data class ProductClass0(override val value: T): ProductClass
data class ProductClass1(override val value: T, val a: A): ProductClass
data class ProductClass2(override val value: T, val a: A, val b: B): ProductClass {
val matchComp get(): Components2 = Components2(a, b)
}
// For example: data class FlatMap(head: Query, body: Query) extends Comp3
data class ProductClass3(override val value: T, val a: A, val b: B, val c: C): ProductClass
sealed interface Components
data class Components1(val a: A): Components
// For example: data class FlatMap(head: Query, body: Query) extends Comp2
data class Components2(val a: A, val b: B): Components
data class Components3(val a: A, val b: B, val c: C): Components