change get/[] to always raise Defect and tryGet to do eh bridge mode (#30)
* also sprinkle mixin randomly across the codebase
This commit is contained in:
parent
8528ce28b4
commit
ff755bbf75
122
stew/results.nim
122
stew/results.nim
|
@ -112,8 +112,9 @@ type
|
||||||
##
|
##
|
||||||
## When the error of a `Result` is an `Exception`, or a `toException` helper
|
## When the error of a `Result` is an `Exception`, or a `toException` helper
|
||||||
## is present for your error type, the "Exception bridge mode" is
|
## is present for your error type, the "Exception bridge mode" is
|
||||||
## enabled and instead of raising `Defect`, we will raise the given
|
## enabled and instead of raising `ResultError`, `tryGet` will raise the
|
||||||
## `Exception` on access.
|
## given `Exception` on access. `[]` and `get` will continue to raise a
|
||||||
|
## `Defect`.
|
||||||
##
|
##
|
||||||
## This is an experimental feature that may be removed.
|
## This is an experimental feature that may be removed.
|
||||||
##
|
##
|
||||||
|
@ -274,16 +275,9 @@ func raiseResultDefect(m: string, v: auto) {.noreturn, noinline.} =
|
||||||
when compiles($v): raise (ref ResultDefect)(msg: m & ": " & $v)
|
when compiles($v): raise (ref ResultDefect)(msg: m & ": " & $v)
|
||||||
else: raise (ref ResultDefect)(msg: m)
|
else: raise (ref ResultDefect)(msg: m)
|
||||||
|
|
||||||
template checkOk(self: Result) =
|
template assertOk(self: Result) =
|
||||||
# TODO This condition is a bit odd in that it raises different exceptions
|
if not self.o:
|
||||||
# depending on the type of E - this is done to support using Result as a
|
raiseResultDefect("Trying to acces value with err Result", self.e)
|
||||||
# bridge type that can transport Exceptions
|
|
||||||
mixin toException
|
|
||||||
if not self.isOk:
|
|
||||||
when E is ref Exception or compiles(toException(self.e)):
|
|
||||||
raiseResultError(self)
|
|
||||||
else:
|
|
||||||
raiseResultDefect("Trying to acces value with err Result", self.e)
|
|
||||||
|
|
||||||
template ok*[T, E](R: type Result[T, E], x: auto): R =
|
template ok*[T, E](R: type Result[T, E], x: auto): R =
|
||||||
## Initialize a result with a success and value
|
## Initialize a result with a success and value
|
||||||
|
@ -319,39 +313,39 @@ func map*[T, E, A](
|
||||||
## let r = Result[int, cstring).ok(42)
|
## let r = Result[int, cstring).ok(42)
|
||||||
## assert r.map(proc (v: int): int = $v).get() == "42"
|
## assert r.map(proc (v: int): int = $v).get() == "42"
|
||||||
## ```
|
## ```
|
||||||
if self.isOk: result.ok(f(self.v))
|
if self.o: result.ok(f(self.v))
|
||||||
else: result.err(self.e)
|
else: result.err(self.e)
|
||||||
|
|
||||||
func flatMap*[T, E, A](
|
func flatMap*[T, E, A](
|
||||||
self: Result[T, E], f: proc(x: T): Result[A, E]): Result[A, E] {.inline.} =
|
self: Result[T, E], f: proc(x: T): Result[A, E]): Result[A, E] {.inline.} =
|
||||||
if self.isOk: f(self.v)
|
if self.o: f(self.v)
|
||||||
else: Result[A, E].err(self.e)
|
else: Result[A, E].err(self.e)
|
||||||
|
|
||||||
func mapErr*[T: not void, E, A](
|
func mapErr*[T: not void, E, A](
|
||||||
self: Result[T, E], f: proc(x: E): A): Result[T, A] {.inline.} =
|
self: Result[T, E], f: proc(x: E): A): Result[T, A] {.inline.} =
|
||||||
## Transform error using f, or return value
|
## Transform error using f, or return value
|
||||||
if self.isOk: result.ok(self.v)
|
if self.o: result.ok(self.v)
|
||||||
else: result.err(f(self.e))
|
else: result.err(f(self.e))
|
||||||
|
|
||||||
func mapConvert*[T0, E0](
|
func mapConvert*[T0, E0](
|
||||||
self: Result[T0, E0], T1: type): Result[T1, E0] {.inline.} =
|
self: Result[T0, E0], T1: type): Result[T1, E0] {.inline.} =
|
||||||
## Convert result value to A using an conversion
|
## Convert result value to A using an conversion
|
||||||
# Would be nice if it was automatic...
|
# Would be nice if it was automatic...
|
||||||
if self.isOk: result.ok(T1(self.v))
|
if self.o: result.ok(T1(self.v))
|
||||||
else: result.err(self.e)
|
else: result.err(self.e)
|
||||||
|
|
||||||
func mapCast*[T0, E0](
|
func mapCast*[T0, E0](
|
||||||
self: Result[T0, E0], T1: type): Result[T1, E0] {.inline.} =
|
self: Result[T0, E0], T1: type): Result[T1, E0] {.inline.} =
|
||||||
## Convert result value to A using a cast
|
## Convert result value to A using a cast
|
||||||
## Would be nice with nicer syntax...
|
## Would be nice with nicer syntax...
|
||||||
if self.isOk: result.ok(cast[T1](self.v))
|
if self.o: result.ok(cast[T1](self.v))
|
||||||
else: result.err(self.e)
|
else: result.err(self.e)
|
||||||
|
|
||||||
template `and`*[T0, E, T1](self: Result[T0, E], other: Result[T1, E]): Result[T1, E] =
|
template `and`*[T0, E, T1](self: Result[T0, E], other: Result[T1, E]): Result[T1, E] =
|
||||||
## Evaluate `other` iff self.isOk, else return error
|
## Evaluate `other` iff self.isOk, else return error
|
||||||
## fail-fast - will not evaluate other if a is an error
|
## fail-fast - will not evaluate other if a is an error
|
||||||
let s = self
|
let s = self
|
||||||
if s.isOk:
|
if s.o:
|
||||||
other
|
other
|
||||||
else:
|
else:
|
||||||
type R = type(other)
|
type R = type(other)
|
||||||
|
@ -361,7 +355,7 @@ template `or`*[T, E](self, other: Result[T, E]): Result[T, E] =
|
||||||
## Evaluate `other` iff not self.isOk, else return self
|
## Evaluate `other` iff not self.isOk, else return self
|
||||||
## fail-fast - will not evaluate other if a is a value
|
## fail-fast - will not evaluate other if a is a value
|
||||||
let s = self
|
let s = self
|
||||||
if s.isOk: s
|
if s.o: s
|
||||||
else: other
|
else: other
|
||||||
|
|
||||||
template catch*(body: typed): Result[type(body), ref CatchableError] =
|
template catch*(body: typed): Result[type(body), ref CatchableError] =
|
||||||
|
@ -399,9 +393,9 @@ template capture*[E: Exception](T: type, someExceptionExpr: ref E): Result[T, re
|
||||||
ret
|
ret
|
||||||
|
|
||||||
func `==`*[T0, E0, T1, E1](lhs: Result[T0, E0], rhs: Result[T1, E1]): bool {.inline.} =
|
func `==`*[T0, E0, T1, E1](lhs: Result[T0, E0], rhs: Result[T1, E1]): bool {.inline.} =
|
||||||
if lhs.isOk != rhs.isOk:
|
if lhs.o != rhs.o:
|
||||||
false
|
false
|
||||||
elif lhs.isOk:
|
elif lhs.o: # and rhs.o implied
|
||||||
lhs.v == rhs.v
|
lhs.v == rhs.v
|
||||||
else:
|
else:
|
||||||
lhs.e == rhs.e
|
lhs.e == rhs.e
|
||||||
|
@ -410,41 +404,47 @@ func get*[T: not void, E](self: Result[T, E]): T {.inline.} =
|
||||||
## Fetch value of result if set, or raise Defect
|
## Fetch value of result if set, or raise Defect
|
||||||
## Exception bridge mode: raise given Exception instead
|
## Exception bridge mode: raise given Exception instead
|
||||||
## See also: Option.get
|
## See also: Option.get
|
||||||
checkOk(self)
|
assertOk(self)
|
||||||
self.v
|
self.v
|
||||||
|
|
||||||
func tryGet*[T: not void, E](self: Result[T, E]): T {.inline.} =
|
func tryGet*[T: not void, E](self: Result[T, E]): T {.inline.} =
|
||||||
## Fetch value of result if set, or raise
|
## Fetch value of result if set, or raise
|
||||||
## When E is an Exception, raise that exception - otherwise, raise a ResultError[E]
|
## When E is an Exception, raise that exception - otherwise, raise a ResultError[E]
|
||||||
if not self.isOk: self.raiseResultError
|
mixin raiseResultError
|
||||||
|
if not self.o: self.raiseResultError()
|
||||||
self.v
|
self.v
|
||||||
|
|
||||||
func get*[T, E](self: Result[T, E], otherwise: T): T {.inline.} =
|
func get*[T, E](self: Result[T, E], otherwise: T): T {.inline.} =
|
||||||
## Fetch value of result if set, or return the value `otherwise`
|
## Fetch value of result if set, or return the value `otherwise`
|
||||||
if self.isErr: otherwise
|
## See `valueOr` for a template version that avoids evaluating `otherwise`
|
||||||
else: self.v
|
## unless necessary
|
||||||
|
if self.o: self.v
|
||||||
|
else: otherwise
|
||||||
|
|
||||||
func get*[T, E](self: var Result[T, E]): var T {.inline.} =
|
func get*[T, E](self: var Result[T, E]): var T {.inline.} =
|
||||||
## Fetch value of result if set, or raise Defect
|
## Fetch value of result if set, or raise Defect
|
||||||
## Exception bridge mode: raise given Exception instead
|
## Exception bridge mode: raise given Exception instead
|
||||||
## See also: Option.get
|
## See also: Option.get
|
||||||
checkOk(self)
|
assertOk(self)
|
||||||
self.v
|
self.v
|
||||||
|
|
||||||
template `[]`*[T, E](self: Result[T, E]): T =
|
template `[]`*[T, E](self: Result[T, E]): T =
|
||||||
## Fetch value of result if set, or raise Defect
|
## Fetch value of result if set, or raise Defect
|
||||||
## Exception bridge mode: raise given Exception instead
|
## Exception bridge mode: raise given Exception instead
|
||||||
|
mixin get
|
||||||
self.get()
|
self.get()
|
||||||
|
|
||||||
template `[]`*[T, E](self: var Result[T, E]): var T =
|
template `[]`*[T, E](self: var Result[T, E]): var T =
|
||||||
## Fetch value of result if set, or raise Defect
|
## Fetch value of result if set, or raise Defect
|
||||||
## Exception bridge mode: raise given Exception instead
|
## Exception bridge mode: raise given Exception instead
|
||||||
|
mixin get
|
||||||
self.get()
|
self.get()
|
||||||
|
|
||||||
template unsafeGet*[T, E](self: Result[T, E]): T =
|
template unsafeGet*[T, E](self: Result[T, E]): T =
|
||||||
## Fetch value of result if set, undefined behavior if unset
|
## Fetch value of result if set, undefined behavior if unset
|
||||||
## See also: Option.unsafeGet
|
## See also: Option.unsafeGet
|
||||||
assert isOk(self)
|
assert self.o
|
||||||
|
|
||||||
self.v
|
self.v
|
||||||
|
|
||||||
func expect*[T: not void, E](self: Result[T, E], m: string): T =
|
func expect*[T: not void, E](self: Result[T, E], m: string): T =
|
||||||
|
@ -457,37 +457,42 @@ func expect*[T: not void, E](self: Result[T, E], m: string): T =
|
||||||
## # Put here a helpful comment why you think this won't fail
|
## # Put here a helpful comment why you think this won't fail
|
||||||
## echo r.expect("r was just set to ok(42)")
|
## echo r.expect("r was just set to ok(42)")
|
||||||
## ```
|
## ```
|
||||||
if not self.isOk():
|
if not self.o:
|
||||||
raiseResultDefect(m, self.error)
|
raiseResultDefect(m, self.e)
|
||||||
self.v
|
self.v
|
||||||
|
|
||||||
func expect*[T: not void, E](self: var Result[T, E], m: string): var T =
|
func expect*[T: not void, E](self: var Result[T, E], m: string): var T =
|
||||||
if not self.isOk():
|
if not self.o:
|
||||||
raiseResultDefect(m, self.error)
|
raiseResultDefect(m, self.e)
|
||||||
self.v
|
self.v
|
||||||
|
|
||||||
func `$`*(self: Result): string =
|
func `$`*(self: Result): string =
|
||||||
## Returns string representation of `self`
|
## Returns string representation of `self`
|
||||||
if self.isOk: "Ok(" & $self.v & ")"
|
if self.o: "Ok(" & $self.v & ")"
|
||||||
else: "Err(" & $self.e & ")"
|
else: "Err(" & $self.e & ")"
|
||||||
|
|
||||||
func error*[T, E](self: Result[T, E]): E =
|
func error*[T, E](self: Result[T, E]): E =
|
||||||
## Fetch error of result if set, or raise Defect
|
## Fetch error of result if set, or raise Defect
|
||||||
if not self.isErr:
|
if self.o:
|
||||||
when T is not void:
|
when T is not void:
|
||||||
raiseResultDefect("Trying to access error when value is set", self.v)
|
raiseResultDefect("Trying to access error when value is set", self.v)
|
||||||
else:
|
else:
|
||||||
raise (ref ResultDefect)(msg: "Trying to access error when value is set")
|
raise (ref ResultDefect)(msg: "Trying to access error when value is set")
|
||||||
|
|
||||||
self.e
|
self.e
|
||||||
|
|
||||||
template value*[T, E](self: Result[T, E]): T = self.get()
|
template value*[T, E](self: Result[T, E]): T =
|
||||||
template value*[T, E](self: var Result[T, E]): T = self.get()
|
mixin get
|
||||||
|
self.get()
|
||||||
|
|
||||||
|
template value*[T, E](self: var Result[T, E]): T =
|
||||||
|
mixin get
|
||||||
|
self.get()
|
||||||
|
|
||||||
template valueOr*[T, E](self: Result[T, E], def: T): T =
|
template valueOr*[T, E](self: Result[T, E], def: T): T =
|
||||||
## Fetch value of result if set, or supplied default
|
## Fetch value of result if set, or supplied default
|
||||||
## default will not be evaluated iff value is set
|
## default will not be evaluated iff value is set
|
||||||
self.get(def)
|
if self.o: self.v
|
||||||
|
else: def
|
||||||
|
|
||||||
# void support
|
# void support
|
||||||
|
|
||||||
|
@ -499,10 +504,16 @@ template ok*[E](R: type Result[void, E]): auto =
|
||||||
template ok*[E](self: var Result[void, E]) =
|
template ok*[E](self: var Result[void, E]) =
|
||||||
## Set the result to success and update value
|
## Set the result to success and update value
|
||||||
## Example: `result.ok(42)`
|
## Example: `result.ok(42)`
|
||||||
|
mixin ok
|
||||||
self = (type self).ok()
|
self = (type self).ok()
|
||||||
|
|
||||||
template ok*(): auto = ok(typeof(result))
|
template ok*(): auto =
|
||||||
template err*(): auto = err(typeof(result))
|
mixin ok
|
||||||
|
ok(typeof(result))
|
||||||
|
|
||||||
|
template err*(): auto =
|
||||||
|
mixin err
|
||||||
|
err(typeof(result))
|
||||||
|
|
||||||
# TODO:
|
# TODO:
|
||||||
# Supporting `map` and `get` operations on a `void` result is quite
|
# Supporting `map` and `get` operations on a `void` result is quite
|
||||||
|
@ -511,55 +522,64 @@ template err*(): auto = err(typeof(result))
|
||||||
func map*[E, A](
|
func map*[E, A](
|
||||||
self: Result[void, E], f: proc(): A): Result[A, E] {.inline.} =
|
self: Result[void, E], f: proc(): A): Result[A, E] {.inline.} =
|
||||||
## Transform value using f, or return error
|
## Transform value using f, or return error
|
||||||
if self.isOk: result.ok(f())
|
if self.o: result.ok(f())
|
||||||
else: result.err(self.e)
|
else: result.err(self.e)
|
||||||
|
|
||||||
func flatMap*[E, A](
|
func flatMap*[E, A](
|
||||||
self: Result[void, E], f: proc(): Result[A, E]): Result[A, E] {.inline.} =
|
self: Result[void, E], f: proc(): Result[A, E]): Result[A, E] {.inline.} =
|
||||||
if self.isOk: f(self.v)
|
if self.o: f(self.v)
|
||||||
else: Result[A, E].err(self.e)
|
else: Result[A, E].err(self.e)
|
||||||
|
|
||||||
func mapErr*[E, A](
|
func mapErr*[E, A](
|
||||||
self: Result[void, E], f: proc(x: E): A): Result[void, A] {.inline.} =
|
self: Result[void, E], f: proc(x: E): A): Result[void, A] {.inline.} =
|
||||||
## Transform error using f, or return value
|
## Transform error using f, or return value
|
||||||
if self.isOk: result.ok()
|
if self.o: result.ok()
|
||||||
else: result.err(f(self.e))
|
else: result.err(f(self.e))
|
||||||
|
|
||||||
func map*[T, E](
|
func map*[T, E](
|
||||||
self: Result[T, E], f: proc(x: T)): Result[void, E] {.inline.} =
|
self: Result[T, E], f: proc(x: T)): Result[void, E] {.inline.} =
|
||||||
## Transform value using f, or return error
|
## Transform value using f, or return error
|
||||||
if self.isOk: f(self.v); result.ok()
|
if self.o: f(self.v); result.ok()
|
||||||
else: result.err(self.e)
|
else: result.err(self.e)
|
||||||
|
|
||||||
func get*[E](self: Result[void, E]) {.inline.} =
|
func get*[E](self: Result[void, E]) {.inline.} =
|
||||||
## Fetch value of result if set, or raise
|
## Fetch value of result if set, or raise
|
||||||
## See also: Option.get
|
## See also: Option.get
|
||||||
checkOk(self)
|
mixin assertOk
|
||||||
|
assertOk(self)
|
||||||
|
|
||||||
func tryGet*[E](self: Result[void, E]) {.inline.} =
|
func tryGet*[E](self: Result[void, E]) {.inline.} =
|
||||||
## Fetch value of result if set, or raise a CatchableError
|
## Fetch value of result if set, or raise a CatchableError
|
||||||
if not self.isOk: self.raiseResultError
|
mixin raiseResultError
|
||||||
|
if not self.o:
|
||||||
|
self.raiseResultError()
|
||||||
|
|
||||||
template `[]`*[E](self: Result[void, E]) =
|
template `[]`*[E](self: Result[void, E]) =
|
||||||
## Fetch value of result if set, or raise
|
## Fetch value of result if set, or raise
|
||||||
|
mixin get
|
||||||
self.get()
|
self.get()
|
||||||
|
|
||||||
template unsafeGet*[E](self: Result[void, E]) =
|
template unsafeGet*[E](self: Result[void, E]) =
|
||||||
## Fetch value of result if set, undefined behavior if unset
|
## Fetch value of result if set, undefined behavior if unset
|
||||||
## See also: Option.unsafeGet
|
## See also: Option.unsafeGet
|
||||||
assert not self.isErr
|
assert self.o
|
||||||
|
|
||||||
func expect*[E](self: Result[void, E], msg: string) =
|
func expect*[E](self: Result[void, E], msg: string) =
|
||||||
if not self.isOk():
|
if not self.o:
|
||||||
raise (ref ResultDefect)(msg: msg)
|
raise (ref ResultDefect)(msg: msg)
|
||||||
|
|
||||||
func `$`*[E](self: Result[void, E]): string =
|
func `$`*[E](self: Result[void, E]): string =
|
||||||
## Returns string representation of `self`
|
## Returns string representation of `self`
|
||||||
if self.isOk: "Ok()"
|
if self.o: "Ok()"
|
||||||
else: "Err(" & $self.e & ")"
|
else: "Err(" & $self.e & ")"
|
||||||
|
|
||||||
template value*[E](self: Result[void, E]) = self.get()
|
template value*[E](self: Result[void, E]) =
|
||||||
template value*[E](self: var Result[void, E]) = self.get()
|
mixin get
|
||||||
|
self.get()
|
||||||
|
|
||||||
|
template value*[E](self: var Result[void, E]) =
|
||||||
|
mixin get
|
||||||
|
self.get()
|
||||||
|
|
||||||
template `?`*[T, E](self: Result[T, E]): T =
|
template `?`*[T, E](self: Result[T, E]): T =
|
||||||
## Early return - if self is an error, we will return from the current
|
## Early return - if self is an error, we will return from the current
|
||||||
|
@ -573,7 +593,7 @@ template `?`*[T, E](self: Result[T, E]): T =
|
||||||
# TODO the v copy is here to prevent multiple evaluations of self - could
|
# TODO the v copy is here to prevent multiple evaluations of self - could
|
||||||
# probably avoid it with some fancy macro magic..
|
# probably avoid it with some fancy macro magic..
|
||||||
let v = (self)
|
let v = (self)
|
||||||
if v.isErr:
|
if not v.o:
|
||||||
when typeof(result) is typeof(v):
|
when typeof(result) is typeof(v):
|
||||||
return v
|
return v
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -194,7 +194,7 @@ func toException(v: AnEnum): AnException = AnException(v: v)
|
||||||
func testToException(): int =
|
func testToException(): int =
|
||||||
try:
|
try:
|
||||||
var r = Result[int, AnEnum].err(anEnumA)
|
var r = Result[int, AnEnum].err(anEnumA)
|
||||||
r[]
|
r.tryGet
|
||||||
except AnException:
|
except AnException:
|
||||||
42
|
42
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue