comparison semiconginev2/old/settings.nim @ 1218:56781cc0fc7c compiletime-tests

did: renamge main package
author sam <sam@basx.dev>
date Wed, 17 Jul 2024 21:01:37 +0700
parents semicongine/old/settings.nim@a3eb305bcac2
children
comparison
equal deleted inserted replaced
1217:f819a874058f 1218:56781cc0fc7c
1 import std/logging
2 import std/streams
3 import std/parsecfg
4 import std/strutils
5 import std/parseutils
6 import std/strformat
7 import std/tables
8 import std/os
9
10 import ./core
11
12 when CONFIGHOTRELOAD:
13 var
14 configUpdates: Channel[(string, string)]
15 configUpdates.open()
16
17 # runtime configuration
18 # =====================
19 # namespace is the path from the CONFIGROOT to the according settings file without the file extension
20 # a settings file must always have the extension CONFIGEXTENSION
21 # a fully qualified settings identifier can be in the form {namespace}.{section}.{key}
22 # {key} and {section} may not contain dots
23
24 # a "namespace" is the path from the settings root to an *.CONFIGEXTENSION file, without the file extension
25 # settings is a namespace <-> settings mapping
26 var allsettings: Table[string, Config]
27
28 proc configRoot(): string =
29 joinPath(absolutePath(getAppDir()), CONFIGROOT)
30
31 proc getFile(namespace: string): string =
32 joinPath(configRoot(), namespace & "." & CONFIGEXTENSION)
33
34 iterator walkConfigNamespaces(): string =
35 for file in walkDirRec(dir = configRoot(), relative = true, checkDir = true):
36 if file.endsWith("." & CONFIGEXTENSION):
37 yield file[0 ..< ^(CONFIGEXTENSION.len + 1)]
38
39 proc loadAllConfig(): Table[string, Config] =
40 for ns in walkConfigNamespaces():
41 result[ns] = ns.getFile().loadConfig()
42
43 proc ReloadSettings*() =
44 allsettings = loadAllConfig()
45
46 proc configStr(key, section, namespace: string): string =
47 when CONFIGHOTRELOAD:
48 while configUpdates.peek() > 0:
49 let (updatedNamespace, updatedConfig) = configUpdates.recv()
50 allsettings[updatedNamespace] = loadConfig(newStringStream(updatedConfig))
51 if not allsettings.hasKey(namespace):
52 raise newException(Exception, &"Settings {namespace}.{section}.{key} was not found")
53 allsettings[namespace].getSectionValue(section, key)
54
55 proc Setting*[T: int|float|string](key, section, namespace: string): T =
56 when T is int:
57 let value = configStr(key, section, namespace)
58 if parseInt(value, result) == 0:
59 raise newException(Exception, &"Unable to parse int from settings {namespace}.{section}.{key}: {value}")
60 elif T is float:
61 let value = configStr(key, section, namespace)
62 if parseFloat(value, result) == 0:
63 raise newException(Exception, &"Unable to parse float from settings {namespace}.{section}.{key}: {value}")
64 else:
65 result = configStr(key, section, namespace)
66
67 proc Setting*[T: int|float|string](identifier: string): T =
68 # identifier can be in the form:
69 # {namespace}.{key}
70 # {namespace}.{section}.{key}
71 let parts = identifier.rsplit(".")
72 if parts.len == 1:
73 raise newException(Exception, &"Setting with name {identifier} has no namespace")
74 if parts.len == 2: result = Setting[T](parts[1], "", parts[0])
75 else: result = Setting[T](parts[^1], parts[^2], joinPath(parts[0 .. ^3]))
76
77 proc HadConfigUpdate*(): bool =
78 when CONFIGHOTRELOAD == true:
79 result = configUpdates.peek() > 0
80
81 allsettings = loadAllConfig()
82
83 when CONFIGHOTRELOAD == true:
84 import std/times
85
86 proc configFileWatchdog() {.thread.} =
87 var configModTimes: Table[string, Time]
88 while true:
89 for namespace in walkConfigNamespaces():
90 if not (namespace in configModTimes):
91 configModTimes[namespace] = Time()
92 let lastMod = namespace.getFile().getLastModificationTime()
93 if lastMod > configModTimes[namespace]:
94 configModTimes[namespace] = lastMod
95 let configStr = newFileStream(namespace.getFile()).readAll()
96 configUpdates.send((namespace, configStr))
97 sleep CONFIGHOTRELOADINTERVAL
98 var thethread: Thread[void]
99 createThread(thethread, configFileWatchdog)
100
101 if DEBUG:
102 setLogFilter(lvlAll)
103 else:
104 setLogFilter(lvlWarn)