import docopt, json, nre, os, strtabs, strutils type CombinedConfig* = object docopt*: Table[string, Value] json*: JsonNode template walkFieldDefs*(t: NimNode, body: untyped) = let tTypeImpl = t.getTypeImpl var nodeToItr: NimNode if tTypeImpl.typeKind == ntyObject: nodeToItr = tTypeImpl[2] elif tTypeImpl.typeKind == ntyTypeDesc: nodeToItr = tTypeImpl.getType[1].getType[2] else: error $t & " is not an object or type desc (it's a " & $tTypeImpl.typeKind & ")." for fieldDef {.inject.} in nodeToItr.children: # ignore AST nodes that are not field definitions if fieldDef.kind == nnkIdentDefs: let fieldIdent {.inject.} = fieldDef[0] let fieldType {.inject.} = fieldDef[1] body elif fieldDef.kind == nnkSym: let fieldIdent {.inject.} = fieldDef let fieldType {.inject.} = fieldDef.getType body # TODO: Generic config loader # macro loadConfig*(filePath: string, cfgType: typed, args: Table[string, docopt.Value): untyped = # result = newNimNode(nnkObjConstr).(cfgType) # # var idx = 0 # cfgType.walkFieldDefs: # let valLookup = quote do: `getVal`( proc getVal*(cfg: CombinedConfig, key: string): string = let argKey = "--" & key let envKey = key.replace('-', '_').toUpper let jsonKey = key.replace(re"(-\w)", proc (m: RegexMatch): string = ($m)[1..1].toUpper) if cfg.docopt.contains(argKey) and cfg.docopt[argKey]: return $cfg.docopt[argKey] elif existsEnv(envKey): return getEnv(envKey) elif cfg.json.hasKey(jsonKey): let node = cfg.json[jsonKey] case node.kind of JString: return node.getStr of JInt: return $node.getInt of JFloat: return $node.getFloat of JBool: return $node.getBool of JNull: return "" of JObject: return $node of JArray: return $node else: raise newException(ValueError, "cannot find a configuration value for \"" & key & "\"") proc getVal*(cfg: CombinedConfig, key, default: string): string = try: return getVal(cfg, key) except: return default proc loadEnv*(): StringTableRef = result = newStringTable() for k, v in envPairs(): result[k] = v