com.ancientlightstudios.quarkus.kotlin.openapi.Maybe.kt Maven / Gradle / Ivy
package com.ancientlightstudios.quarkus.kotlin.openapi
import com.ancientlightstudios.quarkus.kotlin.openapi.Maybe.Failure
import com.ancientlightstudios.quarkus.kotlin.openapi.Maybe.Success
sealed class Maybe(val context: String) {
class Success(context: String, val value: T) : Maybe(context) {
override fun onSuccess(block: Success.() -> Maybe): Maybe = block(this)
fun success(value: O) = Success(context, value)
fun failure(error: ValidationError) = Failure(context, error)
fun failure(errors: List) = Failure(context, errors)
}
class Failure(context: String, val errors: List) : Maybe(context) {
constructor(context: String, error: ValidationError) : this(context, listOf(error))
@Suppress("UNCHECKED_CAST")
override fun onSuccess(block: Success.() -> Maybe): Maybe = this as Maybe
}
/**
* executes the given block and returns its result if this maybe is a [Success] or just returns the [Failure]
*/
abstract fun onSuccess(block: Success.() -> Maybe): Maybe
}
/**
* combines the given maybes. Returns a [Success] with the result of the builder if all given maybes are
* [Success] too, a [Failure] otherwise.
*/
@Suppress("unused")
fun maybeAllOf(context: String, vararg maybes: Maybe<*>, builder: () -> T): Maybe {
val errors = maybes.filterIsInstance>().flatMap { it.errors }
return if (errors.isEmpty()) {
Success(context, builder())
} else {
Failure(context, errors)
}
}
/**
* combines the given maybes. Returns a [Success] with the result of the builder if at least one given maybe is
* a [Success], a [Failure] otherwise.
*/
@Suppress("unused")
fun maybeAnyOf(context: String, vararg maybes: Maybe<*>, builder: () -> T): Maybe {
return if (maybes.any { it is Success }) {
Success(context, builder())
} else {
val errors = maybes.filterIsInstance>().flatMap { it.errors }
Failure(context, errors)
}
}
/**
* combines the given maybes. Returns a [Success] with the result of the builder if exactly one given maybe
* is a [Success], a [Failure] otherwise.
*/
@Suppress("unused")
fun maybeOneOf(context: String, vararg maybes: Maybe<*>, builder: () -> T): Maybe {
val count = maybes.count { it is Success }
return if (count == 1) {
Success(context, builder())
} else if (count > 1) {
Failure(context, ValidationError("is ambiguous", context))
} else {
val errors = maybes.filterIsInstance>().flatMap { it.errors }
Failure(context, errors)
}
}
/**
* executes the given block and returns its result if this maybe is a [Success]. Returns a [Failure] if an
* exception occurred or this maybe already is a [Failure]
*/
@Suppress("unused")
inline fun Maybe.map(
validationMessage: String = "is not a valid value",
crossinline block: (I?) -> O?
): Maybe =
onSuccess {
try {
success(block(value))
} catch (e: Exception) {
failure(ValidationError(validationMessage, context))
}
}
/**
* executes the given block and returns its result if this maybe is a [Success] and has a non-null value. Returns a
* [Failure] if an exception occurred or this maybe already is a [Failure]
*/
@Suppress("unused")
inline fun Maybe.mapNotNull(
validationMessage: String = "is not a valid value",
crossinline block: (I) -> O?
): Maybe =
onNotNull {
try {
success(block(value))
} catch (_: Exception) {
failure(ValidationError(validationMessage, context))
}
}
/**
* executes the given block if this maybe is a [Failure] or returns the value of the [Success]
*/
@Suppress("unused")
inline fun Maybe.validOrElse(block: (List) -> Nothing): T {
if (this is Failure) {
block(errors)
}
return (this as Success).value
}
inline fun Maybe.doOnSuccess(block: (T) -> Unit) {
if (this is Success) {
block(value)
}
}