Configuration compatibility - implemented substitution and reporting for vectors

of bools (including the nullable bools).
This commit is contained in:
bubnikv 2021-06-30 18:55:43 +02:00 committed by Vojtech Bubnik
parent 5b843aa291
commit 3a0b71deed
3 changed files with 96 additions and 39 deletions

View File

@ -506,16 +506,6 @@ void ConfigBase::set_deserialize(std::initializer_list<SetDeserializeItem> items
this->set_deserialize(item.opt_key, item.opt_value, substitutions_ctxt, item.append); this->set_deserialize(item.opt_key, item.opt_value, substitutions_ctxt, item.append);
} }
static inline bool looks_like_enum_value(const std::string &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;
}
bool ConfigBase::set_deserialize_raw(const t_config_option_key &opt_key_src, const std::string &value, ConfigSubstitutionContext& substitutions_ctxt, bool append) bool ConfigBase::set_deserialize_raw(const t_config_option_key &opt_key_src, const std::string &value, ConfigSubstitutionContext& substitutions_ctxt, bool append)
{ {
t_config_option_key opt_key = opt_key_src; t_config_option_key opt_key = opt_key_src;
@ -552,30 +542,41 @@ bool ConfigBase::set_deserialize_raw(const t_config_option_key &opt_key_src, con
ConfigOption *opt = this->option(opt_key, true); ConfigOption *opt = this->option(opt_key, true);
assert(opt != nullptr); assert(opt != nullptr);
bool success = opt->deserialize(value, append); bool success = false;
if (! success && substitutions_ctxt.rule != ForwardCompatibilitySubstitutionRule::Disable && bool substituted = false;
// Only allow substitutions of an enum value by another enum value or a boolean value with an enum value. if (optdef->type == coBools && substitutions_ctxt.rule != ForwardCompatibilitySubstitutionRule::Disable) {
// That means, we expect enum values being added in the future and possibly booleans being converted to enums. //FIXME Special handling of vectors of bools, quick and not so dirty solution before PrusaSlicer 2.3.2 release.
(optdef->type == coEnum || optdef->type == coBool)) auto result = opt->nullable() ?
{ static_cast<ConfigOptionBoolsNullable*>(opt)->deserialize_with_substitutions(value, append, true) :
// Deserialize failed, try to substitute with a default value. static_cast<ConfigOptionBools*>(opt)->deserialize_with_substitutions(value, append, true);
assert(substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::Enable || substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::EnableSilent); success = result != ConfigHelpers::DeserializationResult::Failed;
substituted = result == ConfigHelpers::DeserializationResult::Substituted;
} else {
success = opt->deserialize(value, append);
if (! success && substitutions_ctxt.rule != ForwardCompatibilitySubstitutionRule::Disable &&
// Only allow substitutions of an enum value by another enum value or a boolean value with an enum value.
// That means, we expect enum values being added in the future and possibly booleans being converted to enums.
(optdef->type == coEnum || optdef->type == coBool) && ConfigHelpers::looks_like_enum_value(value)) {
// Deserialize failed, try to substitute with a default value.
assert(substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::Enable || substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::EnableSilent);
if (optdef->type == coBool)
static_cast<ConfigOptionBool*>(opt)->value = ConfigHelpers::enum_looks_like_true_value(value);
else
// Just use the default of the option.
opt->set(optdef->default_value.get());
success = true;
substituted = true;
}
}
if (optdef->type == coBool && looks_like_enum_value(value)) if (substituted && (substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::Enable ||
static_cast<ConfigOptionBool*>(opt)->value = boost::iequals(value, "enabled") || boost::iequals(value, "on"); substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::EnableSystemSilent)) {
else // Log the substitution.
opt->set(optdef->default_value.get()); ConfigSubstitution config_substitution;
config_substitution.opt_def = optdef;
if (substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::Enable || config_substitution.old_value = value;
substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::EnableSystemSilent) { config_substitution.new_value = ConfigOptionUniquePtr(opt->clone());
// Log the substitution. substitutions_ctxt.substitutions.emplace_back(std::move(config_substitution));
ConfigSubstitution config_substitution;
config_substitution.opt_def = optdef;
config_substitution.old_value = value;//std::unique_ptr<ConfigOption>(opt);
config_substitution.new_value = ConfigOptionUniquePtr(this->option(opt_key, true)->clone());
substitutions_ctxt.substitutions.emplace_back(std::move(config_substitution));
}
return true;
} }
return success; return success;
} }

