|
@@ -16,6 +16,7 @@
|
|
|
#include "Exception.hpp"
|
|
|
#include "Point.hpp"
|
|
|
|
|
|
+#include <boost/algorithm/string/predicate.hpp>
|
|
|
#include <boost/algorithm/string/trim.hpp>
|
|
|
#include <boost/format/format_fwd.hpp>
|
|
|
#include <boost/property_tree/ptree_fwd.hpp>
|
|
@@ -75,23 +76,59 @@ enum OptionCategory : int
|
|
|
};
|
|
|
std::string toString(OptionCategory opt);
|
|
|
|
|
|
-/// Specialization of std::exception to indicate that an unknown config option has been encountered.
|
|
|
-class UnknownOptionException : public Slic3r::RuntimeError {
|
|
|
+namespace ConfigHelpers {
|
|
|
+ inline bool looks_like_enum_value(std::string value)
|
|
|
+ {
|
|
|
+ boost::trim(value);
|
|
|
+ if (value.empty() || value.size() > 64 || ! isalpha(value.front()))
|
|
|
+ return false;
|
|
|
+ for (const char c : value)
|
|
|
+ if (! (isalnum(c) || c == '_' || c == '-'))
|
|
|
+ return false;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ inline bool enum_looks_like_true_value(std::string value) {
|
|
|
+ boost::trim(value);
|
|
|
+ return boost::iequals(value, "enabled") || boost::iequals(value, "on");
|
|
|
+ }
|
|
|
+
|
|
|
+ enum class DeserializationSubstitution {
|
|
|
+ Disabled,
|
|
|
+ DefaultsToFalse,
|
|
|
+ DefaultsToTrue
|
|
|
+ };
|
|
|
+
|
|
|
+ enum class DeserializationResult {
|
|
|
+ Loaded,
|
|
|
+ Substituted,
|
|
|
+ Failed,
|
|
|
+ };
|
|
|
+};
|
|
|
+
|
|
|
+// Base for all exceptions thrown by the configuration layer.
|
|
|
+class ConfigurationError : public Slic3r::RuntimeError {
|
|
|
+public:
|
|
|
+ using RuntimeError::RuntimeError;
|
|
|
+};
|
|
|
+
|
|
|
+// Specialization of std::exception to indicate that an unknown config option has been encountered.
|
|
|
+class UnknownOptionException : public ConfigurationError {
|
|
|
public:
|
|
|
UnknownOptionException() :
|
|
|
- Slic3r::RuntimeError("Unknown option exception") {}
|
|
|
+ ConfigurationError("Unknown option exception") {}
|
|
|
UnknownOptionException(const std::string &opt_key) :
|
|
|
- Slic3r::RuntimeError(std::string("Unknown option exception: ") + 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 Slic3r::RuntimeError
|
|
|
+// 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) {}
|
|
|
};
|
|
|
// a bit more specific than a runtime_error
|
|
|
class ConfigurationException : public std::runtime_error
|
|
@@ -103,13 +140,22 @@ public:
|
|
|
std::runtime_error(std::string("Configuration 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.
|
|
@@ -208,8 +254,47 @@ inline OutputFormat operator&=(OutputFormat& a, OutputFormat b) {
|
|
|
a = a & b; return a;
|
|
|
}
|
|
|
|
|
|
+enum ForwardCompatibilitySubstitutionRule
|
|
|
+{
|
|
|
+ // Disable susbtitution, throw exception if an option value is not recognized.
|
|
|
+ Disable,
|
|
|
+ // Enable substitution of an unknown option value with default. Log the substitution.
|
|
|
+ Enable,
|
|
|
+ // Enable substitution of an unknown option value with default. Don't log the substitution.
|
|
|
+ EnableSilent,
|
|
|
+ // Enable substitution of an unknown option value with default. Log substitutions in user profiles, don't log substitutions in system profiles.
|
|
|
+ EnableSystemSilent,
|
|
|
+ // Enable silent substitution of an unknown option value with default when loading user profiles. Throw on an unknown option value in a system profile.
|
|
|
+ EnableSilentDisableSystem,
|
|
|
+};
|
|
|
+
|
|
|
+class ConfigOption;
|
|
|
+class ConfigOptionDef;
|
|
|
+// For forward definition of ConfigOption in ConfigOptionUniquePtr, we have to define a custom deleter.
|
|
|
+struct ConfigOptionDeleter { void operator()(ConfigOption* p); };
|
|
|
+using ConfigOptionUniquePtr = std::unique_ptr<ConfigOption, ConfigOptionDeleter>;
|
|
|
+
|
|
|
+// When parsing a configuration value, if the old_value is not understood by this PrusaSlicer version,
|
|
|
+// it is being substituted with some default value that this PrusaSlicer could work with.
|
|
|
+// This structure serves to inform the user about the substitutions having been done during file import.
|
|
|
+struct ConfigSubstitution {
|
|
|
+ const ConfigOptionDef *opt_def { nullptr };
|
|
|
+ std::string old_value;
|
|
|
+ ConfigOptionUniquePtr new_value;
|
|
|
+};
|
|
|
|
|
|
+using ConfigSubstitutions = std::vector<ConfigSubstitution>;
|
|
|
+
|
|
|
+// Filled in by ConfigBase::set_deserialize_raw(), which based on "rule" either bails out
|
|
|
+// or performs substitutions when encountering an unknown configuration value.
|
|
|
+struct ConfigSubstitutionContext
|
|
|
+{
|
|
|
+ ConfigSubstitutionContext(ForwardCompatibilitySubstitutionRule rl) : rule(rl) {}
|
|
|
+ bool empty() const throw() { return substitutions.empty(); }
|
|
|
|
|
|
+ ForwardCompatibilitySubstitutionRule rule;
|
|
|
+ ConfigSubstitutions substitutions;
|
|
|
+};
|
|
|
|
|
|
// A generic value of a configuration option.
|
|
|
class ConfigOption {
|
|
@@ -277,7 +362,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;
|
|
|
this->phony = rhs->phony;
|
|
@@ -286,7 +371,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;
|
|
|
}
|
|
@@ -352,7 +437,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;
|
|
|
this->phony = rhs->phony;
|
|
@@ -370,12 +455,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");
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -394,12 +479,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
|
|
@@ -426,7 +511,7 @@ public:
|
|
|
if (opt_default == nullptr)
|
|
|
this->values.resize(n, this->default_value);
|
|
|
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.");
|
|
|
if(static_cast<const ConfigOptionVector<T>*>(opt_default)->values.empty())
|
|
|
this->values.resize(n, this->default_value);
|
|
|
else
|
|
@@ -446,7 +531,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;
|
|
|
}
|
|
@@ -458,9 +543,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.
|
|
@@ -478,9 +563,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.
|
|
@@ -571,7 +656,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);
|
|
|
}
|
|
@@ -618,7 +703,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;
|
|
@@ -643,9 +728,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) {
|
|
@@ -764,7 +849,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);
|
|
|
int32_t value;
|
|
@@ -781,7 +866,7 @@ private:
|
|
|
if (NULLABLE)
|
|
|
ss << "nil";
|
|
|
else
|
|
|
- throw Slic3r::RuntimeError("Serializing NaN");
|
|
|
+ throw ConfigurationError("Serializing NaN");
|
|
|
} else
|
|
|
ss << v;
|
|
|
}
|
|
@@ -811,7 +896,7 @@ public:
|
|
|
return escape_string_cstyle(this->value);
|
|
|
}
|
|
|
|
|
|
- bool deserialize(const std::string &str, bool append = false) override
|
|
|
+ bool deserialize(const std::string &str, bool append = false) override
|
|
|
{
|
|
|
UNUSED(append);
|
|
|
return unescape_string_cstyle(str, this->value);
|
|
@@ -970,7 +1055,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);
|
|
|
}
|
|
@@ -981,7 +1066,7 @@ public:
|
|
|
|
|
|
void set(const ConfigOption *rhs) override {
|
|
|
if (rhs->type() != this->type())
|
|
|
- throw ConfigurationException("ConfigOptionFloatOrPercent: Assigning an incompatible type");
|
|
|
+ throw ConfigurationError("ConfigOptionFloatOrPercent: Assigning an incompatible type");
|
|
|
assert(dynamic_cast<const ConfigOptionFloatOrPercent*>(rhs));
|
|
|
*this = *static_cast<const ConfigOptionFloatOrPercent*>(rhs);
|
|
|
}
|
|
@@ -1046,7 +1131,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);
|
|
|
}
|
|
@@ -1093,7 +1178,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);
|
|
@@ -1121,9 +1206,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) {
|
|
@@ -1312,8 +1397,15 @@ public:
|
|
|
bool deserialize(const std::string &str, bool append = false) override
|
|
|
{
|
|
|
UNUSED(append);
|
|
|
- this->value = (str.compare("1") == 0);
|
|
|
- return true;
|
|
|
+ if (str == "1") {
|
|
|
+ this->value = true;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ if (str == "0") {
|
|
|
+ this->value = false;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
private:
|
|
@@ -1374,24 +1466,39 @@ public:
|
|
|
}
|
|
|
return vv;
|
|
|
}
|
|
|
-
|
|
|
- bool deserialize(const std::string &str, bool append = false) override
|
|
|
+
|
|
|
+ ConfigHelpers::DeserializationResult deserialize_with_substitutions(const std::string &str, bool append, ConfigHelpers::DeserializationSubstitution substitution)
|
|
|
{
|
|
|
if (! append)
|
|
|
this->values.clear();
|
|
|
std::istringstream is(str);
|
|
|
std::string item_str;
|
|
|
+ bool substituted = false;
|
|
|
while (std::getline(is, item_str, ',')) {
|
|
|
boost::trim(item_str);
|
|
|
+ unsigned char new_value = 0;
|
|
|
if (item_str == "nil") {
|
|
|
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 if (item_str == "1") {
|
|
|
+ new_value = true;
|
|
|
+ } else if (item_str == "0") {
|
|
|
+ new_value = false;
|
|
|
+ } else if (substitution != ConfigHelpers::DeserializationSubstitution::Disabled && ConfigHelpers::looks_like_enum_value(item_str)) {
|
|
|
+ new_value = ConfigHelpers::enum_looks_like_true_value(item_str) || substitution == ConfigHelpers::DeserializationSubstitution::DefaultsToTrue;
|
|
|
+ substituted = true;
|
|
|
} else
|
|
|
- this->values.push_back(item_str.compare("1") == 0);
|
|
|
+ return ConfigHelpers::DeserializationResult::Failed;
|
|
|
+ this->values.push_back(new_value);
|
|
|
}
|
|
|
- return true;
|
|
|
+ return substituted ? ConfigHelpers::DeserializationResult::Substituted : ConfigHelpers::DeserializationResult::Loaded;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool deserialize(const std::string &str, bool append = false) override
|
|
|
+ {
|
|
|
+ return this->deserialize_with_substitutions(str, append, ConfigHelpers::DeserializationSubstitution::Disabled) == ConfigHelpers::DeserializationResult::Loaded;
|
|
|
}
|
|
|
|
|
|
protected:
|
|
@@ -1400,7 +1507,7 @@ protected:
|
|
|
if (NULLABLE)
|
|
|
ss << "nil";
|
|
|
else
|
|
|
- throw Slic3r::RuntimeError("Serializing NaN");
|
|
|
+ throw ConfigurationError("Serializing NaN");
|
|
|
} else
|
|
|
ss << (v ? "1" : "0");
|
|
|
}
|
|
@@ -1436,14 +1543,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();
|
|
|
this->phony = rhs->phony;
|
|
@@ -1522,14 +1629,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();
|
|
|
this->phony = rhs->phony;
|
|
@@ -1585,7 +1692,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) {
|
|
@@ -1604,7 +1711,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);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -1616,7 +1723,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) {
|
|
@@ -1635,7 +1742,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.
|
|
@@ -1731,6 +1838,14 @@ public:
|
|
|
static const constexpr char *nocli = "~~~noCLI";
|
|
|
};
|
|
|
|
|
|
+inline bool operator<(const ConfigSubstitution &lhs, const ConfigSubstitution &rhs) throw() {
|
|
|
+ return lhs.opt_def->opt_key < rhs.opt_def->opt_key ||
|
|
|
+ (lhs.opt_def->opt_key == rhs.opt_def->opt_key && lhs.old_value < rhs.old_value);
|
|
|
+}
|
|
|
+inline bool operator==(const ConfigSubstitution &lhs, const ConfigSubstitution &rhs) throw() {
|
|
|
+ return lhs.opt_def == rhs.opt_def && lhs.old_value == rhs.old_value;
|
|
|
+}
|
|
|
+
|
|
|
// Map from a config option name to its definition.
|
|
|
// The definition does not carry an actual value of the config option, only its constant default value.
|
|
|
// t_config_option_key is std::string
|
|
@@ -1758,7 +1873,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;
|
|
@@ -1809,6 +1924,8 @@ public:
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+
|
|
|
+
|
|
|
// An abstract configuration store.
|
|
|
class ConfigBase : public ConfigOptionResolver
|
|
|
{
|
|
@@ -1905,9 +2022,11 @@ public:
|
|
|
|
|
|
// Set a configuration value from a string, it will call an overridable handle_legacy()
|
|
|
// to resolve renamed and removed configuration keys.
|
|
|
- bool set_deserialize_nothrow(const t_config_option_key &opt_key_src, const std::string &value_src, bool append = false);
|
|
|
+ bool set_deserialize_nothrow(const t_config_option_key &opt_key_src, const std::string &value_src, ConfigSubstitutionContext& substitutions, bool append = false);
|
|
|
// May throw BadOptionTypeException() if the operation fails.
|
|
|
- void set_deserialize(const t_config_option_key &opt_key, const std::string &str, bool append = false);
|
|
|
+ void set_deserialize(const t_config_option_key &opt_key, const std::string &str, ConfigSubstitutionContext& config_substitutions, bool append = false);
|
|
|
+ void set_deserialize_strict(const t_config_option_key &opt_key, const std::string &str, bool append = false)
|
|
|
+ { ConfigSubstitutionContext ctxt{ ForwardCompatibilitySubstitutionRule::Disable }; this->set_deserialize(opt_key, str, ctxt, append); }
|
|
|
struct SetDeserializeItem {
|
|
|
SetDeserializeItem(const char *opt_key, const char *opt_value, bool append = false) : opt_key(opt_key), opt_value(opt_value), append(append) {}
|
|
|
SetDeserializeItem(const std::string &opt_key, const std::string &opt_value, bool append = false) : opt_key(opt_key), opt_value(opt_value), append(append) {}
|
|
@@ -1922,17 +2041,19 @@ public:
|
|
|
std::string opt_key; std::string opt_value; bool append = false;
|
|
|
};
|
|
|
// May throw BadOptionTypeException() if the operation fails.
|
|
|
- void set_deserialize(std::initializer_list<SetDeserializeItem> items);
|
|
|
+ void set_deserialize(std::initializer_list<SetDeserializeItem> items, ConfigSubstitutionContext& substitutions);
|
|
|
+ void set_deserialize_strict(std::initializer_list<SetDeserializeItem> items)
|
|
|
+ { ConfigSubstitutionContext ctxt{ ForwardCompatibilitySubstitutionRule::Disable }; this->set_deserialize(items, ctxt); }
|
|
|
|
|
|
double get_abs_value(const t_config_option_key &opt_key) const;
|
|
|
double get_abs_value(const t_config_option_key &opt_key, double ratio_over) const;
|
|
|
void setenv_() const;
|
|
|
- void load(const std::string &file);
|
|
|
- void load_from_ini(const std::string &file);
|
|
|
- void load_from_gcode_file(const std::string &file);
|
|
|
+ ConfigSubstitutions load(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule);
|
|
|
+ ConfigSubstitutions load_from_ini(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule);
|
|
|
+ ConfigSubstitutions load_from_gcode_file(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule);
|
|
|
// Returns number of key/value pairs extracted.
|
|
|
- size_t load_from_gcode_string(const char* str);
|
|
|
- void load(const boost::property_tree::ptree &tree);
|
|
|
+ size_t load_from_gcode_string(const char* str, ConfigSubstitutionContext& substitutions);
|
|
|
+ ConfigSubstitutions load(const boost::property_tree::ptree &tree, ForwardCompatibilitySubstitutionRule compatibility_rule);
|
|
|
void save(const std::string &file, bool to_prusa = false) const;
|
|
|
|
|
|
// Set all the nullable values to nils.
|
|
@@ -1940,7 +2061,7 @@ public:
|
|
|
|
|
|
private:
|
|
|
// Set a configuration value from a string.
|
|
|
- bool set_deserialize_raw(const t_config_option_key &opt_key_src, const std::string &str, bool append);
|
|
|
+ bool set_deserialize_raw(const t_config_option_key& opt_key_src, const std::string& value, ConfigSubstitutionContext& substitutions, bool append);
|
|
|
};
|
|
|
|
|
|
// Configuration store with dynamic number of configuration values.
|
|
@@ -2110,9 +2231,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:
|