PATCH-04: backup <path>.bak abans de reescriure config/presets per fallada

Helper `backupFile(path)` que copia el YAML a `<path>.bak` si existeix.
Es crida a `loadFromFile` (versió mismatch i catch) i a
`loadPresetsFromFile<T>` (manca `presets:`, llista buida, catch).
La primera execució no genera .bak. Cada reescriptura forçada registra el
motiu per stderr (INCONS-02 migrarà a std::cout).

Prova manual: config.yaml amb contingut \"===broken===\" genera
config.yaml.bak amb el contingut previ i un nou config.yaml amb defaults.
This commit is contained in:
JailDesigner committed 2026-04-20 17:40:39 +02:00
1 parent 505c20dabc
commit db2e5d66e0
2 files changed
+31 -4

No files matched your search

+29 -2
View File
@@ -76,6 +76,20 @@ namespace {
return file.good();
}
// Còpia `<path>` a `<path>.bak` per protegir ediciones manuals de
// l'usuari quan estem a punt de reescriure el fitxer amb defaults per
// error de parseig o canvi de versió. Si el fitxer no existeix (primera
// execució) o no es pot obrir, no fa res: no hi ha res a salvar.
void backupFile(const std::string& path) {
if (path.empty()) { return; }
std::ifstream src(path, std::ios::binary);
if (!src) { return; }
std::ofstream dst(path + ".bak", std::ios::binary);
if (!dst) { return; }
dst << src.rdbuf();
std::cerr << "Options: còpia de seguretat a " << path << ".bak\n";
}
void parseWindow(const fkyaml::node& yaml) {
if (!yaml.contains("window")) { return; }
const auto& node = yaml["window"];
@@ -351,12 +365,15 @@ namespace {
const std::string CONTENT = readFileOrEmpty(path);
if (CONTENT.empty()) {
// Primera execució (no existia o era buit): no cal backup.
presets = defaults_fn();
return save_fn();
}
try {
auto yaml = fkyaml::node::deserialize(CONTENT);
if (!yaml.contains("presets") || !yaml["presets"].is_sequence()) {
std::cerr << "Options: " << kind_label << " manca `presets:`; reescrivint amb defaults.\n";
backupFile(path);
presets = defaults_fn();
return save_fn();
}
@@ -367,6 +384,8 @@ namespace {
presets.push_back(preset);
}
if (presets.empty()) {
std::cerr << "Options: " << kind_label << " sense presets; reescrivint amb defaults.\n";
backupFile(path);
presets = defaults_fn();
return save_fn();
}
@@ -374,6 +393,7 @@ namespace {
} catch (const std::exception& e) {
std::cerr << "Options: error parsejant " << kind_label << ": " << e.what() << '\n';
std::cerr << "Options: reescrivint el fitxer amb presets per defecte.\n";
backupFile(path);
presets = defaults_fn();
return save_fn();
}
@@ -426,8 +446,14 @@ namespace Options {
file_version = yaml["version"].get_value<std::string>();
}
// Sense migració per ara: si la versió no coincideix, reescrivim
// amb els defaults de la versió nova.
if (file_version != Texts::VERSION) { return saveToFile(); }
// amb els defaults de la versió nova. El fitxer original queda
// a `<path>.bak` per si l'usuari volia preservar tuning manual.
if (file_version != Texts::VERSION) {
std::cerr << "Options: config.yaml versió \"" << file_version
<< "\" != \"" << Texts::VERSION << "\"; reescrivint amb defaults.\n";
backupFile(config_file_path);
return saveToFile();
}
config_version = file_version;
if (yaml.contains("locale")) { Options::locale = yaml["locale"].get_value<std::string>(); }
@@ -438,6 +464,7 @@ namespace Options {
} catch (const std::exception& e) {
std::cerr << "Options: error parsejant config.yaml: " << e.what() << '\n';
std::cerr << "Options: reescrivint el fitxer amb defaults.\n";
backupFile(config_file_path);
return saveToFile();
}
}