View File

@ -80,6 +80,30 @@ 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);
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 DeserializationResult {
Loaded,
Substituted,
Failed,
};
};
// Base for all exceptions thrown by the configuration layer. // Base for all exceptions thrown by the configuration layer.
class ConfigurationError : public Slic3r::RuntimeError { class ConfigurationError : public Slic3r::RuntimeError {
public: public:
@ -1400,24 +1424,39 @@ public:
} }
return vv; return vv;
} }
bool deserialize(const std::string &str, bool append = false) override ConfigHelpers::DeserializationResult deserialize_with_substitutions(const std::string &str, bool append, bool substitute)
{ {
if (! append) if (! append)
this->values.clear(); this->values.clear();
std::istringstream is(str); std::istringstream is(str);
std::string item_str; std::string item_str;
bool substituted = false;
while (std::getline(is, item_str, ',')) { while (std::getline(is, item_str, ',')) {
boost::trim(item_str); boost::trim(item_str);
unsigned char new_value = 0;
if (item_str == "nil") { if (item_str == "nil") {
if (NULLABLE) if (NULLABLE)
this->values.push_back(nil_value()); this->values.push_back(nil_value());
else else
throw ConfigurationError("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 (substitute && ConfigHelpers::looks_like_enum_value(item_str)) {
new_value = ConfigHelpers::enum_looks_like_true_value(item_str);
substituted = true;
} else } 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, false) == ConfigHelpers::DeserializationResult::Loaded;
} }
protected: protected:

View File

@ -258,7 +258,9 @@ static void add_config_substitutions(const ConfigSubstitutions& conf_substitutio
const ConfigOptionDef* def = conf_substitution.opt_def; const ConfigOptionDef* def = conf_substitution.opt_def;
if (!def) if (!def)
continue; continue;
if (def->type == coEnum) { switch (def->type) {
case coEnum:
{
const std::vector<std::string>& labels = def->enum_labels; const std::vector<std::string>& labels = def->enum_labels;
const std::vector<std::string>& values = def->enum_values; const std::vector<std::string>& values = def->enum_values;
int val = conf_substitution.new_value->getInt(); int val = conf_substitution.new_value->getInt();
@ -283,9 +285,24 @@ static void add_config_substitutions(const ConfigSubstitutions& conf_substitutio
} }
else else
new_val = from_u8(_utf8(labels[val])); new_val = from_u8(_utf8(labels[val]));
break;
} }
else if (def->type == coBool) case coBool:
new_val = conf_substitution.new_value->getBool() ? "true" : "false"; new_val = conf_substitution.new_value->getBool() ? "true" : "false";
break;
case coBools:
if (conf_substitution.new_value->nullable())
for (const char v : static_cast<const ConfigOptionBoolsNullable*>(conf_substitution.new_value.get())->values)
new_val += std::string(v == ConfigOptionBoolsNullable::nil_value() ? "nil" : v ? "true" : "false") + ", ";
else
for (const char v : static_cast<const ConfigOptionBools*>(conf_substitution.new_value.get())->values)
new_val += std::string(v ? "true" : "false") + ", ";
if (! new_val.empty())
new_val.erase(new_val.begin() + new_val.size() - 2, new_val.end());
break;
default:
assert(false);
}
changes += "<tr><td>" + bold(_(def->label)) + "</td><td>: " + changes += "<tr><td>" + bold(_(def->label)) + "</td><td>: " +
format_wxstr(_L("new unknown value %1% was changed to default value %2%"), bold(conf_substitution.old_value), bold(new_val)) + format_wxstr(_L("new unknown value %1% was changed to default value %2%"), bold(conf_substitution.old_value), bold(new_val)) +