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 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) {
@ -264,7 +264,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);
}
}
}
@ -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)
{
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)
@ -587,7 +587,7 @@ double ConfigBase::get_abs_value(const t_config_option_key &opt_key) const
return opt_def->ratio_over.empty() ? 0. :
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.
@ -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);
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<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)
{
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(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.
ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule)
{
// 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 Slic3r::RuntimeError("Not a PrusaSlicer / Slic3r PE generated g-code.");
}
ifs.seekg(0, ifs.end);
auto file_length = ifs.tellg();
auto data_length = std::min<std::fstream::pos_type>(65535, file_length);
ifs.seekg(file_length - data_length, ifs.beg);
std::vector<char> data(size_t(data_length) + 1, 0);
ifs.read(data.data(), data_length);
ifs.close();
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<std::fstream::pos_type>(65535, file_length);
ifs.seekg(file_length - data_length, ifs.beg);
std::vector<char> 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 Slic3r::RuntimeError(format("Suspiciously low number of configuration values extracted from %1%: %2%", file, key_value_pairs));
return std::move(substitutions_ctxt.substitutions);
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.
@ -805,7 +813,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();

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);
/// 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.
@ -215,7 +230,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<const ConfigOptionSingle<T>*>(rhs));
this->value = static_cast<const ConfigOptionSingle<T>*>(rhs)->value;
}
@ -223,7 +238,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<const ConfigOptionSingle<T>*>(&rhs));
return this->value == static_cast<const ConfigOptionSingle<T>*>(&rhs)->value;
}
@ -287,7 +302,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<const ConfigOptionVector<T>*>(rhs));
this->values = static_cast<const ConfigOptionVector<T>*>(rhs)->values;
}
@ -304,12 +319,12 @@ public:
if (opt->type() == this->type()) {
auto other = static_cast<const ConfigOptionVector<T>*>(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<const ConfigOptionSingle<T>*>(opt)->value);
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.
auto other = static_cast<const ConfigOptionVector<T>*>(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<const ConfigOptionSingle<T>*>(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
@ -358,9 +373,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<const ConfigOptionVector<T>*>(opt_default)->values.front());
} else {
// Resize by duplicating the last value.
@ -377,7 +392,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<const ConfigOptionVector<T>*>(&rhs));
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.
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<const ConfigOptionVector<T>*>(rhs);
if (! rhs->nullable())
// Overridding a non-nullable object with another non-nullable object.
@ -409,9 +424,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<const ConfigOptionVector<T>*>(rhs);
if (! rhs->nullable()) {
// 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 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<const ConfigOptionVector<double>*>(&rhs));
return vectors_equal(this->values, static_cast<const ConfigOptionVector<double>*>(&rhs)->values);
}
@ -547,7 +562,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;
@ -572,9 +587,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<double> &v1, const std::vector<double> &v2) {
if (NULLABLE) {
@ -693,7 +708,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;
@ -710,7 +725,7 @@ private:
if (NULLABLE)
ss << "nil";
else
throw Slic3r::RuntimeError("Serializing NaN");
throw ConfigurationError("Serializing NaN");
} else
ss << v;
}
@ -895,7 +910,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<const ConfigOptionFloatOrPercent*>(&rhs));
return *this == *static_cast<const ConfigOptionFloatOrPercent*>(&rhs);
}
@ -906,7 +921,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<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 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<const ConfigOptionVector<FloatOrPercent>*>(&rhs));
return vectors_equal(this->values, static_cast<const ConfigOptionVector<FloatOrPercent>*>(&rhs)->values);
}
@ -1018,7 +1033,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);
@ -1046,9 +1061,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<FloatOrPercent> &v1, const std::vector<FloatOrPercent> &v2) {
if (NULLABLE) {
@ -1318,7 +1333,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);
}
@ -1331,7 +1346,7 @@ protected:
if (NULLABLE)
ss << "nil";
else
throw Slic3r::RuntimeError("Serializing NaN");
throw ConfigurationError("Serializing NaN");
} else
ss << (v ? "1" : "0");
}
@ -1367,14 +1382,14 @@ public:
bool operator==(const ConfigOption &rhs) const override
{
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>
return this->value == (T)rhs.getInt();
}
void set(const ConfigOption *rhs) override {
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>
this->value = (T)rhs->getInt();
}
@ -1451,14 +1466,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<T>
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<T>
this->value = rhs->getInt();
}
@ -1513,7 +1528,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) {
@ -1532,7 +1547,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);
}
}
}
@ -1544,7 +1559,7 @@ public:
case coInts: archive(*static_cast<const ConfigOptionIntsNullable*>(opt)); break;
case coPercents: archive(*static_cast<const ConfigOptionPercentsNullable*>(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 {
switch (this->type) {
@ -1563,7 +1578,7 @@ public:
case coBool: archive(*static_cast<const ConfigOptionBool*>(opt)); break;
case coBools: archive(*static_cast<const ConfigOptionBools*>(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.
@ -1686,7 +1701,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<bool(const ConfigOptionDef &)> filter = [](const ConfigOptionDef &){ return true; }) const;
@ -2031,9 +2046,9 @@ private:
template<class Archive> 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:

View File

@ -697,8 +697,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());
}
@ -706,23 +706,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.
@ -1219,7 +1223,7 @@ std::pair<PresetsConfigSubstitutions, size_t> 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();
}
}
@ -1256,32 +1260,36 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_configbundle(
DynamicPrintConfig config;
std::string alias_name;
std::vector<std::string> renamed_from;
auto parse_config_section = [&section, &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 = [&section, &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.
@ -1389,8 +1397,12 @@ std::pair<PresetsConfigSubstitutions, size_t> 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);

View File

@ -2395,6 +2395,10 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& 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;

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.") {
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<ConfigOptionFloat>("perimeter_speed")->getFloat() == 60.0);