fuookami.ospf.kotlin.utils.functional.Either.kt Maven / Gradle / Ivy
package fuookami.ospf.kotlin.utils.functional
import kotlinx.serialization.*
import kotlinx.serialization.json.*
import kotlinx.serialization.encoding.*
import kotlinx.serialization.descriptors.*
import fuookami.ospf.kotlin.utils.concept.*
data class EitherSerializer(
val leftSerializer: KSerializer,
val rightSerializer: KSerializer,
) : KSerializer> {
@OptIn(ExperimentalSerializationApi::class, InternalSerializationApi::class)
override val descriptor: SerialDescriptor = SerialDescriptor("Either", JsonElement::class.serializer().descriptor)
@OptIn(InternalSerializationApi::class)
override fun deserialize(decoder: Decoder): Either {
decoder as? JsonDecoder ?: throw IllegalStateException(
"This serializer can be used only with Json format." +
"Expected Decoder to be JsonDecoder, got ${this::class}"
)
val json = Json {
ignoreUnknownKeys = true
}
val element = decoder.decodeSerializableValue(JsonElement::class.serializer())
return try {
val leftValue = json.decodeFromJsonElement(leftSerializer, element)
Either.Left(leftValue)
} catch (e: Exception) {
e.printStackTrace()
val rightValue = json.decodeFromJsonElement(rightSerializer, element)
Either.Right(rightValue)
}
}
override fun serialize(encoder: Encoder, value: Either) {
when (value) {
is Either.Left -> {
encoder.encodeSerializableValue(leftSerializer, value.value)
}
is Either.Right -> {
encoder.encodeSerializableValue(rightSerializer, value.value)
}
}
}
}
sealed class Either {
data class Left(val value: L) : Either() {
override fun hashCode(): Int = value.hashCode()
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Left<*, *>) return false
if (value != other.value) return false
return true
}
override fun toString() = "$value"
}
data class Right(val value: R) : Either() {
override fun hashCode(): Int = value.hashCode()
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Right<*, *>) return false
if (value != other.value) return false
return true
}
override fun toString() = "$value"
}
val isLeft get() = this is Left
val isRight get() = this is Right
val left: L?
get() = when (this) {
is Left -> {
this.value
}
else -> {
null
}
}
val right: R?
get() = when (this) {
is Right -> {
this.value
}
else -> {
null
}
}
fun ifLeft(extractor: Extractor) = EitherMatcher(this).ifLeft(extractor)
fun ifRight(extractor: Extractor) = EitherMatcher(this).ifRight(extractor)
@JvmName("mapLeft")
fun map(extractor: Extractor): Either {
return when (this) {
is Left -> {
Left(extractor(this.value))
}
is Right -> {
Right(this.value)
}
}
}
@JvmName("mapRight")
fun map(extractor: Extractor): Either {
return when (this) {
is Left -> {
Left(this.value)
}
is Right -> {
Right(extractor(this.value))
}
}
}
fun map(extractor1: Extractor, extractor2: Extractor): Either {
return when (this) {
is Left -> {
Left(extractor1(this.value))
}
is Right -> {
Right(extractor2(this.value))
}
}
}
}
class EitherMatcher(
private val value: Either
) {
private lateinit var leftCallBack: (L) -> Ret
private lateinit var rightCallBack: (R) -> Ret
fun ifLeft(callBack: (L) -> Ret): EitherMatcher {
leftCallBack = callBack
return this
}
fun ifRight(callBack: (R) -> Ret): EitherMatcher {
rightCallBack = callBack
return this
}
@Throws(NullPointerException::class)
operator fun invoke(): Ret = when (value) {
is Either.Left -> {
leftCallBack(value.value); }
is Either.Right -> {
rightCallBack(value.value); }
}
}
@Throws(NullPointerException::class)
fun match(
value: Either,
leftCallBack: (L) -> Ret,
rightCallBack: (R) -> Ret
): Ret {
val matcher = value.ifLeft(leftCallBack).ifRight(rightCallBack)
return matcher()
}
fun , R : Copyable> Either.copy(
value: Either
): Either {
return when (value) {
is Either.Left -> {
Either.Left(copy(value.value))
}
is Either.Right -> {
Either.Right(copy(value.value))
}
}
}
fun , R : Movable> Either.move(
value: Either
): Either {
return when (value) {
is Either.Left -> {
Either.Left(move(value.value))
}
is Either.Right -> {
Either.Right(move(value.value))
}
}
}