mirror of
https://github.com/logos-storage/questionable.git
synced 2026-01-08 08:43:09 +00:00
parent
0e166a6e81
commit
53924aeeda
33
questionable/chaining.nim
Normal file
33
questionable/chaining.nim
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import std/macros
|
||||||
|
|
||||||
|
template `.?`*(option: typed, identifier: untyped{nkIdent}): untyped =
|
||||||
|
option ->? option.unsafeGet.identifier
|
||||||
|
|
||||||
|
macro `.?`*(option: typed, infix: untyped{nkInfix}): untyped =
|
||||||
|
let left = infix[1]
|
||||||
|
infix[1] = quote do: `option`.?`left`
|
||||||
|
infix
|
||||||
|
|
||||||
|
macro `.?`*(option: typed, bracket: untyped{nkBracketExpr}): untyped =
|
||||||
|
let left = bracket[0]
|
||||||
|
bracket[0] = quote do: `option`.?`left`
|
||||||
|
bracket
|
||||||
|
|
||||||
|
macro `.?`*(option: typed, dot: untyped{nkDotExpr}): untyped =
|
||||||
|
let left = dot[0]
|
||||||
|
dot[0] = quote do: `option`.?`left`
|
||||||
|
dot
|
||||||
|
|
||||||
|
macro `.?`*(option: typed, call: untyped{nkCall}): untyped =
|
||||||
|
let procedure = call[0]
|
||||||
|
if call.len > 1:
|
||||||
|
if procedure.kind == nnkDotExpr:
|
||||||
|
let (inner, outer) = (procedure[0], procedure[1])
|
||||||
|
call[0] = outer
|
||||||
|
call.insert(1, quote do: `option`.?`inner`)
|
||||||
|
call
|
||||||
|
else:
|
||||||
|
call.insert(1, quote do: `option`.unsafeGet)
|
||||||
|
quote do: `option` ->? `call`
|
||||||
|
else:
|
||||||
|
quote do: `option`.?`procedure`
|
||||||
12
questionable/operators.nim
Normal file
12
questionable/operators.nim
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
template liftUnary*(T: type, operator: untyped) =
|
||||||
|
|
||||||
|
template `operator`*(a: T): untyped =
|
||||||
|
a ->? `operator`(a.unsafeGet())
|
||||||
|
|
||||||
|
template liftBinary*(T: type, operator: untyped) =
|
||||||
|
|
||||||
|
template `operator`*(a: T, b: T): untyped =
|
||||||
|
(a, b) ->? `operator`(a.unsafeGet, b.unsafeGet)
|
||||||
|
|
||||||
|
template `operator`*(a: T, b: typed): untyped =
|
||||||
|
a ->? `operator`(a.unsafeGet(), b)
|
||||||
@ -1,54 +1,39 @@
|
|||||||
import std/options
|
import std/options
|
||||||
|
import ./chaining
|
||||||
|
import ./operators
|
||||||
|
|
||||||
include ./errorban
|
include ./errorban
|
||||||
|
|
||||||
export options
|
export options
|
||||||
|
export chaining
|
||||||
|
|
||||||
template `?`*(T: typed): type Option[T] =
|
template `?`*(T: typed): type Option[T] =
|
||||||
Option[T]
|
Option[T]
|
||||||
|
|
||||||
template `.?`*(option: ?typed, field: untyped{nkIdent}): ?untyped =
|
template `->?`*(option: ?typed, expression: untyped): untyped =
|
||||||
type T = type option.get.field
|
type T = type expression
|
||||||
if option.isSome:
|
if option.isSome:
|
||||||
option.unsafeGet().field.some
|
expression.some
|
||||||
else:
|
else:
|
||||||
T.none
|
T.none
|
||||||
|
|
||||||
|
template `->?`*(options: (?typed, ?typed), expression: untyped): untyped =
|
||||||
|
type T = type expression
|
||||||
|
if options[0].isSome and options[1].isSome:
|
||||||
|
expression.some
|
||||||
|
else:
|
||||||
|
T.none
|
||||||
|
|
||||||
|
template `=?`*[T](name: untyped{nkIdent}, option: ?T): bool =
|
||||||
|
template name: T {.used.} = option.unsafeGet()
|
||||||
|
option.isSome
|
||||||
|
|
||||||
template `|?`*[T](option: ?T, fallback: T): T =
|
template `|?`*[T](option: ?T, fallback: T): T =
|
||||||
if option.isSome:
|
if option.isSome:
|
||||||
option.unsafeGet()
|
option.unsafeGet()
|
||||||
else:
|
else:
|
||||||
fallback
|
fallback
|
||||||
|
|
||||||
template `=?`*[T](name: untyped{nkIdent}, option: ?T): bool =
|
|
||||||
template name: T {.used.} = option.unsafeGet()
|
|
||||||
option.isSome
|
|
||||||
|
|
||||||
template liftUnary(_: type Option, operator: untyped) =
|
|
||||||
|
|
||||||
template `operator`*(a: ?typed): ?typed =
|
|
||||||
type T {.used.} = type(`operator`(a.unsafeGet))
|
|
||||||
if x =? a:
|
|
||||||
`operator`(x).some
|
|
||||||
else:
|
|
||||||
T.none
|
|
||||||
|
|
||||||
template liftBinary(_: type Option, operator: untyped) =
|
|
||||||
|
|
||||||
template `operator`*(a: ?typed, b: ?typed): ?typed =
|
|
||||||
type T = type(`operator`(a.unsafeGet, b.unsafeGet))
|
|
||||||
if x =? a and y =? b:
|
|
||||||
`operator`(x, y).some
|
|
||||||
else:
|
|
||||||
T.none
|
|
||||||
|
|
||||||
template `operator`*(a: ?typed, b: typed): ?typed =
|
|
||||||
type T = type(`operator`(a.unsafeGet, b))
|
|
||||||
if x =? a:
|
|
||||||
`operator`(x, b).some
|
|
||||||
else:
|
|
||||||
T.none
|
|
||||||
|
|
||||||
Option.liftUnary(`-`)
|
Option.liftUnary(`-`)
|
||||||
Option.liftUnary(`+`)
|
Option.liftUnary(`+`)
|
||||||
Option.liftUnary(`@`)
|
Option.liftUnary(`@`)
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import ./options
|
|
||||||
import ./resultsbase
|
import ./resultsbase
|
||||||
|
import ./options
|
||||||
|
import ./operators
|
||||||
|
|
||||||
include ./errorban
|
include ./errorban
|
||||||
|
|
||||||
@ -8,18 +9,27 @@ export resultsbase
|
|||||||
template `?!`*(T: typed): type Result[T, ref CatchableError] =
|
template `?!`*(T: typed): type Result[T, ref CatchableError] =
|
||||||
Result[T, ref CatchableError]
|
Result[T, ref CatchableError]
|
||||||
|
|
||||||
template success*[T](value: T): ?!T =
|
proc success*[T](value: T): ?!T =
|
||||||
ok(?!T, value)
|
ok(?!T, value)
|
||||||
|
|
||||||
template failure*(T: type, error: ref CatchableError): ?!T =
|
proc failure*(T: type, error: ref CatchableError): ?!T =
|
||||||
err(?!T, error)
|
err(?!T, error)
|
||||||
|
|
||||||
template `.?`*(value: ?!typed, field: untyped{nkIdent}): ?!untyped =
|
template `->?`*(option: ?!typed, expression: untyped): untyped =
|
||||||
type T = type value.get.field
|
type T = type expression
|
||||||
if value.isOk:
|
if option.isErr:
|
||||||
ok(?!T, value.unsafeGet().field)
|
T.failure(option.error)
|
||||||
else:
|
else:
|
||||||
err(?!T, error(value))
|
expression.success
|
||||||
|
|
||||||
|
template `->?`*(options: (?!typed, ?!typed), expression: untyped): untyped =
|
||||||
|
type T = type expression
|
||||||
|
if options[0].isErr:
|
||||||
|
T.failure(options[0].error)
|
||||||
|
elif options[1].isErr:
|
||||||
|
T.failure(options[1].error)
|
||||||
|
else:
|
||||||
|
expression.success
|
||||||
|
|
||||||
template `|?`*[T](value: ?!T, fallback: T): T =
|
template `|?`*[T](value: ?!T, fallback: T): T =
|
||||||
value.valueOr(fallback)
|
value.valueOr(fallback)
|
||||||
@ -34,33 +44,6 @@ proc toOption*[T,E](value: Result[T,E]): ?T =
|
|||||||
else:
|
else:
|
||||||
T.none
|
T.none
|
||||||
|
|
||||||
template liftUnary(_: type Result, operator: untyped) =
|
|
||||||
|
|
||||||
template `operator`*(a: ?!typed): ?!typed =
|
|
||||||
type T {.used.} = type(`operator`(a.unsafeGet))
|
|
||||||
if x =? a:
|
|
||||||
`operator`(x).success
|
|
||||||
else:
|
|
||||||
T.failure(a.error)
|
|
||||||
|
|
||||||
template liftBinary(_: type Result, operator: untyped) =
|
|
||||||
|
|
||||||
template `operator`*(a: ?!typed, b: ?!typed): ?!typed =
|
|
||||||
type T = type(`operator`(a.unsafeGet, b.unsafeGet))
|
|
||||||
if x =? a and y =? b:
|
|
||||||
`operator`(x, y).success
|
|
||||||
elif a.isErr:
|
|
||||||
T.failure(a.error)
|
|
||||||
else:
|
|
||||||
T.failure(b.error)
|
|
||||||
|
|
||||||
template `operator`*(a: ?!typed, b: typed): ?!typed =
|
|
||||||
type T = type(`operator`(a.unsafeGet, b))
|
|
||||||
if x =? a:
|
|
||||||
`operator`(x, b).success
|
|
||||||
else:
|
|
||||||
T.failure(a.error)
|
|
||||||
|
|
||||||
Result.liftUnary(`-`)
|
Result.liftUnary(`-`)
|
||||||
Result.liftUnary(`+`)
|
Result.liftUnary(`+`)
|
||||||
Result.liftUnary(`@`)
|
Result.liftUnary(`@`)
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import std/unittest
|
import std/unittest
|
||||||
import std/sequtils
|
import std/sequtils
|
||||||
|
import std/sugar
|
||||||
import pkg/questionable
|
import pkg/questionable
|
||||||
|
|
||||||
suite "optionals":
|
suite "optionals":
|
||||||
@ -16,6 +17,21 @@ suite "optionals":
|
|||||||
check b.?len == int.none
|
check b.?len == int.none
|
||||||
check a.?len.?uint8 == 2'u8.some
|
check a.?len.?uint8 == 2'u8.some
|
||||||
check b.?len.?uint8 == uint8.none
|
check b.?len.?uint8 == uint8.none
|
||||||
|
check a.?len() == 2.some
|
||||||
|
check b.?len() == int.none
|
||||||
|
check a.?distribute(2).?len() == 2.some
|
||||||
|
check b.?distribute(2).?len() == int.none
|
||||||
|
|
||||||
|
test ".? chain can be followed by . calls and operators":
|
||||||
|
let a = @[41, 42].some
|
||||||
|
check a.?len.get == 2
|
||||||
|
check a.?len.get.uint8.uint64 == 2'u64
|
||||||
|
check a.?len.get() == 2
|
||||||
|
check a.?len.get().uint8.uint64 == 2'u64
|
||||||
|
check a.?deduplicate()[0].?uint8.?uint64 == 41'u64.some
|
||||||
|
check a.?len + 1 == 3.some
|
||||||
|
check a.?deduplicate()[0] + 1 == 42.some
|
||||||
|
check a.?deduplicate.map(x => x) == @[41, 42].some
|
||||||
|
|
||||||
test "[] can be used for indexing optionals":
|
test "[] can be used for indexing optionals":
|
||||||
let a: ?seq[int] = @[1, 2, 3].some
|
let a: ?seq[int] = @[1, 2, 3].some
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import std/unittest
|
import std/unittest
|
||||||
import std/sequtils
|
import std/sequtils
|
||||||
import std/strutils
|
import std/strutils
|
||||||
|
import std/sugar
|
||||||
import pkg/questionable
|
import pkg/questionable
|
||||||
import pkg/questionable/results
|
import pkg/questionable/results
|
||||||
|
|
||||||
@ -20,6 +21,21 @@ suite "result":
|
|||||||
check b.?len == int.failure error
|
check b.?len == int.failure error
|
||||||
check a.?len.?uint8 == 2'u8.success
|
check a.?len.?uint8 == 2'u8.success
|
||||||
check b.?len.?uint8 == uint8.failure error
|
check b.?len.?uint8 == uint8.failure error
|
||||||
|
check a.?len() == 2.success
|
||||||
|
check b.?len() == int.failure error
|
||||||
|
check a.?distribute(2).?len() == 2.success
|
||||||
|
check b.?distribute(2).?len() == int.failure error
|
||||||
|
|
||||||
|
test ".? chain can be followed by . calls and operators":
|
||||||
|
let a = @[41, 42].success
|
||||||
|
check (a.?len.get == 2)
|
||||||
|
check (a.?len.get.uint8.uint64 == 2'u64)
|
||||||
|
check (a.?len.get() == 2)
|
||||||
|
check (a.?len.get().uint8.uint64 == 2'u64)
|
||||||
|
check (a.?deduplicate()[0].?uint8.?uint64 == 41'u64.success)
|
||||||
|
check (a.?len + 1 == 3.success)
|
||||||
|
check (a.?deduplicate()[0] + 1 == 42.success)
|
||||||
|
check (a.?deduplicate.map(x => x) == @[41, 42].success)
|
||||||
|
|
||||||
test "[] can be used for indexing optionals":
|
test "[] can be used for indexing optionals":
|
||||||
let a: ?!seq[int] = @[1, 2, 3].success
|
let a: ?!seq[int] = @[1, 2, 3].success
|
||||||
@ -115,23 +131,23 @@ suite "result":
|
|||||||
|
|
||||||
# chaining:
|
# chaining:
|
||||||
let amount = works().?deduplicate.?len
|
let amount = works().?deduplicate.?len
|
||||||
check amount == 2.success
|
check (amount == 2.success)
|
||||||
|
|
||||||
# fallback values:
|
# fallback values:
|
||||||
let value = fails() |? @[]
|
let value = fails() |? @[]
|
||||||
check value == newSeq[int](0)
|
check (value == newSeq[int](0))
|
||||||
|
|
||||||
# lifted operators:
|
# lifted operators:
|
||||||
let sum = works()[3] + 40
|
let sum = works()[3] + 40
|
||||||
check sum == 42.success
|
check (sum == 42.success)
|
||||||
|
|
||||||
# catch
|
# catch
|
||||||
let x = parseInt("42").catch
|
let x = parseInt("42").catch
|
||||||
check x == 42.success
|
check (x == 42.success)
|
||||||
let y = parseInt("XX").catch
|
let y = parseInt("XX").catch
|
||||||
check y.isErr
|
check y.isErr
|
||||||
|
|
||||||
# Conversion to Option
|
# Conversion to Option
|
||||||
|
|
||||||
let converted = works().toOption
|
let converted = works().toOption
|
||||||
check converted == @[1, 1, 2, 2, 2].some
|
check (converted == @[1, 1, 2, 2, 2].some)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user