# ResultWrapper.kt

    /*
     * The MIT License
     *
     * Original work sponsored and donated by The Danish Health Data Authority (http://www.sundhedsdatastyrelsen.dk)
     *
     * Copyright (C) 2021 The Danish Health Data Authority (http://www.sundhedsdatastyrelsen.dk)
     *
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy of
     * this software and associated documentation files (the "Software"), to deal in
     * the Software without restriction, including without limitation the rights to
     * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
     * of the Software, and to permit persons to whom the Software is furnished to do
     * so, subject to the following conditions:
     *
     *
     * The above copyright notice and this permission notice shall be included in all
     * copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     * SOFTWARE.
     */

    package dk.nsp.shared

    import timber.log.Timber
    import kotlin.contracts.ExperimentalContracts
    import kotlin.contracts.InvocationKind
    import kotlin.contracts.contract

    sealed class ResultWrapper<out T> {
        companion object {
            fun <T, E : Throwable> map(result: T?, error: E): ResultWrapper<T> {
                return if (result != null) {
                    Success(result)
                } else {
                    Failed(error)
                }
            }
        }
    }

    // Declaring outside parent to avoid having to reference these by 'ResultWrapper.'
    data class Success<out T>(val value: T) : ResultWrapper<T>()
    data class Failed(val error: Throwable) : ResultWrapper<Nothing>()

    fun <T> T.toSuccess(): Success<T> {
        return Success(this)
    }

    /**
     * If receiving [ResultWrapper] is a Success, it maps it's [Success.value] with the given mapper function
     * Otherwise it's a [Failed] and it returns this
     * @receiver ResultWrapper<T> The function to be used for mapping of [Success.value]
     * @param mapper Function1<T, R>
     * @return ResultWrapper<R>
     */
    fun <T, R> ResultWrapper<T>.mapSuccess(
        mapper: Function1<T, R>
    ): ResultWrapper<R> = when (this) {
        is Failed -> this
        is Success -> mapper(value).toSuccess()
    }

    inline fun <T, R> ResultWrapper<T>.mapSuccessWithTryOrFailed(
        mapper: (T) -> R
    ): ResultWrapper<R> = when (this) {
        is Failed -> this
        is Success -> tryOrFailed {
            mapper(value)
        }
    }

    fun <T> ResultWrapper<T>.asSuccessValueOrNull(): T? = (this as? Success)?.value

    /**
     * catches any Throwable and wraps the result.
     */
    @OptIn(ExperimentalContracts::class)
    inline fun <T> tryOrFailed(block: () -> T): ResultWrapper<T> {
        contract {
            callsInPlace(block, InvocationKind.EXACTLY_ONCE)
        }
        return try {
            Success(block())
        } catch (e: Throwable) {
            Timber.d(e, "Failed with")
            Failed(e)
        }
    }