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.
This commit is contained in:
Vojtech Bubnik 2021-06-29 15:41:47 +02:00
parent 3cf73f6a06
commit 745aa3536d
5 changed files with 175 additions and 136 deletions

View File

@ -243,7 +243,7 @@ ConfigOption* ConfigOptionDef::create_empty_option() const
case coPercents: return new ConfigOptionPercentsNullable(); case coPercents: return new ConfigOptionPercentsNullable();
case coFloatsOrPercents: return new ConfigOptionFloatsOrPercentsNullable(); case coFloatsOrPercents: return new ConfigOptionFloatsOrPercentsNullable();
case coBools: return new ConfigOptionBoolsNullable(); 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 { } else {
switch (this->type) { switch (this->type) {
@ -264,7 +264,7 @@ ConfigOption* ConfigOptionDef::create_empty_option() const
case coBool: return new ConfigOptionBool(); case coBool: return new ConfigOptionBool();
case coBools: return new ConfigOptionBools(); case coBools: return new ConfigOptionBools();
case coEnum: return new ConfigOptionEnumGeneric(this->enum_keys_map); 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);
} }
} }
} }
@ -492,7 +492,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) 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)) 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<SetDeserializeItem> items, ConfigSubstitutionContext& substitutions_ctxt) void ConfigBase::set_deserialize(std::initializer_list<SetDeserializeItem> items, ConfigSubstitutionContext& substitutions_ctxt)
@ -587,7 +587,7 @@ double ConfigBase::get_abs_value(const t_config_option_key &opt_key) const
return opt_def->ratio_over.empty() ? 0. : return opt_def->ratio_over.empty() ? 0. :
static_cast<const ConfigOptionFloatOrPercent*>(raw_opt)->get_abs_value(this->get_abs_value(opt_def->ratio_over)); static_cast<const ConfigOptionFloatOrPercent*>(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. // Return an absolute value of a possibly relative config variable.
@ -598,7 +598,7 @@ double ConfigBase::get_abs_value(const t_config_option_key &opt_key, double rati
const ConfigOption *raw_opt = this->option(opt_key); const ConfigOption *raw_opt = this->option(opt_key);
assert(raw_opt != nullptr); assert(raw_opt != nullptr);
if (raw_opt->type() != coFloatOrPercent) 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. // Compute absolute value.
return static_cast<const ConfigOptionFloatOrPercent*>(raw_opt)->get_abs_value(ratio_over); return static_cast<const ConfigOptionFloatOrPercent*>(raw_opt)->get_abs_value(ratio_over);
} }
@ -630,10 +630,14 @@ ConfigSubstitutions ConfigBase::load(const std::string &file, ForwardCompatibili
ConfigSubstitutions ConfigBase::load_from_ini(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule) ConfigSubstitutions ConfigBase::load_from_ini(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule)
{ {
boost::property_tree::ptree tree; try {
boost::nowide::ifstream ifs(file); boost::property_tree::ptree tree;
boost::property_tree::read_ini(ifs, tree); boost::nowide::ifstream ifs(file);
return this->load(tree, compatibility_rule); 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(const boost::property_tree::ptree &tree, ForwardCompatibilitySubstitutionRule compatibility_rule) ConfigSubstitutions ConfigBase::load(const boost::property_tree::ptree &tree, ForwardCompatibilitySubstitutionRule compatibility_rule)
@ -653,30 +657,34 @@ ConfigSubstitutions ConfigBase::load(const boost::property_tree::ptree &tree, Fo
// Load the config keys from the tail of a G-code file. // 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) ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule)
{ {
// Read a 64k block from the end of the G-code. try {
boost::nowide::ifstream ifs(file); // 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 "; const char slic3r_gcode_header[] = "; generated by Slic3r ";
std::string firstline; const char prusaslicer_gcode_header[] = "; generated by PrusaSlicer ";
std::getline(ifs, firstline); std::string firstline;
if (strncmp(slic3r_gcode_header, firstline.c_str(), strlen(slic3r_gcode_header)) != 0 && std::getline(ifs, firstline);
strncmp(prusaslicer_gcode_header, firstline.c_str(), strlen(prusaslicer_gcode_header)) != 0) if (strncmp(slic3r_gcode_header, firstline.c_str(), strlen(slic3r_gcode_header)) != 0 &&
throw Slic3r::RuntimeError("Not a PrusaSlicer / Slic3r PE generated g-code."); 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(); ifs.seekg(0, ifs.end);
auto data_length = std::min<std::fstream::pos_type>(65535, file_length); auto file_length = ifs.tellg();
ifs.seekg(file_length - data_length, ifs.beg); auto data_length = std::min<std::fstream::pos_type>(65535, file_length);
std::vector<char> data(size_t(data_length) + 1, 0); ifs.seekg(file_length - data_length, ifs.beg);
ifs.read(data.data(), data_length); std::vector<char> data(size_t(data_length) + 1, 0);
ifs.close(); ifs.read(data.data(), data_length);
ifs.close();
ConfigSubstitutionContext substitutions_ctxt(compatibility_rule); ConfigSubstitutionContext substitutions_ctxt(compatibility_rule);
size_t key_value_pairs = load_from_gcode_string(data.data(), substitutions_ctxt); size_t key_value_pairs = load_from_gcode_string(data.data(), substitutions_ctxt);
if (key_value_pairs < 80) if (key_value_pairs < 80)
throw Slic3r::RuntimeError(format("Suspiciously low number of configuration values extracted from %1%: %2%", file, key_value_pairs)); throw ConfigurationError(format("Suspiciously low number of configuration values extracted from %1%: %2%", file, key_value_pairs));
return std::move(substitutions_ctxt.substitutions); 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. // Load the config keys from the given string.
@ -805,7 +813,7 @@ ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool cre
throw NoDefinitionException(opt_key); throw NoDefinitionException(opt_key);
const ConfigOptionDef *optdef = def->get(opt_key); const ConfigOptionDef *optdef = def->get(opt_key);
if (optdef == nullptr) 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(). // Let the parent decide what to do if the opt_key is not defined by this->def().
return nullptr; return nullptr;
ConfigOption *opt = optdef->create_default_option(); ConfigOption *opt = optdef->create_default_option();

View File

@ -37,32 +37,47 @@ extern bool unescape_strings_cstyle(const std::string &str, std::vector<
extern std::string escape_ampersand(const std::string& str); extern std::string escape_ampersand(const std::string& str);
/// Specialization of std::exception to indicate that an unknown config option has been encountered. // Base for all exceptions thrown by the configuration layer.
class UnknownOptionException : public Slic3r::RuntimeError { class ConfigurationError : public Slic3r::RuntimeError {
public: public:
UnknownOptionException() : using RuntimeError::RuntimeError;
Slic3r::RuntimeError("Unknown option exception") {}
UnknownOptionException(const std::string &opt_key) :
Slic3r::RuntimeError(std::string("Unknown option exception: ") + opt_key) {}
}; };
/// Indicate that the ConfigBase derived class does not provide config definition (the method def() returns null). // Specialization of std::exception to indicate that an unknown config option has been encountered.
class NoDefinitionException : public Slic3r::RuntimeError 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: public:
NoDefinitionException() : NoDefinitionException() :
Slic3r::RuntimeError("No definition exception") {} ConfigurationError("No definition exception") {}
NoDefinitionException(const std::string &opt_key) : 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. // Indicate that an unsupported accessor was called on a config option.
class BadOptionTypeException : public Slic3r::RuntimeError class BadOptionTypeException : public ConfigurationError
{ {
public: public:
BadOptionTypeException() : Slic3r::RuntimeError("Bad option type exception") {} BadOptionTypeException() : ConfigurationError("Bad option type exception") {}
BadOptionTypeException(const std::string &message) : Slic3r::RuntimeError(message) {} BadOptionTypeException(const std::string &message) : ConfigurationError(message) {}
BadOptionTypeException(const char* message) : Slic3r::RuntimeError(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. // Type of a configuration value.
@ -215,7 +230,7 @@ public:
void set(const ConfigOption *rhs) override void set(const ConfigOption *rhs) override
{ {
if (rhs->type() != this->type()) if (rhs->type() != this->type())
throw Slic3r::RuntimeError("ConfigOptionSingle: Assigning an incompatible type"); throw ConfigurationError("ConfigOptionSingle: Assigning an incompatible type");
assert(dynamic_cast<const ConfigOptionSingle<T>*>(rhs)); assert(dynamic_cast<const ConfigOptionSingle<T>*>(rhs));
this->value = static_cast<const ConfigOptionSingle<T>*>(rhs)->value; this->value = static_cast<const ConfigOptionSingle<T>*>(rhs)->value;
} }
@ -223,7 +238,7 @@ public:
bool operator==(const ConfigOption &rhs) const override bool operator==(const ConfigOption &rhs) const override
{ {
if (rhs.type() != this->type()) if (rhs.type() != this->type())
throw Slic3r::RuntimeError("ConfigOptionSingle: Comparing incompatible types"); throw ConfigurationError("ConfigOptionSingle: Comparing incompatible types");
assert(dynamic_cast<const ConfigOptionSingle<T>*>(&rhs)); assert(dynamic_cast<const ConfigOptionSingle<T>*>(&rhs));
return this->value == static_cast<const ConfigOptionSingle<T>*>(&rhs)->value; return this->value == static_cast<const ConfigOptionSingle<T>*>(&rhs)->value;
} }
@ -287,7 +302,7 @@ public:
void set(const ConfigOption *rhs) override void set(const ConfigOption *rhs) override
{ {
if (rhs->type() != this->type()) if (rhs->type() != this->type())
throw Slic3r::RuntimeError("ConfigOptionVector: Assigning an incompatible type"); throw ConfigurationError("ConfigOptionVector: Assigning an incompatible type");
assert(dynamic_cast<const ConfigOptionVector<T>*>(rhs)); assert(dynamic_cast<const ConfigOptionVector<T>*>(rhs));
this->values = static_cast<const ConfigOptionVector<T>*>(rhs)->values; this->values = static_cast<const ConfigOptionVector<T>*>(rhs)->values;
} }
@ -304,12 +319,12 @@ public:
if (opt->type() == this->type()) { if (opt->type() == this->type()) {
auto other = static_cast<const ConfigOptionVector<T>*>(opt); auto other = static_cast<const ConfigOptionVector<T>*>(opt);
if (other->values.empty()) 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()); this->values.emplace_back(other->values.front());
} else if (opt->type() == this->scalar_type()) } else if (opt->type() == this->scalar_type())
this->values.emplace_back(static_cast<const ConfigOptionSingle<T>*>(opt)->value); this->values.emplace_back(static_cast<const ConfigOptionSingle<T>*>(opt)->value);
else else
throw Slic3r::RuntimeError("ConfigOptionVector::set():: Assigning an incompatible type"); throw ConfigurationError("ConfigOptionVector::set():: Assigning an incompatible type");
} }
} }
@ -328,12 +343,12 @@ public:
// Assign the first value of the rhs vector. // Assign the first value of the rhs vector.
auto other = static_cast<const ConfigOptionVector<T>*>(rhs); auto other = static_cast<const ConfigOptionVector<T>*>(rhs);
if (other->values.empty()) 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); this->values[i] = other->get_at(j);
} else if (rhs->type() == this->scalar_type()) } else if (rhs->type() == this->scalar_type())
this->values[i] = static_cast<const ConfigOptionSingle<T>*>(rhs)->value; this->values[i] = static_cast<const ConfigOptionSingle<T>*>(rhs)->value;
else 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 const T& get_at(size_t i) const
@ -358,9 +373,9 @@ public:
else if (n > this->values.size()) { else if (n > this->values.size()) {
if (this->values.empty()) { if (this->values.empty()) {
if (opt_default == nullptr) 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()) 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<const ConfigOptionVector<T>*>(opt_default)->values.front()); this->values.resize(n, static_cast<const ConfigOptionVector<T>*>(opt_default)->values.front());
} else { } else {
// Resize by duplicating the last value. // Resize by duplicating the last value.
@ -377,7 +392,7 @@ public:
bool operator==(const ConfigOption &rhs) const override bool operator==(const ConfigOption &rhs) const override
{ {
if (rhs.type() != this->type()) if (rhs.type() != this->type())
throw Slic3r::RuntimeError("ConfigOptionVector: Comparing incompatible types"); throw ConfigurationError("ConfigOptionVector: Comparing incompatible types");
assert(dynamic_cast<const ConfigOptionVector<T>*>(&rhs)); assert(dynamic_cast<const ConfigOptionVector<T>*>(&rhs));
return this->values == static_cast<const ConfigOptionVector<T>*>(&rhs)->values; return this->values == static_cast<const ConfigOptionVector<T>*>(&rhs)->values;
} }
@ -389,9 +404,9 @@ public:
// An option overrides another option if it is not nil and not equal. // An option overrides another option if it is not nil and not equal.
bool overriden_by(const ConfigOption *rhs) const override { bool overriden_by(const ConfigOption *rhs) const override {
if (this->nullable()) if (this->nullable())
throw Slic3r::RuntimeError("Cannot override a nullable ConfigOption."); throw ConfigurationError("Cannot override a nullable ConfigOption.");
if (rhs->type() != this->type()) 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<const ConfigOptionVector<T>*>(rhs); auto rhs_vec = static_cast<const ConfigOptionVector<T>*>(rhs);
if (! rhs->nullable()) if (! rhs->nullable())
// Overridding a non-nullable object with another non-nullable object. // Overridding a non-nullable object with another non-nullable object.
@ -409,9 +424,9 @@ public:
// Apply an override option, possibly a nullable one. // Apply an override option, possibly a nullable one.
bool apply_override(const ConfigOption *rhs) override { bool apply_override(const ConfigOption *rhs) override {
if (this->nullable()) if (this->nullable())
throw Slic3r::RuntimeError("Cannot override a nullable ConfigOption."); throw ConfigurationError("Cannot override a nullable ConfigOption.");
if (rhs->type() != this->type()) 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<const ConfigOptionVector<T>*>(rhs); auto rhs_vec = static_cast<const ConfigOptionVector<T>*>(rhs);
if (! rhs->nullable()) { if (! rhs->nullable()) {
// Overridding a non-nullable object with another non-nullable object. // Overridding a non-nullable object with another non-nullable object.
@ -500,7 +515,7 @@ public:
bool operator==(const ConfigOptionFloatsTempl &rhs) const { return vectors_equal(this->values, rhs.values); } bool operator==(const ConfigOptionFloatsTempl &rhs) const { return vectors_equal(this->values, rhs.values); }
bool operator==(const ConfigOption &rhs) const override { bool operator==(const ConfigOption &rhs) const override {
if (rhs.type() != this->type()) if (rhs.type() != this->type())
throw Slic3r::RuntimeError("ConfigOptionFloatsTempl: Comparing incompatible types"); throw ConfigurationError("ConfigOptionFloatsTempl: Comparing incompatible types");
assert(dynamic_cast<const ConfigOptionVector<double>*>(&rhs)); assert(dynamic_cast<const ConfigOptionVector<double>*>(&rhs));
return vectors_equal(this->values, static_cast<const ConfigOptionVector<double>*>(&rhs)->values); return vectors_equal(this->values, static_cast<const ConfigOptionVector<double>*>(&rhs)->values);
} }
@ -547,7 +562,7 @@ public:
if (NULLABLE) if (NULLABLE)
this->values.push_back(nil_value()); this->values.push_back(nil_value());
else else
throw Slic3r::RuntimeError("Deserializing nil into a non-nullable object"); throw ConfigurationError("Deserializing nil into a non-nullable object");
} else { } else {
std::istringstream iss(item_str); std::istringstream iss(item_str);
double value; double value;
@ -572,9 +587,9 @@ protected:
if (NULLABLE) if (NULLABLE)
ss << "nil"; ss << "nil";
else else
throw Slic3r::RuntimeError("Serializing NaN"); throw ConfigurationError("Serializing NaN");
} else } else
throw Slic3r::RuntimeError("Serializing invalid number"); throw ConfigurationError("Serializing invalid number");
} }
static bool vectors_equal(const std::vector<double> &v1, const std::vector<double> &v2) { static bool vectors_equal(const std::vector<double> &v1, const std::vector<double> &v2) {
if (NULLABLE) { if (NULLABLE) {
@ -693,7 +708,7 @@ public:
if (NULLABLE) if (NULLABLE)
this->values.push_back(nil_value()); this->values.push_back(nil_value());
else else
throw Slic3r::RuntimeError("Deserializing nil into a non-nullable object"); throw ConfigurationError("Deserializing nil into a non-nullable object");
} else { } else {
std::istringstream iss(item_str); std::istringstream iss(item_str);
int value; int value;
@ -710,7 +725,7 @@ private:
if (NULLABLE) if (NULLABLE)
ss << "nil"; ss << "nil";
else else
throw Slic3r::RuntimeError("Serializing NaN"); throw ConfigurationError("Serializing NaN");
} else } else
ss << v; ss << v;
} }
@ -895,7 +910,7 @@ public:
bool operator==(const ConfigOption &rhs) const override bool operator==(const ConfigOption &rhs) const override
{ {
if (rhs.type() != this->type()) if (rhs.type() != this->type())
throw Slic3r::RuntimeError("ConfigOptionFloatOrPercent: Comparing incompatible types"); throw ConfigurationError("ConfigOptionFloatOrPercent: Comparing incompatible types");
assert(dynamic_cast<const ConfigOptionFloatOrPercent*>(&rhs)); assert(dynamic_cast<const ConfigOptionFloatOrPercent*>(&rhs));
return *this == *static_cast<const ConfigOptionFloatOrPercent*>(&rhs); return *this == *static_cast<const ConfigOptionFloatOrPercent*>(&rhs);
} }
@ -906,7 +921,7 @@ public:
void set(const ConfigOption *rhs) override { void set(const ConfigOption *rhs) override {
if (rhs->type() != this->type()) if (rhs->type() != this->type())
throw Slic3r::RuntimeError("ConfigOptionFloatOrPercent: Assigning an incompatible type"); throw ConfigurationError("ConfigOptionFloatOrPercent: Assigning an incompatible type");
assert(dynamic_cast<const ConfigOptionFloatOrPercent*>(rhs)); assert(dynamic_cast<const ConfigOptionFloatOrPercent*>(rhs));
*this = *static_cast<const ConfigOptionFloatOrPercent*>(rhs); *this = *static_cast<const ConfigOptionFloatOrPercent*>(rhs);
} }
@ -971,7 +986,7 @@ public:
bool operator==(const ConfigOptionFloatsOrPercentsTempl &rhs) const { return vectors_equal(this->values, rhs.values); } bool operator==(const ConfigOptionFloatsOrPercentsTempl &rhs) const { return vectors_equal(this->values, rhs.values); }
bool operator==(const ConfigOption &rhs) const override { bool operator==(const ConfigOption &rhs) const override {
if (rhs.type() != this->type()) if (rhs.type() != this->type())
throw Slic3r::RuntimeError("ConfigOptionFloatsOrPercentsTempl: Comparing incompatible types"); throw ConfigurationError("ConfigOptionFloatsOrPercentsTempl: Comparing incompatible types");
assert(dynamic_cast<const ConfigOptionVector<FloatOrPercent>*>(&rhs)); assert(dynamic_cast<const ConfigOptionVector<FloatOrPercent>*>(&rhs));
return vectors_equal(this->values, static_cast<const ConfigOptionVector<FloatOrPercent>*>(&rhs)->values); return vectors_equal(this->values, static_cast<const ConfigOptionVector<FloatOrPercent>*>(&rhs)->values);
} }
@ -1018,7 +1033,7 @@ public:
if (NULLABLE) if (NULLABLE)
this->values.push_back(nil_value()); this->values.push_back(nil_value());
else else
throw Slic3r::RuntimeError("Deserializing nil into a non-nullable object"); throw ConfigurationError("Deserializing nil into a non-nullable object");
} else { } else {
bool percent = item_str.find_first_of("%") != std::string::npos; bool percent = item_str.find_first_of("%") != std::string::npos;
std::istringstream iss(item_str); std::istringstream iss(item_str);
@ -1046,9 +1061,9 @@ protected:
if (NULLABLE) if (NULLABLE)
ss << "nil"; ss << "nil";
else else
throw Slic3r::RuntimeError("Serializing NaN"); throw ConfigurationError("Serializing NaN");
} else } else
throw Slic3r::RuntimeError("Serializing invalid number"); throw ConfigurationError("Serializing invalid number");
} }
static bool vectors_equal(const std::vector<FloatOrPercent> &v1, const std::vector<FloatOrPercent> &v2) { static bool vectors_equal(const std::vector<FloatOrPercent> &v1, const std::vector<FloatOrPercent> &v2) {
if (NULLABLE) { if (NULLABLE) {
@ -1318,7 +1333,7 @@ public:
if (NULLABLE) if (NULLABLE)
this->values.push_back(nil_value()); this->values.push_back(nil_value());
else else
throw Slic3r::RuntimeError("Deserializing nil into a non-nullable object"); throw ConfigurationError("Deserializing nil into a non-nullable object");
} else } else
this->values.push_back(item_str.compare("1") == 0); this->values.push_back(item_str.compare("1") == 0);
} }
@ -1331,7 +1346,7 @@ protected:
if (NULLABLE) if (NULLABLE)
ss << "nil"; ss << "nil";
else else
throw Slic3r::RuntimeError("Serializing NaN"); throw ConfigurationError("Serializing NaN");
} else } else
ss << (v ? "1" : "0"); ss << (v ? "1" : "0");
} }
@ -1367,14 +1382,14 @@ public:
bool operator==(const ConfigOption &rhs) const override bool operator==(const ConfigOption &rhs) const override
{ {
if (rhs.type() != this->type()) if (rhs.type() != this->type())
throw Slic3r::RuntimeError("ConfigOptionEnum<T>: Comparing incompatible types"); throw ConfigurationError("ConfigOptionEnum<T>: Comparing incompatible types");
// rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum<T> // rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum<T>
return this->value == (T)rhs.getInt(); return this->value == (T)rhs.getInt();
} }
void set(const ConfigOption *rhs) override { void set(const ConfigOption *rhs) override {
if (rhs->type() != this->type()) if (rhs->type() != this->type())
throw Slic3r::RuntimeError("ConfigOptionEnum<T>: Assigning an incompatible type"); throw ConfigurationError("ConfigOptionEnum<T>: Assigning an incompatible type");
// rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum<T> // rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum<T>
this->value = (T)rhs->getInt(); this->value = (T)rhs->getInt();
} }
@ -1451,14 +1466,14 @@ public:
bool operator==(const ConfigOption &rhs) const override bool operator==(const ConfigOption &rhs) const override
{ {
if (rhs.type() != this->type()) 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<T> // rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum<T>
return this->value == rhs.getInt(); return this->value == rhs.getInt();
} }
void set(const ConfigOption *rhs) override { void set(const ConfigOption *rhs) override {
if (rhs->type() != this->type()) 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<T> // rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum<T>
this->value = rhs->getInt(); this->value = rhs->getInt();
} }
@ -1513,7 +1528,7 @@ public:
case coInts: { auto opt = new ConfigOptionIntsNullable(); archive(*opt); return opt; } case coInts: { auto opt = new ConfigOptionIntsNullable(); archive(*opt); return opt; }
case coPercents: { auto opt = new ConfigOptionPercentsNullable();archive(*opt); return opt; } case coPercents: { auto opt = new ConfigOptionPercentsNullable();archive(*opt); return opt; }
case coBools: { auto opt = new ConfigOptionBoolsNullable(); 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 { } else {
switch (this->type) { switch (this->type) {
@ -1532,7 +1547,7 @@ public:
case coBool: { auto opt = new ConfigOptionBool(); archive(*opt); return opt; } case coBool: { auto opt = new ConfigOptionBool(); archive(*opt); return opt; }
case coBools: { auto opt = new ConfigOptionBools(); 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; } 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);
} }
} }
} }
@ -1544,7 +1559,7 @@ public:
case coInts: archive(*static_cast<const ConfigOptionIntsNullable*>(opt)); break; case coInts: archive(*static_cast<const ConfigOptionIntsNullable*>(opt)); break;
case coPercents: archive(*static_cast<const ConfigOptionPercentsNullable*>(opt));break; case coPercents: archive(*static_cast<const ConfigOptionPercentsNullable*>(opt));break;
case coBools: archive(*static_cast<const ConfigOptionBoolsNullable*>(opt)); break; case coBools: archive(*static_cast<const ConfigOptionBoolsNullable*>(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 { } else {
switch (this->type) { switch (this->type) {
@ -1563,7 +1578,7 @@ public:
case coBool: archive(*static_cast<const ConfigOptionBool*>(opt)); break; case coBool: archive(*static_cast<const ConfigOptionBool*>(opt)); break;
case coBools: archive(*static_cast<const ConfigOptionBools*>(opt)); break; case coBools: archive(*static_cast<const ConfigOptionBools*>(opt)); break;
case coEnum: archive(*static_cast<const ConfigOptionEnumGeneric*>(opt)); break; case coEnum: archive(*static_cast<const ConfigOptionEnumGeneric*>(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. // Make the compiler happy, shut up the warnings.
@ -1686,7 +1701,7 @@ public:
return out; 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& print_cli_help(
std::ostream& out, bool show_defaults, std::ostream& out, bool show_defaults,
std::function<bool(const ConfigOptionDef &)> filter = [](const ConfigOptionDef &){ return true; }) const; std::function<bool(const ConfigOptionDef &)> filter = [](const ConfigOptionDef &){ return true; }) const;
@ -2031,9 +2046,9 @@ private:
template<class Archive> void serialize(Archive &ar) { ar(options); } template<class Archive> void serialize(Archive &ar) { ar(options); }
}; };
/// Configuration store with a static definition of configuration values. // 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, // In Slic3r, the static configuration stores are during the slicing / g-code generation for efficiency reasons,
/// because the configuration values could be accessed directly. // because the configuration values could be accessed directly.
class StaticConfig : public virtual ConfigBase class StaticConfig : public virtual ConfigBase
{ {
public: public:

View File

@ -697,8 +697,8 @@ ConfigSubstitutions PresetBundle::load_config_file(const std::string &path, Forw
} catch (const std::ifstream::failure &err) { } catch (const std::ifstream::failure &err) {
throw Slic3r::RuntimeError(std::string("The Config Bundle cannot be loaded: ") + path + "\n\tReason: " + err.what()); 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) { } catch (const boost::property_tree::file_parser_error &err) {
throw Slic3r::RuntimeError((boost::format("Failed loading the Config Bundle \"%1%\": %2% at line %3%") throw Slic3r::RuntimeError(format("Failed loading the Config Bundle \"%1%\": %2% at line %3%",
% err.filename() % err.message() % err.line()).str()); err.filename(), err.message(), err.line()));
} catch (const std::runtime_error &err) { } catch (const std::runtime_error &err) {
throw Slic3r::RuntimeError(std::string("Failed loading the preset file: ") + path + "\n\tReason: " + err.what()); throw Slic3r::RuntimeError(std::string("Failed loading the preset file: ") + path + "\n\tReason: " + err.what());
} }
@ -706,23 +706,27 @@ ConfigSubstitutions PresetBundle::load_config_file(const std::string &path, Forw
// 2) Continue based on the type of the configuration file. // 2) Continue based on the type of the configuration file.
ConfigFileType config_file_type = guess_config_file_type(tree); ConfigFileType config_file_type = guess_config_file_type(tree);
ConfigSubstitutions config_substitutions; ConfigSubstitutions config_substitutions;
switch (config_file_type) { try {
case CONFIG_FILE_TYPE_UNKNOWN: switch (config_file_type) {
throw Slic3r::RuntimeError(std::string("Unknown configuration file type: ") + path); case CONFIG_FILE_TYPE_UNKNOWN:
case CONFIG_FILE_TYPE_APP_CONFIG: throw Slic3r::RuntimeError(std::string("Unknown configuration file type: ") + path);
throw Slic3r::RuntimeError(std::string("Invalid configuration file: ") + path + ". This is an application config file."); case CONFIG_FILE_TYPE_APP_CONFIG:
case CONFIG_FILE_TYPE_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; // Initialize a config from full defaults.
config.apply(FullPrintConfig::defaults()); DynamicPrintConfig config;
config_substitutions = config.load(tree, compatibility_rule); config.apply(FullPrintConfig::defaults());
Preset::normalize(config); config_substitutions = config.load(tree, compatibility_rule);
load_config_file_config(path, true, std::move(config)); Preset::normalize(config);
return config_substitutions; 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); 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. // This shall never happen. Suppres compiler warnings.
@ -1219,7 +1223,7 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_configbundle(
active_sla_material = kvp.second.data(); active_sla_material = kvp.second.data();
} else if (kvp.first == "printer") { } else if (kvp.first == "printer") {
active_printer = kvp.second.data(); active_printer = kvp.second.data();
}else if (kvp.first == "physical_printer") { } else if (kvp.first == "physical_printer") {
active_physical_printer = kvp.second.data(); active_physical_printer = kvp.second.data();
} }
} }
@ -1256,32 +1260,36 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_configbundle(
DynamicPrintConfig config; DynamicPrintConfig config;
std::string alias_name; std::string alias_name;
std::vector<std::string> renamed_from; std::vector<std::string> renamed_from;
auto parse_config_section = [&section, &alias_name, &renamed_from, &substitution_context, &path](DynamicPrintConfig &config) { try {
substitution_context.substitutions.clear(); auto parse_config_section = [&section, &alias_name, &renamed_from, &substitution_context, &path](DynamicPrintConfig &config) {
for (auto &kvp : section.second) { substitution_context.substitutions.clear();
if (kvp.first == "alias") for (auto &kvp : section.second) {
alias_name = kvp.second.data(); if (kvp.first == "alias")
else if (kvp.first == "renamed_from") { alias_name = kvp.second.data();
if (! unescape_strings_cstyle(kvp.second.data(), renamed_from)) { else if (kvp.first == "renamed_from") {
BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << path << "\": The preset \"" << if (! unescape_strings_cstyle(kvp.second.data(), renamed_from)) {
section.first << "\" contains invalid \"renamed_from\" key, which is being ignored."; 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); // 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);
} }
}; } catch (const ConfigurationError &e) {
if (presets == &this->printers) { throw ConfigurationError(format("Invalid configuration bundle \"%1%\", section [%2%]: ", path, section.first) + e.what());
// 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);
} }
Preset::normalize(config); Preset::normalize(config);
// Report configuration fields, which are misplaced into a wrong group. // Report configuration fields, which are misplaced into a wrong group.
@ -1389,8 +1397,12 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_configbundle(
DynamicPrintConfig config = default_config; DynamicPrintConfig config = default_config;
substitution_context.substitutions.clear(); substitution_context.substitutions.clear();
for (auto& kvp : section.second) try {
config.set_deserialize(kvp.first, kvp.second.data(), substitution_context); 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. // Report configuration fields, which are misplaced into a wrong group.
std::string incorrect_keys = Preset::remove_invalid_keys(config, default_config); std::string incorrect_keys = Preset::remove_invalid_keys(config, default_config);

View File

@ -2395,6 +2395,10 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
if (obj->name.empty()) if (obj->name.empty())
obj->name = fs::path(obj->input_file).filename().string(); 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) { } catch (const std::exception &e) {
GUI::show_error(q, e.what()); GUI::show_error(q, e.what());
continue; continue;

View File

@ -91,7 +91,7 @@ SCENARIO("Config accessor functions perform as expected.", "[Config]") {
} }
WHEN("A numeric option is set to a non-numeric value.") { WHEN("A numeric option is set to a non-numeric value.") {
THEN("A BadOptionTypeException exception is thown.") { 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.") { THEN("The value does not change.") {
REQUIRE(config.opt<ConfigOptionFloat>("perimeter_speed")->getFloat() == 60.0); REQUIRE(config.opt<ConfigOptionFloat>("perimeter_speed")->getFloat() == 60.0);