package theorycrafter.utils

/**
 * The result of some computation, or a failure.
 * This class aims to be mostly compatible with [Result], but the failure type is also parameterized.
 */
@Suppress("unused")
class ValueOrError<out V, out E> private constructor(
    val isSuccess: Boolean,
    private val value: V?,
    private val error: E?,
) {


    /**
     * Whether this [ValueOrError] represents a failure.
     */
    val isFailure: Boolean
        get() = !isSuccess


    /**
     * Returns the value, or `null` if the result is a failure.
     */
    fun valueOrNull(): V? = value


    /**
     * Returns the value. This should only be called after checking whether the result is a sucess.
     */
    fun value(): V = value!!


    /**
     * Returns the error, or `null` if the result is a success.
     */
    fun failureOrNull(): E? = error


    /**
     * Returns the failure. This  should only be called after checking whether the result is a failure.
     */
    fun failure(): E = error!!


    companion object {


        /**
         * Returns a [ValueOrError] representing a success.
         */
        fun <V> success(value: V): ValueOrError<V, Nothing> = ValueOrError(true, value, null)


        /**
         * Returns a [ValueOrError] representing a failure.
         */
        fun <E> failure(error: E): ValueOrError<Nothing, E> = ValueOrError(false, null, error)


    }

}


/**
 * Converts a [Result] into a [ValueOrError] with the message of the exception as the failure.
 */
fun <T> Result<T>.toValueOrError(): ValueOrError<T, String> = fold<ValueOrError<T, String>, T>(
    onSuccess = { ValueOrError.success(it) },
    onFailure = { ValueOrError.failure(it.message ?: it.javaClass.simpleName) }
)
