com.lightningkite.lightningdb.paths.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of server-core Show documentation
Show all versions of server-core Show documentation
A set of tools to fill in/replace what Ktor is lacking in.
The newest version!
package com.lightningkite.lightningdb
import com.lightningkite.khrysalis.IsCodableAndHashable
import kotlin.reflect.KProperty1
fun Modification.valueSetForDataClassPath(path: DataClassPath): V? =
(forDataClassPath(path.properties) as? Modification.Assign)?.value
fun Modification.forDataClassPath(path: DataClassPath): Modification? =
forDataClassPath(path.properties)
@Suppress("UNCHECKED_CAST")
private fun Modification<*>.forDataClassPath(list: List>): Modification? {
return when (this) {
is Modification.OnField<*, *> -> if (list.first() == this.key) {
if (list.size == 1) modification as Modification
else this.modification.forDataClassPath(list.drop(1))
} else null
is Modification.SetPerElement<*> -> this.modification.forDataClassPath(list)
is Modification.ListPerElement<*> -> this.modification.forDataClassPath(list)
is Modification.Chain -> this.modifications.mapNotNull { it.forDataClassPath(list) }.let {
when (it.size) {
0 -> null
1 -> it.first()
else -> Modification.Chain(it)
}
}
is Modification.IfNotNull -> this.modification.forDataClassPath(list)
is Modification.Assign -> Modification.Assign(list.fold(value) { value, prop ->
(prop as KProperty1).get(
value
)
} as V)
else -> throw Exception("We have no idea what the partial effect is!")
}
}
fun Modification<*>.affects(path: DataClassPathPartial<*>): Boolean = affects(path.properties)
private fun Modification<*>.affects(list: List>): Boolean {
return when (this) {
is Modification.OnField<*, *> -> if (list.first() == this.key) {
if (list.size == 1) true
else this.modification.affects(list.drop(1))
} else false
is Modification.SetPerElement<*> -> this.modification.affects(list)
is Modification.ListPerElement<*> -> this.modification.affects(list)
is Modification.Chain -> this.modifications.any { it.affects(list) }
is Modification.IfNotNull -> this.modification.affects(list)
else -> true
}
}
fun Condition<*>.reads(path: DataClassPathPartial<*>): Boolean = reads(path.properties)
private fun Condition<*>.reads(list: List>): Boolean {
return when (this) {
is Condition.OnField<*, *> -> if (list.first() == this.key) {
if (list.size == 1) true
else this.condition.reads(list.drop(1))
} else false
is Condition.ListAllElements<*> -> this.condition.reads(list)
is Condition.ListAnyElements<*> -> this.condition.reads(list)
is Condition.SetAllElements<*> -> this.condition.reads(list)
is Condition.SetAnyElements<*> -> this.condition.reads(list)
is Condition.And -> this.conditions.any { it.reads(list) }
is Condition.Or -> this.conditions.any { it.reads(list) }
is Condition.IfNotNull -> this.condition.reads(list)
else -> true
}
}
fun Condition.readPaths(): Set> {
val out = HashSet>()
emitReadPaths { out.add(it) }
return out
}
fun Condition.emitReadPaths(out: (DataClassPathPartial) -> Unit) = emitReadPaths(DataClassPathSelf()) { out(it as DataClassPathPartial) }
private fun Condition<*>.emitReadPaths(soFar: DataClassPath<*, *>, out: (DataClassPathPartial<*>) -> Unit) {
when (this) {
is Condition.Always -> {}
is Condition.Never -> {}
is Condition.OnField<*, *> -> condition.emitReadPaths(DataClassPathAccess(soFar as DataClassPath, key as KProperty1), out)
is Condition.Not -> this.condition.emitReadPaths(soFar, out)
is Condition.And -> this.conditions.forEach { it.emitReadPaths(soFar, out) }
is Condition.Or -> this.conditions.forEach { it.emitReadPaths(soFar, out) }
is Condition.IfNotNull<*> -> this.condition.emitReadPaths(DataClassPathNotNull(soFar as DataClassPath), out)
else -> out(soFar)
}
// COND: (a, (b, (c, x)))
// PATH: (((root, a), b), c)
}
fun Condition.readsResultOf(modification: Modification): Boolean {
return when (this) {
is Condition.Always -> false
is Condition.Never -> false
is Condition.OnField<*, *> -> {
val field = modification as? Modification.OnField<*, *> ?: return false
@Suppress("UNCHECKED_CAST")
field.key == this.key && (this.condition as Condition).readsResultOf(field.modification as Modification)
}
is Condition.ListAllElements<*> -> @Suppress("UNCHECKED_CAST") (this.condition as Condition).readsResultOf(
(modification as? Modification.ListPerElement<*>)?.modification as? Modification ?: return false
)
is Condition.ListAnyElements<*> -> @Suppress("UNCHECKED_CAST") (this.condition as Condition).readsResultOf(
(modification as? Modification.ListPerElement<*>)?.modification as? Modification ?: return false
)
is Condition.SetAllElements<*> -> @Suppress("UNCHECKED_CAST") (this.condition as Condition).readsResultOf(
(modification as? Modification.ListPerElement<*>)?.modification as? Modification ?: return false
)
is Condition.SetAnyElements<*> -> @Suppress("UNCHECKED_CAST") (this.condition as Condition).readsResultOf(
(modification as? Modification.ListPerElement<*>)?.modification as? Modification ?: return false
)
is Condition.And -> this.conditions.any { it.readsResultOf(modification) }
is Condition.Or -> this.conditions.any { it.readsResultOf(modification) }
is Condition.IfNotNull<*> -> {
@Suppress("UNCHECKED_CAST")
(this.condition as Condition).readsResultOf(
(modification as? Modification.IfNotNull)?.modification ?: modification as Modification
)
}
else -> true
}
}
@Suppress("UNCHECKED_CAST")
fun Condition.guaranteedAfter(modification: Modification): Boolean {
return when (modification) {
is Modification.Assign -> this(modification.value)
is Modification.OnField<*, *> -> {
val field = this as? Condition.OnField<*, *> ?: return true
@Suppress("UNCHECKED_CAST")
field.key != modification.key || (field.condition as Condition).guaranteedAfter(modification.modification as Modification)
}
is Modification.SetPerElement<*> -> {
@Suppress("UNCHECKED_CAST")
((this as? Condition.SetAllElements)?.condition
?: (this as? Condition.SetAnyElements)?.condition)?.let {
(it as Condition).guaranteedAfter(modification.modification as Modification)
} ?: false
}
is Modification.ListPerElement<*> -> {
@Suppress("UNCHECKED_CAST")
((this as? Condition.ListAllElements)?.condition
?: (this as? Condition.ListAnyElements)?.condition)?.let {
(it as Condition).guaranteedAfter(modification.modification as Modification)
} ?: false
}
is Modification.Chain -> modification.modifications.all { guaranteedAfter(it) }
is Modification.IfNotNull<*> -> {
@Suppress("UNCHECKED_CAST")
(this as Condition).guaranteedAfter(modification.modification as Modification)
}
else -> false
}
}
@Suppress("UNCHECKED_CAST")
fun Modification.map(
path: DataClassPath,
onModification: (Modification) -> Modification,
): Modification = (this as Modification).map(path.properties, onModification) as Modification
@Suppress("UNCHECKED_CAST")
private fun Modification<*>.map(
list: List>,
onModification: (Modification) -> Modification,
): Modification<*> {
return when (this) {
is Modification.Chain -> modifications.map { it.map(list, onModification) as Modification }
.let { Modification.Chain(it) }
is Modification.OnField<*, *> -> if (list.first() == this.key) {
if (list.size == 1) Modification.OnField(
key = this.key as KProperty1,
modification = onModification(modification as Modification) as Modification
) as Modification
else this.modification.map(list.drop(1), onModification)
} else this
is Modification.SetPerElement<*> -> (this.modification as Modification).map(list, onModification)
is Modification.ListPerElement<*> -> (this.modification as Modification).map(list, onModification)
is Modification.IfNotNull -> (this.modification as Modification).map(list, onModification)
is Modification.Assign -> {
fun mapValue(
value: Any?,
list: List>,
onValue: (V) -> V,
): Any? {
if (value == null) return null
if (list.isEmpty()) return onValue(value as V)
return list.first().setCopy(value, mapValue(list.first().get(value), list.drop(1), onValue))
}
Modification.Assign(mapValue(value, list as List>) {
(onModification(Modification.Assign(it)) as Modification.Assign).value
})
}
else -> throw Exception("We have no idea what the partial effect is!")
}
}
fun Condition<*>.walk(action: (Condition<*>)->Unit) {
action(this)
when(this) {
is Condition.And -> this.conditions.forEach { it.walk(action) }
is Condition.Or -> this.conditions.forEach { it.walk(action) }
is Condition.Not -> this.condition.walk(action)
is Condition.ListAllElements<*> -> this.condition.walk(action)
is Condition.ListAnyElements<*> -> this.condition.walk(action)
is Condition.SetAllElements<*> -> this.condition.walk(action)
is Condition.SetAnyElements<*> -> this.condition.walk(action)
is Condition.OnKey<*> -> this.condition.walk(action)
is Condition.OnField<*, *> -> this.condition.walk(action)
is Condition.IfNotNull -> this.condition.walk(action)
else -> {}
}
}