50 while (std::getline(is, line)) {
53 auto trimmed = Trim(line);
56 if (trimmed.empty() || trimmed[0] ==
'#') {
61 auto eq_pos = trimmed.find(
'=');
62 if (eq_pos == std::string::npos) {
63 return InvalidArgumentError(
"conf parse error: missing '=' on line " + std::to_string(line_number));
66 std::string key = Trim(trimmed.substr(0, eq_pos));
67 std::string value_str = Trim(trimmed.substr(eq_pos + 1));
72 return InvalidArgumentError(
"conf parse error: ill-formed key '" + key +
"' on line " + std::to_string(line_number));
75 result.
SetAtPath(key, InferValue(value_str));
84 std::istringstream stream { std::string(str) };
91 std::vector<std::pair<std::string, const Value*>> leaves;
92 CollectLeaves(data,
"", leaves);
94 std::ostringstream stream;
95 for (
const auto& [path, val] : leaves) {
96 stream << path <<
" = " << ValueToString(*val) <<
'\n';
116 static auto Merge(std::string_view existing,
const Value& data) -> std::string
118 std::vector<std::pair<std::string, const Value*>> leaves;
119 CollectLeaves(data,
"", leaves);
121 std::map<std::string, const Value*, std::less<>> by_path;
122 for (
const auto& [path, val] : leaves) {
123 by_path.emplace(path, val);
126 std::set<std::string, std::less<>> updated;
127 std::ostringstream out;
128 std::istringstream in { std::string(existing) };
131 while (std::getline(in, line)) {
132 auto trimmed = Trim(line);
133 auto eq_pos = line.find(
'=');
137 if (trimmed.empty() || trimmed[0] ==
'#' || eq_pos == std::string::npos) {
142 const std::string key = Trim(line.substr(0, eq_pos));
143 auto entry = by_path.find(key);
144 if (entry == by_path.end()) {
153 const std::string rendered = ValueToString(*entry->second);
154 if (InferValue(Trim(line.substr(eq_pos + 1))) == *entry->second) {
158 out << line.substr(0, eq_pos + 1) <<
' ' << rendered <<
'\n';
162 AppendMissing(out, leaves, updated);
168 static auto Trim(std::string_view sv) -> std::string
170 auto start = sv.find_first_not_of(
" \t\r\n");
171 if (start == std::string_view::npos) {
174 auto end = sv.find_last_not_of(
" \t\r\n");
175 return std::string(sv.substr(start, end - start + 1));
179 static auto InferValue(
const std::string& str) -> Value
182 if (str.size() >= 2 && str.front() ==
'"' && str.back() ==
'"') {
183 return { str.substr(1, str.size() - 2) };
188 return { std::string(
"") };
192 if (str ==
"true" || str ==
"yes" || str ==
"on") {
195 if (str ==
"false" || str ==
"no" || str ==
"off") {
202 auto int_val = std::stoll(str, &pos);
203 if (pos == str.size()) {
204 return {
static_cast<std::int64_t
>(int_val) };
211 if (str.find(
'.') != std::string::npos || str.find(
'e') != std::string::npos || str.find(
'E') != std::string::npos) {
214 auto double_val = std::stod(str, &pos);
215 if (pos == str.size()) {
216 return { double_val };
228 static auto ValueToString(
const Value& val) -> std::string
233 if (val.IsBoolean()) {
234 return val.Get<
bool>() ?
"true" :
"false";
236 if (val.IsInteger()) {
237 return std::to_string(val.Get<std::int64_t>());
239 if (val.IsDouble()) {
240 std::ostringstream stream;
241 stream << val.Get<
double>();
244 if (val.IsString()) {
245 const auto& str = val.Get<std::string>();
247 bool needs_quoting = str.empty() || str.front() ==
' ' || str.back() ==
' ' || str.front() ==
'"' || str.find_first_of(
"=#\n\r") != std::string::npos;
249 return "\"" + str +
"\"";
257 static void AppendMissing(std::ostringstream& out,
258 const std::vector<std::pair<std::string, const Value*>>& leaves,
259 const std::set<std::string, std::less<>>& updated)
261 bool wrote_header =
false;
262 for (
const auto& [path, val] : leaves) {
263 if (updated.contains(path)) {
267 out <<
"\n# Added by cppfig\n";
270 out << path <<
" = " << ValueToString(*val) <<
'\n';
275 static void CollectLeaves(
const Value& node,
const std::string& prefix, std::vector<std::pair<std::string, const Value*>>& leaves)
277 if (node.IsObject()) {
278 for (
const auto& [key, val] : node.Items()) {
279 std::string path = prefix.empty() ? key : prefix +
"." + key;
280 CollectLeaves(val, path, leaves);
284 leaves.emplace_back(prefix, &node);