mirror of
https://github.com/logos-storage/questionable.git
synced 2026-01-02 13:53:11 +00:00
Initial version of questionable
Syntactic sugar for std/options, pkg/result and pkg/stew
This commit is contained in:
commit
81ed9b652c
5
.editorconfig
Normal file
5
.editorconfig
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[*]
|
||||||
|
indent_style = space
|
||||||
|
insert_final_newline = true
|
||||||
|
indent_size = 2
|
||||||
|
trim_trailing_whitespace = true
|
||||||
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
*
|
||||||
|
!*/
|
||||||
|
!*.*
|
||||||
|
nimbledeps
|
||||||
1
.tool-versions
Normal file
1
.tool-versions
Normal file
@ -0,0 +1 @@
|
|||||||
|
nim 1.4.4
|
||||||
3
questionable.nim
Normal file
3
questionable.nim
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import ./questionable/options
|
||||||
|
|
||||||
|
export options
|
||||||
10
questionable.nimble
Normal file
10
questionable.nimble
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
version = "0.1.0"
|
||||||
|
author = "Questionable Authors"
|
||||||
|
description = "Elegant optional types"
|
||||||
|
license = "MIT"
|
||||||
|
|
||||||
|
task test, "Runs the test suite":
|
||||||
|
for module in ["options", "result", "stew"]:
|
||||||
|
withDir "testmodules/" & module:
|
||||||
|
exec "nimble install -d -y"
|
||||||
|
exec "nimble test -y"
|
||||||
8
questionable/errorban.nim
Normal file
8
questionable/errorban.nim
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
## Include this file to indicate that your module does not raise Errors.
|
||||||
|
## Disables compiler hints about unused declarations in Nim < 1.4.0
|
||||||
|
|
||||||
|
when (NimMajor, NimMinor, NimPatch) >= (1, 4, 0):
|
||||||
|
{.push raises:[].}
|
||||||
|
else:
|
||||||
|
{.push raises: [Defect].}
|
||||||
|
{.hint[XDeclaredButNotUsed]: off.}
|
||||||
33
questionable/options.nim
Normal file
33
questionable/options.nim
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import std/options
|
||||||
|
|
||||||
|
include ./errorban
|
||||||
|
|
||||||
|
export options
|
||||||
|
|
||||||
|
template `?`*(T: typed): type Option[T] =
|
||||||
|
Option[T]
|
||||||
|
|
||||||
|
template `.?`*(option: ?typed, field: untyped{nkIdent}): ?untyped =
|
||||||
|
type T = type option.get.field
|
||||||
|
if option.isSome:
|
||||||
|
option.unsafeGet().field.some
|
||||||
|
else:
|
||||||
|
T.none
|
||||||
|
|
||||||
|
template `[]`*(option: ?typed, index: typed): ?typed =
|
||||||
|
type T = type option.get[index]
|
||||||
|
if option.isSome:
|
||||||
|
option.unsafeGet()[index].some
|
||||||
|
else:
|
||||||
|
T.none
|
||||||
|
|
||||||
|
template `|?`*[T](option: ?T, fallback: T): T =
|
||||||
|
if option.isSome:
|
||||||
|
option.unsafeGet()
|
||||||
|
else:
|
||||||
|
fallback
|
||||||
|
|
||||||
|
template `=?`*[T](name: untyped{nkIdent}, option: ?T): bool =
|
||||||
|
template name: T {.used.} = option.unsafeGet()
|
||||||
|
option.isSome
|
||||||
|
|
||||||
35
questionable/results.nim
Normal file
35
questionable/results.nim
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import ./resultsbase
|
||||||
|
|
||||||
|
include ./errorban
|
||||||
|
|
||||||
|
export resultsbase
|
||||||
|
|
||||||
|
template `?!`*(T: typed): type Result[T, ref CatchableError] =
|
||||||
|
Result[T, ref CatchableError]
|
||||||
|
|
||||||
|
template success*[T](value: T): ?!T =
|
||||||
|
ok(?!T, value)
|
||||||
|
|
||||||
|
template failure*(T: type, error: ref CatchableError): ?!T =
|
||||||
|
err(?!T, error)
|
||||||
|
|
||||||
|
template `.?`*(value: ?!typed, field: untyped{nkIdent}): ?!untyped =
|
||||||
|
type T = type value.get.field
|
||||||
|
if value.isOk:
|
||||||
|
ok(?!T, value.unsafeGet().field)
|
||||||
|
else:
|
||||||
|
err(?!T, error(value))
|
||||||
|
|
||||||
|
template `[]`*(value: ?!typed, index: typed): ?!typed =
|
||||||
|
type T = type value.get[index]
|
||||||
|
if value.isOk:
|
||||||
|
ok(?!T, value.unsafeGet()[index])
|
||||||
|
else:
|
||||||
|
err(?!T, error(value))
|
||||||
|
|
||||||
|
template `|?`*[T](value: ?!T, fallback: T): T =
|
||||||
|
value.valueOr(fallback)
|
||||||
|
|
||||||
|
template `=?`*[T](name: untyped{nkIdent}, value: ?!T): bool =
|
||||||
|
template name: T {.used.} = value.unsafeGet()
|
||||||
|
value.isOk
|
||||||
8
questionable/resultsbase.nim
Normal file
8
questionable/resultsbase.nim
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
template tryImport(module) = import module
|
||||||
|
|
||||||
|
when compiles tryimport pkg/result:
|
||||||
|
import pkg/result/../results
|
||||||
|
else:
|
||||||
|
import pkg/stew/results
|
||||||
|
|
||||||
|
export results
|
||||||
1
testmodules/options/nim.cfg
Normal file
1
testmodules/options/nim.cfg
Normal file
@ -0,0 +1 @@
|
|||||||
|
--path:"../.."
|
||||||
0
testmodules/options/nimbledeps/.keep
Normal file
0
testmodules/options/nimbledeps/.keep
Normal file
58
testmodules/options/test.nim
Normal file
58
testmodules/options/test.nim
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import std/unittest
|
||||||
|
import pkg/questionable
|
||||||
|
|
||||||
|
suite "optionals":
|
||||||
|
|
||||||
|
test "?Type is shorthand for Option[Type]":
|
||||||
|
check (?int is Option[int])
|
||||||
|
check (?string is Option[string])
|
||||||
|
check (?seq[bool] is Option[seq[bool]])
|
||||||
|
|
||||||
|
test ".? can be used for chaining optionals":
|
||||||
|
let a: ?seq[int] = @[41, 42].some
|
||||||
|
let b: ?seq[int] = seq[int].none
|
||||||
|
check a.?len == 2.some
|
||||||
|
check b.?len == int.none
|
||||||
|
check a.?len.?uint8 == 2'u8.some
|
||||||
|
check b.?len.?uint8 == uint8.none
|
||||||
|
|
||||||
|
test "[] can be used for indexing optionals":
|
||||||
|
let a: ?seq[int] = @[1, 2, 3].some
|
||||||
|
let b: ?seq[int] = seq[int].none
|
||||||
|
check a[1] == 2.some
|
||||||
|
check a[^1] == 3.some
|
||||||
|
check a[0..1] == @[1, 2].some
|
||||||
|
check b[1] == int.none
|
||||||
|
|
||||||
|
test "|? can be used to specify a fallback value":
|
||||||
|
check 42.some |? 40 == 42
|
||||||
|
check int.none |? 42 == 42
|
||||||
|
|
||||||
|
test "=? can be used for optional binding":
|
||||||
|
if a =? int.none:
|
||||||
|
check false
|
||||||
|
|
||||||
|
if b =? 42.some:
|
||||||
|
check b == 42
|
||||||
|
else:
|
||||||
|
check false
|
||||||
|
|
||||||
|
while a =? 42.some:
|
||||||
|
check a == 42
|
||||||
|
break
|
||||||
|
|
||||||
|
while a =? int.none:
|
||||||
|
check false
|
||||||
|
break
|
||||||
|
|
||||||
|
test "=? can appear multiple times in conditional expression":
|
||||||
|
if a =? 42.some and b =? "foo".some:
|
||||||
|
check a == 42
|
||||||
|
check b == "foo"
|
||||||
|
else:
|
||||||
|
check false
|
||||||
|
|
||||||
|
test "=? works with variable hiding":
|
||||||
|
let a = 42.some
|
||||||
|
if a =? a:
|
||||||
|
check a == 42
|
||||||
7
testmodules/options/test.nimble
Normal file
7
testmodules/options/test.nimble
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
version = "0.1.0"
|
||||||
|
author = "Questionable Authors"
|
||||||
|
description = "Questionable tests for std/option"
|
||||||
|
license = "MIT"
|
||||||
|
|
||||||
|
task test, "Runs the test suite":
|
||||||
|
exec "nim c -f -r test.nim"
|
||||||
1
testmodules/result/nim.cfg
Normal file
1
testmodules/result/nim.cfg
Normal file
@ -0,0 +1 @@
|
|||||||
|
--path:"../.."
|
||||||
0
testmodules/result/nimbledeps/.keep
Normal file
0
testmodules/result/nimbledeps/.keep
Normal file
65
testmodules/result/test.nim
Normal file
65
testmodules/result/test.nim
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import std/unittest
|
||||||
|
import std/strutils
|
||||||
|
import pkg/questionable/results
|
||||||
|
|
||||||
|
suite "result":
|
||||||
|
|
||||||
|
let error = newException(CatchableError, "error")
|
||||||
|
|
||||||
|
test "?!Type is shorthand for Result[Type, ref CatchableError]":
|
||||||
|
check (?!int is Result[int, ref CatchableError])
|
||||||
|
check (?!string is Result[string, ref CatchableError])
|
||||||
|
check (?!seq[bool] is Result[seq[bool], ref CatchableError])
|
||||||
|
|
||||||
|
test ".? can be used for chaining results":
|
||||||
|
let a: ?!seq[int] = @[41, 42].success
|
||||||
|
let b: ?!seq[int] = seq[int].failure error
|
||||||
|
check a.?len == 2.success
|
||||||
|
check b.?len == int.failure error
|
||||||
|
check a.?len.?uint8 == 2'u8.success
|
||||||
|
check b.?len.?uint8 == uint8.failure error
|
||||||
|
|
||||||
|
test "[] can be used for indexing optionals":
|
||||||
|
let a: ?!seq[int] = @[1, 2, 3].success
|
||||||
|
let b: ?!seq[int] = seq[int].failure error
|
||||||
|
check a[1] == 2.success
|
||||||
|
check a[^1] == 3.success
|
||||||
|
check a[0..1] == @[1, 2].success
|
||||||
|
check b[1] == int.failure error
|
||||||
|
|
||||||
|
test "|? can be used to specify a fallback value":
|
||||||
|
check 42.success |? 40 == 42
|
||||||
|
check int.failure(error) |? 42 == 42
|
||||||
|
|
||||||
|
test "=? can be used for optional binding":
|
||||||
|
if a =? int.failure(error):
|
||||||
|
check false
|
||||||
|
|
||||||
|
if b =? 42.success:
|
||||||
|
check b == 42
|
||||||
|
else:
|
||||||
|
check false
|
||||||
|
|
||||||
|
while a =? 42.success:
|
||||||
|
check a == 42
|
||||||
|
break
|
||||||
|
|
||||||
|
while a =? int.failure(error):
|
||||||
|
check false
|
||||||
|
break
|
||||||
|
|
||||||
|
test "=? can appear multiple times in conditional expression":
|
||||||
|
if a =? 42.success and b =? "foo".success:
|
||||||
|
check a == 42
|
||||||
|
check b == "foo"
|
||||||
|
else:
|
||||||
|
check false
|
||||||
|
|
||||||
|
test "=? works with variable hiding":
|
||||||
|
let a = 42.success
|
||||||
|
if a =? a:
|
||||||
|
check a == 42
|
||||||
|
|
||||||
|
test "catch can be used to convert exceptions to results":
|
||||||
|
check parseInt("42").catch == 42.success
|
||||||
|
check parseInt("foo").catch.error of ValueError
|
||||||
9
testmodules/result/test.nimble
Normal file
9
testmodules/result/test.nimble
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
version = "0.1.0"
|
||||||
|
author = "Questionable Authors"
|
||||||
|
description = "Questionable tests for pkg/result"
|
||||||
|
license = "MIT"
|
||||||
|
|
||||||
|
requires "result"
|
||||||
|
|
||||||
|
task test, "Runs the test suite":
|
||||||
|
exec "nim c -f -r test.nim"
|
||||||
1
testmodules/stew/nim.cfg
Normal file
1
testmodules/stew/nim.cfg
Normal file
@ -0,0 +1 @@
|
|||||||
|
--path:"../.."
|
||||||
0
testmodules/stew/nimbledeps/.keep
Normal file
0
testmodules/stew/nimbledeps/.keep
Normal file
1
testmodules/stew/test.nim
Normal file
1
testmodules/stew/test.nim
Normal file
@ -0,0 +1 @@
|
|||||||
|
include ../result/test
|
||||||
9
testmodules/stew/test.nimble
Normal file
9
testmodules/stew/test.nimble
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
version = "0.1.0"
|
||||||
|
author = "Questionable Authors"
|
||||||
|
description = "Questionable tests for pkg/stew"
|
||||||
|
license = "MIT"
|
||||||
|
|
||||||
|
requires "stew"
|
||||||
|
|
||||||
|
task test, "Runs the test suite":
|
||||||
|
exec "nim c -f -r test.nim"
|
||||||
Loading…
x
Reference in New Issue
Block a user