2020-10-22 07:18:08 +00:00
|
|
|
import
|
|
|
|
tables, typetraits, options, os,
|
|
|
|
serialization/[object_serialization, errors],
|
|
|
|
./utils
|
|
|
|
|
|
|
|
type
|
|
|
|
EnvvarReader* = object
|
|
|
|
prefix: string
|
|
|
|
key: seq[string]
|
|
|
|
|
|
|
|
EnvvarError* = object of SerializationError
|
|
|
|
|
|
|
|
EnvvarReaderError* = object of EnvvarError
|
|
|
|
|
|
|
|
GenericEnvvarReaderError* = object of EnvvarReaderError
|
|
|
|
deserializedField*: string
|
|
|
|
innerException*: ref CatchableError
|
|
|
|
|
|
|
|
proc handleReadException*(r: EnvvarReader,
|
|
|
|
Record: type,
|
|
|
|
fieldName: string,
|
|
|
|
field: auto,
|
|
|
|
err: ref CatchableError) =
|
|
|
|
var ex = new GenericEnvvarReaderError
|
|
|
|
ex.deserializedField = fieldName
|
|
|
|
ex.innerException = err
|
|
|
|
raise ex
|
|
|
|
|
|
|
|
proc init*(T: type EnvvarReader, prefix: string): T =
|
|
|
|
result.prefix = prefix
|
|
|
|
|
2020-10-31 10:44:03 +00:00
|
|
|
proc readValue*[T](r: var EnvvarReader, value: var T) =
|
2020-10-22 07:18:08 +00:00
|
|
|
mixin readValue
|
|
|
|
# TODO: reduce allocation
|
|
|
|
|
2020-10-23 01:45:19 +00:00
|
|
|
when T is string:
|
|
|
|
let key = constructKey(r.prefix, r.key)
|
|
|
|
value = os.getEnv(key)
|
|
|
|
|
|
|
|
elif T is (SomePrimitives or range):
|
2020-10-22 07:18:08 +00:00
|
|
|
let key = constructKey(r.prefix, r.key)
|
|
|
|
getValue(key, value)
|
|
|
|
|
|
|
|
elif T is Option:
|
2020-10-28 09:17:22 +00:00
|
|
|
template getUnderlyingType[T](_: Option[T]): untyped = T
|
2020-10-22 07:18:08 +00:00
|
|
|
let key = constructKey(r.prefix, r.key)
|
|
|
|
if existsEnv(key):
|
2020-10-31 10:44:03 +00:00
|
|
|
type uType = getUnderlyingType(value)
|
2020-10-23 01:45:19 +00:00
|
|
|
when uType is string:
|
|
|
|
value = some(os.getEnv(key))
|
2020-10-31 10:44:03 +00:00
|
|
|
else:
|
2020-10-28 09:17:22 +00:00
|
|
|
value = some(r.readValue(uType))
|
2020-10-22 07:18:08 +00:00
|
|
|
|
|
|
|
elif T is (seq or array):
|
|
|
|
when uTypeIsPrimitives(T):
|
|
|
|
let key = constructKey(r.prefix, r.key)
|
|
|
|
getValue(key, value)
|
|
|
|
|
2020-10-31 10:44:03 +00:00
|
|
|
else:
|
2020-10-22 07:18:08 +00:00
|
|
|
let key = r.key[^1]
|
|
|
|
for i in 0..<value.len:
|
|
|
|
r.key[^1] = key & $i
|
|
|
|
r.readValue(value[i])
|
|
|
|
|
|
|
|
elif T is (object or tuple):
|
|
|
|
type T = type(value)
|
|
|
|
when T.totalSerializedFields > 0:
|
|
|
|
let fields = T.fieldReadersTable(EnvvarReader)
|
|
|
|
var expectedFieldPos = 0
|
|
|
|
r.key.add ""
|
|
|
|
value.enumInstanceSerializedFields(fieldName, field):
|
|
|
|
when T is tuple:
|
|
|
|
r.key[^1] = $expectedFieldPos
|
|
|
|
var reader = fields[][expectedFieldPos].reader
|
|
|
|
expectedFieldPos += 1
|
|
|
|
else:
|
|
|
|
r.key[^1] = fieldName
|
|
|
|
var reader = findFieldReader(fields[], fieldName, expectedFieldPos)
|
|
|
|
|
|
|
|
if reader != nil:
|
|
|
|
reader(value, r)
|
|
|
|
discard r.key.pop()
|
|
|
|
|
|
|
|
else:
|
|
|
|
const typeName = typetraits.name(T)
|
|
|
|
{.fatal: "Failed to convert from Envvar an unsupported type: " & typeName.}
|