mirror of
https://github.com/logos-storage/questionable.git
synced 2026-01-05 23:33:12 +00:00
Fix: without statement with error works in generic code
This commit is contained in:
parent
d7e9f0bf7f
commit
0895a9c065
@ -1,5 +1,6 @@
|
|||||||
import std/options
|
import std/options
|
||||||
import std/macros
|
import std/macros
|
||||||
|
import ./private/binderror
|
||||||
|
|
||||||
proc option[T](option: Option[T]): Option[T] =
|
proc option[T](option: Option[T]): Option[T] =
|
||||||
option
|
option
|
||||||
@ -8,17 +9,25 @@ proc placeholder(T: type): T =
|
|||||||
discard
|
discard
|
||||||
|
|
||||||
template bindLet(name, expression): bool =
|
template bindLet(name, expression): bool =
|
||||||
let option = expression.option
|
let evaluated = expression
|
||||||
|
let option = evaluated.option
|
||||||
type T = typeof(option.unsafeGet())
|
type T = typeof(option.unsafeGet())
|
||||||
let name {.used.} = if option.isSome: option.unsafeGet() else: placeholder(T)
|
let name {.used.} = if option.isSome:
|
||||||
|
option.unsafeGet()
|
||||||
|
else:
|
||||||
|
bindFailed(evaluated)
|
||||||
|
placeholder(T)
|
||||||
option.isSome
|
option.isSome
|
||||||
|
|
||||||
template bindVar(name, expression): bool =
|
template bindVar(name, expression): bool =
|
||||||
let option = expression.option
|
let evaluated = expression
|
||||||
|
let option = evaluated.option
|
||||||
type T = typeof(option.unsafeGet())
|
type T = typeof(option.unsafeGet())
|
||||||
var name {.used.} : T = placeholder(T)
|
var name {.used.} = if option.isSome:
|
||||||
if option.isSome:
|
option.unsafeGet()
|
||||||
name = option.unsafeGet()
|
else:
|
||||||
|
bindFailed(evaluated)
|
||||||
|
placeholder(T)
|
||||||
option.isSome
|
option.isSome
|
||||||
|
|
||||||
macro `=?`*(name, expression): bool =
|
macro `=?`*(name, expression): bool =
|
||||||
|
|||||||
19
questionable/private/binderror.nim
Normal file
19
questionable/private/binderror.nim
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import std/options
|
||||||
|
|
||||||
|
var captureEnabled {.global, compiletime.}: bool
|
||||||
|
var errorVariable: ptr ref CatchableError
|
||||||
|
|
||||||
|
template captureBindError*(error: var ref CatchableError, expression): auto =
|
||||||
|
static: captureEnabled = true
|
||||||
|
errorVariable = addr error
|
||||||
|
let evaluated = expression
|
||||||
|
static: captureEnabled = false
|
||||||
|
evaluated
|
||||||
|
|
||||||
|
func error[T](option: Option[T]): ref CatchableError =
|
||||||
|
newException(ValueError, "Option is set to `none`")
|
||||||
|
|
||||||
|
template bindFailed*(expression) =
|
||||||
|
when captureEnabled:
|
||||||
|
mixin error
|
||||||
|
errorVariable[] = expression.error
|
||||||
@ -1,39 +1,12 @@
|
|||||||
import std/macros
|
|
||||||
import ./binding
|
|
||||||
import ./without
|
import ./without
|
||||||
|
import ./private/binderror
|
||||||
|
|
||||||
macro replaceInfix(expression, operator, replacement): untyped =
|
template without*(condition, errorname, body): untyped =
|
||||||
## Replaces an infix operator in an expression. The AST of the expression is
|
|
||||||
## traversed to find and replace all instances of the operator.
|
|
||||||
|
|
||||||
proc replace(expression, operator, replacement: NimNode): NimNode =
|
|
||||||
if expression.kind == nnkInfix and eqIdent(expression[0], operator):
|
|
||||||
expression[0] = replacement
|
|
||||||
expression[2] = replace(expression[2], operator, replacement)
|
|
||||||
else:
|
|
||||||
for i in 0..<expression.len:
|
|
||||||
expression[i] = replace(expression[i], operator, replacement)
|
|
||||||
expression
|
|
||||||
|
|
||||||
replace(expression, operator, replacement)
|
|
||||||
|
|
||||||
template without*(condition, errorname, body) =
|
|
||||||
## Used to place guards that ensure that a Result contains a value.
|
## Used to place guards that ensure that a Result contains a value.
|
||||||
## Exposes error when Result does not contain a value.
|
## Exposes error when Result does not contain a value.
|
||||||
|
|
||||||
var error: ref CatchableError
|
var error: ref CatchableError
|
||||||
|
|
||||||
# override =? operator such that it stores the error if there is one
|
without captureBindError(error, condition):
|
||||||
template `override=?`(name, expression): bool {.gensym, used.} =
|
|
||||||
let optional = expression
|
|
||||||
when optional is Result:
|
|
||||||
if optional.isFailure:
|
|
||||||
error = optional.error
|
|
||||||
when optional is Option:
|
|
||||||
if optional.isNone:
|
|
||||||
error = newException(ValueError, "Option is set to `none`")
|
|
||||||
name =? optional
|
|
||||||
|
|
||||||
without replaceInfix(condition, `=?`, `override=?`):
|
|
||||||
template errorname: ref CatchableError = error
|
template errorname: ref CatchableError = error
|
||||||
body
|
body
|
||||||
|
|||||||
@ -265,6 +265,22 @@ suite "result":
|
|||||||
|
|
||||||
test()
|
test()
|
||||||
|
|
||||||
|
test "without statement with error works in generic code":
|
||||||
|
proc test(_: type) =
|
||||||
|
without a =? int.failure "error", e:
|
||||||
|
check e.msg == "error"
|
||||||
|
return
|
||||||
|
fail
|
||||||
|
|
||||||
|
test(int)
|
||||||
|
|
||||||
|
test "without statements with error can be nested":
|
||||||
|
without a =? int.failure "error1", e1:
|
||||||
|
without b =? int.failure "error2", e2:
|
||||||
|
check e1.msg == "error1"
|
||||||
|
check e2.msg == "error2"
|
||||||
|
check e1.msg == "error1"
|
||||||
|
|
||||||
test "catch can be used to convert exceptions to results":
|
test "catch can be used to convert exceptions to results":
|
||||||
check parseInt("42").catch == 42.success
|
check parseInt("42").catch == 42.success
|
||||||
check parseInt("foo").catch.error of ValueError
|
check parseInt("foo").catch.error of ValueError
|
||||||
@ -387,11 +403,11 @@ import pkg/questionable/resultsbase
|
|||||||
|
|
||||||
suite "result compatibility":
|
suite "result compatibility":
|
||||||
|
|
||||||
test "|?, =? and .option work on other types of Result":
|
type R = Result[int, string]
|
||||||
type R = Result[int, string]
|
let good = R.ok 42
|
||||||
let good = R.ok 42
|
let bad = R.err "error"
|
||||||
let bad = R.err "error"
|
|
||||||
|
|
||||||
|
test "|?, =? and .option work on other types of Result":
|
||||||
check bad |? 43 == 43
|
check bad |? 43 == 43
|
||||||
|
|
||||||
if value =? good:
|
if value =? good:
|
||||||
@ -400,3 +416,10 @@ suite "result compatibility":
|
|||||||
fail
|
fail
|
||||||
|
|
||||||
check good.option == 42.some
|
check good.option == 42.some
|
||||||
|
|
||||||
|
test "=? works on other type of Result after without statement with error":
|
||||||
|
without a =? 42.success, error:
|
||||||
|
discard error # fixes warning about unused variable "error"
|
||||||
|
fail
|
||||||
|
without b =? good:
|
||||||
|
fail
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user