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
|
||||
|
||||
# TODO https://github.com/nim-lang/Nim/issues/20699
|
||||
# case o: bool
|
||||
# case oResultPrivate: bool
|
||||
# of false:
|
||||
# e: E
|
||||
# eResultPrivate: E
|
||||
# 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 E is void:
|
||||
o: bool
|
||||
oResultPrivate*: bool
|
||||
else:
|
||||
case o: bool
|
||||
case oResultPrivate*: bool
|
||||
of false:
|
||||
e: E
|
||||
eResultPrivate*: E
|
||||
of true:
|
||||
discard
|
||||
else:
|
||||
when E is void:
|
||||
case o: bool
|
||||
case oResultPrivate*: bool
|
||||
of false:
|
||||
discard
|
||||
of true:
|
||||
v: T
|
||||
vResultPrivate*: T
|
||||
else:
|
||||
case o: bool
|
||||
case oResultPrivate*: bool
|
||||
of false:
|
||||
e: E
|
||||
eResultPrivate*: E
|
||||
of true:
|
||||
v: T
|
||||
vResultPrivate*: T
|
||||
|
||||
Opt*[T] = Result[T, void]
|
||||
|
||||
|
@ -342,7 +348,7 @@ func raiseResultOk[T, E](self: Result[T, E]) {.noreturn, noinline.} =
|
|||
when T is void:
|
||||
raise (ref ResultError[void])(msg: "Trying to access error with value")
|
||||
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.} =
|
||||
# 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
|
||||
|
||||
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 self.e
|
||||
raise self.eResultPrivate
|
||||
elif E is void:
|
||||
raise (ref ResultError[void])(msg: "Trying to access value with err")
|
||||
elif compiles(toException(self.e)):
|
||||
raise toException(self.e)
|
||||
elif compiles($self.e):
|
||||
elif compiles(toException(self.eResultPrivate)):
|
||||
raise toException(self.eResultPrivate)
|
||||
elif compiles($self.eResultPrivate):
|
||||
raise (ref ResultError[E])(
|
||||
error: self.e, msg: $self.e)
|
||||
error: self.eResultPrivate, msg: $self.eResultPrivate)
|
||||
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.} =
|
||||
mixin `$`
|
||||
|
@ -373,21 +379,21 @@ func raiseResultDefect(m: string) {.noreturn, noinline.} =
|
|||
|
||||
template assertOk(self: Result) =
|
||||
# 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:
|
||||
raiseResultDefect("Trying to access value with err Result", self.e)
|
||||
raiseResultDefect("Trying to access value with err Result", self.eResultPrivate)
|
||||
else:
|
||||
raiseResultDefect("Trying to access value with err Result")
|
||||
|
||||
template ok*[T, E](R: type Result[T, E], x: untyped): R =
|
||||
## Initialize a result with a success and value
|
||||
## 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 =
|
||||
## Initialize a result with a success and value
|
||||
## Example: `Result[void, string].ok()`
|
||||
R(o: true)
|
||||
R(oResultPrivate: true)
|
||||
|
||||
template ok*[T: not void, E](self: var Result[T, E], x: untyped) =
|
||||
## 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 =
|
||||
## Initialize the result to an error
|
||||
## 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 =
|
||||
## Initialize the result to an error
|
||||
## Example: `Result[int, string].err("uh-oh")`
|
||||
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 =
|
||||
## Initialize the result to an error
|
||||
## Example: `Result[int, void].err()`
|
||||
R(o: false)
|
||||
R(oResultPrivate: false)
|
||||
|
||||
template err*[T, E](self: var Result[T, E], x: untyped) =
|
||||
## 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*(): auto = err(typeof(result))
|
||||
|
||||
template isOk*(self: Result): bool = self.o
|
||||
template isErr*(self: Result): bool = not self.o
|
||||
template isOk*(self: Result): bool = self.oResultPrivate
|
||||
template isErr*(self: Result): bool = not self.oResultPrivate
|
||||
|
||||
when not defined(nimHasEffectsOfs):
|
||||
template effectsOf(f: untyped) {.pragma.}
|
||||
|
@ -450,13 +456,13 @@ func map*[T0, E, T1](
|
|||
## let r = Result[int, cstring).ok(42)
|
||||
## assert r.map(proc (v: int): int = $v).get() == "42"
|
||||
## ```
|
||||
if self.o:
|
||||
result.ok(f(self.v))
|
||||
if self.oResultPrivate:
|
||||
result.ok(f(self.vResultPrivate))
|
||||
else:
|
||||
when E is void:
|
||||
result.err()
|
||||
else:
|
||||
result.err(self.e)
|
||||
result.err(self.eResultPrivate)
|
||||
|
||||
func map*[T, E](
|
||||
self: Result[T, E], f: proc(x: T)):
|
||||
|
@ -467,81 +473,81 @@ func map*[T, E](
|
|||
## let r = Result[int, cstring).ok(42)
|
||||
## assert r.map(proc (v: int): int = $v).get() == "42"
|
||||
## ```
|
||||
if self.o:
|
||||
f(self.v)
|
||||
if self.oResultPrivate:
|
||||
f(self.vResultPrivate)
|
||||
result.ok()
|
||||
else:
|
||||
when E is void:
|
||||
result.err()
|
||||
else:
|
||||
result.err(self.e)
|
||||
result.err(self.eResultPrivate)
|
||||
|
||||
func map*[E, T1](
|
||||
self: Result[void, E], f: proc(): T1):
|
||||
Result[T1, E] {.inline, effectsOf: f.} =
|
||||
## Transform value using f, or return error
|
||||
if self.o:
|
||||
if self.oResultPrivate:
|
||||
result.ok(f())
|
||||
else:
|
||||
when E is void:
|
||||
result.err()
|
||||
else:
|
||||
result.err(self.e)
|
||||
result.err(self.eResultPrivate)
|
||||
|
||||
func map*[E](
|
||||
self: Result[void, E], f: proc()):
|
||||
Result[void, E] {.inline, effectsOf: f.} =
|
||||
## Call f if value is
|
||||
if self.o:
|
||||
if self.oResultPrivate:
|
||||
f()
|
||||
result.ok()
|
||||
else:
|
||||
when E is void:
|
||||
result.err()
|
||||
else:
|
||||
result.err(self.e)
|
||||
result.err(self.eResultPrivate)
|
||||
|
||||
func flatMap*[T0, E, T1](
|
||||
self: Result[T0, E], f: proc(x: T0): Result[T1, E]):
|
||||
Result[T1, E] {.inline, effectsOf: f.} =
|
||||
if self.o: f(self.v)
|
||||
if self.oResultPrivate: f(self.vResultPrivate)
|
||||
else:
|
||||
when E is void:
|
||||
Result[T1, void].err()
|
||||
else:
|
||||
Result[T1, E].err(self.e)
|
||||
Result[T1, E].err(self.eResultPrivate)
|
||||
|
||||
func flatMap*[E, T1](
|
||||
self: Result[void, E], f: proc(): Result[T1, E]):
|
||||
Result[T1, E] {.inline, effectsOf: f.} =
|
||||
if self.o: f()
|
||||
if self.oResultPrivate: f()
|
||||
else:
|
||||
when E is void:
|
||||
Result[T1, void].err()
|
||||
else:
|
||||
Result[T1, E].err(self.e)
|
||||
Result[T1, E].err(self.eResultPrivate)
|
||||
|
||||
func mapErr*[T, E0, E1](
|
||||
self: Result[T, E0], f: proc(x: E0): E1):
|
||||
Result[T, E1] {.inline, effectsOf: f.} =
|
||||
## Transform error using f, or leave untouched
|
||||
if self.o:
|
||||
if self.oResultPrivate:
|
||||
when T is void:
|
||||
result.ok()
|
||||
else:
|
||||
result.ok(self.v)
|
||||
result.ok(self.vResultPrivate)
|
||||
else:
|
||||
result.err(f(self.e))
|
||||
result.err(f(self.eResultPrivate))
|
||||
|
||||
func mapErr*[T, E1](
|
||||
self: Result[T, void], f: proc(): E1):
|
||||
Result[T, E1] {.inline, effectsOf: f.} =
|
||||
## Transform error using f, or return value
|
||||
if self.o:
|
||||
if self.oResultPrivate:
|
||||
when T is void:
|
||||
result.ok()
|
||||
else:
|
||||
result.ok(self.v)
|
||||
result.ok(self.vResultPrivate)
|
||||
else:
|
||||
result.err(f())
|
||||
|
||||
|
@ -549,24 +555,24 @@ func mapErr*[T, E0](
|
|||
self: Result[T, E0], f: proc(x: E0)):
|
||||
Result[T, void] {.inline, effectsOf: f.} =
|
||||
## Transform error using f, or return value
|
||||
if self.o:
|
||||
if self.oResultPrivate:
|
||||
when T is void:
|
||||
result.ok()
|
||||
else:
|
||||
result.ok(self.v)
|
||||
result.ok(self.vResultPrivate)
|
||||
else:
|
||||
f(self.e)
|
||||
f(self.eResultPrivate)
|
||||
result.err()
|
||||
|
||||
func mapErr*[T](
|
||||
self: Result[T, void], f: proc()):
|
||||
Result[T, void] {.inline, effectsOf: f.} =
|
||||
## Transform error using f, or return value
|
||||
if self.o:
|
||||
if self.oResultPrivate:
|
||||
when T is void:
|
||||
result.ok()
|
||||
else:
|
||||
result.ok(self.v)
|
||||
result.ok(self.vResultPrivate)
|
||||
else:
|
||||
f()
|
||||
result.err()
|
||||
|
@ -575,33 +581,33 @@ func mapConvert*[T0, E](
|
|||
self: Result[T0, E], T1: type): Result[T1, E] {.inline.} =
|
||||
## Convert result value to A using an conversion
|
||||
# Would be nice if it was automatic...
|
||||
if self.o:
|
||||
if self.oResultPrivate:
|
||||
when T1 is void:
|
||||
result.ok()
|
||||
else:
|
||||
result.ok(T1(self.v))
|
||||
result.ok(T1(self.vResultPrivate))
|
||||
else:
|
||||
when E is void:
|
||||
result.err()
|
||||
else:
|
||||
result.err(self.e)
|
||||
result.err(self.eResultPrivate)
|
||||
|
||||
func mapCast*[T0, E](
|
||||
self: Result[T0, E], T1: type): Result[T1, E] {.inline.} =
|
||||
## Convert result value to A using a cast
|
||||
## 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:
|
||||
when E is void:
|
||||
result.err()
|
||||
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] =
|
||||
## Evaluate `other` iff self.isOk, else return error
|
||||
## fail-fast - will not evaluate other if a is an error
|
||||
let s = (self) # TODO avoid copy
|
||||
if s.o:
|
||||
if s.oResultPrivate:
|
||||
other
|
||||
else:
|
||||
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:
|
||||
err(R)
|
||||
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] =
|
||||
## 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
|
||||
## ```
|
||||
let s = (self) # TODO avoid copy
|
||||
if s.o:
|
||||
if s.oResultPrivate:
|
||||
when type(self) is type(other):
|
||||
s
|
||||
else:
|
||||
|
@ -630,7 +636,7 @@ template `or`*[T, E0, E1](self: Result[T, E0], other: Result[T, E1]): Result[T,
|
|||
when T is void:
|
||||
ok(R)
|
||||
else:
|
||||
ok(R, s.v)
|
||||
ok(R, s.vResultPrivate)
|
||||
else:
|
||||
other
|
||||
|
||||
|
@ -646,14 +652,14 @@ template orErr*[T, E0, E1](self: Result[T, E0], error: E1): Result[T, E1] =
|
|||
## ** Experimental, may be removed **
|
||||
let s = (self) # TODO avoid copy
|
||||
type R = Result[T, E1]
|
||||
if s.o:
|
||||
if s.oResultPrivate:
|
||||
when type(self) is R:
|
||||
s
|
||||
else:
|
||||
when T is void:
|
||||
ok(R)
|
||||
else:
|
||||
ok(R, s.v)
|
||||
ok(R, s.vResultPrivate)
|
||||
else:
|
||||
err(R, error)
|
||||
|
||||
|
@ -668,16 +674,16 @@ template catch*(body: typed): Result[type(body), ref CatchableError] =
|
|||
|
||||
try:
|
||||
R.ok(body)
|
||||
except CatchableError as e:
|
||||
R.err(e)
|
||||
except CatchableError as eResultPrivate:
|
||||
R.err(eResultPrivate)
|
||||
|
||||
template capture*[E: Exception](T: type, someExceptionExpr: ref E): Result[T, ref E] =
|
||||
## Evaluate someExceptionExpr and put the exception into a result, making sure
|
||||
## to capture a call stack at the capture site:
|
||||
##
|
||||
## ```
|
||||
## let e: Result[void, ValueError] = void.capture((ref ValueError)(msg: "test"))
|
||||
## echo e.error().getStackTrace()
|
||||
## let eResultPrivate: Result[void, ValueError] = void.capture((ref ValueError)(msg: "test"))
|
||||
## echo eResultPrivate.error().getStackTrace()
|
||||
## ```
|
||||
type R = Result[T, ref E]
|
||||
|
||||
|
@ -696,28 +702,28 @@ func `==`*[
|
|||
T0: not void, E0: not void,
|
||||
T1: not void, E1: not void](
|
||||
lhs: Result[T0, E0], rhs: Result[T1, E1]): bool {.inline.} =
|
||||
if lhs.o != rhs.o:
|
||||
if lhs.oResultPrivate != rhs.oResultPrivate:
|
||||
false
|
||||
elif lhs.o: # and rhs.o implied
|
||||
lhs.v == rhs.v
|
||||
elif lhs.oResultPrivate: # and rhs.oResultPrivate implied
|
||||
lhs.vResultPrivate == rhs.vResultPrivate
|
||||
else:
|
||||
lhs.e == rhs.e
|
||||
lhs.eResultPrivate == rhs.eResultPrivate
|
||||
|
||||
func `==`*[E0, E1](
|
||||
lhs: Result[void, E0], rhs: Result[void, E1]): bool {.inline.} =
|
||||
if lhs.o != rhs.o:
|
||||
if lhs.oResultPrivate != rhs.oResultPrivate:
|
||||
false
|
||||
elif lhs.o: # and rhs.o implied
|
||||
elif lhs.oResultPrivate: # and rhs.oResultPrivate implied
|
||||
true
|
||||
else:
|
||||
lhs.e == rhs.e
|
||||
lhs.eResultPrivate == rhs.eResultPrivate
|
||||
|
||||
func `==`*[T0, T1](
|
||||
lhs: Result[T0, void], rhs: Result[T1, void]): bool {.inline.} =
|
||||
if lhs.o != rhs.o:
|
||||
if lhs.oResultPrivate != rhs.oResultPrivate:
|
||||
false
|
||||
elif lhs.o: # and rhs.o implied
|
||||
lhs.v == rhs.v
|
||||
elif lhs.oResultPrivate: # and rhs.oResultPrivate implied
|
||||
lhs.vResultPrivate == rhs.vResultPrivate
|
||||
else:
|
||||
true
|
||||
|
||||
|
@ -727,21 +733,21 @@ func get*[T, E](self: Result[T, E]): T {.inline.} =
|
|||
## See also: Option.get
|
||||
assertOk(self)
|
||||
when T isnot void:
|
||||
self.v
|
||||
self.vResultPrivate
|
||||
|
||||
func tryGet*[T, E](self: Result[T, E]): T {.inline.} =
|
||||
## Fetch value of result if set, or raise
|
||||
## When E is an Exception, raise that exception - otherwise, raise a ResultError[E]
|
||||
mixin raiseResultError
|
||||
if not self.o: self.raiseResultError()
|
||||
if not self.oResultPrivate: self.raiseResultError()
|
||||
when T isnot void:
|
||||
self.v
|
||||
self.vResultPrivate
|
||||
|
||||
func get*[T, E](self: Result[T, E], otherwise: T): T {.inline.} =
|
||||
## Fetch value of result if set, or return the value `otherwise`
|
||||
## See `valueOr` for a template version that avoids evaluating `otherwise`
|
||||
## unless necessary
|
||||
if self.o: self.v
|
||||
if self.oResultPrivate: self.vResultPrivate
|
||||
else: otherwise
|
||||
|
||||
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
|
||||
## See also: Option.get
|
||||
assertOk(self)
|
||||
self.v
|
||||
self.vResultPrivate
|
||||
|
||||
template `[]`*[T: not void, E](self: Result[T, E]): T =
|
||||
## 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 =
|
||||
## Fetch value of result if set, undefined behavior if unset
|
||||
## See also: `unsafeError`
|
||||
self.v
|
||||
self.vResultPrivate
|
||||
|
||||
template unsafeGet*[E](self: Result[void, E]) =
|
||||
## Fetch value of result if set, undefined behavior if unset
|
||||
## See also: `unsafeError`
|
||||
assert self.o
|
||||
assert self.oResultPrivate
|
||||
|
||||
func expect*[T, E](self: Result[T, E], m: string): T =
|
||||
## 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
|
||||
## echo r.expect("r was just set to ok(42)")
|
||||
## ```
|
||||
if not self.o:
|
||||
if not self.oResultPrivate:
|
||||
when E isnot void:
|
||||
raiseResultDefect(m, self.e)
|
||||
raiseResultDefect(m, self.eResultPrivate)
|
||||
else:
|
||||
raiseResultDefect(m)
|
||||
when T isnot void:
|
||||
self.v
|
||||
self.vResultPrivate
|
||||
|
||||
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:
|
||||
raiseResultDefect(m, self.e)
|
||||
raiseResultDefect(m, self.eResultPrivate)
|
||||
else:
|
||||
raiseResultDefect(m)
|
||||
self.v
|
||||
self.vResultPrivate
|
||||
|
||||
func `$`*[T, E](self: Result[T, E]): string =
|
||||
## Returns string representation of `self`
|
||||
if self.o:
|
||||
if self.oResultPrivate:
|
||||
when T is void: "ok()"
|
||||
else: "ok(" & $self.v & ")"
|
||||
else: "ok(" & $self.vResultPrivate & ")"
|
||||
else:
|
||||
when E is void: "none()"
|
||||
else: "err(" & $self.e & ")"
|
||||
else: "err(" & $self.eResultPrivate & ")"
|
||||
|
||||
func error*[T, E](self: Result[T, E]): E =
|
||||
## Fetch error of result if set, or raise Defect
|
||||
if self.o:
|
||||
if self.oResultPrivate:
|
||||
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:
|
||||
raiseResultDefect("Trying to access error when value is set")
|
||||
when E isnot void:
|
||||
self.e
|
||||
self.eResultPrivate
|
||||
|
||||
func tryError*[T, E](self: Result[T, E]): E {.inline.} =
|
||||
## Fetch error of result if set, or raise
|
||||
## Raises a ResultError[T]
|
||||
mixin raiseResultOk
|
||||
if self.o: self.raiseResultOk()
|
||||
if self.oResultPrivate: self.raiseResultOk()
|
||||
when E isnot void:
|
||||
self.e
|
||||
self.eResultPrivate
|
||||
|
||||
template unsafeError*[T, E: not void](self: Result[T, E]): E =
|
||||
## Fetch value of result if set, undefined behavior if unset
|
||||
## See also: `unsafeGet`
|
||||
self.e
|
||||
self.eResultPrivate
|
||||
|
||||
template unsafeError*[T](self: Result[T, void]) =
|
||||
## Fetch value of result if set, undefined behavior if unset
|
||||
## 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
|
||||
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
|
||||
##
|
||||
let s = (self) # TODO avoid copy
|
||||
if s.o: s.v
|
||||
if s.oResultPrivate: s.vResultPrivate
|
||||
else:
|
||||
when E isnot void:
|
||||
template error: E {.used, inject.} = s.e
|
||||
template error: E {.used, inject.} = s.eResultPrivate
|
||||
def
|
||||
|
||||
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
|
||||
## the scope (for example using `return` / `raise`)
|
||||
let s = (self) # TODO avoid copy
|
||||
if not s.o: s.e
|
||||
if not s.oResultPrivate: s.eResultPrivate
|
||||
else:
|
||||
when T isnot void:
|
||||
template value: T {.used, inject.} = s.v
|
||||
template value: T {.used, inject.} = s.vResultPrivate
|
||||
def
|
||||
|
||||
func flatten*[T, E](self: Result[Result[T, E], E]): Result[T, E] =
|
||||
## Remove one level of nesting
|
||||
if self.o:
|
||||
self.v
|
||||
if self.oResultPrivate:
|
||||
self.vResultPrivate
|
||||
else:
|
||||
when E is void:
|
||||
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`
|
||||
## returns an error, return that error, else return `self`
|
||||
|
||||
if self.o:
|
||||
callback(self.v) and self
|
||||
if self.oResultPrivate:
|
||||
callback(self.vResultPrivate) and self
|
||||
else:
|
||||
self
|
||||
|
||||
|
@ -913,7 +919,7 @@ func filter*[E](
|
|||
## Apply `callback` to the `self`, iff `self` is not an error. If `callback`
|
||||
## returns an error, return that error, else return `self`
|
||||
|
||||
if self.o:
|
||||
if self.oResultPrivate:
|
||||
callback() and self
|
||||
else:
|
||||
self
|
||||
|
@ -925,8 +931,8 @@ func filter*[T](
|
|||
## Apply `callback` to the `self`, iff `self` is not an error. If `callback`
|
||||
## returns an error, return that error, else return `self`
|
||||
|
||||
if self.o:
|
||||
if callback(self.v):
|
||||
if self.oResultPrivate:
|
||||
if callback(self.vResultPrivate):
|
||||
self
|
||||
else:
|
||||
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
|
||||
##
|
||||
## ```
|
||||
## let o = Opt.some(42)
|
||||
## assert o.isSome and o.get() == 42
|
||||
## let oResultPrivate = Opt.some(42)
|
||||
## assert oResultPrivate.isSome and oResultPrivate.get() == 42
|
||||
## ```
|
||||
Opt[T].ok(v)
|
||||
|
||||
|
@ -948,18 +954,18 @@ template none*(O: type Opt, T: type): Opt[T] =
|
|||
## Create an `Opt` set to none
|
||||
##
|
||||
## ```
|
||||
## let o = Opt.none(int)
|
||||
## assert o.isNone
|
||||
## let oResultPrivate = Opt.none(int)
|
||||
## assert oResultPrivate.isNone
|
||||
## ```
|
||||
Opt[T].err()
|
||||
|
||||
template isSome*(o: Opt): bool =
|
||||
template isSome*(oResultPrivate: Opt): bool =
|
||||
## Alias for `isOk`
|
||||
isOk o
|
||||
isOk oResultPrivate
|
||||
|
||||
template isNone*(o: Opt): bool =
|
||||
template isNone*(oResultPrivate: Opt): bool =
|
||||
## Alias of `isErr`
|
||||
isErr o
|
||||
isErr oResultPrivate
|
||||
|
||||
# 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
|
||||
# probably avoid it with some fancy macro magic..
|
||||
let v = (self)
|
||||
if not v.o:
|
||||
if not v.oResultPrivate:
|
||||
when typeof(result) is typeof(v):
|
||||
return v
|
||||
else:
|
||||
when E is void:
|
||||
return err(typeof(result))
|
||||
else:
|
||||
return err(typeof(result), v.e)
|
||||
return err(typeof(result), v.eResultPrivate)
|
||||
|
||||
when not(T is void):
|
||||
v.v
|
||||
v.vResultPrivate
|
||||
|
|
|
@ -32,5 +32,6 @@ import
|
|||
test_sets,
|
||||
test_templateutils,
|
||||
test_results,
|
||||
test_results2,
|
||||
test_varints,
|
||||
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