From ba353b27e4578e30dd1671a46e885214ff1ca9d3 Mon Sep 17 00:00:00 2001 From: Tanguy Date: Thu, 16 Jun 2022 17:58:20 +0200 Subject: [PATCH] Attempt to fix #11 --- questionable/binding.nim | 27 +++++++++++++-------------- questionable/results.nim | 7 +++++++ questionable/withoutresult.nim | 24 +++++++++--------------- testmodules/result/test.nim | 9 +++++++++ 4 files changed, 38 insertions(+), 29 deletions(-) diff --git a/questionable/binding.nim b/questionable/binding.nim index da01187..265b428 100644 --- a/questionable/binding.nim +++ b/questionable/binding.nim @@ -7,19 +7,11 @@ proc option[T](option: Option[T]): Option[T] = proc placeholder(T: type): T = discard -template bindLet(name, expression): bool = - let option = expression.option +template unpack*(expression: Option): untyped = + let option = expression type T = typeof(option.unsafeGet()) - let name {.used.} = if option.isSome: option.unsafeGet() else: placeholder(T) - option.isSome - -template bindVar(name, expression): bool = - let option = expression.option - type T = typeof(option.unsafeGet()) - var name {.used.} : T = placeholder(T) - if option.isSome: - name = option.unsafeGet() - option.isSome + let res = if option.isSome: option.unsafeGet() else: placeholder(T) + (res, option.isSome) macro `=?`*(name, expression): bool = ## The `=?` operator lets you bind the value inside an Option or Result to a @@ -28,7 +20,14 @@ macro `=?`*(name, expression): bool = name.expectKind({nnkIdent, nnkVarTy}) if name.kind == nnkIdent: - quote do: bindLet(`name`, `expression`) + quote do: + mixin unpack + let (`name`, isOk) = unpack(`expression`) + isOk + else: let name = name[0] - quote do: bindVar(`name`, `expression`) + quote do: + mixin unpack + var (`name`, isOk) = unpack(`expression`) + isOk diff --git a/questionable/results.nim b/questionable/results.nim index b15c8ab..eefa75b 100644 --- a/questionable/results.nim +++ b/questionable/results.nim @@ -99,6 +99,13 @@ template `|?`*[T,E](value: Result[T,E], fallback: T): T = value.valueOr(fallback) +template unpack*(expression: Result): untyped = + let res = expression + when declaredInScope(internalWithoutError): + if res.isFailure: + internalWithoutError = res.error + unpack(res.option) + proc option*[T,E](value: Result[T,E]): ?T = ## Converts a Result into an Option. diff --git a/questionable/withoutresult.nim b/questionable/withoutresult.nim index 1188db2..6109925 100644 --- a/questionable/withoutresult.nim +++ b/questionable/withoutresult.nim @@ -21,19 +21,13 @@ 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 `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 + when not declaredInScope(internalWithoutError): + var internalWithoutError {.inject.}: ref CatchableError + else: + internalWithoutError = nil + + without condition: + template errorname: ref CatchableError = internalWithoutError + if isNil(errorname): + errorname = newException(ValueError, "Option is set to `none`") body diff --git a/testmodules/result/test.nim b/testmodules/result/test.nim index 869b27d..7d3c2e4 100644 --- a/testmodules/result/test.nim +++ b/testmodules/result/test.nim @@ -265,6 +265,15 @@ suite "result": test() + test "without statement works in generic code": + proc test(_: type) = + without a =? int.failure "error", e: + check e.msg == "error" + return + fail + + test(int) + test "catch can be used to convert exceptions to results": check parseInt("42").catch == 42.success check parseInt("foo").catch.error of ValueError