Mercurial > games > semicongine
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) |