From 965c2f2c5592431515fcad4924537539a8c12ecf Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Tue, 29 Jun 2021 15:41:47 +0200 Subject: [PATCH] Improved error reporting when importing various configuration files: 1) Slic3r::RuntimeError was replaced with ConfigurationError, all exceptions thrown by the configuration layer are derived from ConfigurationError. 2) When parsing configuration files, ConfigurationError is catched and rethrown extended with the file name being parsed. --- src/libslic3r/Config.cpp | 57 ++++++++++++--- src/libslic3r/Config.hpp | 125 ++++++++++++++++++-------------- src/libslic3r/PresetBundle.cpp | 106 +++++++++++++++------------ src/slic3r/GUI/Plater.cpp | 4 + tests/libslic3r/test_config.cpp | 2 +- 5 files changed, 181 insertions(+), 113 deletions(-) diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index ae154bf27..e0c639fdf 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -248,7 +248,7 @@ ConfigOption* ConfigOptionDef::create_empty_option() const case coPercents: return new ConfigOptionPercentsNullable(); case coFloatsOrPercents: return new ConfigOptionFloatsOrPercentsNullable(); case coBools: return new ConfigOptionBoolsNullable(); - default: throw Slic3r::RuntimeError(std::string("Unknown option type for nullable option ") + this->label); + default: throw ConfigurationError(std::string("Unknown option type for nullable option ") + this->label); } } else { switch (this->type) { @@ -269,7 +269,7 @@ ConfigOption* ConfigOptionDef::create_empty_option() const case coBool: return new ConfigOptionBool(); case coBools: return new ConfigOptionBools(); case coEnum: return new ConfigOptionEnumGeneric(this->enum_keys_map); - default: throw Slic3r::RuntimeError(std::string("Unknown option type for option ") + this->label); + default: throw ConfigurationError(std::string("Unknown option type for option ") + this->label); } } } @@ -497,7 +497,7 @@ bool ConfigBase::set_deserialize_nothrow(const t_config_option_key &opt_key_src, void ConfigBase::set_deserialize(const t_config_option_key &opt_key_src, const std::string &value_src, ConfigSubstitutionContext& substitutions_ctxt, bool append) { if (! this->set_deserialize_nothrow(opt_key_src, value_src, substitutions_ctxt, append)) - throw BadOptionTypeException(format("ConfigBase::set_deserialize() failed for parameter \"%1%\", value \"%2%\"", opt_key_src, value_src)); + throw BadOptionValueException(format("Invalid value provided for parameter %1%: %2%", opt_key_src, value_src)); } void ConfigBase::set_deserialize(std::initializer_list items, ConfigSubstitutionContext& substitutions_ctxt) @@ -589,7 +589,7 @@ double ConfigBase::get_abs_value(const t_config_option_key &opt_key) const return opt_def->ratio_over.empty() ? 0. : static_cast(raw_opt)->get_abs_value(this->get_abs_value(opt_def->ratio_over)); } - throw Slic3r::RuntimeError("ConfigBase::get_abs_value(): Not a valid option type for get_abs_value()"); + throw ConfigurationError("ConfigBase::get_abs_value(): Not a valid option type for get_abs_value()"); } // Return an absolute value of a possibly relative config variable. @@ -600,7 +600,7 @@ double ConfigBase::get_abs_value(const t_config_option_key &opt_key, double rati const ConfigOption *raw_opt = this->option(opt_key); assert(raw_opt != nullptr); if (raw_opt->type() != coFloatOrPercent) - throw Slic3r::RuntimeError("ConfigBase::get_abs_value(): opt_key is not of coFloatOrPercent"); + throw ConfigurationError("ConfigBase::get_abs_value(): opt_key is not of coFloatOrPercent"); // Compute absolute value. return static_cast(raw_opt)->get_abs_value(ratio_over); } @@ -632,10 +632,14 @@ ConfigSubstitutions ConfigBase::load(const std::string &file, ForwardCompatibili ConfigSubstitutions ConfigBase::load_from_ini(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule) { - boost::property_tree::ptree tree; - boost::nowide::ifstream ifs(file); - boost::property_tree::read_ini(ifs, tree); - return this->load(tree, compatibility_rule); + try { + boost::property_tree::ptree tree; + boost::nowide::ifstream ifs(file); + boost::property_tree::read_ini(ifs, tree); + return this->load(tree, compatibility_rule); + } catch (const ConfigurationError &e) { + throw ConfigurationError(format("Failed loading configuration file \"%1%\": %2%", file, e.what())); + } } ConfigSubstitutions ConfigBase::load_from_ini_string(const std::string &data, ForwardCompatibilitySubstitutionRule compatibility_rule) @@ -701,6 +705,39 @@ ConfigSubstitutions ConfigBase::load(const boost::property_tree::ptree &tree, Fo return std::move(substitutions_ctxt.substitutions); } +// Load the config keys from the tail of a G-code file. +ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule) +{ + try { + // Read a 64k block from the end of the G-code. + boost::nowide::ifstream ifs(file); + { + const char slic3r_gcode_header[] = "; generated by Slic3r "; + const char prusaslicer_gcode_header[] = "; generated by PrusaSlicer "; + std::string firstline; + std::getline(ifs, firstline); + if (strncmp(slic3r_gcode_header, firstline.c_str(), strlen(slic3r_gcode_header)) != 0 && + strncmp(prusaslicer_gcode_header, firstline.c_str(), strlen(prusaslicer_gcode_header)) != 0) + throw ConfigurationError("Not a PrusaSlicer / Slic3r PE generated g-code."); + } + ifs.seekg(0, ifs.end); + auto file_length = ifs.tellg(); + auto data_length = std::min(65535, file_length); + ifs.seekg(file_length - data_length, ifs.beg); + std::vector data(size_t(data_length) + 1, 0); + ifs.read(data.data(), data_length); + ifs.close(); + + ConfigSubstitutionContext substitutions_ctxt(compatibility_rule); + size_t key_value_pairs = load_from_gcode_string(data.data(), substitutions_ctxt); + if (key_value_pairs < 80) + throw ConfigurationError(format("Suspiciously low number of configuration values extracted from %1%: %2%", file, key_value_pairs)); + return std::move(substitutions_ctxt.substitutions); + } catch (const ConfigurationError &e) { + throw ConfigurationError(format("Failed loading configuration from G-code \"%1%\": %2%", file, e.what())); + } +} + // Load the config keys from the given string. static inline size_t load_from_gcode_string_legacy(ConfigBase &config, const char *str, ConfigSubstitutionContext &substitutions) { @@ -995,7 +1032,7 @@ ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool cre throw NoDefinitionException(opt_key); const ConfigOptionDef *optdef = def->get(opt_key); if (optdef == nullptr) -// throw Slic3r::RuntimeError(std::string("Invalid option name: ") + opt_key); +// throw ConfigurationError(std::string("Invalid option name: ") + opt_key); // Let the parent decide what to do if the opt_key is not defined by this->def(). return nullptr; ConfigOption *opt = optdef->create_default_option(); diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index ab3caebb8..078e94cfb 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -80,32 +80,47 @@ extern bool unescape_strings_cstyle(const std::string &str, std::vector< extern std::string escape_ampersand(const std::string& str); -/// Specialization of std::exception to indicate that an unknown config option has been encountered. -class UnknownOptionException : public Slic3r::RuntimeError { +// Base for all exceptions thrown by the configuration layer. +class ConfigurationError : public Slic3r::RuntimeError { public: - UnknownOptionException() : - Slic3r::RuntimeError("Unknown option exception") {} - UnknownOptionException(const std::string &opt_key) : - Slic3r::RuntimeError(std::string("Unknown option exception: ") + opt_key) {} + using RuntimeError::RuntimeError; }; -/// Indicate that the ConfigBase derived class does not provide config definition (the method def() returns null). -class NoDefinitionException : public Slic3r::RuntimeError +// Specialization of std::exception to indicate that an unknown config option has been encountered. +class UnknownOptionException : public ConfigurationError { +public: + UnknownOptionException() : + ConfigurationError("Unknown option exception") {} + UnknownOptionException(const std::string &opt_key) : + ConfigurationError(std::string("Unknown option exception: ") + opt_key) {} +}; + +// Indicate that the ConfigBase derived class does not provide config definition (the method def() returns null). +class NoDefinitionException : public ConfigurationError { public: NoDefinitionException() : - Slic3r::RuntimeError("No definition exception") {} + ConfigurationError("No definition exception") {} NoDefinitionException(const std::string &opt_key) : - Slic3r::RuntimeError(std::string("No definition exception: ") + opt_key) {} + ConfigurationError(std::string("No definition exception: ") + opt_key) {} }; -/// Indicate that an unsupported accessor was called on a config option. -class BadOptionTypeException : public Slic3r::RuntimeError +// Indicate that an unsupported accessor was called on a config option. +class BadOptionTypeException : public ConfigurationError { public: - BadOptionTypeException() : Slic3r::RuntimeError("Bad option type exception") {} - BadOptionTypeException(const std::string &message) : Slic3r::RuntimeError(message) {} - BadOptionTypeException(const char* message) : Slic3r::RuntimeError(message) {} + BadOptionTypeException() : ConfigurationError("Bad option type exception") {} + BadOptionTypeException(const std::string &message) : ConfigurationError(message) {} + BadOptionTypeException(const char* message) : ConfigurationError(message) {} +}; + +// Indicate that an option has been deserialized from an invalid value. +class BadOptionValueException : public ConfigurationError +{ +public: + BadOptionValueException() : ConfigurationError("Bad option value exception") {} + BadOptionValueException(const std::string &message) : ConfigurationError(message) {} + BadOptionValueException(const char* message) : ConfigurationError(message) {} }; // Type of a configuration value. @@ -259,7 +274,7 @@ public: void set(const ConfigOption *rhs) override { if (rhs->type() != this->type()) - throw Slic3r::RuntimeError("ConfigOptionSingle: Assigning an incompatible type"); + throw ConfigurationError("ConfigOptionSingle: Assigning an incompatible type"); assert(dynamic_cast*>(rhs)); this->value = static_cast*>(rhs)->value; } @@ -267,7 +282,7 @@ public: bool operator==(const ConfigOption &rhs) const override { if (rhs.type() != this->type()) - throw Slic3r::RuntimeError("ConfigOptionSingle: Comparing incompatible types"); + throw ConfigurationError("ConfigOptionSingle: Comparing incompatible types"); assert(dynamic_cast*>(&rhs)); return this->value == static_cast*>(&rhs)->value; } @@ -334,7 +349,7 @@ public: void set(const ConfigOption *rhs) override { if (rhs->type() != this->type()) - throw Slic3r::RuntimeError("ConfigOptionVector: Assigning an incompatible type"); + throw ConfigurationError("ConfigOptionVector: Assigning an incompatible type"); assert(dynamic_cast*>(rhs)); this->values = static_cast*>(rhs)->values; } @@ -351,12 +366,12 @@ public: if (opt->type() == this->type()) { auto other = static_cast*>(opt); if (other->values.empty()) - throw Slic3r::RuntimeError("ConfigOptionVector::set(): Assigning from an empty vector"); + throw ConfigurationError("ConfigOptionVector::set(): Assigning from an empty vector"); this->values.emplace_back(other->values.front()); } else if (opt->type() == this->scalar_type()) this->values.emplace_back(static_cast*>(opt)->value); else - throw Slic3r::RuntimeError("ConfigOptionVector::set():: Assigning an incompatible type"); + throw ConfigurationError("ConfigOptionVector::set():: Assigning an incompatible type"); } } @@ -375,12 +390,12 @@ public: // Assign the first value of the rhs vector. auto other = static_cast*>(rhs); if (other->values.empty()) - throw Slic3r::RuntimeError("ConfigOptionVector::set_at(): Assigning from an empty vector"); + throw ConfigurationError("ConfigOptionVector::set_at(): Assigning from an empty vector"); this->values[i] = other->get_at(j); } else if (rhs->type() == this->scalar_type()) this->values[i] = static_cast*>(rhs)->value; else - throw Slic3r::RuntimeError("ConfigOptionVector::set_at(): Assigning an incompatible type"); + throw ConfigurationError("ConfigOptionVector::set_at(): Assigning an incompatible type"); } const T& get_at(size_t i) const @@ -405,9 +420,9 @@ public: else if (n > this->values.size()) { if (this->values.empty()) { if (opt_default == nullptr) - throw Slic3r::RuntimeError("ConfigOptionVector::resize(): No default value provided."); + throw ConfigurationError("ConfigOptionVector::resize(): No default value provided."); if (opt_default->type() != this->type()) - throw Slic3r::RuntimeError("ConfigOptionVector::resize(): Extending with an incompatible type."); + throw ConfigurationError("ConfigOptionVector::resize(): Extending with an incompatible type."); this->values.resize(n, static_cast*>(opt_default)->values.front()); } else { // Resize by duplicating the last value. @@ -424,7 +439,7 @@ public: bool operator==(const ConfigOption &rhs) const override { if (rhs.type() != this->type()) - throw Slic3r::RuntimeError("ConfigOptionVector: Comparing incompatible types"); + throw ConfigurationError("ConfigOptionVector: Comparing incompatible types"); assert(dynamic_cast*>(&rhs)); return this->values == static_cast*>(&rhs)->values; } @@ -444,9 +459,9 @@ public: // An option overrides another option if it is not nil and not equal. bool overriden_by(const ConfigOption *rhs) const override { if (this->nullable()) - throw Slic3r::RuntimeError("Cannot override a nullable ConfigOption."); + throw ConfigurationError("Cannot override a nullable ConfigOption."); if (rhs->type() != this->type()) - throw Slic3r::RuntimeError("ConfigOptionVector.overriden_by() applied to different types."); + throw ConfigurationError("ConfigOptionVector.overriden_by() applied to different types."); auto rhs_vec = static_cast*>(rhs); if (! rhs->nullable()) // Overridding a non-nullable object with another non-nullable object. @@ -464,9 +479,9 @@ public: // Apply an override option, possibly a nullable one. bool apply_override(const ConfigOption *rhs) override { if (this->nullable()) - throw Slic3r::RuntimeError("Cannot override a nullable ConfigOption."); + throw ConfigurationError("Cannot override a nullable ConfigOption."); if (rhs->type() != this->type()) - throw Slic3r::RuntimeError("ConfigOptionVector.apply_override() applied to different types."); + throw ConfigurationError("ConfigOptionVector.apply_override() applied to different types."); auto rhs_vec = static_cast*>(rhs); if (! rhs->nullable()) { // Overridding a non-nullable object with another non-nullable object. @@ -557,7 +572,7 @@ public: bool operator< (const ConfigOptionFloatsTempl &rhs) const throw() { return vectors_lower(this->values, rhs.values); } bool operator==(const ConfigOption &rhs) const override { if (rhs.type() != this->type()) - throw Slic3r::RuntimeError("ConfigOptionFloatsTempl: Comparing incompatible types"); + throw ConfigurationError("ConfigOptionFloatsTempl: Comparing incompatible types"); assert(dynamic_cast*>(&rhs)); return vectors_equal(this->values, static_cast*>(&rhs)->values); } @@ -604,7 +619,7 @@ public: if (NULLABLE) this->values.push_back(nil_value()); else - throw Slic3r::RuntimeError("Deserializing nil into a non-nullable object"); + throw ConfigurationError("Deserializing nil into a non-nullable object"); } else { std::istringstream iss(item_str); double value; @@ -629,9 +644,9 @@ protected: if (NULLABLE) ss << "nil"; else - throw Slic3r::RuntimeError("Serializing NaN"); + throw ConfigurationError("Serializing NaN"); } else - throw Slic3r::RuntimeError("Serializing invalid number"); + throw ConfigurationError("Serializing invalid number"); } static bool vectors_equal(const std::vector &v1, const std::vector &v2) { if (NULLABLE) { @@ -763,7 +778,7 @@ public: if (NULLABLE) this->values.push_back(nil_value()); else - throw Slic3r::RuntimeError("Deserializing nil into a non-nullable object"); + throw ConfigurationError("Deserializing nil into a non-nullable object"); } else { std::istringstream iss(item_str); int value; @@ -780,7 +795,7 @@ private: if (NULLABLE) ss << "nil"; else - throw Slic3r::RuntimeError("Serializing NaN"); + throw ConfigurationError("Serializing NaN"); } else ss << v; } @@ -970,7 +985,7 @@ public: bool operator==(const ConfigOption &rhs) const override { if (rhs.type() != this->type()) - throw Slic3r::RuntimeError("ConfigOptionFloatOrPercent: Comparing incompatible types"); + throw ConfigurationError("ConfigOptionFloatOrPercent: Comparing incompatible types"); assert(dynamic_cast(&rhs)); return *this == *static_cast(&rhs); } @@ -986,7 +1001,7 @@ public: void set(const ConfigOption *rhs) override { if (rhs->type() != this->type()) - throw Slic3r::RuntimeError("ConfigOptionFloatOrPercent: Assigning an incompatible type"); + throw ConfigurationError("ConfigOptionFloatOrPercent: Assigning an incompatible type"); assert(dynamic_cast(rhs)); *this = *static_cast(rhs); } @@ -1030,7 +1045,7 @@ public: bool operator==(const ConfigOptionFloatsOrPercentsTempl &rhs) const throw() { return vectors_equal(this->values, rhs.values); } bool operator==(const ConfigOption &rhs) const override { if (rhs.type() != this->type()) - throw Slic3r::RuntimeError("ConfigOptionFloatsOrPercentsTempl: Comparing incompatible types"); + throw ConfigurationError("ConfigOptionFloatsOrPercentsTempl: Comparing incompatible types"); assert(dynamic_cast*>(&rhs)); return vectors_equal(this->values, static_cast*>(&rhs)->values); } @@ -1079,7 +1094,7 @@ public: if (NULLABLE) this->values.push_back(nil_value()); else - throw Slic3r::RuntimeError("Deserializing nil into a non-nullable object"); + throw ConfigurationError("Deserializing nil into a non-nullable object"); } else { bool percent = item_str.find_first_of("%") != std::string::npos; std::istringstream iss(item_str); @@ -1107,9 +1122,9 @@ protected: if (NULLABLE) ss << "nil"; else - throw Slic3r::RuntimeError("Serializing NaN"); + throw ConfigurationError("Serializing NaN"); } else - throw Slic3r::RuntimeError("Serializing invalid number"); + throw ConfigurationError("Serializing invalid number"); } static bool vectors_equal(const std::vector &v1, const std::vector &v2) { if (NULLABLE) { @@ -1398,7 +1413,7 @@ public: if (NULLABLE) this->values.push_back(nil_value()); else - throw Slic3r::RuntimeError("Deserializing nil into a non-nullable object"); + throw ConfigurationError("Deserializing nil into a non-nullable object"); } else this->values.push_back(item_str.compare("1") == 0); } @@ -1411,7 +1426,7 @@ protected: if (NULLABLE) ss << "nil"; else - throw Slic3r::RuntimeError("Serializing NaN"); + throw ConfigurationError("Serializing NaN"); } else ss << (v ? "1" : "0"); } @@ -1449,14 +1464,14 @@ public: bool operator==(const ConfigOption &rhs) const override { if (rhs.type() != this->type()) - throw Slic3r::RuntimeError("ConfigOptionEnum: Comparing incompatible types"); + throw ConfigurationError("ConfigOptionEnum: Comparing incompatible types"); // rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum return this->value == (T)rhs.getInt(); } void set(const ConfigOption *rhs) override { if (rhs->type() != this->type()) - throw Slic3r::RuntimeError("ConfigOptionEnum: Assigning an incompatible type"); + throw ConfigurationError("ConfigOptionEnum: Assigning an incompatible type"); // rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum this->value = (T)rhs->getInt(); } @@ -1519,14 +1534,14 @@ public: bool operator==(const ConfigOption &rhs) const override { if (rhs.type() != this->type()) - throw Slic3r::RuntimeError("ConfigOptionEnumGeneric: Comparing incompatible types"); + throw ConfigurationError("ConfigOptionEnumGeneric: Comparing incompatible types"); // rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum return this->value == rhs.getInt(); } void set(const ConfigOption *rhs) override { if (rhs->type() != this->type()) - throw Slic3r::RuntimeError("ConfigOptionEnumGeneric: Assigning an incompatible type"); + throw ConfigurationError("ConfigOptionEnumGeneric: Assigning an incompatible type"); // rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum this->value = rhs->getInt(); } @@ -1599,7 +1614,7 @@ public: case coInts: { auto opt = new ConfigOptionIntsNullable(); archive(*opt); return opt; } case coPercents: { auto opt = new ConfigOptionPercentsNullable();archive(*opt); return opt; } case coBools: { auto opt = new ConfigOptionBoolsNullable(); archive(*opt); return opt; } - default: throw Slic3r::RuntimeError(std::string("ConfigOptionDef::load_option_from_archive(): Unknown nullable option type for option ") + this->opt_key); + default: throw ConfigurationError(std::string("ConfigOptionDef::load_option_from_archive(): Unknown nullable option type for option ") + this->opt_key); } } else { switch (this->type) { @@ -1618,7 +1633,7 @@ public: case coBool: { auto opt = new ConfigOptionBool(); archive(*opt); return opt; } case coBools: { auto opt = new ConfigOptionBools(); archive(*opt); return opt; } case coEnum: { auto opt = new ConfigOptionEnumGeneric(this->enum_keys_map); archive(*opt); return opt; } - default: throw Slic3r::RuntimeError(std::string("ConfigOptionDef::load_option_from_archive(): Unknown option type for option ") + this->opt_key); + default: throw ConfigurationError(std::string("ConfigOptionDef::load_option_from_archive(): Unknown option type for option ") + this->opt_key); } } } @@ -1630,7 +1645,7 @@ public: case coInts: archive(*static_cast(opt)); break; case coPercents: archive(*static_cast(opt));break; case coBools: archive(*static_cast(opt)); break; - default: throw Slic3r::RuntimeError(std::string("ConfigOptionDef::save_option_to_archive(): Unknown nullable option type for option ") + this->opt_key); + default: throw ConfigurationError(std::string("ConfigOptionDef::save_option_to_archive(): Unknown nullable option type for option ") + this->opt_key); } } else { switch (this->type) { @@ -1649,7 +1664,7 @@ public: case coBool: archive(*static_cast(opt)); break; case coBools: archive(*static_cast(opt)); break; case coEnum: archive(*static_cast(opt)); break; - default: throw Slic3r::RuntimeError(std::string("ConfigOptionDef::save_option_to_archive(): Unknown option type for option ") + this->opt_key); + default: throw ConfigurationError(std::string("ConfigOptionDef::save_option_to_archive(): Unknown option type for option ") + this->opt_key); } } // Make the compiler happy, shut up the warnings. @@ -1772,7 +1787,7 @@ public: return out; } - /// Iterate through all of the CLI options and write them to a stream. + // Iterate through all of the CLI options and write them to a stream. std::ostream& print_cli_help( std::ostream& out, bool show_defaults, std::function filter = [](const ConfigOptionDef &){ return true; }) const; @@ -2121,9 +2136,9 @@ private: template void serialize(Archive &ar) { ar(options); } }; -/// Configuration store with a static definition of configuration values. -/// In Slic3r, the static configuration stores are during the slicing / g-code generation for efficiency reasons, -/// because the configuration values could be accessed directly. +// Configuration store with a static definition of configuration values. +// In Slic3r, the static configuration stores are during the slicing / g-code generation for efficiency reasons, +// because the configuration values could be accessed directly. class StaticConfig : public virtual ConfigBase { public: diff --git a/src/libslic3r/PresetBundle.cpp b/src/libslic3r/PresetBundle.cpp index 53743411c..adb84df67 100644 --- a/src/libslic3r/PresetBundle.cpp +++ b/src/libslic3r/PresetBundle.cpp @@ -723,8 +723,8 @@ ConfigSubstitutions PresetBundle::load_config_file(const std::string &path, Forw } catch (const std::ifstream::failure &err) { throw Slic3r::RuntimeError(std::string("The Config Bundle cannot be loaded: ") + path + "\n\tReason: " + err.what()); } catch (const boost::property_tree::file_parser_error &err) { - throw Slic3r::RuntimeError((boost::format("Failed loading the Config Bundle \"%1%\": %2% at line %3%") - % err.filename() % err.message() % err.line()).str()); + throw Slic3r::RuntimeError(format("Failed loading the Config Bundle \"%1%\": %2% at line %3%", + err.filename(), err.message(), err.line())); } catch (const std::runtime_error &err) { throw Slic3r::RuntimeError(std::string("Failed loading the preset file: ") + path + "\n\tReason: " + err.what()); } @@ -732,23 +732,27 @@ ConfigSubstitutions PresetBundle::load_config_file(const std::string &path, Forw // 2) Continue based on the type of the configuration file. ConfigFileType config_file_type = guess_config_file_type(tree); ConfigSubstitutions config_substitutions; - switch (config_file_type) { - case CONFIG_FILE_TYPE_UNKNOWN: - throw Slic3r::RuntimeError(std::string("Unknown configuration file type: ") + path); - case CONFIG_FILE_TYPE_APP_CONFIG: - throw Slic3r::RuntimeError(std::string("Invalid configuration file: ") + path + ". This is an application config file."); - case CONFIG_FILE_TYPE_CONFIG: - { - // Initialize a config from full defaults. - DynamicPrintConfig config; - config.apply(FullPrintConfig::defaults()); - config_substitutions = config.load(tree, compatibility_rule); - Preset::normalize(config); - load_config_file_config(path, true, std::move(config)); - return config_substitutions; - } - case CONFIG_FILE_TYPE_CONFIG_BUNDLE: - return load_config_file_config_bundle(path, tree, compatibility_rule); + try { + switch (config_file_type) { + case CONFIG_FILE_TYPE_UNKNOWN: + throw Slic3r::RuntimeError(std::string("Unknown configuration file type: ") + path); + case CONFIG_FILE_TYPE_APP_CONFIG: + throw Slic3r::RuntimeError(std::string("Invalid configuration file: ") + path + ". This is an application config file."); + case CONFIG_FILE_TYPE_CONFIG: + { + // Initialize a config from full defaults. + DynamicPrintConfig config; + config.apply(FullPrintConfig::defaults()); + config_substitutions = config.load(tree, compatibility_rule); + Preset::normalize(config); + load_config_file_config(path, true, std::move(config)); + return config_substitutions; + } + case CONFIG_FILE_TYPE_CONFIG_BUNDLE: + return load_config_file_config_bundle(path, tree, compatibility_rule); + } + } catch (const ConfigurationError &e) { + throw Slic3r::RuntimeError(format("Invalid configuration file %1%: %2%", path, e.what())); } // This shall never happen. Suppres compiler warnings. @@ -1244,7 +1248,7 @@ std::pair PresetBundle::load_configbundle( active_sla_material = kvp.second.data(); } else if (kvp.first == "printer") { active_printer = kvp.second.data(); - }else if (kvp.first == "physical_printer") { + } else if (kvp.first == "physical_printer") { active_physical_printer = kvp.second.data(); } } @@ -1281,32 +1285,36 @@ std::pair PresetBundle::load_configbundle( DynamicPrintConfig config; std::string alias_name; std::vector renamed_from; - auto parse_config_section = [§ion, &alias_name, &renamed_from, &substitution_context, &path](DynamicPrintConfig &config) { - substitution_context.substitutions.clear(); - for (auto &kvp : section.second) { - if (kvp.first == "alias") - alias_name = kvp.second.data(); - else if (kvp.first == "renamed_from") { - if (! unescape_strings_cstyle(kvp.second.data(), renamed_from)) { - BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << path << "\": The preset \"" << - section.first << "\" contains invalid \"renamed_from\" key, which is being ignored."; - } - } - // Throws on parsing error. For system presets, no substituion is being done, but an exception is thrown. - config.set_deserialize(kvp.first, kvp.second.data(), substitution_context); + try { + auto parse_config_section = [§ion, &alias_name, &renamed_from, &substitution_context, &path](DynamicPrintConfig &config) { + substitution_context.substitutions.clear(); + for (auto &kvp : section.second) { + if (kvp.first == "alias") + alias_name = kvp.second.data(); + else if (kvp.first == "renamed_from") { + if (! unescape_strings_cstyle(kvp.second.data(), renamed_from)) { + BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << path << "\": The preset \"" << + section.first << "\" contains invalid \"renamed_from\" key, which is being ignored."; + } + } + // Throws on parsing error. For system presets, no substituion is being done, but an exception is thrown. + config.set_deserialize(kvp.first, kvp.second.data(), substitution_context); + } + }; + if (presets == &this->printers) { + // Select the default config based on the printer_technology field extracted from kvp. + DynamicPrintConfig config_src; + parse_config_section(config_src); + default_config = &presets->default_preset_for(config_src).config; + config = *default_config; + config.apply(config_src); + } else { + default_config = &presets->default_preset().config; + config = *default_config; + parse_config_section(config); } - }; - if (presets == &this->printers) { - // Select the default config based on the printer_technology field extracted from kvp. - DynamicPrintConfig config_src; - parse_config_section(config_src); - default_config = &presets->default_preset_for(config_src).config; - config = *default_config; - config.apply(config_src); - } else { - default_config = &presets->default_preset().config; - config = *default_config; - parse_config_section(config); + } catch (const ConfigurationError &e) { + throw ConfigurationError(format("Invalid configuration bundle \"%1%\", section [%2%]: ", path, section.first) + e.what()); } Preset::normalize(config); // Report configuration fields, which are misplaced into a wrong group. @@ -1414,8 +1422,12 @@ std::pair PresetBundle::load_configbundle( DynamicPrintConfig config = default_config; substitution_context.substitutions.clear(); - for (auto& kvp : section.second) - config.set_deserialize(kvp.first, kvp.second.data(), substitution_context); + try { + for (auto& kvp : section.second) + config.set_deserialize(kvp.first, kvp.second.data(), substitution_context); + } catch (const ConfigurationError &e) { + throw ConfigurationError(format("Invalid configuration bundle \"%1%\", section [%2%]: ", path, section.first) + e.what()); + } // Report configuration fields, which are misplaced into a wrong group. std::string incorrect_keys = Preset::remove_invalid_keys(config, default_config); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index c57fffa6e..e74a271b4 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2337,6 +2337,10 @@ std::vector Plater::priv::load_files(const std::vector& input_ if (obj->name.empty()) obj->name = fs::path(obj->input_file).filename().string(); } + } catch (const ConfigurationError &e) { + std::string message = GUI::format(_L("Failed loading file \"%1%\" due to an invalid configuration."), filename.string()) + "\n\n" + e.what(); + GUI::show_error(q, message); + continue; } catch (const std::exception &e) { GUI::show_error(q, e.what()); continue; diff --git a/tests/libslic3r/test_config.cpp b/tests/libslic3r/test_config.cpp index 7fbf31b11..97729ac8e 100644 --- a/tests/libslic3r/test_config.cpp +++ b/tests/libslic3r/test_config.cpp @@ -92,7 +92,7 @@ SCENARIO("Config accessor functions perform as expected.", "[Config]") { } WHEN("A numeric option is set to a non-numeric value.") { THEN("A BadOptionTypeException exception is thown.") { - REQUIRE_THROWS_AS(config.set_deserialize_strict("perimeter_speed", "zzzz"), BadOptionTypeException); + REQUIRE_THROWS_AS(config.set_deserialize_strict("perimeter_speed", "zzzz"), BadOptionValueException); } THEN("The value does not change.") { REQUIRE(config.opt("perimeter_speed")->getFloat() == 60.0);