mirror of
https://github.com/logos-storage/questionable.git
synced 2026-01-05 23:33:12 +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/options
|
||||||
import std/macros
|
import std/macros
|
||||||
|
import std/sequtils
|
||||||
import ./private/binderror
|
import ./private/binderror
|
||||||
|
|
||||||
proc option[T](option: Option[T]): Option[T] =
|
proc option[T](option: Option[T]): Option[T] =
|
||||||
@ -30,14 +31,45 @@ template bindVar(name, expression): bool =
|
|||||||
placeholder(T)
|
placeholder(T)
|
||||||
option.isSome
|
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 =
|
macro `=?`*(name, expression): bool =
|
||||||
## The `=?` operator lets you bind the value inside an Option or Result to a
|
## 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
|
## new variable. It can be used inside of a conditional expression, for
|
||||||
## instance in an `if` statement.
|
## instance in an `if` statement.
|
||||||
|
|
||||||
name.expectKind({nnkIdent, nnkVarTy})
|
name.expectKind({nnkIdent, nnkVarTy, nnkTupleConstr})
|
||||||
if name.kind == nnkIdent:
|
if name.kind == nnkIdent:
|
||||||
quote do: bindLet(`name`, `expression`)
|
quote do: bindLet(`name`, `expression`)
|
||||||
|
elif name.kind == nnkTupleConstr:
|
||||||
|
quote do: bindTuple(`name`, `expression`)
|
||||||
else:
|
else:
|
||||||
let name = name[0]
|
let name = name[0]
|
||||||
quote do: bindVar(`name`, `expression`)
|
quote do: bindVar(`name`, `expression`)
|
||||||
|
|||||||
@ -165,6 +165,77 @@ suite "optionals":
|
|||||||
else:
|
else:
|
||||||
fail()
|
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":
|
test "without statement can be used for early returns":
|
||||||
proc test1 =
|
proc test1 =
|
||||||
without a =? 42.some:
|
without a =? 42.some:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user