import std/options import std/os import std/sequtils import std/strutils import std/sugar import std/tables from pkg/chronicles import LogLevel import pkg/storage/conf import pkg/storage/units import pkg/confutils import pkg/confutils/defs import libp2p except setup import pkg/questionable import ./clioption export clioption export confutils type StorageConfigs* = object configs*: seq[StorageConfig] StorageConfig* = object cliOptions: Table[StartUpCmd, Table[string, CliOption]] debugEnabled*: bool StorageConfigError* = object of CatchableError proc cliArgs*( config: StorageConfig ): seq[string] {.gcsafe, raises: [StorageConfigError].} proc raiseStorageConfigError(msg: string) {.raises: [StorageConfigError].} = raise newException(StorageConfigError, msg) template convertError(body) = try: body except CatchableError as e: raiseStorageConfigError e.msg proc init*(_: type StorageConfigs, nodes = 1): StorageConfigs {.raises: [].} = StorageConfigs(configs: newSeq[StorageConfig](nodes)) func nodes*(self: StorageConfigs): int = self.configs.len proc checkBounds(self: StorageConfigs, idx: int) {.raises: [StorageConfigError].} = if idx notin 0 ..< self.configs.len: raiseStorageConfigError "index must be in bounds of the number of nodes" proc buildConfig( config: StorageConfig, msg: string ): StorageConf {.raises: [StorageConfigError].} = proc postFix(msg: string): string = if msg.len > 0: ": " & msg else: "" try: return StorageConf.load(cmdLine = config.cliArgs, quitOnFailure = false) except ConfigurationError as e: raiseStorageConfigError msg & e.msg.postFix except Exception as e: ## TODO: remove once proper exception handling added to nim-confutils raiseStorageConfigError msg & e.msg.postFix proc addCliOption*( config: var StorageConfig, group = StartUpCmd.noCmd, cliOption: CliOption ) {.raises: [StorageConfigError].} = var options = config.cliOptions.getOrDefault(group) options[cliOption.key] = cliOption # overwrite if already exists config.cliOptions[group] = options discard config.buildConfig("Invalid cli arg " & $cliOption) proc addCliOption*( config: var StorageConfig, group = StartUpCmd.noCmd, key: string, value = "" ) {.raises: [StorageConfigError].} = config.addCliOption(group, CliOption(key: key, value: value)) proc addCliOption*( config: var StorageConfig, cliOption: CliOption ) {.raises: [StorageConfigError].} = config.addCliOption(StartUpCmd.noCmd, cliOption) proc addCliOption*( config: var StorageConfig, key: string, value = "" ) {.raises: [StorageConfigError].} = config.addCliOption(StartUpCmd.noCmd, CliOption(key: key, value: value)) proc cliArgs*( config: StorageConfig ): seq[string] {.gcsafe, raises: [StorageConfigError].} = ## converts StorageConfig cli options and command groups in a sequence of args ## and filters out cli options by node index if provided in the CliOption var args: seq[string] = @[] convertError: for cmd in StartUpCmd: if config.cliOptions.hasKey(cmd): if cmd != StartUpCmd.noCmd: args.add $cmd var opts = config.cliOptions[cmd].values.toSeq args = args.concat(opts.map(o => $o)) return args proc logFile*(config: StorageConfig): ?string {.raises: [StorageConfigError].} = let built = config.buildConfig("Invalid storage config cli params") built.logFile proc logLevel*(config: StorageConfig): LogLevel {.raises: [StorageConfigError].} = convertError: let built = config.buildConfig("Invalid storage config cli params") return parseEnum[LogLevel](built.logLevel.toUpperAscii) proc debug*( self: StorageConfigs, idx: int, enabled = true ): StorageConfigs {.raises: [StorageConfigError].} = ## output log in stdout for a specific node in the group self.checkBounds idx var startConfig = self startConfig.configs[idx].debugEnabled = enabled return startConfig proc debug*(self: StorageConfigs, enabled = true): StorageConfigs {.raises: [].} = ## output log in stdout for all nodes in group var startConfig = self for config in startConfig.configs.mitems: config.debugEnabled = enabled return startConfig proc withLogFile*( self: StorageConfigs, idx: int ): StorageConfigs {.raises: [StorageConfigError].} = self.checkBounds idx var startConfig = self startConfig.configs[idx].addCliOption("--log-file", "") return startConfig proc withLogFile*( self: StorageConfigs ): StorageConfigs {.raises: [StorageConfigError].} = ## typically called from test, sets config such that a log file should be ## created var startConfig = self for config in startConfig.configs.mitems: config.addCliOption("--log-file", "") return startConfig proc withLogFile*( self: var StorageConfig, logFile: string ) {.raises: [StorageConfigError].} = #: StorageConfigs = ## typically called internally from the test suite, sets a log file path to ## be created during the test run, for a specified node in the group # var config = self self.addCliOption("--log-file", logFile) # return startConfig proc withLogLevel*( self: StorageConfig, level: LogLevel | string ): StorageConfig {.raises: [StorageConfigError].} = var config = self config.addCliOption("--log-level", $level) return config proc withLogLevel*( self: StorageConfigs, idx: int, level: LogLevel | string ): StorageConfigs {.raises: [StorageConfigError].} = self.checkBounds idx var startConfig = self startConfig.configs[idx].addCliOption("--log-level", $level) return startConfig proc withLogLevel*( self: StorageConfigs, level: LogLevel | string ): StorageConfigs {.raises: [StorageConfigError].} = var startConfig = self for config in startConfig.configs.mitems: config.addCliOption("--log-level", $level) return startConfig proc withBlockTtl*( self: StorageConfig, ttl: int ): StorageConfig {.raises: [StorageConfigError].} = var config = self config.addCliOption("--block-ttl", $ttl) return config proc withBlockTtl*( self: StorageConfigs, idx: int, ttl: int ): StorageConfigs {.raises: [StorageConfigError].} = self.checkBounds idx var startConfig = self startConfig.configs[idx].addCliOption("--block-ttl", $ttl) return startConfig proc withBlockTtl*( self: StorageConfigs, ttl: int ): StorageConfigs {.raises: [StorageConfigError].} = var startConfig = self for config in startConfig.configs.mitems: config.addCliOption("--block-ttl", $ttl) return startConfig proc withBlockMaintenanceInterval*( self: StorageConfig, interval: int ): StorageConfig {.raises: [StorageConfigError].} = var config = self config.addCliOption("--block-mi", $interval) return config proc withBlockMaintenanceInterval*( self: StorageConfigs, idx: int, interval: int ): StorageConfigs {.raises: [StorageConfigError].} = self.checkBounds idx var startConfig = self startConfig.configs[idx].addCliOption("--block-mi", $interval) return startConfig proc withBlockMaintenanceInterval*( self: StorageConfigs, interval: int ): StorageConfigs {.raises: [StorageConfigError].} = var startConfig = self for config in startConfig.configs.mitems: config.addCliOption("--block-mi", $interval) return startConfig proc logLevelWithTopics( config: StorageConfig, topics: varargs[string] ): string {.raises: [StorageConfigError].} = convertError: var logLevel = LogLevel.INFO let built = config.buildConfig("Invalid storage config cli params") logLevel = parseEnum[LogLevel](built.logLevel.toUpperAscii) let level = $logLevel & ";TRACE: " & topics.join(",") return level proc withLogTopics*( self: StorageConfigs, idx: int, topics: varargs[string] ): StorageConfigs {.raises: [StorageConfigError].} = self.checkBounds idx convertError: let config = self.configs[idx] let level = config.logLevelWithTopics(topics) var startConfig = self return startConfig.withLogLevel(idx, level) proc withLogTopics*( self: StorageConfigs, topics: varargs[string] ): StorageConfigs {.raises: [StorageConfigError].} = var startConfig = self for config in startConfig.configs.mitems: let level = config.logLevelWithTopics(topics) config = config.withLogLevel(level) return startConfig proc withStorageQuota*( self: StorageConfigs, idx: int, quota: NBytes ): StorageConfigs {.raises: [StorageConfigError].} = self.checkBounds idx var startConfig = self startConfig.configs[idx].addCliOption("--storage-quota", $quota) return startConfig proc withStorageQuota*( self: StorageConfigs, quota: NBytes ): StorageConfigs {.raises: [StorageConfigError].} = var startConfig = self for config in startConfig.configs.mitems: config.addCliOption("--storage-quota", $quota) return startConfig