ru.hnau.jutils.possible.Possible.kt Maven / Gradle / Ivy
package ru.hnau.jutils.possible
import ru.hnau.jutils.tryOrElse
class Possible private constructor(
val data: T?,
val error: Exception?
) {
companion object {
fun success(data: T): Possible =
Possible(data = data, error = null)
fun success() = success(Unit)
fun successOrUndefined(data: T?): Possible =
data?.let { success(it) } ?: undefined()
fun successOrError(data: T?): Possible =
data?.let { success(it) } ?: error()
fun error(error: Exception? = null): Possible =
Possible(data = null, error = error ?: Exception())
fun error(message: String?): Possible =
error(Exception(message))
fun errorOrUndefined(error: Exception?): Possible =
error?.let { error(it) } ?: undefined()
fun error(th: Throwable?): Possible =
(th as? Exception)?.let { Possible.error(it) } ?: Possible.error(th?.message)
fun errorOrUndefined(th: Throwable?): Possible =
(th as? Exception)?.let { Possible.errorOrUndefined(it) }
?: Possible.errorOrUndefined(th?.message)
fun errorOrUndefined(message: String?): Possible =
message?.let { error(it) } ?: undefined()
fun undefined(): Possible =
Possible(data = null, error = null)
inline fun trySuccessCatchError(throwsAction: () -> T): Possible =
trySuccessCatchErrorPossible { success(throwsAction.invoke()) }
inline fun trySuccessCatchUndefined(throwsAction: () -> T): Possible =
trySuccessCatchUndefinedPossible { success(throwsAction.invoke()) }
inline fun trySuccessCatchErrorPossible(throwsAction: () -> Possible): Possible =
tryOrElse(
throwsAction = throwsAction,
onThrow = { Possible.error(it) }
)
inline fun trySuccessCatchUndefinedPossible(throwsAction: () -> Possible): Possible =
tryOrElse(
throwsAction = throwsAction,
onThrow = { Possible.undefined() }
)
}
operator fun component1() = data
operator fun component2() = error
override fun toString() = "Possible{" +
"status=${handle("SUCCESS", "ERROR", "UNDEFINED")}" +
(data?.let { ", data=$it" } ?: "") +
(error?.let { ", error=$it" } ?: "") +
"}"
operator fun plus(other: Possible) =
mapPossible { thisData ->
other.mapPossible { otherData ->
Possible.success(thisData to otherData)
}
}
inline fun ifSuccess(action: (T) -> R) =
data?.let { action.invoke(it) }
inline fun ifError(action: (Exception) -> R) =
error?.let { action.invoke(it) }
inline fun ifUndefined(action: () -> R) =
if (data != null || error != null) null else action.invoke()
inline fun ifSuccessOrUndefined(action: (T?) -> R) =
if (error != null) null else action.invoke(data)
inline fun ifSuccessOrError(action: (T?) -> R) =
if (error == null && data == null) null else action.invoke(data)
inline fun ifErrorOrUndefined(action: (Exception?) -> R) =
if (data != null) null else action.invoke(error)
inline fun handle(
onSuccess: (T) -> R,
onError: (Exception) -> R,
onUndefined: () -> R
): R {
if (data != null) {
return onSuccess.invoke(data)
}
if (error != null) {
return onError.invoke(error)
}
return onUndefined.invoke()
}
inline fun handle(
onSuccess: (T) -> R,
onError: (Exception?) -> R
): R {
if (data != null) {
return onSuccess.invoke(data)
}
return onError.invoke(error)
}
fun handle(
forSuccess: R,
forError: R,
forUndefined: R
): R {
if (data != null) {
return forSuccess
}
if (error != null) {
return forError
}
return forUndefined
}
fun handle(
forSuccess: R,
forError: R
): R {
if (data != null) {
return forSuccess
}
return forError
}
fun getDataOrThrow() = handle(
onSuccess = { it },
onError = { throw (it ?: Exception()) }
)
inline fun getDataOrHandleError(
onError: (ex: Exception) -> Unit,
onUndefined: () -> Unit
) =
handle(
onSuccess = { it },
onError = { onError.invoke(it); null },
onUndefined = { onUndefined.invoke(); null }
)
inline fun getDataOrHandleError(
onError: (ex: Exception?) -> Unit
) =
handle(
onSuccess = { it },
onError = { onError.invoke(it); null }
)
inline fun map(
converter: (T) -> O
) =
data?.let { success(converter.invoke(it)) } ?: error?.let { error(it) }
?: undefined()
inline fun tryMap(
throwsConverter: (T) -> O
) =
mapPossible { trySuccessCatchError { throwsConverter.invoke(it) } }
inline fun mapPossible(
converter: (T) -> Possible
) =
data?.let { converter.invoke(it) } ?: error?.let { error(it) } ?: undefined()
inline fun tryMapPossible(
throwsConverter: (T) -> Possible
) =
mapPossible { data ->
tryOrElse(
throwsAction = { throwsConverter.invoke(data) },
onThrow = { Possible.error(it) }
)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Possible<*>) return false
if (data != null && data == other.data) return true
if (error != null && error == other.error) return true
if (data == null && other.data == null && error == null && other.error == null) return true
return false
}
override fun hashCode(): Int {
var result = data?.hashCode() ?: 0
result = 31 * result + (error?.hashCode() ?: 0)
return result
}
}