Fix: ensure that overridden =? operator does not remain in scope

This commit is contained in:
Mark Spanbroek 2022-01-11 12:31:25 +01:00 committed by markspanbroek
parent ef29000f94
commit a748d22350
2 changed files with 48 additions and 9 deletions

View File

@ -1,22 +1,39 @@
import std/macros
import ./binding
import ./without
template without*(expression, errorname, body) =
macro replaceInfix(expression, operator, replacement): 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.
## Exposes error when Result does not contain a value.
var error: ref CatchableError
# override =? operator such that it stores the error if there is one
template `=?`(name, result): bool =
when result is Result:
if result.isFailure:
error = result.error
when result is Option:
if result.isNone:
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`")
binding.`=?`(name, result)
name =? optional
without expression:
without replaceInfix(condition, `=?`, `override=?`):
template errorname: ref CatchableError = error
body

View File

@ -243,6 +243,28 @@ suite "result":
test1()
test2()
test "without statement with error can be used more than once":
proc test =
without a =? 42.success, error:
discard error
fail
without b =? 42.success, error:
discard error
fail
test()
test "without statement with error works with deeply nested =? operators":
proc test =
let fail1 = int.failure "error 1"
let fail2 = int.failure "error 2"
without (block: a =? (if b =? fail1: b.success else: fail2)), error:
check error.msg == "error 2"
return
fail
test()
test "catch can be used to convert exceptions to results":
check parseInt("42").catch == 42.success
check parseInt("foo").catch.error of ValueError