mirror of
https://github.com/logos-storage/questionable.git
synced 2026-01-03 06:13:09 +00:00
tuple binding and unpacking support
This commit is contained in:
parent
1dcef4b302
commit
7a3d3430a4
@ -1,5 +1,6 @@
|
||||
import std/options
|
||||
import std/macros
|
||||
import std/sequtils
|
||||
import ./private/binderror
|
||||
|
||||
proc option[T](option: Option[T]): Option[T] =
|
||||
@ -30,14 +31,45 @@ template bindVar(name, expression): bool =
|
||||
placeholder(T)
|
||||
option.isSome
|
||||
|
||||
macro bindTuple(name, expression): bool =
|
||||
let stmtList = newStmtList()
|
||||
let opt = genSym(nskLet, "option")
|
||||
let T = genSym(nskType, "T")
|
||||
|
||||
stmtList.add quote do:
|
||||
let evaluated = `expression`
|
||||
let `opt` = evaluated.option
|
||||
type `T` = typeof(`opt`.unsafeGet())
|
||||
|
||||
let valueNode = quote do:
|
||||
if `opt`.isSome:
|
||||
`opt`.unsafeGet()
|
||||
else:
|
||||
bindFailed(evaluated)
|
||||
placeholder(`T`)
|
||||
|
||||
stmtList.add nnkStmtList.newTree(
|
||||
nnkLetSection.newTree(
|
||||
nnkVarTuple.newTree(
|
||||
name.children.toSeq.concat(
|
||||
@[newEmptyNode(), valueNode]
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
stmtList.add quote do: `opt`.isSome
|
||||
return stmtList
|
||||
|
||||
macro `=?`*(name, expression): bool =
|
||||
## The `=?` operator lets you bind the value inside an Option or Result to a
|
||||
## new variable. It can be used inside of a conditional expression, for
|
||||
## instance in an `if` statement.
|
||||
|
||||
name.expectKind({nnkIdent, nnkVarTy})
|
||||
name.expectKind({nnkIdent, nnkVarTy, nnkTupleConstr})
|
||||
if name.kind == nnkIdent:
|
||||
quote do: bindLet(`name`, `expression`)
|
||||
elif name.kind == nnkTupleConstr:
|
||||
quote do: bindTuple(`name`, `expression`)
|
||||
else:
|
||||
let name = name[0]
|
||||
quote do: bindVar(`name`, `expression`)
|
||||
|
||||
@ -165,6 +165,77 @@ suite "optionals":
|
||||
else:
|
||||
fail()
|
||||
|
||||
test "=? binds and unpacks tuples":
|
||||
if (a, b) =? (some ("test", 1)):
|
||||
check a == "test"
|
||||
check b == 1
|
||||
else:
|
||||
fail()
|
||||
|
||||
test "=? binds and unpacks tuples with named fields":
|
||||
if (a, b) =? (some (desc: "test", id: 1)):
|
||||
check a == "test"
|
||||
check b == 1
|
||||
else:
|
||||
fail()
|
||||
|
||||
test "=? binds and unpacks tuples returned from proc":
|
||||
proc returnsTuple(): Option[tuple[name: string, id: int]] = some ("test", 1)
|
||||
|
||||
if (a, b) =? returnsTuple():
|
||||
check a == "test"
|
||||
check b == 1
|
||||
else:
|
||||
fail()
|
||||
|
||||
test "=? binds and unpacks tuples returned from proc with unnamed fields":
|
||||
proc returnsTuple(): Option[(string, int,)] = some ("test", 1,)
|
||||
|
||||
if (a, b,) =? returnsTuple():
|
||||
check a == "test"
|
||||
check b == 1
|
||||
else:
|
||||
fail()
|
||||
|
||||
test "=? binds and unpacks tuples with _":
|
||||
if (_, b) =? ("test", 1):
|
||||
check b == 1
|
||||
else:
|
||||
fail()
|
||||
|
||||
test "=? binds and unpacks tuples with named fields":
|
||||
if (a, b) =? (desc: "test", id: 1):
|
||||
check a == "test"
|
||||
check b == 1
|
||||
else:
|
||||
fail()
|
||||
|
||||
test "=? binds variable to tuples with named fields":
|
||||
if t =? (desc: "test", id: 1):
|
||||
check t.desc == "test"
|
||||
check t.id == 1
|
||||
else:
|
||||
fail()
|
||||
|
||||
test "=? binds to tuple types":
|
||||
type MyTuple = tuple
|
||||
desc: string
|
||||
id: int
|
||||
|
||||
let mt: MyTuple = ("test", 1)
|
||||
|
||||
if t =? (some mt):
|
||||
check t.desc == "test"
|
||||
check t.id == 1
|
||||
else:
|
||||
fail()
|
||||
|
||||
if (a, b) =? (some mt):
|
||||
check a == "test"
|
||||
check b == 1
|
||||
else:
|
||||
fail()
|
||||
|
||||
test "without statement can be used for early returns":
|
||||
proc test1 =
|
||||
without a =? 42.some:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user