46 while (std::getline(is, line)) {
49 auto trimmed = Trim(line);
52 if (trimmed.empty() || trimmed[0] ==
'#') {
57 auto eq_pos = trimmed.find(
'=');
58 if (eq_pos == std::string::npos) {
59 return InvalidArgumentError(
"conf parse error: missing '=' on line " + std::to_string(line_number));
62 std::string key = Trim(trimmed.substr(0, eq_pos));
63 std::string value_str = Trim(trimmed.substr(eq_pos + 1));
65 result.
SetAtPath(key, InferValue(value_str));
74 std::istringstream stream { std::string(str) };
81 std::vector<std::pair<std::string, const Value*>> leaves;
82 CollectLeaves(data,
"", leaves);
84 std::ostringstream stream;
85 for (
const auto& [path, val] : leaves) {
86 stream << path <<
" = " << ValueToString(*val) <<
'\n';
94 static auto Trim(std::string_view sv) -> std::string
96 auto start = sv.find_first_not_of(
" \t\r\n");
97 if (start == std::string_view::npos) {
100 auto end = sv.find_last_not_of(
" \t\r\n");
101 return std::string(sv.substr(start, end - start + 1));
105 static auto InferValue(
const std::string& str) -> Value
108 if (str.size() >= 2 && str.front() ==
'"' && str.back() ==
'"') {
109 return { str.substr(1, str.size() - 2) };
114 return { std::string(
"") };
118 if (str ==
"true" || str ==
"yes" || str ==
"on") {
121 if (str ==
"false" || str ==
"no" || str ==
"off") {
128 auto int_val = std::stoll(str, &pos);
129 if (pos == str.size()) {
130 return {
static_cast<std::int64_t
>(int_val) };
137 if (str.find(
'.') != std::string::npos || str.find(
'e') != std::string::npos || str.find(
'E') != std::string::npos) {
140 auto double_val = std::stod(str, &pos);
141 if (pos == str.size()) {
142 return { double_val };
154 static auto ValueToString(
const Value& val) -> std::string
159 if (val.IsBoolean()) {
160 return val.Get<
bool>() ?
"true" :
"false";
162 if (val.IsInteger()) {
163 return std::to_string(val.Get<std::int64_t>());
165 if (val.IsDouble()) {
166 std::ostringstream stream;
167 stream << val.Get<
double>();
170 if (val.IsString()) {
171 const auto& str = val.Get<std::string>();
173 bool needs_quoting = str.empty() || str.front() ==
' ' || str.back() ==
' ' || str.front() ==
'"' || str.find_first_of(
"=#\n\r") != std::string::npos;
175 return "\"" + str +
"\"";
183 static void CollectLeaves(
const Value& node,
const std::string& prefix, std::vector<std::pair<std::string, const Value*>>& leaves)
185 if (node.IsObject()) {
186 for (
const auto& [key, val] : node.Items()) {
187 std::string path = prefix.empty() ? key : prefix +
"." + key;
188 CollectLeaves(val, path, leaves);
192 leaves.emplace_back(prefix, &node);