Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
package io.nlopez.asyncresult

/**
* Scope for the [result] DSL block. Provides [bind], [error], [loading], [ensure], and
* [ensureNotNull] for short-circuit evaluation of [AsyncResult] values.
* Scope for the [result] DSL block. Provides [bind], [error], [loading], [notStarted],
* [errorWithMetadata], [ensure], and [ensureNotNull] for short-circuit evaluation of
* [AsyncResult] values.
*/
public interface ResultScope {
/** Extracts the [Success] value, or short-circuits with the non-success state. */
Expand All @@ -15,9 +16,18 @@ public interface ResultScope {
/** Short-circuits with [Error] wrapping the given [error]. */
public fun error(error: Throwable): Nothing

/** Short-circuits with the given [Error] instance. */
public fun error(error: Error): Nothing

/** Short-circuits with [Loading]. */
public fun loading(): Nothing

/** Short-circuits with [NotStarted]. */
public fun notStarted(): Nothing

/** Short-circuits with an [Error] carrying typed [metadata]. */
public fun <T> errorWithMetadata(metadata: T, errorId: ErrorId? = null): Nothing

/** Ensures [condition] is true, or short-circuits with [Error] from [lazyError]. */
public fun ensure(condition: Boolean, lazyError: () -> Throwable)

Expand Down Expand Up @@ -76,11 +86,26 @@ internal class ResultScopeImpl : ResultScope {
throw ResultShortCircuit()
}

override fun error(error: Error): Nothing {
shortCircuitResult = error
throw ResultShortCircuit()
}

override fun loading(): Nothing {
shortCircuitResult = Loading
throw ResultShortCircuit()
}

override fun notStarted(): Nothing {
shortCircuitResult = NotStarted
throw ResultShortCircuit()
}

override fun <T> errorWithMetadata(metadata: T, errorId: ErrorId?): Nothing {
shortCircuitResult = ErrorWithMetadata(metadata as Any, errorId)
throw ResultShortCircuit()
}

override fun ensure(condition: Boolean, lazyError: () -> Throwable) {
if (!condition) {
error(lazyError())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,49 @@ class AsyncResultResultDslTest {
assertThat(result).isEqualTo(Loading)
}

@Test
fun `notStarted short-circuits to NotStarted`() {
val result = result<Int> { notStarted() }
assertThat(result).isEqualTo(NotStarted)
}

@Test
fun `error short-circuits to Error with given Throwable`() {
val throwable = Throwable("boom")
val result = result<Int> { error(throwable) }
assertThat(result).isEqualTo(Error(throwable))
}

@Test
fun `error short-circuits with given Error instance`() {
val throwable = Throwable("boom")
val errorId = ErrorId("test-error")
val errorInstance = Error(throwable, metadata = "metadata", errorId = errorId)
val result = result<Int> { error(errorInstance) }
assertThat(result).isEqualTo(errorInstance)
}

@Test
fun `errorWithMetadata short-circuits with Error containing typed metadata`() {
data class MyMetadata(val code: Int, val message: String)
val metadata = MyMetadata(404, "Not Found")
val errorId = ErrorId("not-found")
val result = result<Int> { errorWithMetadata(metadata, errorId) }

assertThat(result).isEqualTo(ErrorWithMetadata(metadata, errorId))
assertThat((result as Error).metadataOrNull<MyMetadata>()).isEqualTo(metadata)
assertThat(result.errorId).isEqualTo(errorId)
}

@Test
fun `errorWithMetadata without errorId works`() {
val metadata = "Simple error"
val result = result<Int> { errorWithMetadata(metadata) }

assertThat((result as Error).metadataOrNull<String>()).isEqualTo(metadata)
assertThat(result.errorId).isEqualTo(null)
}

@Test
fun `ensure true continues and ensure false short-circuits`() {
val success = result {
Expand Down Expand Up @@ -134,4 +170,38 @@ class AsyncResultResultDslTest {

assertThat(result).isEqualTo(Success(6))
}

@Test
fun `notStarted short-circuits to NotStarted`() {
val result = result<Int> { notStarted() }
assertThat(result).isEqualTo(NotStarted)
}

@Test
fun `error with Error instance short-circuits to that Error`() {
val throwable = Throwable("boom")
val errorId = ErrorId("error-123")
val errorInstance = Error(throwable, "custom metadata", errorId)
val result = result<Int> { error(errorInstance) }
assertThat(result).isEqualTo(errorInstance)
}

@Test
fun `errorWithMetadata short-circuits with Error carrying typed metadata`() {
data class ErrorData(val code: Int, val message: String)
val metadata = ErrorData(404, "Not Found")
val errorId = ErrorId("err-404")

val result = result<Int> { errorWithMetadata(metadata, errorId) }

assertThat(result).isEqualTo(ErrorWithMetadata(metadata, errorId))
}

@Test
fun `errorWithMetadata without errorId short-circuits with Error carrying metadata only`() {
val metadata = "Simple error message"
val result = result<Int> { errorWithMetadata(metadata) }

assertThat(result).isEqualTo(ErrorWithMetadata(metadata, null))
}
}
Loading