62 lines
1.9 KiB
Nim
62 lines
1.9 KiB
Nim
|
import macros
|
||
|
import strutils
|
||
|
import pkg/questionable
|
||
|
import pkg/questionable/operators
|
||
|
|
||
|
export questionable
|
||
|
|
||
|
proc `as`*[T](value: T, U: type): ?U =
|
||
|
## Casts a value to another type, returns an Option.
|
||
|
## When the cast succeeds, the option will contain the casted value.
|
||
|
## When the cast fails, the option will have no value.
|
||
|
when value is U:
|
||
|
return some value
|
||
|
elif value is ref object:
|
||
|
if value of U:
|
||
|
return some U(value)
|
||
|
|
||
|
Option.liftBinary `as`
|
||
|
|
||
|
# Template that wraps type with `Option[]` only if it is already not `Option` type
|
||
|
template WrapOption*(input: untyped): type =
|
||
|
when input is Option:
|
||
|
input
|
||
|
else:
|
||
|
Option[input]
|
||
|
|
||
|
|
||
|
macro createType(t: typedesc): untyped =
|
||
|
var objectType = getType(t)
|
||
|
|
||
|
# Work around for https://github.com/nim-lang/Nim/issues/23112
|
||
|
while objectType.kind == nnkBracketExpr and objectType[0].eqIdent"typeDesc":
|
||
|
objectType = getType(objectType[1])
|
||
|
|
||
|
expectKind(objectType, NimNodeKind.nnkObjectTy)
|
||
|
var fields = nnkRecList.newTree()
|
||
|
|
||
|
# Generates the list of fields that are wrapped in `Option[T]`.
|
||
|
# Technically wrapped with `WrapOption` which is template used to prevent
|
||
|
# re-wrapping already filed which is `Option[T]`.
|
||
|
for field in objectType[2]:
|
||
|
let fieldType = getTypeInst(field)
|
||
|
let newFieldNode =
|
||
|
nnkIdentDefs.newTree(ident($field), nnkCall.newTree(ident("WrapOption"), fieldType), newEmptyNode())
|
||
|
|
||
|
fields.add(newFieldNode)
|
||
|
|
||
|
# Creates new object type T with the fields lists from steps above.
|
||
|
let tSym = genSym(nskType, "T")
|
||
|
nnkStmtList.newTree(
|
||
|
nnkTypeSection.newTree(
|
||
|
nnkTypeDef.newTree(tSym, newEmptyNode(), nnkObjectTy.newTree(newEmptyNode(), newEmptyNode(), fields))
|
||
|
),
|
||
|
tSym
|
||
|
)
|
||
|
|
||
|
template Optionalize*(t: typed): untyped =
|
||
|
## Takes object type and wraps all the first level fields into
|
||
|
## Option type unless it is already Option type.
|
||
|
createType(t)
|
||
|
|