results: work around field access bugs (#167)
* results: work around field access bugs See: * https://github.com/nim-lang/Nim/issues/3770 * https://github.com/nim-lang/Nim/issues/20900 * comment fixes * add test * document test better
This commit is contained in:
parent
19a6aea53b
commit
447b23d3bf
250
stew/results.nim
250
stew/results.nim
|
@ -305,34 +305,40 @@ type
|
||||||
## https://github.com/nim-lang/Nim/issues/14318 - generic error raises pragma
|
## https://github.com/nim-lang/Nim/issues/14318 - generic error raises pragma
|
||||||
|
|
||||||
# TODO https://github.com/nim-lang/Nim/issues/20699
|
# TODO https://github.com/nim-lang/Nim/issues/20699
|
||||||
# case o: bool
|
# case oResultPrivate: bool
|
||||||
# of false:
|
# of false:
|
||||||
# e: E
|
# eResultPrivate: E
|
||||||
# of true:
|
# of true:
|
||||||
# v: T
|
# vResultPrivate: T
|
||||||
|
|
||||||
|
# TODO ResultPrivate works around
|
||||||
|
# * https://github.com/nim-lang/Nim/issues/3770
|
||||||
|
# * https://github.com/nim-lang/Nim/issues/20900
|
||||||
|
#
|
||||||
|
# Do not use these fields directly in your code, they're not meant to be
|
||||||
|
# public!
|
||||||
when T is void:
|
when T is void:
|
||||||
when E is void:
|
when E is void:
|
||||||
o: bool
|
oResultPrivate*: bool
|
||||||
else:
|
else:
|
||||||
case o: bool
|
case oResultPrivate*: bool
|
||||||
of false:
|
of false:
|
||||||
e: E
|
eResultPrivate*: E
|
||||||
of true:
|
of true:
|
||||||
discard
|
discard
|
||||||
else:
|
else:
|
||||||
when E is void:
|
when E is void:
|
||||||
case o: bool
|
case oResultPrivate*: bool
|
||||||
of false:
|
of false:
|
||||||
discard
|
discard
|
||||||
of true:
|
of true:
|
||||||
v: T
|
vResultPrivate*: T
|
||||||
else:
|
else:
|
||||||
case o: bool
|
case oResultPrivate*: bool
|
||||||
of false:
|
of false:
|
||||||
e: E
|
eResultPrivate*: E
|
||||||
of true:
|
of true:
|
||||||
v: T
|
vResultPrivate*: T
|
||||||
|
|
||||||
Opt*[T] = Result[T, void]
|
Opt*[T] = Result[T, void]
|
||||||
|
|
||||||
|
@ -342,7 +348,7 @@ func raiseResultOk[T, E](self: Result[T, E]) {.noreturn, noinline.} =
|
||||||
when T is void:
|
when T is void:
|
||||||
raise (ref ResultError[void])(msg: "Trying to access error with value")
|
raise (ref ResultError[void])(msg: "Trying to access error with value")
|
||||||
else:
|
else:
|
||||||
raise (ref ResultError[T])(msg: "Trying to access error with value", error: self.v)
|
raise (ref ResultError[T])(msg: "Trying to access error with value", error: self.vResultPrivate)
|
||||||
|
|
||||||
func raiseResultError[T, E](self: Result[T, E]) {.noreturn, noinline.} =
|
func raiseResultError[T, E](self: Result[T, E]) {.noreturn, noinline.} =
|
||||||
# noinline because raising should take as little space as possible at call
|
# noinline because raising should take as little space as possible at call
|
||||||
|
@ -350,18 +356,18 @@ func raiseResultError[T, E](self: Result[T, E]) {.noreturn, noinline.} =
|
||||||
mixin toException
|
mixin toException
|
||||||
|
|
||||||
when E is ref Exception:
|
when E is ref Exception:
|
||||||
if self.e.isNil: # for example Result.default()!
|
if self.eResultPrivate.isNil: # for example Result.default()!
|
||||||
raise (ref ResultError[void])(msg: "Trying to access value with err (nil)")
|
raise (ref ResultError[void])(msg: "Trying to access value with err (nil)")
|
||||||
raise self.e
|
raise self.eResultPrivate
|
||||||
elif E is void:
|
elif E is void:
|
||||||
raise (ref ResultError[void])(msg: "Trying to access value with err")
|
raise (ref ResultError[void])(msg: "Trying to access value with err")
|
||||||
elif compiles(toException(self.e)):
|
elif compiles(toException(self.eResultPrivate)):
|
||||||
raise toException(self.e)
|
raise toException(self.eResultPrivate)
|
||||||
elif compiles($self.e):
|
elif compiles($self.eResultPrivate):
|
||||||
raise (ref ResultError[E])(
|
raise (ref ResultError[E])(
|
||||||
error: self.e, msg: $self.e)
|
error: self.eResultPrivate, msg: $self.eResultPrivate)
|
||||||
else:
|
else:
|
||||||
raise (ref ResultError[E])(msg: "Trying to access value with err", error: self.e)
|
raise (ref ResultError[E])(msg: "Trying to access value with err", error: self.eResultPrivate)
|
||||||
|
|
||||||
func raiseResultDefect(m: string, v: auto) {.noreturn, noinline.} =
|
func raiseResultDefect(m: string, v: auto) {.noreturn, noinline.} =
|
||||||
mixin `$`
|
mixin `$`
|
||||||
|
@ -373,21 +379,21 @@ func raiseResultDefect(m: string) {.noreturn, noinline.} =
|
||||||
|
|
||||||
template assertOk(self: Result) =
|
template assertOk(self: Result) =
|
||||||
# Careful - `self` evaluated multiple times, which is fine in all current uses
|
# Careful - `self` evaluated multiple times, which is fine in all current uses
|
||||||
if not self.o:
|
if not self.oResultPrivate:
|
||||||
when self.E isnot void:
|
when self.E isnot void:
|
||||||
raiseResultDefect("Trying to access value with err Result", self.e)
|
raiseResultDefect("Trying to access value with err Result", self.eResultPrivate)
|
||||||
else:
|
else:
|
||||||
raiseResultDefect("Trying to access value with err Result")
|
raiseResultDefect("Trying to access value with err Result")
|
||||||
|
|
||||||
template ok*[T, E](R: type Result[T, E], x: untyped): R =
|
template ok*[T, E](R: type Result[T, E], x: untyped): R =
|
||||||
## Initialize a result with a success and value
|
## Initialize a result with a success and value
|
||||||
## Example: `Result[int, string].ok(42)`
|
## Example: `Result[int, string].ok(42)`
|
||||||
R(o: true, v: x)
|
R(oResultPrivate: true, vResultPrivate: x)
|
||||||
|
|
||||||
template ok*[E](R: type Result[void, E]): R =
|
template ok*[E](R: type Result[void, E]): R =
|
||||||
## Initialize a result with a success and value
|
## Initialize a result with a success and value
|
||||||
## Example: `Result[void, string].ok()`
|
## Example: `Result[void, string].ok()`
|
||||||
R(o: true)
|
R(oResultPrivate: true)
|
||||||
|
|
||||||
template ok*[T: not void, E](self: var Result[T, E], x: untyped) =
|
template ok*[T: not void, E](self: var Result[T, E], x: untyped) =
|
||||||
## Set the result to success and update value
|
## Set the result to success and update value
|
||||||
|
@ -402,18 +408,18 @@ template ok*[E](self: var Result[void, E]) =
|
||||||
template err*[T, E](R: type Result[T, E], x: untyped): R =
|
template err*[T, E](R: type Result[T, E], x: untyped): R =
|
||||||
## Initialize the result to an error
|
## Initialize the result to an error
|
||||||
## Example: `Result[int, string].err("uh-oh")`
|
## Example: `Result[int, string].err("uh-oh")`
|
||||||
R(o: false, e: x)
|
R(oResultPrivate: false, eResultPrivate: x)
|
||||||
|
|
||||||
template err*[T](R: type Result[T, cstring], x: string): R =
|
template err*[T](R: type Result[T, cstring], x: string): R =
|
||||||
## Initialize the result to an error
|
## Initialize the result to an error
|
||||||
## Example: `Result[int, string].err("uh-oh")`
|
## Example: `Result[int, string].err("uh-oh")`
|
||||||
const s = x # avoid dangling cstring pointers
|
const s = x # avoid dangling cstring pointers
|
||||||
R(o: false, e: cstring(s))
|
R(oResultPrivate: false, eResultPrivate: cstring(s))
|
||||||
|
|
||||||
template err*[T](R: type Result[T, void]): R =
|
template err*[T](R: type Result[T, void]): R =
|
||||||
## Initialize the result to an error
|
## Initialize the result to an error
|
||||||
## Example: `Result[int, void].err()`
|
## Example: `Result[int, void].err()`
|
||||||
R(o: false)
|
R(oResultPrivate: false)
|
||||||
|
|
||||||
template err*[T, E](self: var Result[T, E], x: untyped) =
|
template err*[T, E](self: var Result[T, E], x: untyped) =
|
||||||
## Set the result as an error
|
## Set the result as an error
|
||||||
|
@ -435,8 +441,8 @@ template ok*(): auto = ok(typeof(result))
|
||||||
template err*(v: auto): auto = err(typeof(result), v)
|
template err*(v: auto): auto = err(typeof(result), v)
|
||||||
template err*(): auto = err(typeof(result))
|
template err*(): auto = err(typeof(result))
|
||||||
|
|
||||||
template isOk*(self: Result): bool = self.o
|
template isOk*(self: Result): bool = self.oResultPrivate
|
||||||
template isErr*(self: Result): bool = not self.o
|
template isErr*(self: Result): bool = not self.oResultPrivate
|
||||||
|
|
||||||
when not defined(nimHasEffectsOfs):
|
when not defined(nimHasEffectsOfs):
|
||||||
template effectsOf(f: untyped) {.pragma.}
|
template effectsOf(f: untyped) {.pragma.}
|
||||||
|
@ -450,13 +456,13 @@ func map*[T0, E, T1](
|
||||||
## 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.o:
|
if self.oResultPrivate:
|
||||||
result.ok(f(self.v))
|
result.ok(f(self.vResultPrivate))
|
||||||
else:
|
else:
|
||||||
when E is void:
|
when E is void:
|
||||||
result.err()
|
result.err()
|
||||||
else:
|
else:
|
||||||
result.err(self.e)
|
result.err(self.eResultPrivate)
|
||||||
|
|
||||||
func map*[T, E](
|
func map*[T, E](
|
||||||
self: Result[T, E], f: proc(x: T)):
|
self: Result[T, E], f: proc(x: T)):
|
||||||
|
@ -467,81 +473,81 @@ func map*[T, E](
|
||||||
## 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.o:
|
if self.oResultPrivate:
|
||||||
f(self.v)
|
f(self.vResultPrivate)
|
||||||
result.ok()
|
result.ok()
|
||||||
else:
|
else:
|
||||||
when E is void:
|
when E is void:
|
||||||
result.err()
|
result.err()
|
||||||
else:
|
else:
|
||||||
result.err(self.e)
|
result.err(self.eResultPrivate)
|
||||||
|
|
||||||
func map*[E, T1](
|
func map*[E, T1](
|
||||||
self: Result[void, E], f: proc(): T1):
|
self: Result[void, E], f: proc(): T1):
|
||||||
Result[T1, E] {.inline, effectsOf: f.} =
|
Result[T1, E] {.inline, effectsOf: f.} =
|
||||||
## Transform value using f, or return error
|
## Transform value using f, or return error
|
||||||
if self.o:
|
if self.oResultPrivate:
|
||||||
result.ok(f())
|
result.ok(f())
|
||||||
else:
|
else:
|
||||||
when E is void:
|
when E is void:
|
||||||
result.err()
|
result.err()
|
||||||
else:
|
else:
|
||||||
result.err(self.e)
|
result.err(self.eResultPrivate)
|
||||||
|
|
||||||
func map*[E](
|
func map*[E](
|
||||||
self: Result[void, E], f: proc()):
|
self: Result[void, E], f: proc()):
|
||||||
Result[void, E] {.inline, effectsOf: f.} =
|
Result[void, E] {.inline, effectsOf: f.} =
|
||||||
## Call f if value is
|
## Call f if value is
|
||||||
if self.o:
|
if self.oResultPrivate:
|
||||||
f()
|
f()
|
||||||
result.ok()
|
result.ok()
|
||||||
else:
|
else:
|
||||||
when E is void:
|
when E is void:
|
||||||
result.err()
|
result.err()
|
||||||
else:
|
else:
|
||||||
result.err(self.e)
|
result.err(self.eResultPrivate)
|
||||||
|
|
||||||
func flatMap*[T0, E, T1](
|
func flatMap*[T0, E, T1](
|
||||||
self: Result[T0, E], f: proc(x: T0): Result[T1, E]):
|
self: Result[T0, E], f: proc(x: T0): Result[T1, E]):
|
||||||
Result[T1, E] {.inline, effectsOf: f.} =
|
Result[T1, E] {.inline, effectsOf: f.} =
|
||||||
if self.o: f(self.v)
|
if self.oResultPrivate: f(self.vResultPrivate)
|
||||||
else:
|
else:
|
||||||
when E is void:
|
when E is void:
|
||||||
Result[T1, void].err()
|
Result[T1, void].err()
|
||||||
else:
|
else:
|
||||||
Result[T1, E].err(self.e)
|
Result[T1, E].err(self.eResultPrivate)
|
||||||
|
|
||||||
func flatMap*[E, T1](
|
func flatMap*[E, T1](
|
||||||
self: Result[void, E], f: proc(): Result[T1, E]):
|
self: Result[void, E], f: proc(): Result[T1, E]):
|
||||||
Result[T1, E] {.inline, effectsOf: f.} =
|
Result[T1, E] {.inline, effectsOf: f.} =
|
||||||
if self.o: f()
|
if self.oResultPrivate: f()
|
||||||
else:
|
else:
|
||||||
when E is void:
|
when E is void:
|
||||||
Result[T1, void].err()
|
Result[T1, void].err()
|
||||||
else:
|
else:
|
||||||
Result[T1, E].err(self.e)
|
Result[T1, E].err(self.eResultPrivate)
|
||||||
|
|
||||||
func mapErr*[T, E0, E1](
|
func mapErr*[T, E0, E1](
|
||||||
self: Result[T, E0], f: proc(x: E0): E1):
|
self: Result[T, E0], f: proc(x: E0): E1):
|
||||||
Result[T, E1] {.inline, effectsOf: f.} =
|
Result[T, E1] {.inline, effectsOf: f.} =
|
||||||
## Transform error using f, or leave untouched
|
## Transform error using f, or leave untouched
|
||||||
if self.o:
|
if self.oResultPrivate:
|
||||||
when T is void:
|
when T is void:
|
||||||
result.ok()
|
result.ok()
|
||||||
else:
|
else:
|
||||||
result.ok(self.v)
|
result.ok(self.vResultPrivate)
|
||||||
else:
|
else:
|
||||||
result.err(f(self.e))
|
result.err(f(self.eResultPrivate))
|
||||||
|
|
||||||
func mapErr*[T, E1](
|
func mapErr*[T, E1](
|
||||||
self: Result[T, void], f: proc(): E1):
|
self: Result[T, void], f: proc(): E1):
|
||||||
Result[T, E1] {.inline, effectsOf: f.} =
|
Result[T, E1] {.inline, effectsOf: f.} =
|
||||||
## Transform error using f, or return value
|
## Transform error using f, or return value
|
||||||
if self.o:
|
if self.oResultPrivate:
|
||||||
when T is void:
|
when T is void:
|
||||||
result.ok()
|
result.ok()
|
||||||
else:
|
else:
|
||||||
result.ok(self.v)
|
result.ok(self.vResultPrivate)
|
||||||
else:
|
else:
|
||||||
result.err(f())
|
result.err(f())
|
||||||
|
|
||||||
|
@ -549,24 +555,24 @@ func mapErr*[T, E0](
|
||||||
self: Result[T, E0], f: proc(x: E0)):
|
self: Result[T, E0], f: proc(x: E0)):
|
||||||
Result[T, void] {.inline, effectsOf: f.} =
|
Result[T, void] {.inline, effectsOf: f.} =
|
||||||
## Transform error using f, or return value
|
## Transform error using f, or return value
|
||||||
if self.o:
|
if self.oResultPrivate:
|
||||||
when T is void:
|
when T is void:
|
||||||
result.ok()
|
result.ok()
|
||||||
else:
|
else:
|
||||||
result.ok(self.v)
|
result.ok(self.vResultPrivate)
|
||||||
else:
|
else:
|
||||||
f(self.e)
|
f(self.eResultPrivate)
|
||||||
result.err()
|
result.err()
|
||||||
|
|
||||||
func mapErr*[T](
|
func mapErr*[T](
|
||||||
self: Result[T, void], f: proc()):
|
self: Result[T, void], f: proc()):
|
||||||
Result[T, void] {.inline, effectsOf: f.} =
|
Result[T, void] {.inline, effectsOf: f.} =
|
||||||
## Transform error using f, or return value
|
## Transform error using f, or return value
|
||||||
if self.o:
|
if self.oResultPrivate:
|
||||||
when T is void:
|
when T is void:
|
||||||
result.ok()
|
result.ok()
|
||||||
else:
|
else:
|
||||||
result.ok(self.v)
|
result.ok(self.vResultPrivate)
|
||||||
else:
|
else:
|
||||||
f()
|
f()
|
||||||
result.err()
|
result.err()
|
||||||
|
@ -575,33 +581,33 @@ func mapConvert*[T0, E](
|
||||||
self: Result[T0, E], T1: type): Result[T1, E] {.inline.} =
|
self: Result[T0, E], T1: type): Result[T1, E] {.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.o:
|
if self.oResultPrivate:
|
||||||
when T1 is void:
|
when T1 is void:
|
||||||
result.ok()
|
result.ok()
|
||||||
else:
|
else:
|
||||||
result.ok(T1(self.v))
|
result.ok(T1(self.vResultPrivate))
|
||||||
else:
|
else:
|
||||||
when E is void:
|
when E is void:
|
||||||
result.err()
|
result.err()
|
||||||
else:
|
else:
|
||||||
result.err(self.e)
|
result.err(self.eResultPrivate)
|
||||||
|
|
||||||
func mapCast*[T0, E](
|
func mapCast*[T0, E](
|
||||||
self: Result[T0, E], T1: type): Result[T1, E] {.inline.} =
|
self: Result[T0, E], T1: type): Result[T1, E] {.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.o: result.ok(cast[T1](self.v))
|
if self.oResultPrivate: result.ok(cast[T1](self.vResultPrivate))
|
||||||
else:
|
else:
|
||||||
when E is void:
|
when E is void:
|
||||||
result.err()
|
result.err()
|
||||||
else:
|
else:
|
||||||
result.err(self.e)
|
result.err(self.eResultPrivate)
|
||||||
|
|
||||||
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) # TODO avoid copy
|
let s = (self) # TODO avoid copy
|
||||||
if s.o:
|
if s.oResultPrivate:
|
||||||
other
|
other
|
||||||
else:
|
else:
|
||||||
when type(self) is type(other):
|
when type(self) is type(other):
|
||||||
|
@ -611,7 +617,7 @@ template `and`*[T0, E, T1](self: Result[T0, E], other: Result[T1, E]): Result[T1
|
||||||
when E is void:
|
when E is void:
|
||||||
err(R)
|
err(R)
|
||||||
else:
|
else:
|
||||||
err(R, s.e)
|
err(R, s.eResultPrivate)
|
||||||
|
|
||||||
template `or`*[T, E0, E1](self: Result[T, E0], other: Result[T, E1]): Result[T, E1] =
|
template `or`*[T, E0, E1](self: Result[T, E0], other: Result[T, E1]): Result[T, E1] =
|
||||||
## Evaluate `other` iff `not self.isOk`, else return `self`
|
## Evaluate `other` iff `not self.isOk`, else return `self`
|
||||||
|
@ -622,7 +628,7 @@ template `or`*[T, E0, E1](self: Result[T, E0], other: Result[T, E1]): Result[T,
|
||||||
## f2() or err(SomeEnum.V) # Collapse errors from other module / function
|
## f2() or err(SomeEnum.V) # Collapse errors from other module / function
|
||||||
## ```
|
## ```
|
||||||
let s = (self) # TODO avoid copy
|
let s = (self) # TODO avoid copy
|
||||||
if s.o:
|
if s.oResultPrivate:
|
||||||
when type(self) is type(other):
|
when type(self) is type(other):
|
||||||
s
|
s
|
||||||
else:
|
else:
|
||||||
|
@ -630,7 +636,7 @@ template `or`*[T, E0, E1](self: Result[T, E0], other: Result[T, E1]): Result[T,
|
||||||
when T is void:
|
when T is void:
|
||||||
ok(R)
|
ok(R)
|
||||||
else:
|
else:
|
||||||
ok(R, s.v)
|
ok(R, s.vResultPrivate)
|
||||||
else:
|
else:
|
||||||
other
|
other
|
||||||
|
|
||||||
|
@ -646,14 +652,14 @@ template orErr*[T, E0, E1](self: Result[T, E0], error: E1): Result[T, E1] =
|
||||||
## ** Experimental, may be removed **
|
## ** Experimental, may be removed **
|
||||||
let s = (self) # TODO avoid copy
|
let s = (self) # TODO avoid copy
|
||||||
type R = Result[T, E1]
|
type R = Result[T, E1]
|
||||||
if s.o:
|
if s.oResultPrivate:
|
||||||
when type(self) is R:
|
when type(self) is R:
|
||||||
s
|
s
|
||||||
else:
|
else:
|
||||||
when T is void:
|
when T is void:
|
||||||
ok(R)
|
ok(R)
|
||||||
else:
|
else:
|
||||||
ok(R, s.v)
|
ok(R, s.vResultPrivate)
|
||||||
else:
|
else:
|
||||||
err(R, error)
|
err(R, error)
|
||||||
|
|
||||||
|
@ -668,16 +674,16 @@ template catch*(body: typed): Result[type(body), ref CatchableError] =
|
||||||
|
|
||||||
try:
|
try:
|
||||||
R.ok(body)
|
R.ok(body)
|
||||||
except CatchableError as e:
|
except CatchableError as eResultPrivate:
|
||||||
R.err(e)
|
R.err(eResultPrivate)
|
||||||
|
|
||||||
template capture*[E: Exception](T: type, someExceptionExpr: ref E): Result[T, ref E] =
|
template capture*[E: Exception](T: type, someExceptionExpr: ref E): Result[T, ref E] =
|
||||||
## Evaluate someExceptionExpr and put the exception into a result, making sure
|
## Evaluate someExceptionExpr and put the exception into a result, making sure
|
||||||
## to capture a call stack at the capture site:
|
## to capture a call stack at the capture site:
|
||||||
##
|
##
|
||||||
## ```
|
## ```
|
||||||
## let e: Result[void, ValueError] = void.capture((ref ValueError)(msg: "test"))
|
## let eResultPrivate: Result[void, ValueError] = void.capture((ref ValueError)(msg: "test"))
|
||||||
## echo e.error().getStackTrace()
|
## echo eResultPrivate.error().getStackTrace()
|
||||||
## ```
|
## ```
|
||||||
type R = Result[T, ref E]
|
type R = Result[T, ref E]
|
||||||
|
|
||||||
|
@ -696,28 +702,28 @@ func `==`*[
|
||||||
T0: not void, E0: not void,
|
T0: not void, E0: not void,
|
||||||
T1: not void, E1: not void](
|
T1: not void, E1: not void](
|
||||||
lhs: Result[T0, E0], rhs: Result[T1, E1]): bool {.inline.} =
|
lhs: Result[T0, E0], rhs: Result[T1, E1]): bool {.inline.} =
|
||||||
if lhs.o != rhs.o:
|
if lhs.oResultPrivate != rhs.oResultPrivate:
|
||||||
false
|
false
|
||||||
elif lhs.o: # and rhs.o implied
|
elif lhs.oResultPrivate: # and rhs.oResultPrivate implied
|
||||||
lhs.v == rhs.v
|
lhs.vResultPrivate == rhs.vResultPrivate
|
||||||
else:
|
else:
|
||||||
lhs.e == rhs.e
|
lhs.eResultPrivate == rhs.eResultPrivate
|
||||||
|
|
||||||
func `==`*[E0, E1](
|
func `==`*[E0, E1](
|
||||||
lhs: Result[void, E0], rhs: Result[void, E1]): bool {.inline.} =
|
lhs: Result[void, E0], rhs: Result[void, E1]): bool {.inline.} =
|
||||||
if lhs.o != rhs.o:
|
if lhs.oResultPrivate != rhs.oResultPrivate:
|
||||||
false
|
false
|
||||||
elif lhs.o: # and rhs.o implied
|
elif lhs.oResultPrivate: # and rhs.oResultPrivate implied
|
||||||
true
|
true
|
||||||
else:
|
else:
|
||||||
lhs.e == rhs.e
|
lhs.eResultPrivate == rhs.eResultPrivate
|
||||||
|
|
||||||
func `==`*[T0, T1](
|
func `==`*[T0, T1](
|
||||||
lhs: Result[T0, void], rhs: Result[T1, void]): bool {.inline.} =
|
lhs: Result[T0, void], rhs: Result[T1, void]): bool {.inline.} =
|
||||||
if lhs.o != rhs.o:
|
if lhs.oResultPrivate != rhs.oResultPrivate:
|
||||||
false
|
false
|
||||||
elif lhs.o: # and rhs.o implied
|
elif lhs.oResultPrivate: # and rhs.oResultPrivate implied
|
||||||
lhs.v == rhs.v
|
lhs.vResultPrivate == rhs.vResultPrivate
|
||||||
else:
|
else:
|
||||||
true
|
true
|
||||||
|
|
||||||
|
@ -727,21 +733,21 @@ func get*[T, E](self: Result[T, E]): T {.inline.} =
|
||||||
## See also: Option.get
|
## See also: Option.get
|
||||||
assertOk(self)
|
assertOk(self)
|
||||||
when T isnot void:
|
when T isnot void:
|
||||||
self.v
|
self.vResultPrivate
|
||||||
|
|
||||||
func tryGet*[T, E](self: Result[T, E]): T {.inline.} =
|
func tryGet*[T, 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]
|
||||||
mixin raiseResultError
|
mixin raiseResultError
|
||||||
if not self.o: self.raiseResultError()
|
if not self.oResultPrivate: self.raiseResultError()
|
||||||
when T isnot void:
|
when T isnot void:
|
||||||
self.v
|
self.vResultPrivate
|
||||||
|
|
||||||
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`
|
||||||
## See `valueOr` for a template version that avoids evaluating `otherwise`
|
## See `valueOr` for a template version that avoids evaluating `otherwise`
|
||||||
## unless necessary
|
## unless necessary
|
||||||
if self.o: self.v
|
if self.oResultPrivate: self.vResultPrivate
|
||||||
else: otherwise
|
else: otherwise
|
||||||
|
|
||||||
func get*[T: not void, E](self: var Result[T, E]): var T {.inline.} =
|
func get*[T: not void, E](self: var Result[T, E]): var T {.inline.} =
|
||||||
|
@ -749,7 +755,7 @@ func get*[T: not void, E](self: var Result[T, E]): var T {.inline.} =
|
||||||
## Exception bridge mode: raise given Exception instead
|
## Exception bridge mode: raise given Exception instead
|
||||||
## See also: Option.get
|
## See also: Option.get
|
||||||
assertOk(self)
|
assertOk(self)
|
||||||
self.v
|
self.vResultPrivate
|
||||||
|
|
||||||
template `[]`*[T: not void, E](self: Result[T, E]): T =
|
template `[]`*[T: not void, E](self: Result[T, E]): T =
|
||||||
## Fetch value of result if set, or raise Defect
|
## Fetch value of result if set, or raise Defect
|
||||||
|
@ -769,12 +775,12 @@ template `[]`*[T: not void, E](self: var Result[T, E]): var T =
|
||||||
template unsafeGet*[T: not void, E](self: Result[T, E]): T =
|
template unsafeGet*[T: not void, 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: `unsafeError`
|
## See also: `unsafeError`
|
||||||
self.v
|
self.vResultPrivate
|
||||||
|
|
||||||
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: `unsafeError`
|
## See also: `unsafeError`
|
||||||
assert self.o
|
assert self.oResultPrivate
|
||||||
|
|
||||||
func expect*[T, E](self: Result[T, E], m: string): T =
|
func expect*[T, E](self: Result[T, E], m: string): T =
|
||||||
## Return value of Result, or raise a `Defect` with the given message - use
|
## Return value of Result, or raise a `Defect` with the given message - use
|
||||||
|
@ -786,58 +792,58 @@ func expect*[T, 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.o:
|
if not self.oResultPrivate:
|
||||||
when E isnot void:
|
when E isnot void:
|
||||||
raiseResultDefect(m, self.e)
|
raiseResultDefect(m, self.eResultPrivate)
|
||||||
else:
|
else:
|
||||||
raiseResultDefect(m)
|
raiseResultDefect(m)
|
||||||
when T isnot void:
|
when T isnot void:
|
||||||
self.v
|
self.vResultPrivate
|
||||||
|
|
||||||
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.o:
|
if not self.oResultPrivate:
|
||||||
when E isnot void:
|
when E isnot void:
|
||||||
raiseResultDefect(m, self.e)
|
raiseResultDefect(m, self.eResultPrivate)
|
||||||
else:
|
else:
|
||||||
raiseResultDefect(m)
|
raiseResultDefect(m)
|
||||||
self.v
|
self.vResultPrivate
|
||||||
|
|
||||||
func `$`*[T, E](self: Result[T, E]): string =
|
func `$`*[T, E](self: Result[T, E]): string =
|
||||||
## Returns string representation of `self`
|
## Returns string representation of `self`
|
||||||
if self.o:
|
if self.oResultPrivate:
|
||||||
when T is void: "ok()"
|
when T is void: "ok()"
|
||||||
else: "ok(" & $self.v & ")"
|
else: "ok(" & $self.vResultPrivate & ")"
|
||||||
else:
|
else:
|
||||||
when E is void: "none()"
|
when E is void: "none()"
|
||||||
else: "err(" & $self.e & ")"
|
else: "err(" & $self.eResultPrivate & ")"
|
||||||
|
|
||||||
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 self.o:
|
if self.oResultPrivate:
|
||||||
when T isnot void:
|
when T isnot void:
|
||||||
raiseResultDefect("Trying to access error when value is set", self.v)
|
raiseResultDefect("Trying to access error when value is set", self.vResultPrivate)
|
||||||
else:
|
else:
|
||||||
raiseResultDefect("Trying to access error when value is set")
|
raiseResultDefect("Trying to access error when value is set")
|
||||||
when E isnot void:
|
when E isnot void:
|
||||||
self.e
|
self.eResultPrivate
|
||||||
|
|
||||||
func tryError*[T, E](self: Result[T, E]): E {.inline.} =
|
func tryError*[T, E](self: Result[T, E]): E {.inline.} =
|
||||||
## Fetch error of result if set, or raise
|
## Fetch error of result if set, or raise
|
||||||
## Raises a ResultError[T]
|
## Raises a ResultError[T]
|
||||||
mixin raiseResultOk
|
mixin raiseResultOk
|
||||||
if self.o: self.raiseResultOk()
|
if self.oResultPrivate: self.raiseResultOk()
|
||||||
when E isnot void:
|
when E isnot void:
|
||||||
self.e
|
self.eResultPrivate
|
||||||
|
|
||||||
template unsafeError*[T, E: not void](self: Result[T, E]): E =
|
template unsafeError*[T, E: not void](self: Result[T, E]): E =
|
||||||
## Fetch value of result if set, undefined behavior if unset
|
## Fetch value of result if set, undefined behavior if unset
|
||||||
## See also: `unsafeGet`
|
## See also: `unsafeGet`
|
||||||
self.e
|
self.eResultPrivate
|
||||||
|
|
||||||
template unsafeError*[T](self: Result[T, void]) =
|
template unsafeError*[T](self: Result[T, void]) =
|
||||||
## Fetch value of result if set, undefined behavior if unset
|
## Fetch value of result if set, undefined behavior if unset
|
||||||
## See also: `unsafeGet`
|
## See also: `unsafeGet`
|
||||||
assert not self.o # Emulate field access defect in debug builds
|
assert not self.oResultPrivate # Emulate field access defect in debug builds
|
||||||
|
|
||||||
# Alternative spellings for get
|
# Alternative spellings for get
|
||||||
template value*[T, E](self: Result[T, E]): T = self.get()
|
template value*[T, E](self: Result[T, E]): T = self.get()
|
||||||
|
@ -867,10 +873,10 @@ template valueOr*[T: not void, E](self: Result[T, E], def: untyped): T =
|
||||||
## see https://github.com/status-im/nim-stew/issues/161#issuecomment-1397121386
|
## see https://github.com/status-im/nim-stew/issues/161#issuecomment-1397121386
|
||||||
##
|
##
|
||||||
let s = (self) # TODO avoid copy
|
let s = (self) # TODO avoid copy
|
||||||
if s.o: s.v
|
if s.oResultPrivate: s.vResultPrivate
|
||||||
else:
|
else:
|
||||||
when E isnot void:
|
when E isnot void:
|
||||||
template error: E {.used, inject.} = s.e
|
template error: E {.used, inject.} = s.eResultPrivate
|
||||||
def
|
def
|
||||||
|
|
||||||
template errorOr*[T, E: not void](self: Result[T, E], def: untyped): E =
|
template errorOr*[T, E: not void](self: Result[T, E], def: untyped): E =
|
||||||
|
@ -878,16 +884,16 @@ template errorOr*[T, E: not void](self: Result[T, E], def: untyped): E =
|
||||||
## `def` is evaluated lazily, and must be an expression of `T` or exit
|
## `def` is evaluated lazily, and must be an expression of `T` or exit
|
||||||
## the scope (for example using `return` / `raise`)
|
## the scope (for example using `return` / `raise`)
|
||||||
let s = (self) # TODO avoid copy
|
let s = (self) # TODO avoid copy
|
||||||
if not s.o: s.e
|
if not s.oResultPrivate: s.eResultPrivate
|
||||||
else:
|
else:
|
||||||
when T isnot void:
|
when T isnot void:
|
||||||
template value: T {.used, inject.} = s.v
|
template value: T {.used, inject.} = s.vResultPrivate
|
||||||
def
|
def
|
||||||
|
|
||||||
func flatten*[T, E](self: Result[Result[T, E], E]): Result[T, E] =
|
func flatten*[T, E](self: Result[Result[T, E], E]): Result[T, E] =
|
||||||
## Remove one level of nesting
|
## Remove one level of nesting
|
||||||
if self.o:
|
if self.oResultPrivate:
|
||||||
self.v
|
self.vResultPrivate
|
||||||
else:
|
else:
|
||||||
when E is void:
|
when E is void:
|
||||||
err(Result[T, E])
|
err(Result[T, E])
|
||||||
|
@ -901,8 +907,8 @@ func filter*[T, E](
|
||||||
## Apply `callback` to the `self`, iff `self` is not an error. If `callback`
|
## Apply `callback` to the `self`, iff `self` is not an error. If `callback`
|
||||||
## returns an error, return that error, else return `self`
|
## returns an error, return that error, else return `self`
|
||||||
|
|
||||||
if self.o:
|
if self.oResultPrivate:
|
||||||
callback(self.v) and self
|
callback(self.vResultPrivate) and self
|
||||||
else:
|
else:
|
||||||
self
|
self
|
||||||
|
|
||||||
|
@ -913,7 +919,7 @@ func filter*[E](
|
||||||
## Apply `callback` to the `self`, iff `self` is not an error. If `callback`
|
## Apply `callback` to the `self`, iff `self` is not an error. If `callback`
|
||||||
## returns an error, return that error, else return `self`
|
## returns an error, return that error, else return `self`
|
||||||
|
|
||||||
if self.o:
|
if self.oResultPrivate:
|
||||||
callback() and self
|
callback() and self
|
||||||
else:
|
else:
|
||||||
self
|
self
|
||||||
|
@ -925,8 +931,8 @@ func filter*[T](
|
||||||
## Apply `callback` to the `self`, iff `self` is not an error. If `callback`
|
## Apply `callback` to the `self`, iff `self` is not an error. If `callback`
|
||||||
## returns an error, return that error, else return `self`
|
## returns an error, return that error, else return `self`
|
||||||
|
|
||||||
if self.o:
|
if self.oResultPrivate:
|
||||||
if callback(self.v):
|
if callback(self.vResultPrivate):
|
||||||
self
|
self
|
||||||
else:
|
else:
|
||||||
Result[T, void].err()
|
Result[T, void].err()
|
||||||
|
@ -939,8 +945,8 @@ template some*[T](O: type Opt, v: T): Opt[T] =
|
||||||
## Create an `Opt` set to a value
|
## Create an `Opt` set to a value
|
||||||
##
|
##
|
||||||
## ```
|
## ```
|
||||||
## let o = Opt.some(42)
|
## let oResultPrivate = Opt.some(42)
|
||||||
## assert o.isSome and o.get() == 42
|
## assert oResultPrivate.isSome and oResultPrivate.get() == 42
|
||||||
## ```
|
## ```
|
||||||
Opt[T].ok(v)
|
Opt[T].ok(v)
|
||||||
|
|
||||||
|
@ -948,18 +954,18 @@ template none*(O: type Opt, T: type): Opt[T] =
|
||||||
## Create an `Opt` set to none
|
## Create an `Opt` set to none
|
||||||
##
|
##
|
||||||
## ```
|
## ```
|
||||||
## let o = Opt.none(int)
|
## let oResultPrivate = Opt.none(int)
|
||||||
## assert o.isNone
|
## assert oResultPrivate.isNone
|
||||||
## ```
|
## ```
|
||||||
Opt[T].err()
|
Opt[T].err()
|
||||||
|
|
||||||
template isSome*(o: Opt): bool =
|
template isSome*(oResultPrivate: Opt): bool =
|
||||||
## Alias for `isOk`
|
## Alias for `isOk`
|
||||||
isOk o
|
isOk oResultPrivate
|
||||||
|
|
||||||
template isNone*(o: Opt): bool =
|
template isNone*(oResultPrivate: Opt): bool =
|
||||||
## Alias of `isErr`
|
## Alias of `isErr`
|
||||||
isErr o
|
isErr oResultPrivate
|
||||||
|
|
||||||
# Syntactic convenience
|
# Syntactic convenience
|
||||||
|
|
||||||
|
@ -975,14 +981,14 @@ template `?`*[T, E](self: Result[T, E]): auto =
|
||||||
# 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 not v.o:
|
if not v.oResultPrivate:
|
||||||
when typeof(result) is typeof(v):
|
when typeof(result) is typeof(v):
|
||||||
return v
|
return v
|
||||||
else:
|
else:
|
||||||
when E is void:
|
when E is void:
|
||||||
return err(typeof(result))
|
return err(typeof(result))
|
||||||
else:
|
else:
|
||||||
return err(typeof(result), v.e)
|
return err(typeof(result), v.eResultPrivate)
|
||||||
|
|
||||||
when not(T is void):
|
when not(T is void):
|
||||||
v.v
|
v.vResultPrivate
|
||||||
|
|
|
@ -32,5 +32,6 @@ import
|
||||||
test_sets,
|
test_sets,
|
||||||
test_templateutils,
|
test_templateutils,
|
||||||
test_results,
|
test_results,
|
||||||
|
test_results2,
|
||||||
test_varints,
|
test_varints,
|
||||||
test_winacl
|
test_winacl
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
import ../stew/results
|
||||||
|
|
||||||
|
{.used.}
|
||||||
|
|
||||||
|
# Oddly, this piece of code works when placed in `test_results.nim`
|
||||||
|
# See also https://github.com/status-im/nim-stew/pull/167
|
||||||
|
|
||||||
|
template repeater(b: Opt[int]): untyped =
|
||||||
|
# Check that Result can be used inside a template - this fails
|
||||||
|
# sometimes with field access errors as noted in above issue
|
||||||
|
b
|
||||||
|
let x = repeater(Opt.none(int))
|
||||||
|
doAssert x.isNone()
|
Loading…
Reference in New Issue