add profile validator checks

This commit is contained in:
SoftFever
2026-07-01 21:55:19 +08:00
parent 57297d5ab1
commit f516f47c8e
5 changed files with 211 additions and 18 deletions

View File

@@ -532,8 +532,16 @@ PresetsConfigSubstitutions PresetBundle::load_presets(AppConfig &config, Forward
load_user_presets(dir_user_presets, substitution_rule);
}
// Rewrite renamed compatible_printers / compatible_prints references before selection.
this->normalize_compatible_presets();
// Rewrite renamed compatible_printers / compatible_prints references before selection. Skipped
// in validation mode so the profile validator (has_errors -> check_preset_references) sees the
// raw vendor-JSON references instead of the silently-repaired ones.
if (!validation_mode)
this->normalize_compatible_presets();
// Rewrite renamed compatible_printers / compatible_prints references before selection. Skipped
// in validation mode so the profile validator (has_errors -> check_preset_references) sees the
// raw vendor-JSON references instead of the silently-repaired ones.
if (!validation_mode)
this->normalize_compatible_presets();
this->update_multi_material_filament_presets();
this->update_compatible(PresetSelectCompatibleType::Never);
@@ -5278,21 +5286,20 @@ static void normalize_compatible_field(Preset &preset, const char *field_key, Pr
void PresetBundle::normalize_compatible_presets()
{
// compatible_printers references a printer preset; compatible_prints (filaments / SLA materials)
// references a process preset. Skip system presets: they are the targets of renames, not holders
// of stale references, and their vendor data must not be mutated. (begin()/end() skip defaults.)
auto normalize = [](PresetCollection &holders, PresetCollection &printers, PresetCollection *processes) {
// references a process preset. System presets are normalized too: a vendor profile can itself
// reference a sibling preset by a name that was later renamed, and the rewrite is in-memory only
// (system presets are never persisted back to vendor JSON). (begin()/end() skip defaults.)
auto normalize = [this](PresetCollection &holders, PresetCollection *processes) {
for (Preset &p : holders) {
if (p.is_system)
continue;
normalize_compatible_field(p, "compatible_printers", printers);
normalize_compatible_field(p, "compatible_printers", this->printers);
if (processes != nullptr)
normalize_compatible_field(p, "compatible_prints", *processes);
}
};
normalize(this->prints, this->printers, nullptr);
normalize(this->filaments, this->printers, &this->prints);
normalize(this->sla_prints, this->printers, nullptr);
normalize(this->sla_materials, this->printers, &this->sla_prints);
normalize(this->prints, nullptr);
normalize(this->filaments, &this->prints);
normalize(this->sla_prints, nullptr);
normalize(this->sla_materials, &this->sla_prints);
}
void PresetBundle::update_compatible(PresetSelectCompatibleType select_other_print_if_incompatible, PresetSelectCompatibleType select_other_filament_if_incompatible)
@@ -5526,7 +5533,8 @@ void PresetBundle::set_default_suppressed(bool default_suppressed)
printers.set_default_suppressed(default_suppressed);
}
bool PresetBundle::has_errors(bool check_duplicate_filament_subtypes) const
bool PresetBundle::has_errors(bool check_duplicate_filament_subtypes, bool check_references) const
bool PresetBundle::has_errors(bool check_duplicate_filament_subtypes, bool check_references) const
{
if (m_errors != 0 || printers.m_errors != 0 || filaments.m_errors != 0 || prints.m_errors != 0)
return true;
@@ -5549,6 +5557,12 @@ bool PresetBundle::has_errors(bool check_duplicate_filament_subtypes) const
if (check_duplicate_filament_subtypes && this->check_duplicate_filament_subtypes())
has_errors = true;
if (check_references && this->check_preset_references())
has_errors = true;
if (check_references && this->check_preset_references())
has_errors = true;
return has_errors;
}
@@ -5579,6 +5593,68 @@ static std::string preset_file_uri(const std::string &file)
return uri;
}
// Orca: validator-only. Flag any system preset whose inherits / compatible_printers /
// compatible_prints references a name that no longer resolves. Uses find_preset (exact match, then
// the renamed_from map - no fuzzy find_preset2, no alias resolution), so:
// nullptr -> the referenced preset was deleted/renamed away (name is dangling),
// resolved != name -> the reference uses an old name that renamed_from maps to a current one.
// Both should be fixed at the source rather than relying on load-time normalization - which is why
// normalize_compatible_presets() is skipped in validation mode (see load_presets), so this sees the
// raw vendor-JSON references. Safe under a single-vendor run (-v) too: inherits / compatible_printers
// / compatible_prints only name same-vendor or OrcaFilamentLibrary presets (both loaded), so a
// reference that does not resolve is genuinely dangling rather than an unloaded cross-vendor preset.
bool PresetBundle::check_preset_references() const
{
bool found = false;
// Resolve one reference (an inherits parent or a compatible_* entry) against its target
// collection and log if it is dangling (unknown) or uses a renamed preset's old name.
auto report_ref = [&](const Preset &p, const std::string &name, const PresetCollection &target,
const char *verb, const char *noun) {
const Preset *resolved = target.find_preset(name, false);
if (resolved == nullptr) {
found = true;
BOOST_LOG_TRIVIAL(error) << "Preset \"" << p.name << "\" " << verb << " unknown " << noun << " \"" << name << "\":\n"
<< preset_file_uri(p.file);
} else if (resolved->name != name) {
found = true;
BOOST_LOG_TRIVIAL(error) << "Preset \"" << p.name << "\" " << verb << " renamed " << noun << " \"" << name
<< "\" (now \"" << resolved->name << "\"):\n" << preset_file_uri(p.file);
}
};
auto check_list = [&](const Preset &p, const char *key, const PresetCollection &target) {
const auto *opt = p.config.option<ConfigOptionStrings>(key);
if (opt == nullptr)
return;
for (const std::string &name : opt->values)
if (!name.empty())
report_ref(p, name, target, "references", key);
};
auto check_collection = [&](const PresetCollection &holders, const PresetCollection *processes) {
for (const Preset &p : holders) {
if (!p.is_system)
continue;
if (const std::string &inh = p.inherits(); !inh.empty())
report_ref(p, inh, holders, "inherits", "parent");
check_list(p, "compatible_printers", this->printers);
if (processes != nullptr)
check_list(p, "compatible_prints", *processes);
}
};
// Printers carry no compatible_printers/compatible_prints (those name a printer, so a printer
// holding them makes no sense); check_list is a no-op for them, so only their inherits is checked.
check_collection(this->printers, nullptr);
check_collection(this->prints, nullptr);
check_collection(this->filaments, &this->prints);
check_collection(this->sla_prints, nullptr);
check_collection(this->sla_materials, &this->sla_prints);
return found;
}
// Orca: a filament is matched from the AMS by (filament_id + printer compatibility).
// For any one printer, at most one instantiated filament preset with a given
// filament_id may be compatible - otherwise the AMS match is ambiguous and the