Skip to content

Instantly share code, notes, and snippets.

@rommansabbir
Created February 1, 2022 09:08
Show Gist options
  • Save rommansabbir/1923a10eb1896bf8c05f4776a1f1a81a to your computer and use it in GitHub Desktop.
Save rommansabbir/1923a10eb1896bf8c05f4776a1f1a81a to your computer and use it in GitHub Desktop.
class SafeExecutorExample {
companion object {
@JvmStatic
fun main(args: Array<String>) {
withSafeExecutor<Any> {
Either.Right(Any()) }
/*
Test by throwing exception.
*/
withSafeExecutor<Any> { throw Exception("Test") }.either(
{ println(it) /* java.lang.Exception: Test */ },
{ println(it) /* Not Invoked */ }
)
withSafeExecutor<String> {
println("Doing some work here before returning result.")
return@withSafeExecutor Either.Right("Correct Result.")
}.either(
{ println(it) /* Not Invoked */ },
{ println(it) /* Correct Result. */}
)
}
}
}
/**
* Extension function to execute instruction through [SafeExecutor]
*/
fun <T> withSafeExecutor(callback: () -> Either<Exception, T>): Either<Exception, T> {
return SafeExecutor.getInstance().execute(callback)
}
/**
* Suspended extension function to execute instruction through [SafeExecutor]
*/
suspend fun <T> withSafeExecutorCoroutine(callback: () -> Either<Exception, T>): Either<Exception, T> {
return SafeExecutor.getInstance().executeCoroutine(callback)
}
/**
* [SafeExecutor] which enable to execute business logic without
* explicitly defining or worrying about managing [Exception].
* Also, support [suspend] functions too.
*/
interface SafeExecutor {
/**
* Execute the given instruction.
*
* @param callback callback to invoke the given instruction.
*
* @return [Either<Exception, T>]
*/
fun <T> execute(callback: () -> Either<Exception, T>): Either<Exception, T>
/**
* Execute the given instruction under a respective coroutine scope.
*
* @param callback callback to invoke the given instruction.
*
* @return [Either<Exception, T>]
*/
suspend fun <T> executeCoroutine(callback: () -> Either<Exception, T>): Either<Exception, T>
companion object {
private val instance by lazy { SafeExecutorImpl() }
/*
Return the SafeExecutor instance.
*/
fun getInstance(): SafeExecutor = instance
}
}
class SafeExecutorImpl : SafeExecutor {
override fun <T> execute(callback: () -> Either<Exception, T>): Either<Exception, T> {
return try {
callback.invoke()
} catch (e: Exception) {
Either.Left(e)
} catch (e: RuntimeException) {
Either.Left(e)
}
}
override suspend fun <T> executeCoroutine(callback: () -> Either<Exception, T>): Either<Exception, T> {
return try {
callback.invoke()
} catch (e: Exception) {
Either.Left(e)
} catch (e: RuntimeException) {
Either.Left(e)
}
}
}
sealed class Either<out L, out R> {
/** * Represents the left side of [Either] class which by convention is a "Failure". */
data class Left<out L>(val a: L) : Either<L, Nothing>()
/** * Represents the right side of [Either] class which by convention is a "Success". */
data class Right<out R>(val b: R) : Either<Nothing, R>()
val isRight get() = this is Right<R>
val isLeft get() = this is Left<L>
fun <L> left(a: L) = Left(a)
fun <R> right(b: R) = Right(b)
fun either(fnL: (L) -> Any, fnR: (R) -> Any): Any = when (this) {
is Left -> fnL(a)
is Right -> fnR(b)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment