NeBuild dev
Loading...
Searching...
No Matches
toml_formatter.inl
Go to the documentation of this file.
1// # This file is a part of toml++ and is subject to the the terms of the MIT license.
2// # Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
3// # See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
4// SPDX-License-Identifier: MIT
5#pragma once
6
7#include "preprocessor.hpp"
8// # {{
9#if !TOML_IMPLEMENTATION
10#error This is an implementation-only header.
11#endif
12// # }}
13#if TOML_ENABLE_FORMATTERS
14
15#include "array.hpp"
16#include "header_start.hpp"
17#include "print_to_stream.hpp"
18#include "table.hpp"
19#include "toml_formatter.hpp"
20#include "unicode.hpp"
21#include "value.hpp"
23
26 size_t toml_formatter_count_inline_columns(const node& node, size_t line_wrap_cols) noexcept {
27 switch (node.type()) {
28 case node_type::table: {
29 auto& tbl = *reinterpret_cast<const table*>(&node);
30 if (tbl.empty()) return 2u; // "{}"
31 size_t weight = 3u; // "{ }"
32 for (auto&& [k, v] : tbl) {
33 weight +=
34 k.length() + toml_formatter_count_inline_columns(v, line_wrap_cols) + 2u; // + ", "
35 if (weight >= line_wrap_cols) break;
36 }
37 return weight;
38 }
39
40 case node_type::array: {
41 auto& arr = *reinterpret_cast<const array*>(&node);
42 if (arr.empty()) return 2u; // "[]"
43 size_t weight = 3u; // "[ ]"
44 for (auto& elem : arr) {
45 weight += toml_formatter_count_inline_columns(elem, line_wrap_cols) + 2u; // + ", "
46 if (weight >= line_wrap_cols) break;
47 }
48 return weight;
49 }
50
51 case node_type::string: {
52 // todo: proper utf8 decoding?
53 // todo: tab awareness?
54 auto& str = (*reinterpret_cast<const value<std::string>*>(&node)).get();
55 return str.length() + 2u; // + ""
56 }
57
58 case node_type::integer: {
59 auto val = (*reinterpret_cast<const value<int64_t>*>(&node)).get();
60 if (!val) return 1u;
61 size_t weight = {};
62 if (val < 0) {
63 weight += 1u;
64 val *= -1;
65 }
66 return weight + static_cast<size_t>(log10(static_cast<double>(val))) + 1u;
67 }
68
69 case node_type::floating_point: {
70 auto val = (*reinterpret_cast<const value<double>*>(&node)).get();
71 if (val == 0.0) return 3u; // "0.0"
72 size_t weight = 2u; // ".0"
73 if (val < 0.0) {
74 weight += 1u;
75 val *= -1.0;
76 }
77 return weight + static_cast<size_t>(log10(val)) + 1u;
78 break;
79 }
80
81 case node_type::boolean:
82 return 5u;
83 case node_type::date:
84 [[fallthrough]];
85 case node_type::time:
86 return 10u;
87 case node_type::date_time:
88 return 30u;
89 case node_type::none:
91 default:
93 }
94
96 }
97
99 bool toml_formatter_forces_multiline(const node& node, size_t line_wrap_cols,
100 size_t starting_column_bias) noexcept {
101 return (toml_formatter_count_inline_columns(node, line_wrap_cols) + starting_column_bias) >=
102 line_wrap_cols;
103 }
104}
106
109 void toml_formatter::print_pending_table_separator() {
110 if (pending_table_separator_) {
111 print_newline(true);
112 print_newline(true);
113 pending_table_separator_ = false;
114 }
115 }
116
118 void toml_formatter::print(const key& k) {
119 print_string(k.str(), false, true, false);
120 }
121
123 void toml_formatter::print_inline(const table& tbl) {
124 if (tbl.empty()) {
125 print_unformatted("{}"sv);
126 return;
127 }
128
129 print_unformatted("{ "sv);
130
131 bool first = false;
132 for (auto&& [k, v] : tbl) {
133 if (first) print_unformatted(", "sv);
134 first = true;
135
136 print(k);
137 if (terse_kvps())
138 print_unformatted("="sv);
139 else
140 print_unformatted(" = "sv);
141
142 const auto type = v.type();
143 TOML_ASSUME(type != node_type::none);
144 switch (type) {
145 case node_type::table:
146 print_inline(*reinterpret_cast<const table*>(&v));
147 break;
148 case node_type::array:
149 print(*reinterpret_cast<const array*>(&v));
150 break;
151 default:
152 print_value(v, type);
153 }
154 }
155
156 print_unformatted(" }"sv);
157 }
158
160 void toml_formatter::print(const array& arr) {
161 if (arr.empty()) {
162 print_unformatted("[]"sv);
163 return;
164 }
165
166 const auto original_indent = indent();
167 const auto multiline = TOML_ANON_NAMESPACE::toml_formatter_forces_multiline(
168 arr, 120u,
169 indent_columns() * static_cast<size_t>(original_indent < 0 ? 0 : original_indent));
170
171 print_unformatted("["sv);
172
173 if (multiline) {
174 if (original_indent < 0) indent(0);
175 if (indent_array_elements()) increase_indent();
176 } else
177 print_unformatted(' ');
178
179 for (size_t i = 0; i < arr.size(); i++) {
180 if (i > 0u) {
181 print_unformatted(',');
182 if (!multiline) print_unformatted(' ');
183 }
184
185 if (multiline) {
186 print_newline(true);
187 print_indent();
188 }
189
190 auto& v = arr[i];
191 const auto type = v.type();
192 TOML_ASSUME(type != node_type::none);
193 switch (type) {
194 case node_type::table:
195 print_inline(*reinterpret_cast<const table*>(&v));
196 break;
197 case node_type::array:
198 print(*reinterpret_cast<const array*>(&v));
199 break;
200 default:
201 print_value(v, type);
202 }
203 }
204 if (multiline) {
205 indent(original_indent);
206 print_newline(true);
207 print_indent();
208 } else
209 print_unformatted(' ');
210
211 print_unformatted("]"sv);
212 }
213
215 void toml_formatter::print(const table& tbl) {
216 static constexpr auto is_non_inline_array_of_tables = [](const node& n) noexcept {
217 const auto arr = n.as_array();
218 if (!arr || !arr->is_array_of_tables()) return false;
219
220 return !reinterpret_cast<const table*>(&(*arr)[0])->is_inline();
221 };
222
223 // values, arrays, and inline tables/table arrays
224 for (auto&& [k, v] : tbl) {
225 const auto type = v.type();
226 if ((type == node_type::table && !reinterpret_cast<const table*>(&v)->is_inline()) ||
227 (type == node_type::array && is_non_inline_array_of_tables(v)))
228 continue;
229
230 pending_table_separator_ = true;
231 print_newline();
232 print_indent();
233 print(k);
234 if (terse_kvps())
235 print_unformatted("="sv);
236 else
237 print_unformatted(" = "sv);
238 TOML_ASSUME(type != node_type::none);
239 switch (type) {
240 case node_type::table:
241 print_inline(*reinterpret_cast<const table*>(&v));
242 break;
243 case node_type::array:
244 print(*reinterpret_cast<const array*>(&v));
245 break;
246 default:
247 print_value(v, type);
248 }
249 }
250
251 const auto print_key_path = [&]() {
252 size_t i{};
253 for (const auto k : key_path_) {
254 if (i++) print_unformatted('.');
255 print(*k);
256 }
257 };
258
259 // non-inline tables
260 for (auto&& [k, v] : tbl) {
261 const auto type = v.type();
262 if (type != node_type::table || reinterpret_cast<const table*>(&v)->is_inline()) continue;
263 auto& child_tbl = *reinterpret_cast<const table*>(&v);
264
265 // we can skip indenting and emitting the headers for tables that only contain other tables
266 // (so we don't over-nest)
267 size_t child_value_count{}; // includes inline tables and non-table arrays
268 size_t child_table_count{};
269 size_t child_table_array_count{};
270 for (auto&& [child_k, child_v] : child_tbl) {
271 TOML_UNUSED(child_k);
272 const auto child_type = child_v.type();
273 TOML_ASSUME(child_type != node_type::none);
274 switch (child_type) {
275 case node_type::table:
276 if (reinterpret_cast<const table*>(&child_v)->is_inline())
277 child_value_count++;
278 else
279 child_table_count++;
280 break;
281
282 case node_type::array:
283 if (is_non_inline_array_of_tables(child_v))
284 child_table_array_count++;
285 else
286 child_value_count++;
287 break;
288
289 default:
290 child_value_count++;
291 }
292 }
293 bool skip_self = false;
294 if (child_value_count == 0u && (child_table_count > 0u || child_table_array_count > 0u))
295 skip_self = true;
296
297 key_path_.push_back(&k);
298
299 if (!skip_self) {
300 print_pending_table_separator();
301 if (indent_sub_tables()) increase_indent();
302 print_indent();
303 print_unformatted("["sv);
304 print_key_path();
305 print_unformatted("]"sv);
306 pending_table_separator_ = true;
307 }
308
309 print(child_tbl);
310
311 key_path_.pop_back();
312 if (!skip_self && indent_sub_tables()) decrease_indent();
313 }
314
315 // table arrays
316 for (auto&& [k, v] : tbl) {
317 if (!is_non_inline_array_of_tables(v)) continue;
318 auto& arr = *reinterpret_cast<const array*>(&v);
319
320 if (indent_sub_tables()) increase_indent();
321 key_path_.push_back(&k);
322
323 for (size_t i = 0; i < arr.size(); i++) {
324 print_pending_table_separator();
325 print_indent();
326 print_unformatted("[["sv);
327 print_key_path();
328 print_unformatted("]]"sv);
329 pending_table_separator_ = true;
330 print(*reinterpret_cast<const table*>(&arr[i]));
331 }
332
333 key_path_.pop_back();
334 if (indent_sub_tables()) decrease_indent();
335 }
336 }
337
339 void toml_formatter::print() {
340 if (dump_failed_parse_result()) return;
341
342 switch (auto source_type = source().type()) {
343 case node_type::table: {
344 auto& tbl = *reinterpret_cast<const table*>(&source());
345 if (tbl.is_inline())
346 print_inline(tbl);
347 else {
348 decrease_indent(); // so root kvps and tables have the same indent
349 print(tbl);
350 }
351 break;
352 }
353
354 case node_type::array:
355 print(*reinterpret_cast<const array*>(&source()));
356 break;
357
358 default:
359 print_value(source(), source_type);
360 }
361 }
362}
364
365#include "header_end.hpp"
366#endif // TOML_ENABLE_FORMATTERS
A TOML array.
Definition array.hpp:285
TOML_PURE_INLINE_GETTER node * get(size_t index) noexcept
Gets a pointer to the element at a specific index.
Definition array.hpp:685
TOML_PURE_GETTER bool is_array_of_tables() const noexcept final
Returns true if the array contains only tables.
Definition array.hpp:459
TOML_CONST_INLINE_GETTER node_type type() const noexcept final
Returns #toml::node_type::array.
Definition array.hpp:412
TOML_NODISCARD bool empty() const noexcept
Returns true if the array is empty.
Definition array.hpp:1129
TOML_CONST_INLINE_GETTER array * as_array() noexcept final
Returns a pointer to the array.
Definition array.hpp:541
TOML_NODISCARD size_t size() const noexcept
Returns the number of elements in the array.
Definition array.hpp:1136
void push_back(ElemType &&val, value_flags flags=preserve_source_value_flags)
Appends a new element to the end of the array.
Definition array.hpp:1633
A TOML table.
Definition table.hpp:220
TOML_PURE_INLINE_GETTER bool empty() const noexcept
Returns true if the table is empty.
Definition table.hpp:1112
TOML_CONST_INLINE_GETTER node_type type() const noexcept final
Returns #toml::node_type::table.
Definition table.hpp:292
TOML_PURE_INLINE_GETTER bool is_inline() const noexcept
Returns true if this table is an inline table.
Definition table.hpp:548
enum TOML_CLOSED_FLAGS_ENUM indent_sub_tables
Apply indentation to tables nested within other tables/arrays.
Definition forward_declarations.hpp:330
enum TOML_CLOSED_FLAGS_ENUM indent_array_elements
Apply indentation to array elements when the array is forced to wrap over multiple lines.
Definition forward_declarations.hpp:333
#define TOML_ANON_NAMESPACE_END
Definition preprocessor.hpp:1337
#define TOML_UNUSED(...)
Definition preprocessor.hpp:603
#define TOML_INTERNAL_LINKAGE
Definition preprocessor.hpp:1340
#define TOML_EXTERNAL_LINKAGE
Definition preprocessor.hpp:1339
#define TOML_UNREACHABLE
Definition preprocessor.hpp:515
#define TOML_NAMESPACE_END
Definition preprocessor.hpp:1320
#define TOML_ASSUME(expr)
Definition preprocessor.hpp:506
TOML_NAMESPACE_START
Definition toml_formatter.inl:107
TOML_ANON_NAMESPACE_START
Definition toml_formatter.inl:24
TOML_DISABLE_ARITHMETIC_WARNINGS
Definition toml_formatter.inl:22