32 ConfigSource paths = ConfigSource::Default;
33 ConfigSource loading = ConfigSource::Default;
34 ConfigSource runtime = ConfigSource::Default;
35 ConfigSource security = ConfigSource::Default;
36 ConfigSource logging = ConfigSource::Default;
41 std::filesystem::path baseDir;
42 std::filesystem::path extensionsDir =
"extensions";
43 std::filesystem::path configsDir =
"configs";
44 std::filesystem::path dataDir =
"data";
45 std::filesystem::path logsDir =
"logs";
46 std::filesystem::path cacheDir =
"cache";
49 bool HasCustomExtensionsDir()
const {
50 return extensionsDir !=
"extensions";
53 bool HasCustomConfigsDir()
const {
54 return configsDir !=
"configs";
57 bool HasCustomDataDir()
const {
58 return dataDir !=
"data";
61 bool HasCustomLogsDir()
const {
62 return logsDir !=
"logs";
65 bool HasCustomCacheDir()
const {
66 return cacheDir !=
"cache";
69 void ResolveRelativePaths() {
70 auto makeAbsolute = [
this](std::filesystem::path& path) {
71 if (!path.empty() && path.is_relative() && !baseDir.empty()) {
72 path = (baseDir / path).lexically_normal();
76 makeAbsolute(extensionsDir);
77 makeAbsolute(configsDir);
78 makeAbsolute(dataDir);
79 makeAbsolute(logsDir);
80 makeAbsolute(cacheDir);
86 bool preferOwnSymbols =
false;
87 size_t maxConcurrentLoads = 4;
88 std::chrono::milliseconds loadTimeout{ 500 };
89 std::chrono::milliseconds exportTimeout{ 100 };
90 std::chrono::milliseconds startTimeout{ 250 };
93 bool HasCustomPreferOwnSymbols()
const {
94 return preferOwnSymbols !=
false;
97 bool HasCustomMaxConcurrentLoads()
const {
98 return maxConcurrentLoads != 4;
101 bool HasCustomLoadTimeout()
const {
102 return loadTimeout != std::chrono::milliseconds{ 500 };
105 bool HasCustomExportTimeout()
const {
106 return exportTimeout != std::chrono::milliseconds{ 100 };
109 bool HasCustomStartTimeout()
const {
110 return startTimeout != std::chrono::milliseconds{ 250 };
116 bool pinToMainThread =
true;
117 UpdateMode updateMode = UpdateMode::Manual;
118 std::chrono::milliseconds updateInterval{ 16 };
119 std::function<void(std::chrono::milliseconds)> updateCallback;
120 std::optional<size_t> threadPriority;
123 bool HasCustomPinToMainThread()
const {
124 return pinToMainThread !=
true;
127 bool HasCustomUpdateMode()
const {
128 return updateMode != UpdateMode::Manual;
131 bool HasCustomUpdateInterval()
const {
132 return updateInterval != std::chrono::milliseconds{ 16 };
135 bool HasThreadPriority()
const {
136 return threadPriority.has_value();
142 std::unordered_set<std::string> whitelistedExtensions;
143 std::unordered_set<std::string> blacklistedExtensions;
145 bool HasWhitelist()
const {
146 return !whitelistedExtensions.empty();
149 bool HasBlacklist()
const {
150 return !blacklistedExtensions.empty();
156 Severity severity{ Severity::Error };
157 bool printReport =
false;
158 bool printLoadOrder =
false;
159 bool printDependencyGraph =
false;
160 bool printDigraphDot =
false;
161 std::filesystem::path exportDigraphDot;
163 bool HasCustomSeverity()
const {
164 return severity != Severity::Error;
167 bool HasCustomPrintReport()
const {
168 return printReport !=
false;
171 bool HasCustomPrintLoadOrder()
const {
172 return printLoadOrder !=
false;
175 bool HasCustomPrintDependencyGraph()
const {
176 return printDependencyGraph !=
false;
179 bool HasCustomPrintDigraphDot()
const {
180 return printDigraphDot !=
false;
183 bool HasExportPath()
const {
184 return !exportDigraphDot.empty();
189 void MergeFrom(
const Config& other, ConfigSource source = ConfigSource::Override) {
193 if (source >= _sources.paths) {
194 bool pathsChanged =
false;
197 if (!other.paths.baseDir.empty()) {
198 paths.baseDir = other.paths.baseDir;
203 if (other.paths.HasCustomExtensionsDir()) {
204 paths.extensionsDir = other.paths.extensionsDir;
207 if (other.paths.HasCustomConfigsDir()) {
208 paths.configsDir = other.paths.configsDir;
211 if (other.paths.HasCustomDataDir()) {
212 paths.dataDir = other.paths.dataDir;
215 if (other.paths.HasCustomLogsDir()) {
216 paths.logsDir = other.paths.logsDir;
219 if (other.paths.HasCustomCacheDir()) {
220 paths.cacheDir = other.paths.cacheDir;
225 _sources.paths = source;
230 if (source >= _sources.loading) {
231 bool loadingChanged =
false;
233 if (other.loading.HasCustomPreferOwnSymbols()) {
234 loading.preferOwnSymbols = other.loading.preferOwnSymbols;
235 loadingChanged =
true;
237 if (other.loading.HasCustomMaxConcurrentLoads()) {
238 loading.maxConcurrentLoads = other.loading.maxConcurrentLoads;
239 loadingChanged =
true;
241 if (other.loading.HasCustomLoadTimeout()) {
242 loading.loadTimeout = other.loading.loadTimeout;
243 loadingChanged =
true;
245 if (other.loading.HasCustomExportTimeout()) {
246 loading.exportTimeout = other.loading.exportTimeout;
247 loadingChanged =
true;
249 if (other.loading.HasCustomStartTimeout()) {
250 loading.startTimeout = other.loading.startTimeout;
251 loadingChanged =
true;
254 if (loadingChanged) {
255 _sources.loading = source;
260 if (source >= _sources.runtime) {
261 bool runtimeChanged =
false;
263 if (other.runtime.HasCustomUpdateMode()) {
264 runtime.updateMode = other.runtime.updateMode;
265 runtimeChanged =
true;
267 if (other.runtime.HasCustomUpdateInterval()) {
268 runtime.updateInterval = other.runtime.updateInterval;
269 runtimeChanged =
true;
271 if (other.runtime.updateCallback) {
272 runtime.updateCallback = other.runtime.updateCallback;
273 runtimeChanged =
true;
275 if (other.runtime.HasCustomPinToMainThread()) {
276 runtime.pinToMainThread = other.runtime.pinToMainThread;
277 runtimeChanged =
true;
279 if (other.runtime.HasThreadPriority()) {
280 runtime.threadPriority = other.runtime.threadPriority;
281 runtimeChanged =
true;
284 if (runtimeChanged) {
285 _sources.runtime = source;
290 if (source >= _sources.security) {
291 bool securityChanged =
false;
294 if (source == ConfigSource::Override) {
296 if (other.security.HasWhitelist()) {
297 security.whitelistedExtensions = other.security.whitelistedExtensions;
298 securityChanged =
true;
300 if (other.security.HasBlacklist()) {
301 security.blacklistedExtensions = other.security.blacklistedExtensions;
302 securityChanged =
true;
306 if (other.security.HasWhitelist()) {
307 security.whitelistedExtensions.insert(
308 other.security.whitelistedExtensions.begin(),
309 other.security.whitelistedExtensions.end()
311 securityChanged =
true;
313 if (other.security.HasBlacklist()) {
314 security.blacklistedExtensions.insert(
315 other.security.blacklistedExtensions.begin(),
316 other.security.blacklistedExtensions.end()
318 securityChanged =
true;
322 if (securityChanged) {
323 _sources.security = source;
328 if (source >= _sources.logging) {
329 bool loggingChanged =
false;
331 if (other.logging.HasCustomSeverity()) {
332 logging.severity = other.logging.severity;
333 loggingChanged =
true;
335 if (other.logging.HasCustomPrintReport()) {
336 logging.printReport = other.logging.printReport;
337 loggingChanged =
true;
339 if (other.logging.HasCustomPrintLoadOrder()) {
340 logging.printLoadOrder = other.logging.printLoadOrder;
341 loggingChanged =
true;
343 if (other.logging.HasCustomPrintDependencyGraph()) {
344 logging.printDependencyGraph = other.logging.printDependencyGraph;
345 loggingChanged =
true;
347 if (other.logging.HasCustomPrintDigraphDot()) {
348 logging.printDigraphDot = other.logging.printDigraphDot;
349 loggingChanged =
true;
351 if (other.logging.HasExportPath()) {
352 logging.exportDigraphDot = other.logging.exportDigraphDot;
353 loggingChanged =
true;
356 if (loggingChanged) {
357 _sources.logging = source;
362 paths.ResolveRelativePaths();
416 Result<void> Validate()
const {
417 if (paths.baseDir.empty()) {
418 return MakeError(
"Base directory not set");
422 using pair = std::pair<std::string_view, const std::filesystem::path*>;
423 std::array pathList = { pair{
"extensions", &paths.extensionsDir },
424 pair{
"configs", &paths.configsDir },
425 pair{
"data", &paths.dataDir },
426 pair{
"logs", &paths.logsDir },
427 pair{
"cache", &paths.cacheDir } };
429 for (
size_t i = 0; i < pathList.size(); ++i) {
430 for (
size_t j = i + 1; j < pathList.size(); ++j) {
431 if (*pathList[i].second == *pathList[j].second) {
433 "{} and {} directories are the same: {}",
436 plg::as_string(*pathList[i].second)
443 if (runtime.updateMode == UpdateMode::BackgroundThread
444 && runtime.updateInterval <= std::chrono::milliseconds{ 0 }) {
445 return MakeError(
"Invalid update interval for background thread mode");
448 if (runtime.updateMode == UpdateMode::Callback && !runtime.updateCallback) {
449 return MakeError(
"Update callback not set for callback mode");