diff --git a/questionable/binding.nim b/questionable/binding.nim index 3a68107..e688085 100644 --- a/questionable/binding.nim +++ b/questionable/binding.nim @@ -2,15 +2,20 @@ import std/options import std/macros import ./private/binderror -proc option[T](option: Option[T]): Option[T] = +template toOption[T](option: Option[T]): Option[T] = option +template toOption[T: ref | ptr | pointer | proc](value: T): Option[T] = + # `std/options` don't consider closure iterators to be pointer types + # (probably a bug) so we don't list them here. + value.option + proc placeholder(T: type): T = discard template bindLet(name, expression): untyped = let evaluated = expression - let option = evaluated.option + let option = evaluated.toOption type T = typeof(option.unsafeGet()) let name {.used.} = if option.isSome: option.unsafeGet() @@ -21,7 +26,7 @@ template bindLet(name, expression): untyped = template bindVar(name, expression): untyped = let evaluated = expression - let option = evaluated.option + let option = evaluated.toOption type T = typeof(option.unsafeGet()) var name {.used.} = if option.isSome: option.unsafeGet() @@ -55,7 +60,7 @@ macro bindTuple(names, expression): bool = quote do: let `evaluated` = `expression` - let `opt` = `evaluated`.option + let `opt` = `evaluated`.toOption type `T` = typeof(`opt`.unsafeGet()) `letsection` `opt`.isSome diff --git a/testmodules/options/test.nim b/testmodules/options/test.nim index e461543..62d4e6f 100644 --- a/testmodules/options/test.nim +++ b/testmodules/options/test.nim @@ -165,6 +165,53 @@ suite "optionals": else: fail() + test "=? works with reference types": + var x = new int + x[] = 42 + if a =? x: + check a[] == 42 + else: + fail + + x = nil + if a =? x: + fail + + var p = proc = discard + if a =? p: + a() + else: + fail + + p = nil + if a =? p: + fail + + test "=? rejects non-reference types": + check `not` compiles do: + if a =? 0: + discard + check `not` compiles do: + if var a =? 0: + discard + check `not` compiles do: + if (a,) =? (0,): + discard + + test "=? works with custom optional types": + type MyOption = distinct int + proc isSome(x: MyOption): bool = x.int >= 0 + proc unsafeGet(x: MyOption): int = x.int + template toOption(x: MyOption): MyOption = x + + if a =? MyOption 42: + check a == 42 + else: + fail + + if a =? MyOption -1: + fail + test "=? binds and unpacks tuples": if (a, b) =? (some ("test", 1)): check a == "test"