From 9b985e8ea82d0664295359d8621ad880f6f6073c Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Thu, 20 Apr 2023 13:08:54 +0200 Subject: [PATCH] results: Add `isOkOr`, `isErrOr` (#176) These two helpers complete `valueOr` and `errorOr` to cover `void` cases where no value should be returned or `Result[void, E]` / `Result[T, void]` is being used - they can be used for a convient early-return style in side-effectful proc:s: ```nim v.update().isOkOr: echo "update failed: ", error ``` --- stew/results.nim | 66 ++++++++++++++++++++++++++++++++++++++++-- tests/test_results.nim | 9 ++++++ 2 files changed, 72 insertions(+), 3 deletions(-) diff --git a/stew/results.nim b/stew/results.nim index 85e08e8..e6af21e 100644 --- a/stew/results.nim +++ b/stew/results.nim @@ -849,11 +849,67 @@ template unsafeError*[T](self: Result[T, void]) = template value*[T, E](self: Result[T, E]): T = self.get() template value*[T: not void, E](self: var Result[T, E]): var T = self.get() +template isOkOr*[T, E](self: Result[T, E], body: untyped) = + ## Evaluate `body` iff result has been assigned an error + ## `body` is evaluated lazily. + ## + ## Example: + ## ``` + ## let + ## v = Result[int, string].err("hello") + ## x = v.isOkOr: echo "not ok" + ## # experimental: direct error access using an unqualified `error` symbol + ## z = v.isOkOr: echo error + ## ``` + ## + ## `error` access: + ## + ## TODO experimental, might change in the future + ## + ## The template contains a shortcut for accessing the error of the result, + ## it can only be used outside of generic code, + ## see https://github.com/status-im/nim-stew/issues/161#issuecomment-1397121386 + + let s = (self) # TODO avoid copy + if not s.oResultPrivate: + when E isnot void: + template error: E {.used, inject.} = s.eResultPrivate + body + +template isErrOr*[T, E](self: Result[T, E], body: untyped) = + ## Evaluate `body` iff result has been assigned a value + ## `body` is evaluated lazily. + ## + ## Example: + ## ``` + ## let + ## v = Result[int, string].err("hello") + ## x = v.isOkOr: echo "not ok" + ## # experimental: direct error access using an unqualified `error` symbol + ## z = v.isOkOr: echo error + ## ``` + ## + ## `value` access: + ## + ## TODO experimental, might change in the future + ## + ## The template contains a shortcut for accessing the value of the result, + ## it can only be used outside of generic code, + ## see https://github.com/status-im/nim-stew/issues/161#issuecomment-1397121386 + + let s = (self) # TODO avoid copy + if s.oResultPrivate: + when T isnot void: + template value: T {.used, inject.} = s.vResultPrivate + body + template valueOr*[T: not void, E](self: Result[T, E], def: untyped): T = ## Fetch value of result if set, or evaluate `def` ## `def` is evaluated lazily, and must be an expression of `T` or exit ## the scope (for example using `return` / `raise`) ## + ## See `isOkOr` for a version that works with `Result[void, E]`. + ## ## Example: ## ``` ## let @@ -869,11 +925,12 @@ template valueOr*[T: not void, E](self: Result[T, E], def: untyped): T = ## TODO experimental, might change in the future ## ## The template contains a shortcut for accessing the error of the result, - ## without specifying the error - it can only be used outside of generic code, + ## it can only be used outside of generic code, ## see https://github.com/status-im/nim-stew/issues/161#issuecomment-1397121386 ## let s = (self) # TODO avoid copy - if s.oResultPrivate: s.vResultPrivate + if s.oResultPrivate: + s.vResultPrivate else: when E isnot void: template error: E {.used, inject.} = s.eResultPrivate @@ -883,8 +940,11 @@ template errorOr*[T, E: not void](self: Result[T, E], def: untyped): E = ## Fetch error of result if not set, or evaluate `def` ## `def` is evaluated lazily, and must be an expression of `T` or exit ## the scope (for example using `return` / `raise`) + ## + ## See `isErrOr` for a version that works with `Result[T, void]`. let s = (self) # TODO avoid copy - if not s.oResultPrivate: s.eResultPrivate + if not s.oResultPrivate: + s.eResultPrivate else: when T isnot void: template value: T {.used, inject.} = s.vResultPrivate diff --git a/tests/test_results.nim b/tests/test_results.nim index 3358751..73203a3 100644 --- a/tests/test_results.nim +++ b/tests/test_results.nim @@ -70,6 +70,15 @@ block: doAssert rOk.get() == rOk.unsafeGet() + rOk.isOkOr: raiseAssert "should not end up in here" + rErr.isErrOr: raiseAssert "should not end up in here" + + rErr.isOkOr: + doAssert error == rErr.error() + + rOk.isErrOr: + doAssert value == rOk.value() + doAssert rOk.valueOr(failFast()) == rOk.value() let rErrV = rErr.valueOr: error.len