diff --git a/.run/silo --api.run.xml b/.run/silo --api.run.xml deleted file mode 100644 index 10616d551..000000000 --- a/.run/silo --api.run.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.run/silo --preprocessing.run.xml b/.run/silo --preprocessing.run.xml deleted file mode 100644 index 68b55e50d..000000000 --- a/.run/silo --preprocessing.run.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/.run/silo api.run.xml b/.run/silo api.run.xml new file mode 100644 index 000000000..95a9887ee --- /dev/null +++ b/.run/silo api.run.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/.run/silo preprocessing.run.xml b/.run/silo preprocessing.run.xml new file mode 100644 index 000000000..c786daf94 --- /dev/null +++ b/.run/silo preprocessing.run.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/include/config/config_interface.h b/include/config/config_interface.h index 84defdedc..1e28f01e6 100644 --- a/include/config/config_interface.h +++ b/include/config/config_interface.h @@ -16,27 +16,21 @@ namespace silo::config { -/* #[error("error in {config_context}: unknown key(s) {invalid_config_keys:?}")] */ -struct InvalidConfigKeyError { // TODO move? - std::string config_context; - std::vector invalid_config_keys; -}; -/* #[error("error in {config_context}: {message}")] */ -struct ParseError { // TODO move? - std::string config_context; - std::string message; -}; - -/// For top-level config structs (containing help and possibly config +/// For config structs (containing help and possibly config /// file paths): -class Config { +template +concept Config = requires(C c, const C cc, const VerifiedConfigSource& config_source) { + /// Whether the user gave the --help option or environment /// variable equivalent. - [[nodiscard]] virtual bool asksForHelp() const = 0; + /// bool asksForHelp() const = 0; + { cc.asksForHelp() } -> std::same_as; + /// Optional config file that the user gave (or that is provided /// by the type via its defaults) that should be loaded. - [[nodiscard]] virtual std::optional configPath() const = 0; + /// std::optional configPath() const = 0; + { cc.configPath() } -> std::same_as>; /// Overwrite the fields of an instance of the target type; done /// that way so that multiple kinds of config sources can shadow @@ -45,72 +39,55 @@ class Config { /// .to_vec_reverse() and wrap in ConfigKeyPath). Throws /// `silo::config::ConfigException` for config value parse errors /// (subclass as ConfigValueParseError?). - virtual void overwriteFrom(const VerifiedConfigSource& config_source) = 0; -}; - -template -std::optional rawGetConfig( - std::span cmd, - const ConfigSpecification& config_specification -) { - auto env_source = - EnvironmentVariables::decodeEnvironmentVariables().verify(config_specification); - auto cmd_source = CommandLineArguments{cmd}.verify(config_specification); - - const ConsList no_parents{}; - - C config; - - // First, only check command line arguments, for "--help"; avoid - // potential errors from env processing, and we don't have the - // path to the config file yet. Since we're only interested in the - // help option, there's no need to read config_struct first, OK? - config = {}; - config.overwriteFrom(cmd_source); - if (config.asksForHelp()) { - return std::nullopt; - } - - // Then process env and cmd, to get to the config file - // path. Re-initialize since env must be processed for cmd. - config = {}; - config.overwriteFrom(env_source); - config.overwriteFrom(cmd_source); - // Would anyone request help via SILO_ENV=true? Well, allow it: - if (config.asksForHelp()) { - return std::nullopt; - } + /// void overwriteFrom(const VerifiedConfigSource& config_source) = 0; + { c.overwriteFrom(config_source) } -> std::same_as; - auto config_path = config.configPath(); - if (config_path.has_value()) { - auto file_source = YamlFile::readFile(*config_path).verify(config_specification); - // Now read again with the file first: - config = {}; - config.overwriteFrom(file_source); - config.overwriteFrom(env_source); - config.overwriteFrom(cmd_source); - // (The config file might specify --help, too, but we ignore - // that.) - } - return std::optional{config}; -} + /// Validation / Sanity checks about the values of this config + /// void validate() = 0; + { c.validate() } -> std::same_as; +}; /// In case of error, returns the exit code that the caller should /// pass to exit(): 0 if the user gave --help, 1 in case of erroneous /// usage (the error is already printed in that case). -template +template std::variant getConfig( - std::span cmd, - const ConfigSpecification& config_specification + std::span cmd ) { + const auto config_specification = C::getConfigSpecification(); try { - auto config = rawGetConfig(cmd, config_specification); - if (!config.has_value()) { - // User requested help + + auto env_source = + EnvironmentVariables::decodeEnvironmentVariables().verify(config_specification); + auto cmd_source = CommandLineArguments{cmd}.verify(config_specification); + + C config; + + // First, only check command line arguments, for "--help"; avoid + // potential errors from env processing, and we don't have the + // path to the config file yet. + config = {}; + config.overwriteFrom(cmd_source); + if (config.asksForHelp()) { std::cout << config_specification.helpText() << "\n" << std::flush; return 0; } - return std::move(*config); + config.overwriteFrom(env_source); + + // Was a config file given as an argument or by environment variable? + auto config_path = config.configPath(); + if (config_path.has_value()) { + auto file_source = YamlFile::readFile(*config_path).verify(config_specification); + // Now read again with the file first: + config = {}; + config.overwriteFrom(file_source); + config.overwriteFrom(env_source); + config.overwriteFrom(cmd_source); + // (The config file might specify --help, too, but we ignore + // that.) + } + config.validate(); + return std::move(config); } catch (const silo::config::ConfigException& e) { std::cerr << fmt::format( "Usage error: {}.\n\nRun with the --help option for help.\n", e.what() diff --git a/include/config/config_specification.h b/include/config/config_specification.h index bf4c5c81d..1a5cb1e0e 100644 --- a/include/config/config_specification.h +++ b/include/config/config_specification.h @@ -20,7 +20,6 @@ namespace silo::config { /// currently. class ConfigSpecification : public VerifiedConfigSource { public: - std::string_view config_name; // TODO check unused /// The name of the program for which this config is used. This will be printed in the help text std::string_view program_name; // Using std::vector so that initialization in place is possible; diff --git a/include/silo/config/preprocessing_config.h b/include/silo/config/preprocessing_config.h index 2a91ca744..efa7b91f6 100644 --- a/include/silo/config/preprocessing_config.h +++ b/include/silo/config/preprocessing_config.h @@ -16,7 +16,7 @@ namespace silo::config { -class PreprocessingConfig : public Config { +class PreprocessingConfig { friend class fmt::formatter; public: @@ -31,8 +31,7 @@ class PreprocessingConfig : public Config { std::filesystem::path database_config_file = "database_config.yaml"; // TODO std::filesystem::path reference_genome_file = "reference_genomes.json"; // TODO remove dup default - - std::filesystem::path preprocessing_config; + std::optional preprocessing_config; /// Create PreprocessingConfig with all default values from the specification PreprocessingConfig(); @@ -51,11 +50,11 @@ class PreprocessingConfig : public Config { [[nodiscard]] uint32_t getDuckdbMemoryLimitInG() const; - [[nodiscard]] bool asksForHelp() const override; + [[nodiscard]] bool asksForHelp() const; - void overwriteFrom(const silo::config::VerifiedConfigSource& config_source) override; + void overwriteFrom(const silo::config::VerifiedConfigSource& config_source); - [[nodiscard]] std::optional configPath() const override; + [[nodiscard]] std::optional configPath() const; }; } // namespace silo::config diff --git a/include/silo/config/runtime_config.h b/include/silo/config/runtime_config.h index 3edd0f872..32d5f178b 100644 --- a/include/silo/config/runtime_config.h +++ b/include/silo/config/runtime_config.h @@ -25,7 +25,7 @@ struct QueryOptions { size_t materialization_cutoff = 10000; }; -struct RuntimeConfig : public Config { +struct RuntimeConfig { bool help; std::optional runtime_config; std::filesystem::path data_directory = silo::config::DEFAULT_OUTPUT_DIRECTORY; @@ -34,12 +34,14 @@ struct RuntimeConfig : public Config { static ConfigSpecification getConfigSpecification(); - [[nodiscard]] bool asksForHelp() const override; - [[nodiscard]] std::optional configPath() const override; + void validate() const {}; - void overwriteFrom(const VerifiedConfigSource& config_source) override; + [[nodiscard]] bool asksForHelp() const; + + [[nodiscard]] std::optional configPath() const; + + void overwriteFrom(const VerifiedConfigSource& config_source); - void validate() const {}; }; } // namespace silo::config diff --git a/src/silo/config/preprocessing_config.cpp b/src/silo/config/preprocessing_config.cpp index 1a54889d8..b7b93a462 100644 --- a/src/silo/config/preprocessing_config.cpp +++ b/src/silo/config/preprocessing_config.cpp @@ -27,14 +27,13 @@ const ConfigKeyPath LINEAGE_DEFINITIONS_FILE_OPTION_KEY = const ConfigKeyPath NDJSON_INPUT_FILENAME_OPTION_KEY = YamlFile::stringToConfigKeyPath("ndjsonInputFilename"); const ConfigKeyPath DATABASE_CONFIG_FILE_OPTION_KEY = - YamlFile::stringToConfigKeyPath("databaseConfigFile"); + YamlFile::stringToConfigKeyPath("databaseConfig"); const ConfigKeyPath REFERENCE_GENOMES_FILENAME_OPTION_KEY = YamlFile::stringToConfigKeyPath("referenceGenomeFilename"); // Specification of the fields in inputs to the PreprocessingConfig struct ConfigSpecification PreprocessingConfig::getConfigSpecification() { return ConfigSpecification{ - .config_name = "preprocessing_config", .program_name = "siloPreprocessing", .fields{ ConfigValueSpecification::createWithoutDefault( @@ -95,7 +94,9 @@ ConfigSpecification PreprocessingConfig::getConfigSpecification() { }; } -PreprocessingConfig::PreprocessingConfig() {} +PreprocessingConfig::PreprocessingConfig() { + overwriteFrom(getConfigSpecification()); +} void PreprocessingConfig::validate() const { if (!std::filesystem::exists(input_directory)) { diff --git a/src/silo/config/runtime_config.cpp b/src/silo/config/runtime_config.cpp index 24a8207ac..1807bf11a 100644 --- a/src/silo/config/runtime_config.cpp +++ b/src/silo/config/runtime_config.cpp @@ -25,7 +25,6 @@ const ConfigKeyPath QUERY_MATERIALIZATION_CUTOFF_OPTION_KEY = ConfigSpecification RuntimeConfig::getConfigSpecification() { return { - .config_name = "runtime config", .program_name = "siloServer", .fields = { diff --git a/src/silo_api/main.cpp b/src/silo_api/main.cpp index 79cf4d8b8..d24f358f5 100644 --- a/src/silo_api/main.cpp +++ b/src/silo_api/main.cpp @@ -6,6 +6,7 @@ #include "silo/common/overloaded.h" #include "silo/config/runtime_config.h" +#include "silo/config/preprocessing_config.h" #include "silo_api/logging.h" int main(int argc, char** argv) { @@ -27,24 +28,33 @@ int main(int argc, char** argv) { } else if (!args.empty()) { const std::string& mode_argument = args[0]; args = {args.begin() + 1, args.end()}; - if (mode_argument == "preprocess") { + if (mode_argument == "preprocessing") { mode = 0; } else if (mode_argument == "api") { mode = 1; } else { std::cerr << program_name - << ": need either 'preprocess' or 'api' as the first program argument, got '" + << ": need either 'preprocessing' or 'api' as the first program argument, got '" << mode_argument << "'\n"; return 1; } } else { std::cerr << program_name - << ": need either 'preprocess' or 'api' as the first program argument\n"; + << ": need either 'preprocessing' or 'api' as the first program argument\n"; return 1; } if (mode == 0) { - // XXX preprocessing config + return std::visit( + overloaded{ + [&](const silo::config::PreprocessingConfig& preprocessing_config) { + SPDLOG_TRACE("preprocessing_config = {}", preprocessing_config); + return 0; + }, + [&](int32_t exit_code) { return exit_code; } + }, + silo::config::getConfig(args) + ); } else { return std::visit( overloaded{ @@ -54,9 +64,7 @@ int main(int argc, char** argv) { }, [&](int32_t exit_code) { return exit_code; } }, - getConfig( - args, silo::config::RuntimeConfig::getConfigSpecification() - ) // TODO duplication of RuntimeConfig + silo::config::getConfig(args) ); } }