package io.dolby.sdk.comms.reactnative.utils import android.util.Log import com.voxeet.promise.PromiseInOut import com.voxeet.promise.solve.PromiseLikeGeneric import com.voxeet.promise.solve.ThenPromise import com.voxeet.promise.solve.ThenValue import com.voxeet.promise.solve.ThenVoid typealias VoxeetPromise = com.voxeet.promise.Promise typealias ReactPromise = com.facebook.react.bridge.Promise /** * Provide extension methods that simplify: *

- creation of [VoxeetPromise] *

- joining [VoxeetPromise]'s and [ReactPromise]'s

*/ object Promises { private val TAG = Promises.javaClass.simpleName /** * Creates promise from Java Callable * Resolves value returned by callable, rejects in case of null value * * @param callable callable to execute * @param nullErrorMessage error message provider in case of rejection */ @JvmStatic fun promise( callable: () -> T?, nullErrorMessage: () -> String = { "Required value is null" } ): VoxeetPromise { return VoxeetPromise { solver -> try { callable.invoke()?.let { solver.resolve(it) } ?: solver.reject(Exception(nullErrorMessage())) } catch (cause: Exception) { Log.w(TAG, cause.message, cause) solver.reject(cause) } } } /** * Creates promise from object * Rejects in case of null object supplied * * @param value object to return * @param nullErrorMessage error message provider in case of rejection */ @JvmStatic fun promise(value: T?, nullErrorMessage: () -> String = { "Required value is null" }): VoxeetPromise { return value?.let { VoxeetPromise.resolve(value) } ?: VoxeetPromise.reject(Exception(nullErrorMessage())) } /** * Throws [IllegalArgumentException] if emitted value is null * * ``` * promiseThatCanEmitNull * .rejectIfNull() * ``` * * @param nullErrorMessage error message provider in case of null value */ fun VoxeetPromise.rejectIfNull(nullErrorMessage: () -> String = { "Required value is null" }): PromiseInOut = thenValue { requireNotNull(it, nullErrorMessage) } /** * Throws [IllegalArgumentException] if emitted value is null * * ``` * Promises.promise(Obj) * .rejectIfNull() * ``` * * @param nullErrorMessage error message provider in case of null value */ fun PromiseInOut.rejectIfNull(nullErrorMessage: () -> String = { "Required value is null" }): PromiseInOut = thenValue { requireNotNull(it, nullErrorMessage) } /** * Throws [IllegalArgumentException] if emitted value is false * * ``` * promiseThatEmitBoolean * .rejectIfFalse() * ``` * * @param lazyMessage error message provider in case of false value */ fun VoxeetPromise.rejectIfFalse( lazyMessage: () -> String = { "Returned promise value is false" } ): PromiseInOut = thenValue { require(it, lazyMessage) null } /** * Throws [IllegalArgumentException] if emitted value is false * * ``` * promiseThatEmitBoolean * .rejectIfFalse() * ``` * * @param lazyMessage error message provider in case of false value */ fun PromiseInOut.rejectIfFalse( lazyMessage: () -> String = { "Returned promise value is false" } ): PromiseInOut { return thenValue { require(it, lazyMessage) null } } /** * Util method to simplify building promises chain mapping * * ``` * Promises.promise(Obj) * .thenValue { obj -> Obj2 } * ``` * * @param thenValue [ThenValue] mapped value provider */ fun VoxeetPromise.thenValue(thenValue: ThenValue): PromiseInOut = then(thenValue) /** * Util method to simplify building promises chain mapping * * ``` * Promises.promise(Obj) * .thenValue { obj -> Obj2 } * ``` * * @param thenValue [ThenValue] mapped value provider */ fun PromiseInOut.thenValue(thenValue: ThenValue): PromiseInOut = then(thenValue) /** * Util method to simplify building promises chain * * ``` * Promises.promise(Obj) * .thenPromise { obj -> FurtherPromise(obj) } * ``` * * @param thenPromise [ThenPromise] further promise provider */ fun VoxeetPromise.thenPromise(thenPromise: ThenPromise): PromiseInOut = then(thenPromise) /** * Util method to simplify building promises chain * * ``` * Promises.promise(Obj) * .thenPromise { obj -> FurtherPromise(obj) } * ``` * * @param thenPromise [ThenPromise] further promise provider */ fun PromiseInOut.thenPromise(thenPromise: ThenPromise): PromiseInOut = then(thenPromise) /** * Util method to allow building nested promises chain * ``` * Promises.promise(Obj) * .thenNestedPromise { obj -> FurtherPromise(obj).thenValue { ... } } * `` */ fun VoxeetPromise.thenNestedPromise(thenPromise: ThenNestedPromise): PromiseInOut = then { result, solver -> try { solver.resolve(thenPromise.call(result)) } catch (e: Exception) { solver.reject(e) } } /** * Util method to allow building nested promises chain * ``` * Promises.promise(Obj) * .thenNestedPromise { obj -> FurtherPromise(obj).thenValue { ... } } * `` */ fun PromiseInOut.thenNestedPromise(thenPromise: ThenNestedPromise): PromiseInOut = then { result, solver -> try { solver.resolve(thenPromise.call(result)) } catch (e: Exception) { solver.reject(e) } } /** * Util method to simplify forwarding [VoxeetPromise] to [ReactPromise] * * @param promise [ReactPromise] to forward * @param ignoreReturnType flag if [ReactPromise] returns null */ fun VoxeetPromise.forward(promise: ReactPromise, ignoreReturnType: Boolean = false) = then(ThenVoid { result -> if (ignoreReturnType) promise.resolve(null) else promise.resolve(result) }).error { Log.d(TAG, "Dispatch error", it) promise.reject(it) } /** * Util method to simplify forwarding [PromiseInOut] to [ReactPromise] * * @param promise [ReactPromise] to forward * @param ignoreReturnType flag if [ReactPromise] returns null */ fun PromiseInOut.forward(promise: ReactPromise, ignoreReturnType: Boolean = false) = then(ThenVoid { result -> if (ignoreReturnType) promise.resolve(null) else promise.resolve(result) }).error { Log.d(TAG, "Dispatch error", it) promise.reject(it) } fun interface ThenNestedPromise : PromiseLikeGeneric> }