Add feature parity with Rust's Result type
This commit is contained in:
parent
c3534276b4
commit
bffa455ee4
@ -1,9 +1,20 @@
|
||||
package com.mikebull94.result
|
||||
|
||||
/**
|
||||
* - Elm: [Result.andThen](http://package.elm-lang.org/packages/elm-lang/core/latest/Result#andThen)
|
||||
* Rust: [Result.and](https://doc.rust-lang.org/std/result/enum.Result.html#method.and)
|
||||
*/
|
||||
inline fun <V, E, U> Result<V, E>.andThen(transform: (V) -> Result<U, E>): Result<U, E> {
|
||||
infix fun <V, E> Result<V, E>.and(result: Result<V, E>): Result<V, E> {
|
||||
return when(this) {
|
||||
is Ok -> result
|
||||
is Error -> err(error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* - Elm: [Result.andThen](http://package.elm-lang.org/packages/elm-lang/core/latest/Result#andThen)
|
||||
* - Rust: [Result.and_then](https://doc.rust-lang.org/std/result/enum.Result.html#method.and_then)
|
||||
*/
|
||||
infix inline fun <V, E, U> Result<V, E>.andThen(transform: (V) -> Result<U, E>): Result<U, E> {
|
||||
return when (this) {
|
||||
is Ok -> transform(value)
|
||||
is Error -> err(error)
|
||||
|
@ -2,6 +2,7 @@ package com.mikebull94.result
|
||||
|
||||
/**
|
||||
* - Elm: [Result.toMaybe](http://package.elm-lang.org/packages/elm-lang/core/latest/Result#toMaybe)
|
||||
* - Rust: [Result.ok](https://doc.rust-lang.org/std/result/enum.Result.html#method.ok)
|
||||
*/
|
||||
fun <V, E> Result<V, E>.get(): V? {
|
||||
return when (this) {
|
||||
@ -11,11 +12,33 @@ fun <V, E> Result<V, E>.get(): V? {
|
||||
}
|
||||
|
||||
/**
|
||||
* - Elm: [Result.withDefault](http://package.elm-lang.org/packages/elm-lang/core/latest/Result#withDefault)
|
||||
* - Rust: [Result.err](https://doc.rust-lang.org/std/result/enum.Result.html#method.err)
|
||||
*/
|
||||
infix fun <V, E> Result<V, E>.getOrElse(default: V): V {
|
||||
fun <V, E> Result<V, E>.getError(): E? {
|
||||
return when(this) {
|
||||
is Ok -> null
|
||||
is Error -> error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* - Elm: [Result.withDefault](http://package.elm-lang.org/packages/elm-lang/core/latest/Result#withDefault)
|
||||
* - Rust: [Result.unwrap_or](https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap_or)
|
||||
*/
|
||||
infix fun <V, E> Result<V, E>.getOr(default: V): V {
|
||||
return when (this) {
|
||||
is Ok -> value
|
||||
is Error -> default
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* - Elm: [Result.extract](http://package.elm-lang.org/packages/circuithub/elm-result-extra/1.4.0/Result-Extra#extract)
|
||||
* - Rust: [Result.unwrap_or_else](https://doc.rust-lang.org/src/core/result.rs.html#735-740)
|
||||
*/
|
||||
infix inline fun <V,E> Result<V,E>.getOrElse(transform: (E) -> V): V {
|
||||
return when (this) {
|
||||
is Ok -> value
|
||||
is Error -> transform(error)
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,9 @@ package com.mikebull94.result
|
||||
/**
|
||||
* - Elm: [Result.map](http://package.elm-lang.org/packages/elm-lang/core/latest/Result#map)
|
||||
* - Haskell: [Data.Bifunctor.first](https://hackage.haskell.org/package/base-4.10.0.0/docs/Data-Bifunctor.html#v:first)
|
||||
* - Rust: [Result.map](https://doc.rust-lang.org/std/result/enum.Result.html#method.map)
|
||||
*/
|
||||
inline fun <V, E, U> Result<V, E>.map(transform: (V) -> U): Result<U, E> {
|
||||
infix inline fun <V, E, U> Result<V, E>.map(transform: (V) -> U): Result<U, E> {
|
||||
return when (this) {
|
||||
is Ok -> ok(transform(value))
|
||||
is Error -> err(error)
|
||||
@ -14,8 +15,9 @@ inline fun <V, E, U> Result<V, E>.map(transform: (V) -> U): Result<U, E> {
|
||||
/**
|
||||
* - Elm: [Result.mapError](http://package.elm-lang.org/packages/elm-lang/core/latest/Result#mapError)
|
||||
* - Haskell: [Data.Bifunctor.right](https://hackage.haskell.org/package/base-4.10.0.0/docs/Data-Bifunctor.html#v:second)
|
||||
* - Rust: [Result.map_err](https://doc.rust-lang.org/std/result/enum.Result.html#method.map_err)
|
||||
*/
|
||||
inline fun <V, E, U> Result<V, E>.mapError(transform: (E) -> U): Result<V, U> {
|
||||
infix inline fun <V, E, U> Result<V, E>.mapError(transform: (E) -> U): Result<V, U> {
|
||||
return when (this) {
|
||||
is Ok -> ok(value)
|
||||
is Error -> err(transform(error))
|
||||
|
@ -1,18 +1,21 @@
|
||||
package com.mikebull94.result
|
||||
|
||||
infix fun <V, E> Result<V, E>.or(default: V): Result<V, E> {
|
||||
return when (this) {
|
||||
is Ok -> this
|
||||
is Error -> ok(default)
|
||||
/**
|
||||
* - Rust: [Result.or](https://doc.rust-lang.org/std/result/enum.Result.html#method.or)
|
||||
*/
|
||||
infix fun <V, E> Result<V, E>.or(result: Result<V, E>): Result<V, E> {
|
||||
return when(this) {
|
||||
is Ok -> ok(value)
|
||||
is Error -> result
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* - Elm: [Result.extract](http://package.elm-lang.org/packages/circuithub/elm-result-extra/1.4.0/Result-Extra#extract)
|
||||
* - Rust: [Result.or_else](https://doc.rust-lang.org/std/result/enum.Result.html#method.or_else)
|
||||
*/
|
||||
inline fun <V, E> Result<V, E>.extract(transform: (E) -> V): V {
|
||||
infix inline fun <V, E> Result<V, E>.orElse(transform: (E) -> Result<V, E>): Result<V, E> {
|
||||
return when (this) {
|
||||
is Ok -> value
|
||||
is Ok -> ok(this.value)
|
||||
is Error -> transform(error)
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package com.mikebull94.result
|
||||
/**
|
||||
* - Elm: [Result](http://package.elm-lang.org/packages/elm-lang/core/5.1.1/Result)
|
||||
* - Haskell: [Data.Either](https://hackage.haskell.org/package/base-4.10.0.0/docs/Data-Either.html)
|
||||
* - Rust: [Result](https://doc.rust-lang.org/std/result/enum.Result.html)
|
||||
*/
|
||||
sealed class Result<out V, out E>
|
||||
|
||||
|
43
src/main/kotlin/com/mikebull94/result/ResultIterator.kt
Normal file
43
src/main/kotlin/com/mikebull94/result/ResultIterator.kt
Normal file
@ -0,0 +1,43 @@
|
||||
package com.mikebull94.result
|
||||
|
||||
/**
|
||||
* - Rust: [Result.iter](https://doc.rust-lang.org/std/result/enum.Result.html#method.iter)
|
||||
*/
|
||||
fun <V, E> Result<V, E>.iterator(): Iterator<V> {
|
||||
return ResultIterator(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Rust: [Result.iter_mut](https://doc.rust-lang.org/std/result/enum.Result.html#method.iter_mut)
|
||||
*/
|
||||
fun <V, E> Result<V, E>.mutableIterator(): MutableIterator<V> {
|
||||
return ResultIterator(this)
|
||||
}
|
||||
|
||||
private class ResultIterator<out V, out E>(private val result: Result<V, E>) : MutableIterator<V> {
|
||||
private var yielded = false
|
||||
|
||||
override fun hasNext(): Boolean {
|
||||
if (yielded) {
|
||||
return false
|
||||
}
|
||||
|
||||
return when (result) {
|
||||
is Ok -> true
|
||||
is Error -> false
|
||||
}
|
||||
}
|
||||
|
||||
override fun next(): V {
|
||||
if (!yielded && result is Ok) {
|
||||
yielded = true
|
||||
return result.value
|
||||
} else {
|
||||
throw NoSuchElementException()
|
||||
}
|
||||
}
|
||||
|
||||
override fun remove() {
|
||||
yielded = true
|
||||
}
|
||||
}
|
43
src/main/kotlin/com/mikebull94/result/Unwrap.kt
Normal file
43
src/main/kotlin/com/mikebull94/result/Unwrap.kt
Normal file
@ -0,0 +1,43 @@
|
||||
package com.mikebull94.result
|
||||
|
||||
class UnwrapException(message: String): Exception(message)
|
||||
|
||||
/**
|
||||
* - Rust: [Result.unwrap](https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap)
|
||||
*/
|
||||
fun <V, E> Result<V, E>.unwrap(): V {
|
||||
return when (this) {
|
||||
is Ok -> value
|
||||
is Error -> throw UnwrapException("called Result.wrap on an Error value $error")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* - Rust: [Result.expect](https://doc.rust-lang.org/std/result/enum.Result.html#method.expect)
|
||||
*/
|
||||
infix fun <V, E> Result<V, E>.expect(message: String): V {
|
||||
return when (this) {
|
||||
is Ok -> value
|
||||
is Error -> throw UnwrapException(message)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* - Rust: [Result.unwrap_err](https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap_err)
|
||||
*/
|
||||
fun <V, E> Result<V, E>.unwrapError(): E {
|
||||
return when (this) {
|
||||
is Ok -> throw UnwrapException("called Result.unwrapError on an Ok value $value")
|
||||
is Error -> error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* - Rust: [Reseult.expect_err](https://doc.rust-lang.org/std/result/enum.Result.html#method.expect_err)
|
||||
*/
|
||||
infix fun <V, E> Result<V, E>.expectError(message: String): E {
|
||||
return when (this) {
|
||||
is Ok -> throw UnwrapException(message)
|
||||
is Error -> error
|
||||
}
|
||||
}
|
@ -8,6 +8,18 @@ import org.junit.jupiter.api.Test
|
||||
internal class AndTest {
|
||||
private object AndError
|
||||
|
||||
@Test
|
||||
internal fun `and should return the result value if ok`() {
|
||||
val value = ok(230).and(ok(500)).get()
|
||||
assertThat(value, equalTo(500))
|
||||
}
|
||||
|
||||
@Test
|
||||
internal fun `and should return the result value if not ok`() {
|
||||
val error = ok(300).and(err("hello world")).getError()
|
||||
assertThat(error, equalTo("hello world"))
|
||||
}
|
||||
|
||||
@Test
|
||||
internal fun `andThen should return the transformed result value if ok`() {
|
||||
val value = ok(5).andThen { ok(it + 7) }.get()
|
||||
@ -16,10 +28,7 @@ internal class AndTest {
|
||||
|
||||
@Test
|
||||
internal fun `andThen should return the result error if not ok`() {
|
||||
val result = ok(20).andThen { ok(it + 43) }.andThen { err(AndError) }
|
||||
|
||||
result as Error
|
||||
|
||||
assertThat(result.error, sameInstance(AndError))
|
||||
val error = ok(20).andThen { ok(it + 43) }.andThen { err(AndError) }.getError()!!
|
||||
assertThat(error, sameInstance(AndError))
|
||||
}
|
||||
}
|
||||
|
@ -20,14 +20,38 @@ internal class GetTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
internal fun `getOrElse should return the result value if ok`() {
|
||||
val value = ok("hello").getOrElse("world")
|
||||
internal fun `getError should return null if ok`() {
|
||||
val error = ok("example").getError()
|
||||
assertThat(error, equalTo(null))
|
||||
}
|
||||
|
||||
@Test
|
||||
internal fun `getError should return the result error if not ok`() {
|
||||
val error = err("example").getError()
|
||||
assertThat(error, equalTo("example"))
|
||||
}
|
||||
|
||||
@Test
|
||||
internal fun `getOr should return the result value if ok`() {
|
||||
val value = ok("hello").getOr("world")
|
||||
assertThat(value, equalTo("hello"))
|
||||
}
|
||||
|
||||
@Test
|
||||
internal fun `getOrElse should return default value if not ok`() {
|
||||
val value = err(GetError).getOrElse("default")
|
||||
internal fun `getOr should return default value if not ok`() {
|
||||
val value = err(GetError).getOr("default")
|
||||
assertThat(value, equalTo("default"))
|
||||
}
|
||||
|
||||
@Test
|
||||
internal fun `getOrElse should return the result value if ok`() {
|
||||
val value = ok("hello").getOrElse { "world" }
|
||||
assertThat(value, equalTo("hello"))
|
||||
}
|
||||
|
||||
@Test
|
||||
internal fun `getOrElse should return the transformed result error if ok`() {
|
||||
val value = err("hello").getOrElse { "world" }
|
||||
assertThat(value, equalTo("world"))
|
||||
}
|
||||
}
|
||||
|
@ -9,25 +9,25 @@ internal class OrTest {
|
||||
|
||||
@Test
|
||||
internal fun `or should return the result value if ok`() {
|
||||
val value = ok(500).or(1000).get()
|
||||
val value = ok(500).or(ok(1000)).get()
|
||||
assertThat(value, equalTo(500))
|
||||
}
|
||||
|
||||
@Test
|
||||
internal fun `or should return the default value if not ok`() {
|
||||
val error = err(OrError).or(5000).get()
|
||||
val error = err(OrError).or(ok(5000)).get()
|
||||
assertThat(error, equalTo(5000))
|
||||
}
|
||||
|
||||
@Test
|
||||
internal fun `extract should return the result value if ok`() {
|
||||
val value = ok("hello").extract { OrError } as String
|
||||
assertThat(value, equalTo("hello"))
|
||||
internal fun `orElse should return the result value if ok`() {
|
||||
val value = ok(3000).orElse { ok(4000) }.get()
|
||||
assertThat(value, equalTo(3000))
|
||||
}
|
||||
|
||||
@Test
|
||||
internal fun `extract should return the transformed result error if not ok`() {
|
||||
val error = err("hello").extract { "$it darkness" }
|
||||
assertThat(error, equalTo("hello darkness"))
|
||||
internal fun `orElse should return the transformed value if not ok`() {
|
||||
val error = err(4000).orElse { ok(2000) }.get()
|
||||
assertThat(error, equalTo(2000))
|
||||
}
|
||||
}
|
||||
|
69
src/test/kotlin/com/mikebull94/result/ResultIteratorTest.kt
Normal file
69
src/test/kotlin/com/mikebull94/result/ResultIteratorTest.kt
Normal file
@ -0,0 +1,69 @@
|
||||
package com.mikebull94.result
|
||||
|
||||
import com.natpryce.hamkrest.assertion.assertThat
|
||||
import com.natpryce.hamkrest.equalTo
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
internal class ResultIteratorTest {
|
||||
@Test
|
||||
internal fun `hasNext should return true if unyielded and result is ok`() {
|
||||
val iterator = ok("hello").iterator()
|
||||
assertThat(iterator.hasNext(), equalTo(true))
|
||||
}
|
||||
|
||||
@Test
|
||||
internal fun `hasNext should return false if result is not ok`() {
|
||||
val iterator = err("hello").iterator()
|
||||
assertThat(iterator.hasNext(), equalTo(false))
|
||||
}
|
||||
|
||||
@Test
|
||||
internal fun `hasNext should return false if yielded`() {
|
||||
val iterator = ok("hello").iterator()
|
||||
iterator.next()
|
||||
assertThat(iterator.hasNext(), equalTo(false))
|
||||
}
|
||||
|
||||
@Test
|
||||
internal fun `next should return the result value if unyielded and result is ok`() {
|
||||
val value = ok("hello").iterator().next()
|
||||
assertThat(value, equalTo("hello"))
|
||||
}
|
||||
|
||||
@Test
|
||||
internal fun `next should throw NoSuchElementException if unyielded and result is not ok`() {
|
||||
val iterator = err("hello").iterator()
|
||||
|
||||
assertThrows(NoSuchElementException::class.java) {
|
||||
iterator.next()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
internal fun `next should throw NoSuchElementException if yielded and result is ok`() {
|
||||
val iterator = ok("hello").iterator()
|
||||
iterator.next()
|
||||
|
||||
assertThrows(NoSuchElementException::class.java) {
|
||||
iterator.next()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
internal fun `remove should make hasNext return false`() {
|
||||
val iterator = ok("hello").mutableIterator()
|
||||
iterator.remove()
|
||||
assertThat(iterator.hasNext(), equalTo(false))
|
||||
}
|
||||
|
||||
@Test
|
||||
internal fun `remove should make next throw NoSuchElementException`() {
|
||||
val iterator = ok("hello").mutableIterator()
|
||||
iterator.remove()
|
||||
|
||||
assertThrows(NoSuchElementException::class.java) {
|
||||
iterator.next()
|
||||
}
|
||||
}
|
||||
}
|
60
src/test/kotlin/com/mikebull94/result/UnwrapTest.kt
Normal file
60
src/test/kotlin/com/mikebull94/result/UnwrapTest.kt
Normal file
@ -0,0 +1,60 @@
|
||||
package com.mikebull94.result
|
||||
|
||||
import com.natpryce.hamkrest.assertion.assertThat
|
||||
import com.natpryce.hamkrest.equalTo
|
||||
import org.junit.jupiter.api.Assertions.assertThrows
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
internal class UnwrapTest {
|
||||
@Test
|
||||
internal fun `unwrap should return the result value if ok`() {
|
||||
val value = ok(5000).unwrap()
|
||||
assertThat(value, equalTo(5000))
|
||||
}
|
||||
|
||||
@Test
|
||||
internal fun `unwrap should throw an UnwrapException if not ok`() {
|
||||
assertThrows(UnwrapException::class.java, {
|
||||
err(5000).unwrap()
|
||||
}, "called Result.wrap on an Error value 5000")
|
||||
}
|
||||
|
||||
@Test
|
||||
internal fun `expect should return the result value if ok`() {
|
||||
val value = ok(1994).expect("the year should be 1994")
|
||||
assertThat(value, equalTo(1994))
|
||||
}
|
||||
|
||||
@Test
|
||||
internal fun `expect should throw an UnwrapException with a specified message if not ok`() {
|
||||
assertThrows(UnwrapException::class.java, {
|
||||
err(1994).expect("the year should be 1994")
|
||||
}, "the year should be 1994")
|
||||
}
|
||||
|
||||
@Test
|
||||
internal fun `unwrapError should throw an UnwrapException if ok`() {
|
||||
assertThrows(UnwrapException::class.java, {
|
||||
ok("example").unwrapError()
|
||||
}, "called Result.unwrapError on an Ok value example")
|
||||
}
|
||||
|
||||
@Test
|
||||
internal fun `unwrapError should return the result error if not ok`() {
|
||||
val error = err("example").unwrapError()
|
||||
assertThat(error, equalTo("example"))
|
||||
}
|
||||
|
||||
@Test
|
||||
internal fun `expectError should throw an UnwrapException with a specified message if ok`() {
|
||||
assertThrows(UnwrapException::class.java, {
|
||||
ok(2010).expectError("the year should be 2010")
|
||||
}, "the year should be 2010")
|
||||
}
|
||||
|
||||
@Test
|
||||
internal fun `expectError should return the result error if not ok`() {
|
||||
val error = err(2010).expectError("the year should be 2010")
|
||||
assertThat(error, equalTo(2010))
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user