fwt_software/include/fgc/IniEdit.h

57 lines
2.8 KiB
C++

#pragma once
// Editing the two INI files a tower is configured by - config.ini and
// hardware.ini - without breaking them. Pure functions on file contents, so
// every rule is unit-testable; tools/fgc_config.cpp does the file IO around them.
//
// The rules exist because each has already broken a real file:
// - inih SPLITS a line longer than its buffer instead of truncating it, and the
// tail then parses as a bogus line of its own (see ini.h INI_MAX_LINE). A
// generated comment of exactly 200 characters once took out a whole config.
// - A value left in config.ini that hardware.ini also sets is dead, and warned
// about at every start. A placeholder left there is worse: deploy.sh's guard
// greps the raw text, so even a commented-out DEV_0000000000 trips it.
#include <map>
#include <optional>
#include <string>
#include <utility>
#include <vector>
namespace fgc {
// The longest line content inih reads whole. Its buffer is INI_MAX_LINE bytes
// and holds the newline and the terminator too.
std::size_t iniMaxLineContent();
// 1-based number of the first line too long for inih to read whole, or 0.
int firstOverlongIniLine(const std::string& contents);
// "Camera.rgb.id" -> {"Camera.rgb", "id"}: the key is after the LAST dot, since
// section names may contain dots and key names never do. nullopt when there is
// no dot, or either side is empty.
std::optional<std::pair<std::string, std::string>> splitIniKey(const std::string& flat);
// Every key the contents set, as "Section.key" -> value, exactly as the program
// reads them. Throws std::runtime_error naming the line if inih rejects it.
std::map<std::string, std::string> parseIniString(const std::string& contents);
// Set "Section.key" = value pairs. Grouped per section and applied through
// updateIniSectionKeys(), so comments and order survive, a missing key is
// appended, and a non-empty `provenance` keeps one ";prev" generation per
// changed key. Throws std::invalid_argument on a malformed key.
std::string applyIniSets(const std::string& contents,
const std::vector<std::pair<std::string, std::string>>& sets,
const std::string& provenance);
// Replace each live `key = value` line whose "Section.key" is in `drops` with a
// one-line comment "; <key> <note>", where note is the map's value. The VALUE is
// deliberately not kept: a dead placeholder in a comment still trips deploy.sh's
// raw-text guard, and a dead real value invites someone to "fix" the wrong file.
// Returns the new contents; `dropped` (if given) receives what was replaced.
std::string dropIniKeys(const std::string& contents,
const std::map<std::string, std::string>& drops,
std::vector<std::string>* dropped = nullptr);
} // namespace fgc