From e7d7ed3543d86bae4c0134619af6046a68a35e22 Mon Sep 17 00:00:00 2001 From: Joseph Ivie Date: Tue, 13 Aug 2024 09:44:27 -0600 Subject: [PATCH] Better toString from condition and modification --- .../lightningkite/lightningdb/Condition.kt | 38 +++++++++++++++++++ .../lightningkite/lightningdb/Modification.kt | 30 +++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/shared/src/commonMain/kotlin/com/lightningkite/lightningdb/Condition.kt b/shared/src/commonMain/kotlin/com/lightningkite/lightningdb/Condition.kt index 2bd2f223..0fa1af0b 100644 --- a/shared/src/commonMain/kotlin/com/lightningkite/lightningdb/Condition.kt +++ b/shared/src/commonMain/kotlin/com/lightningkite/lightningdb/Condition.kt @@ -24,6 +24,8 @@ sealed class Condition { @Suppress("UNCHECKED_CAST") override fun equals(other: Any?): Boolean = (other as? Condition.Never) != null + + override fun toString(): String = "false" } @Serializable(ConditionAlwaysSerializer::class) @@ -34,84 +36,99 @@ sealed class Condition { @Suppress("UNCHECKED_CAST") override fun equals(other: Any?): Boolean = (other as? Condition.Always) != null + + override fun toString(): String = "true" } @Serializable(ConditionAndSerializer::class) @SerialName("And") data class And(val conditions: List>) : Condition() { override fun invoke(on: T): Boolean = conditions.all { it(on) } + override fun toString(): String = conditions.joinToString(" && ", "(", ")") } @Serializable(ConditionOrSerializer::class) @SerialName("Or") data class Or(val conditions: List>) : Condition() { override fun invoke(on: T): Boolean = conditions.any { it(on) } + override fun toString(): String = conditions.joinToString(" || ", "(", ")") } @Serializable(ConditionNotSerializer::class) @SerialName("Not") data class Not(val condition: Condition) : Condition() { override fun invoke(on: T): Boolean = !condition(on) + override fun toString(): String = "!($condition)" } @Serializable(ConditionEqualSerializer::class) @SerialName("Equal") data class Equal(val value: T) : Condition() { override fun invoke(on: T): Boolean = on == value + override fun toString(): String = " == $value" } @Serializable(ConditionNotEqualSerializer::class) @SerialName("NotEqual") data class NotEqual(val value: T) : Condition() { override fun invoke(on: T): Boolean = on != value + override fun toString(): String = " != $value" } @Serializable(ConditionInsideSerializer::class) @SerialName("Inside") data class Inside(val values: List) : Condition() { override fun invoke(on: T): Boolean = values.contains(on) + override fun toString(): String = " in $values" } @Serializable(ConditionNotInsideSerializer::class) @SerialName("NotInside") data class NotInside(val values: List) : Condition() { override fun invoke(on: T): Boolean = !values.contains(on) + override fun toString(): String = " !in $values" } @Serializable(ConditionGreaterThanSerializer::class) @SerialName("GreaterThan") data class GreaterThan>(val value: T) : Condition() { override fun invoke(on: T): Boolean = on > value + override fun toString(): String = " > $value" } @Serializable(ConditionLessThanSerializer::class) @SerialName("LessThan") data class LessThan>(val value: T) : Condition() { override fun invoke(on: T): Boolean = on < value + override fun toString(): String = " < $value" } @Serializable(ConditionGreaterThanOrEqualSerializer::class) @SerialName("GreaterThanOrEqual") data class GreaterThanOrEqual>(val value: T) : Condition() { override fun invoke(on: T): Boolean = on >= value + override fun toString(): String = " >= $value" } @Serializable(ConditionLessThanOrEqualSerializer::class) @SerialName("LessThanOrEqual") data class LessThanOrEqual>(val value: T) : Condition() { override fun invoke(on: T): Boolean = on <= value + override fun toString(): String = " <= $value" } @Serializable @SerialName("StringContains") data class StringContains(val value: String, val ignoreCase: Boolean = false) : Condition() { override fun invoke(on: String): Boolean = on.contains(value, ignoreCase) + override fun toString(): String = ".contains($value, $ignoreCase)" } @Serializable @SerialName("GeoDistance") data class GeoDistance(val value: GeoCoordinate, val greaterThanKilometers: Double = 0.0, val lessThanKilometers: Double = 100_000.0) : Condition() { override fun invoke(on: GeoCoordinate): Boolean = on.distanceToKilometers(value) in greaterThanKilometers..lessThanKilometers + override fun toString(): String = ".distanceToKilometers(value) in $greaterThanKilometers..$lessThanKilometers" } @Serializable @@ -122,6 +139,7 @@ sealed class Condition { val asText = on.toString() return value.split(' ').all { asText.contains(it, ignoreCase) } } + override fun toString(): String = ".fullTextSearch($value, $ignoreCase)" } @Serializable @@ -130,30 +148,35 @@ sealed class Condition { @Transient val regex = Regex(pattern, if (ignoreCase) setOf(RegexOption.IGNORE_CASE) else setOf()) override fun invoke(on: String): Boolean = regex.matches(on) + override fun toString(): String = ".contains(Regex($pattern), $ignoreCase)" } @Serializable(ConditionIntBitsClearSerializer::class) @SerialName("IntBitsClear") data class IntBitsClear(val mask: Int) : Condition() { override fun invoke(on: Int): Boolean = on and mask == 0 + override fun toString(): String = ".bitsClear(${mask.toString(16)})" } @Serializable(ConditionIntBitsSetSerializer::class) @SerialName("IntBitsSet") data class IntBitsSet(val mask: Int) : Condition() { override fun invoke(on: Int): Boolean = on and mask == mask + override fun toString(): String = ".bitsSet(${mask.toString(16)})" } @Serializable(ConditionIntBitsAnyClearSerializer::class) @SerialName("IntBitsAnyClear") data class IntBitsAnyClear(val mask: Int) : Condition() { override fun invoke(on: Int): Boolean = on and mask < mask + override fun toString(): String = ".bitsAnyClear(${mask.toString(16)})" } @Serializable(ConditionIntBitsAnySetSerializer::class) @SerialName("IntBitsAnySet") data class IntBitsAnySet(val mask: Int) : Condition() { override fun invoke(on: Int): Boolean = on and mask > 0 + override fun toString(): String = ".bitsAnySet(${mask.toString(16)})" } // TODO: Merge collection operations once Khrysalis is fully deprecated @@ -161,12 +184,14 @@ sealed class Condition { @SerialName("ListAllElements") data class ListAllElements(val condition: Condition) : Condition>() { override fun invoke(on: List): Boolean = on.all { condition(it) } + override fun toString(): String = ".all { it$condition }" } @Serializable(ConditionListAnyElementsSerializer::class) @SerialName("ListAnyElements") data class ListAnyElements(val condition: Condition) : Condition>() { override fun invoke(on: List): Boolean = on.any { condition(it) } + override fun toString(): String = ".any { it$condition }" } // TODO: Change to empty check @@ -174,18 +199,21 @@ sealed class Condition { @SerialName("ListSizesEquals") data class ListSizesEquals(val count: Int) : Condition>() { override fun invoke(on: List): Boolean = on.size == count + override fun toString(): String = ".size == $count" } @Serializable(ConditionSetAllElementsSerializer::class) @SerialName("SetAllElements") data class SetAllElements(val condition: Condition) : Condition>() { override fun invoke(on: Set): Boolean = on.all { condition(it) } + override fun toString(): String = ".all { it$condition }" } @Serializable(ConditionSetAnyElementsSerializer::class) @SerialName("SetAnyElements") data class SetAnyElements(val condition: Condition) : Condition>() { override fun invoke(on: Set): Boolean = on.any { condition(it) } + override fun toString(): String = ".any { it$condition }" } // TODO: Change to empty check @@ -193,6 +221,7 @@ sealed class Condition { @SerialName("SetSizesEquals") data class SetSizesEquals(val count: Int) : Condition>() { override fun invoke(on: Set): Boolean = on.size == count + override fun toString(): String = ".size == $count" } // TODO: Allow alternate keys once Khrysalis is fully deprecated @@ -200,6 +229,7 @@ sealed class Condition { @SerialName("Exists") data class Exists(val key: String) : Condition>() { override fun invoke(on: Map): Boolean = on.containsKey(key) + override fun toString(): String = ".containsKey($key)" } @Serializable @@ -208,6 +238,7 @@ sealed class Condition { Condition>() { @Suppress("UNCHECKED_CAST") override fun invoke(on: Map): Boolean = on.containsKey(key) && condition(on[key] as V) + override fun toString(): String = "[$key]$condition" } data class OnField( @@ -215,11 +246,18 @@ sealed class Condition { val condition: Condition, ) : Condition() { override fun invoke(on: K): Boolean = condition(key.get(on)) + override fun toString(): String { + return if(condition is Condition.OnField<*, *>) + "${key.name}.$condition" + else + "${key.name}$condition" + } } @Serializable(ConditionIfNotNullSerializer::class) @SerialName("IfNotNull") data class IfNotNull(val condition: Condition) : Condition() { override fun invoke(on: T?): Boolean = on != null && condition(on) + override fun toString(): String = " != null && $condition" } } diff --git a/shared/src/commonMain/kotlin/com/lightningkite/lightningdb/Modification.kt b/shared/src/commonMain/kotlin/com/lightningkite/lightningdb/Modification.kt index e6f44c2f..84bc87a9 100644 --- a/shared/src/commonMain/kotlin/com/lightningkite/lightningdb/Modification.kt +++ b/shared/src/commonMain/kotlin/com/lightningkite/lightningdb/Modification.kt @@ -23,36 +23,42 @@ sealed class Modification { val on = modifications[0].invokeDefault() return modifications.drop(1).fold(on) { item, mod -> mod(item) } } + override fun toString(): String = modifications.joinToString("; ") } @Serializable(ModificationIfNotNullSerializer::class) data class IfNotNull(val modification: Modification): Modification() { override fun invoke(on: T?): T? = on?.let { modification(it) } override fun invokeDefault(): T? = null + override fun toString(): String = "?$modification" } @Serializable(ModificationAssignSerializer::class) data class Assign(val value: T): Modification() { override fun invoke(on: T): T = value override fun invokeDefault(): T = value + override fun toString(): String = " = $value" } @Serializable(ModificationCoerceAtMostSerializer::class) data class CoerceAtMost>(val value: T): Modification() { override fun invoke(on: T): T = on.coerceAtMost(value) override fun invokeDefault(): T = value + override fun toString(): String = " = .coerceAtMost($value)" } @Serializable(ModificationCoerceAtLeastSerializer::class) data class CoerceAtLeast>(val value: T): Modification() { override fun invoke(on: T): T = on.coerceAtLeast(value) override fun invokeDefault(): T = value + override fun toString(): String = " = .coerceAtLeast($value)" } @Serializable(ModificationIncrementSerializer::class) data class Increment(val by: T): Modification() { override fun invoke(on: T): T = on + by override fun invokeDefault(): T = by + override fun toString(): String = " += $by" } @Serializable(ModificationMultiplySerializer::class) @@ -68,30 +74,35 @@ sealed class Modification { is Double -> 0.0 as T else -> throw NotImplementedError() } + override fun toString(): String = " *= $by" } @Serializable(ModificationAppendStringSerializer::class) data class AppendString(val value: String): Modification() { override fun invoke(on: String): String = on + value override fun invokeDefault(): String = value + override fun toString(): String = " += $value" } @Serializable(ModificationListAppendSerializer::class) data class ListAppend(val items: List): Modification>() { override fun invoke(on: List): List = on + items override fun invokeDefault(): List = items + override fun toString(): String = " += $items" } @Serializable(ModificationListRemoveSerializer::class) data class ListRemove(val condition: Condition): Modification>() { override fun invoke(on: List): List = on.filter { !condition(it) } override fun invokeDefault(): List = listOf() + override fun toString(): String = ".removeAll { it.$condition }" } @Serializable(ModificationListRemoveInstancesSerializer::class) data class ListRemoveInstances(val items: List): Modification>() { override fun invoke(on: List): List = on - items override fun invokeDefault(): List = listOf() + override fun toString(): String = " -= $items" } @Serializable(ModificationListDropFirstSerializer::class) @@ -101,6 +112,7 @@ sealed class Modification { override fun hashCode(): Int = 1 @Suppress("UNCHECKED_CAST") override fun equals(other: Any?): Boolean = (other as? Modification.ListDropFirst) != null + override fun toString(): String = ".removeFirst()" } @Serializable(ModificationListDropLastSerializer::class) @@ -110,6 +122,7 @@ sealed class Modification { override fun hashCode(): Int = 1 @Suppress("UNCHECKED_CAST") override fun equals(other: Any?): Boolean = (other as? Modification.ListDropLast) != null + override fun toString(): String = ".removeLast()" } @Serializable() @@ -117,24 +130,28 @@ sealed class Modification { data class ListPerElement(val condition: Condition, val modification: Modification): Modification>() { override fun invoke(on: List): List = on.map { if(condition(it)) modification(it) else it } override fun invokeDefault(): List = listOf() + override fun toString(): String = ".onEach { if (it.$condition) it.$modification }" } @Serializable(ModificationSetAppendSerializer::class) data class SetAppend(val items: Set): Modification>() { override fun invoke(on: Set): Set = (on + items) override fun invokeDefault(): Set = items + override fun toString(): String = " += $items" } @Serializable(ModificationSetRemoveSerializer::class) data class SetRemove(val condition: Condition): Modification>() { override fun invoke(on: Set): Set = on.filter { !condition(it) }.toSet() override fun invokeDefault(): Set = setOf() + override fun toString(): String = ".removeAll { it.$condition }" } @Serializable(ModificationSetRemoveInstancesSerializer::class) data class SetRemoveInstances(val items: Set): Modification>() { override fun invoke(on: Set): Set = on - items override fun invokeDefault(): Set = setOf() + override fun toString(): String = " -= $items" } @Serializable(ModificationSetDropFirstSerializer::class) @@ -144,6 +161,7 @@ sealed class Modification { override fun hashCode(): Int = 1 @Suppress("UNCHECKED_CAST") override fun equals(other: Any?): Boolean = (other as? Modification.SetDropFirst) != null + override fun toString(): String = ".removeFirst()" } @Serializable(ModificationSetDropLastSerializer::class) @@ -153,6 +171,7 @@ sealed class Modification { override fun hashCode(): Int = 1 @Suppress("UNCHECKED_CAST") override fun equals(other: Any?): Boolean = (other as? Modification.SetDropLast) != null + override fun toString(): String = ".removeLast()" } @Serializable() @@ -160,12 +179,14 @@ sealed class Modification { data class SetPerElement(val condition: Condition, val modification: Modification): Modification>() { override fun invoke(on: Set): Set = on.map { if(condition(it)) modification(it) else it }.toSet() override fun invokeDefault(): Set = setOf() + override fun toString(): String = ".onEach { if ($condition) $modification }" } @Serializable(ModificationCombineSerializer::class) data class Combine(val map: Map): Modification>() { override fun invoke(on: Map): Map = on + map override fun invokeDefault(): Map = map + override fun toString(): String = " += $map" } @Serializable(ModificationModifyByKeySerializer::class) @@ -178,6 +199,7 @@ sealed class Modification { data class RemoveKeys(val fields: Set): Modification>() { override fun invoke(on: Map): Map = on.filterKeys { it !in fields } override fun invokeDefault(): Map = mapOf() + override fun toString(): String = " -= $fields" } data class OnField(val key: SerializableProperty, val modification: Modification): Modification() { @@ -185,5 +207,13 @@ sealed class Modification { override fun invokeDefault(): K { throw IllegalStateException("Cannot mutate a field that doesn't exist") } + override fun toString(): String { + return if(modification is Modification.OnField<*, *>) + "${key.name}.$modification" + else if(modification is Modification.Chain<*>) + "${key.name}.let { $modification }" + else + "${key.name}$modification" + } } }