nim-stew/stew/enums.nim
2022-07-03 13:58:44 +03:00

59 lines
1.9 KiB
Nim

import
macros, sequtils
macro enumRangeInt64*(a: type[enum]): untyped =
## This macro returns an array with all the ordinal values of an enum
let
values = a.getType[1][1..^1]
valuesOrded = values.mapIt(newCall("int64", it))
newNimNode(nnkBracket).add(valuesOrded)
macro enumStrValuesArrayImpl(a: type[enum]): untyped =
## This macro returns an array with all the ordinal values of an enum
let
values = a.getType[1][1..^1]
valuesOrded = values.mapIt(newCall("$", it))
newNimNode(nnkBracket).add(valuesOrded)
# TODO: This should be a proc returning a lent view over the
# const value. This will ensure only a single instace
# of the array is generated.
template enumStrValuesArray*(E: type[enum]): auto =
const values = enumStrValuesArrayImpl E
values
# TODO: This should be a proc returning a lent view over the
# const value. This will ensure only a single instace
# of the sequence is generated.
template enumStrValuesSeq*(E: type[enum]): seq[string] =
const values = @(enumStrValuesArray E)
values
macro hasHoles*(T: type[enum]): bool =
# As an enum is always sorted, just substract the first and the last ordinal value
# and compare the result to the number of element in it will do the trick.
let len = T.getType[1].len - 2
quote: `T`.high.ord - `T`.low.ord != `len`
proc contains*[I: SomeInteger](e: type[enum], v: I): bool =
when I is uint64:
if v > int.high.uint64:
return false
when e.hasHoles():
v.int64 in enumRangeInt64(e)
else:
v.int64 in e.low.int64 .. e.high.int64
func checkedEnumAssign*[E: enum, I: SomeInteger](res: var E, value: I): bool =
## This function can be used to safely assign a tainted integer value (coming
## from untrusted source) to an enum variable. The function will return `true`
## if the integer value is within the acceped values of the enum and `false`
## otherwise.
if value notin E:
return false
res = E value
return true