rapidyaml 0.14.0
parse and emit YAML, and do it fast
Loading...
Searching...
No Matches
emit.hpp
Go to the documentation of this file.
1#ifndef _C4_YML_EMIT_HPP_
2#define _C4_YML_EMIT_HPP_
3
4/** @file emit.hpp Utilities to emit YAML and JSON. */
5
6#ifndef _C4_YML_WRITER_HPP_
7#include "./writer.hpp"
8#endif
9
10#ifndef _C4_YML_TREE_HPP_
11#include "./tree.hpp"
12#endif
13
14#ifndef _C4_YML_NODE_HPP_
15#include "./node.hpp"
16#endif
17
18
19C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")
20// NOLINTBEGIN(modernize-avoid-c-style-cast)
21
22
23//-----------------------------------------------------------------------------
24//-----------------------------------------------------------------------------
25//-----------------------------------------------------------------------------
26
27namespace c4 {
28namespace yml {
29
30/** @addtogroup doc_emit
31 *
32 * @{
33 */
34
35// fwd declarations
36template<class Writer> class Emitter;
37template<class OStream>
41
42
43//-----------------------------------------------------------------------------
44//-----------------------------------------------------------------------------
45//-----------------------------------------------------------------------------
46
47/** Specifies the type of content to emit */
48typedef enum { // NOLINT
49 EMIT_YAML = 0, ///< emit YAML
50 EMIT_JSON = 1, ///< emit JSON
51} EmitType_e;
52
53
54//-----------------------------------------------------------------------------
55//-----------------------------------------------------------------------------
56//-----------------------------------------------------------------------------
57
58/** A lightweight object containing options to be used when emitting. */
60{
61public:
62
63 /** @cond dev */
64 typedef enum : uint32_t { // NOLINT
65 EMIT_NONROOT_KEY = 1u << 0u,
66 EMIT_NONROOT_DASH = 1u << 1u,
67 EMIT_NONROOT_MARKUP = EMIT_NONROOT_KEY|EMIT_NONROOT_DASH,
68 INDENT_FLOW_ML = 1u << 2u,
69 JSON_ERR_ON_TAG = 1u << 3u,
70 JSON_ERR_ON_ANCHOR = 1u << 4u,
71 _JSON_ERR_MASK = JSON_ERR_ON_TAG|JSON_ERR_ON_ANCHOR,
72 DEFAULT_FLAGS = EMIT_NONROOT_KEY|INDENT_FLOW_ML,
73 } EmitOptionFlags_e;
74 /** @endcond */
75
76public:
77
78 /** @name option flags
79 *
80 * @{ */
81
82 C4_ALWAYS_INLINE bool emit_nonroot_key() const noexcept { return (m_option_flags & EMIT_NONROOT_KEY) != 0; }
83 EmitOptions& emit_nonroot_key(bool enabled) noexcept { m_option_flags = (EmitOptionFlags_e)(enabled ? (m_option_flags | EMIT_NONROOT_KEY) : (m_option_flags & ~EMIT_NONROOT_KEY)); return *this; }
84
85 C4_ALWAYS_INLINE bool emit_nonroot_dash() const noexcept { return (m_option_flags & EMIT_NONROOT_DASH) != 0; }
86 EmitOptions& emit_nonroot_dash(bool enabled) noexcept { m_option_flags = (EmitOptionFlags_e)(enabled ? (m_option_flags | EMIT_NONROOT_DASH) : (m_option_flags & ~EMIT_NONROOT_DASH)); return *this; }
87
88 C4_ALWAYS_INLINE bool indent_flow_ml() const noexcept { return (m_option_flags & INDENT_FLOW_ML) != 0; }
89 EmitOptions& indent_flow_ml(bool enabled) noexcept { m_option_flags = (EmitOptionFlags_e)(enabled ? (m_option_flags | INDENT_FLOW_ML) : (m_option_flags & ~INDENT_FLOW_ML)); return *this; }
90
91 C4_ALWAYS_INLINE EmitOptionFlags_e json_error_flags() const noexcept { return (EmitOptionFlags_e)(m_option_flags & _JSON_ERR_MASK); }
92 EmitOptions& json_error_flags(EmitOptionFlags_e d) noexcept { m_option_flags = (EmitOptionFlags_e)(d & _JSON_ERR_MASK); return *this; }
93
94 /** @} */
95
96public:
97
98 /** @name max depth for the emitted tree
99 *
100 * This makes the emitter fail when emitting trees exceeding the
101 * max_depth.
102 *
103 * @{ */
104 C4_ALWAYS_INLINE id_type max_depth() const noexcept { return m_max_depth; }
105 EmitOptions& max_depth(id_type d) noexcept { m_max_depth = d; return *this; }
106 static constexpr const id_type max_depth_default = 64;
107 /** @} */
108
109public:
110
111 bool operator== (const EmitOptions& that) const noexcept
112 {
113 return m_max_depth == that.m_max_depth &&
114 m_option_flags == that.m_option_flags;
115 }
116
117private:
118
119 /** @cond dev */
120 id_type m_max_depth{max_depth_default};
121 EmitOptionFlags_e m_option_flags{DEFAULT_FLAGS};
122 /** @endcond */
123};
124
125
126//-----------------------------------------------------------------------------
127//-----------------------------------------------------------------------------
128//-----------------------------------------------------------------------------
129
130/** A stateful emitter, for use with a writer such as @ref WriterBuf,
131 * @ref WriterFile, or @ref WriterOStream */
132template<class Writer>
133class Emitter : public Writer
134{
135public:
136
137 /** Construct the emitter and its internal Writer state.
138 *
139 * @param opts @ref EmitOptions
140 * @param args arguments to be forwarded to the constructor of the writer.
141 */
142 template<class ...WriterArgs>
143 Emitter(EmitOptions const& opts, WriterArgs &&...args)
144 : Writer(std::forward<WriterArgs>(args)...)
145 , m_tree()
146 , m_opts(opts)
147 , m_col()
148 , m_depth()
149 , m_ilevel()
150 , m_pws()
151 {}
152
153 /** Construct the emitter and its internal Writer state, using default emit options.
154 * @param args arguments to be forwarded to the constructor of the writer.
155 */
156 template<class ...WriterArgs>
157 Emitter(WriterArgs &&...args)
158 : Writer(std::forward<WriterArgs>(args)...)
159 , m_tree()
160 , m_opts()
161 , m_col()
162 , m_depth()
163 , m_ilevel()
164 , m_pws()
165 {}
166
167public:
168
169 /** emit!
170 *
171 * When writing to a buffer, returns a substr of the emitted YAML.
172 * If the given buffer has insufficient space, the returned substr
173 * will be null and its size will be the needed space. Whatever
174 * the size of the buffer, it is guaranteed that no writes are
175 * done past its end.
176 *
177 * When writing to a file, the returned substr will be null, but its
178 * length will be set to the number of bytes written.
179 *
180 * @param type specify what to emit (YAML or JSON)
181 * @param t the tree to emit
182 * @param id the id of the node to emit
183 * @param error_on_excess when true, an error is raised when the
184 * output buffer is too small for the emitted YAML/JSON
185 * */
186 substr emit_as(EmitType_e type, Tree const& t, id_type id, bool error_on_excess);
187 /** emit starting at the root node */
188 substr emit_as(EmitType_e type, Tree const& t, bool error_on_excess=true)
189 {
190 if(t.empty())
191 return {};
192 return this->emit_as(type, t, t.root_id(), error_on_excess);
193 }
194 /** emit starting at the given node */
195 substr emit_as(EmitType_e type, ConstNodeRef const& n, bool error_on_excess=true)
196 {
197 if(!n.readable())
198 return {};
199 return this->emit_as(type, *n.tree(), n.id(), error_on_excess);
200 }
201
202public:
203
204 /** get the emit options for this object */
205 EmitOptions const& options() const noexcept { return m_opts; }
206 /** set the emit options for this object */
207 void options(EmitOptions opts) noexcept { m_opts = opts; }
208
209 /** set the max depth for emitted trees (to prevent a stack overflow) */
210 void max_depth(id_type max_depth) noexcept { m_opts.max_depth(max_depth); }
211 /** get the max depth for emitted trees (to prevent a stack overflow) */
212 id_type max_depth() const noexcept { return m_opts.max_depth(); }
213
214private:
215
216 /** @cond dev */
217
218 void _emit_yaml(id_type id);
219
220 void _visit_stream(id_type id);
221 void _visit_doc(id_type id);
222 void _visit_doc_val(id_type id);
223 void _visit_blck_container(id_type id);
224 void _visit_flow_container(id_type id);
225
226 void _visit_flow_sl(id_type id);
227 void _visit_flow_sl_seq(id_type id);
228 void _visit_flow_sl_map(id_type id);
229
230 void _visit_flow_ml(id_type id);
231 void _visit_flow_ml_seq(id_type id);
232 void _visit_flow_ml_map(id_type id);
233
234 void _visit_blck(id_type id);
235 void _visit_blck_seq(id_type id);
236 void _visit_blck_map(id_type id);
237
238 void _top_open_entry(id_type id);
239 void _top_close_entry(id_type id);
240 void _blck_seq_open_entry(id_type id);
241 void _blck_map_open_entry(id_type id);
242 void _blck_close_entry(id_type id);
243 void _blck_write_qmrk(id_type id, csubstr key, type_bits type, bool has_qmrk_comments);
244 void _blck_write_scalar(csubstr str, type_bits type);
245
246 void _flow_seq_open_entry(id_type id);
247 void _flow_map_open_entry(id_type id);
248 void _flow_close_entry_sl(id_type id, id_type last_sibling);
249 void _flow_close_entry_ml(id_type id, id_type last_sibling);
250 void _flow_write_scalar(csubstr str, type_bits type);
251
252private:
253
254 void _json_emit(id_type id);
255 void _write_scalar_literal(csubstr s, id_type level);
256 void _write_scalar_folded(csubstr s, id_type level);
257 void _write_scalar_squo(csubstr s, id_type level);
258 void _write_scalar_dquo(csubstr s, id_type level);
259 void _write_scalar_plain(csubstr s, id_type level);
260
261 size_t _write_escaped_newlines(csubstr s, size_t i);
262 size_t _write_indented_block(csubstr s, size_t i, id_type level);
263
264private:
265
266 void _json_visit_ml(id_type id, id_type depth);
267 void _json_visit_sl(id_type id, id_type depth);
268 bool _json_maybe_write_naninf(csubstr s);
269 void _json_writek(id_type id, NodeType ty);
270 void _json_writev(id_type id, NodeType ty);
271 void _json_write_scalar_dquo(csubstr s);
272 void _json_write_number(csubstr s);
273
274private:
275
276 void _write_tag(csubstr tag)
277 {
278 if(!tag.begins_with('!'))
279 _write('!');
280 _write(tag);
281 }
282 void _write_ref(csubstr ref)
283 {
284 if(ref != "<<")
285 {
286 if(!ref.begins_with('*'))
287 _write('*');
288 _write(ref);
289 }
290 }
291
292private:
293
294 C4_ALWAYS_INLINE void _indent(id_type level)
295 {
296 size_t num = (size_t)(2u * level);
297 this->Writer::_do_write(' ', num);
298 m_col += num;
299 }
300
301 template<size_t N>
302 C4_ALWAYS_INLINE void _write(const char (&a)[N])
303 {
304 this->Writer::_do_write(std::forward<const char (&)[N]>(a));
305 m_col += N-1;
306 }
307 C4_ALWAYS_INLINE void _write(csubstr s)
308 {
309 this->Writer::_do_write(s);
310 m_col += s.len;
311 }
312 C4_ALWAYS_INLINE void _write(char c)
313 {
314 this->Writer::_do_write(c);
315 ++m_col;
316 }
317 C4_ALWAYS_INLINE void _write(char c, size_t num)
318 {
319 this->Writer::_do_write(c, num);
320 m_col += num;
321 }
322
323 /// write a newline and reset the column
324 C4_ALWAYS_INLINE void _newl()
325 {
326 this->Writer::_do_write('\n');
327 m_col = 0;
328 }
329
330private: // pending whitespace
331
332 /// pending whitespace
333 typedef enum : uint32_t { _PWS_NONE, _PWS_SPACE, _PWS_NEWL } Pws_e; // NOLINT
334
335 /// set pending whitespace, ignoring pending
336 C4_ALWAYS_INLINE void _pend_none() noexcept
337 {
338 m_pws = _PWS_NONE;
339 }
340 /// set pending whitespace, ignoring pending
341 C4_ALWAYS_INLINE void _pend_newl() noexcept
342 {
343 m_pws = _PWS_NEWL;
344 }
345 /// set pending whitespace, ignoring pending
346 C4_ALWAYS_INLINE void _pend_space() noexcept
347 {
348 m_pws = _PWS_SPACE;
349 }
350 /// write pending whitespace, and then set the next pending whitespace
351 C4_ALWAYS_INLINE void _write_pws_and_pend(Pws_e next=_PWS_NONE) noexcept
352 {
353 if(m_pws == _PWS_SPACE)
354 {
355 _write(' ');
356 }
357 else if(m_pws == _PWS_NEWL)
358 {
359 _newl();
360 _indent(m_ilevel);
361 }
362 m_pws = next;
363 }
364
365private:
366
367 Tree const* C4_RESTRICT m_tree;
368 EmitOptions m_opts;
369 size_t m_col;
370 id_type m_depth;
371 id_type m_ilevel;
372 Pws_e m_pws;
373
374private:
375
376 // g++-4.8 has problems with the operand types here...
377 #if defined(__GNUC__) && (__GNUC__ < 5) && (!defined(__clang__))
378 #pragma GCC diagnostic push
379 #pragma GCC diagnostic ignored "-Wparentheses"
380 #endif
381 enum : type_bits { // NOLINT
382 _styles_block_key = KEY_LITERAL|KEY_FOLDED,
383 _styles_block_val = VAL_LITERAL|VAL_FOLDED,
384 _styles_block = ((type_bits)_styles_block_key) | ((type_bits)_styles_block_val),
385 _styles_flow_key = KEY_STYLE & (~((type_bits)_styles_block_key)),
386 _styles_flow_val = VAL_STYLE & (~((type_bits)_styles_block_val)),
387 _styles_flow = ((type_bits)_styles_flow_key) | ((type_bits)_styles_flow_val),
388 _styles_squo = KEY_SQUO|VAL_SQUO,
389 _styles_dquo = KEY_DQUO|VAL_DQUO,
390 _styles_plain = KEY_PLAIN|VAL_PLAIN,
391 _styles_literal = KEY_LITERAL|VAL_LITERAL,
392 _styles_folded = KEY_FOLDED|VAL_FOLDED,
393 };
394 #if defined(__GNUC__) && (__GNUC__ < 5) && (!defined(__clang__))
395 #pragma GCC diagnostic pop
396 #endif
397
398 /** @endcond */
399};
400
401
402//-----------------------------------------------------------------------------
403//-----------------------------------------------------------------------------
404//-----------------------------------------------------------------------------
405
406/** @defgroup doc_emit_to_file Emit to file
407 *
408 * @{
409 */
410
411
412// emit from tree and node id -----------------------
413
414/** (1) emit YAML to the given file, starting at the given node. A null
415 * file defaults to stdout. Return the number of bytes written. */
416inline size_t emit_yaml(Tree const& t, id_type id, EmitOptions const& opts, FILE *f)
417{
418 EmitterFile em(opts, f);
419 return em.emit_as(EMIT_YAML, t, id, /*error_on_excess*/true).len;
420}
421/** (2) like (1), but use default emit options */
422inline size_t emit_yaml(Tree const& t, id_type id, FILE *f)
423{
424 EmitterFile em(f);
425 return em.emit_as(EMIT_YAML, t, id, /*error_on_excess*/true).len;
426}
427/** (1) emit JSON to the given file, starting at the given node. A null
428 * file defaults to stdout. Return the number of bytes written. */
429inline size_t emit_json(Tree const& t, id_type id, EmitOptions const& opts, FILE *f)
430{
431 EmitterFile em(opts, f);
432 return em.emit_as(EMIT_JSON, t, id, /*error_on_excess*/true).len;
433}
434/** (2) like (1), but use default emit options */
435inline size_t emit_json(Tree const& t, id_type id, FILE *f)
436{
437 EmitterFile em(f);
438 return em.emit_as(EMIT_JSON, t, id, /*error_on_excess*/true).len;
439}
440
441
442// emit from root -------------------------
443
444/** (1) emit YAML to the given file, starting at the root node. A null file defaults to stdout.
445 * Return the number of bytes written. */
446inline size_t emit_yaml(Tree const& t, EmitOptions const& opts, FILE *f=nullptr)
447{
448 EmitterFile em(opts, f);
449 return em.emit_as(EMIT_YAML, t, /*error_on_excess*/true).len;
450}
451/** (2) like (1), but use default emit options */
452inline size_t emit_yaml(Tree const& t, FILE *f=nullptr)
453{
454 EmitterFile em(f);
455 return em.emit_as(EMIT_YAML, t, /*error_on_excess*/true).len;
456}
457/** (1) emit JSON to the given file. A null file defaults to stdout.
458 * Return the number of bytes written. */
459inline size_t emit_json(Tree const& t, EmitOptions const& opts, FILE *f=nullptr)
460{
461 EmitterFile em(opts, f);
462 return em.emit_as(EMIT_JSON, t, /*error_on_excess*/true).len;
463}
464/** (2) like (1), but use default emit options */
465inline size_t emit_json(Tree const& t, FILE *f=nullptr)
466{
467 EmitterFile em(f);
468 return em.emit_as(EMIT_JSON, t, /*error_on_excess*/true).len;
469}
470
471
472// emit from ConstNodeRef ------------------------
473
474/** (1) emit YAML to the given file. A null file defaults to stdout.
475 * Return the number of bytes written. */
476inline size_t emit_yaml(ConstNodeRef const& r, EmitOptions const& opts, FILE *f=nullptr)
477{
478 if(!r.readable())
479 return {};
480 EmitterFile em(opts, f);
481 return em.emit_as(EMIT_YAML, r, /*error_on_excess*/true).len;
482}
483/** (2) like (1), but use default emit options */
484inline size_t emit_yaml(ConstNodeRef const& r, FILE *f=nullptr)
485{
486 if(!r.readable())
487 return {};
488 EmitterFile em(f);
489 return em.emit_as(EMIT_YAML, r, /*error_on_excess*/true).len;
490}
491/** (1) emit JSON to the given file. A null file defaults to stdout.
492 * Return the number of bytes written. */
493inline size_t emit_json(ConstNodeRef const& r, EmitOptions const& opts, FILE *f=nullptr)
494{
495 if(!r.readable())
496 return {};
497 EmitterFile em(opts, f);
498 return em.emit_as(EMIT_JSON, r, /*error_on_excess*/true).len;
499}
500/** (2) like (1), but use default emit options */
501inline size_t emit_json(ConstNodeRef const& r, FILE *f=nullptr)
502{
503 if(!r.readable())
504 return {};
505 EmitterFile em(f);
506 return em.emit_as(EMIT_JSON, r, /*error_on_excess*/true).len;
507}
508
509/** @} */
510
511
512//-----------------------------------------------------------------------------
513
514/** @defgroup doc_emit_to_ostream Emit to an STL-like ostream
515 *
516 * @{
517 */
518
519/** emit YAML to an STL-like ostream */
520template<class OStream>
521inline OStream& operator<< (OStream& s, Tree const& t)
522{
524 em.emit_as(EMIT_YAML, t);
525 return s;
526}
527
528/** emit YAML to an STL-like ostream
529 * @overload */
530template<class OStream>
531inline OStream& operator<< (OStream& s, ConstNodeRef const& n)
532{
533 if(!n.readable())
534 return s;
536 em.emit_as(EMIT_YAML, n);
537 return s;
538}
539
540/** mark a tree or node to be emitted as yaml when using @ref
541 * operator<<, with options. For example:
542 *
543 * ```cpp
544 * Tree t = parse_in_arena("{foo: bar}");
545 * std::cout << t; // emits YAML
546 * std::cout << as_yaml(t); // emits YAML, same as above
547 * std::cout << as_yaml(t, EmitOptions().max_depth(10)); // emits JSON with a max tree depth
548 * ```
549 *
550 * @see @ref operator<< */
552{
553 Tree const* tree;
556 as_json(Tree const& t, EmitOptions const& opts={}) : tree(&t), node(t.empty() ? NONE : t.root_id()), options(opts) {}
557 as_json(Tree const& t, id_type id, EmitOptions const& opts={}) : tree(&t), node(id), options(opts) {}
558 as_json(ConstNodeRef const& n, EmitOptions const& opts={}) : tree(n.tree()), node(n.id()), options(opts) {}
559};
560
561/** mark a tree or node to be emitted as yaml when using @ref
562 * operator<< . For example:
563 *
564 * ```cpp
565 * Tree t = parse_in_arena("{foo: bar}");
566 * std::cout << t; // emits YAML
567 * std::cout << as_json(t); // emits JSON
568 * std::cout << as_json(t, EmitOptions().max_depth(10)); // emits JSON with a max tree depth
569 * ```
570 *
571 * @see @ref operator<< */
573{
574 Tree const* tree;
577 as_yaml(Tree const& t, EmitOptions const& opts={}) : tree(&t), node(t.empty() ? NONE : t.root_id()), options(opts) {}
578 as_yaml(Tree const& t, id_type id, EmitOptions const& opts={}) : tree(&t), node(id), options(opts) {}
579 as_yaml(ConstNodeRef const& n, EmitOptions const& opts={}) : tree(n.tree()), node(n.id()), options(opts) {}
580};
581
582/** emit json to an STL-like stream */
583template<class OStream>
584inline OStream& operator<< (OStream& s, as_json const& j)
585{
586 if(!j.tree || j.tree->empty())
587 return s;
589 em.emit_as(EMIT_JSON, *j.tree, j.node != NONE ? j.node : j.tree->root_id(), true);
590 return s;
591}
592
593/** emit yaml to an STL-like stream */
594template<class OStream>
595inline OStream& operator<< (OStream& s, as_yaml const& y)
596{
597 if(!y.tree || y.tree->empty())
598 return s;
600 em.emit_as(EMIT_YAML, *y.tree, y.node != NONE ? y.node : y.tree->root_id(), true);
601 return s;
602}
603
604/** @} */
605
606
607//-----------------------------------------------------------------------------
608
609/** @defgroup doc_emit_to_buffer Emit to memory buffer
610 *
611 * @{
612 */
613
614// emit from tree and node id -----------------------
615
616/** (1) emit YAML to the given buffer. Return a substr trimmed to the emitted YAML.
617 * @param t the tree to emit.
618 * @param id the node where to start emitting.
619 * @param opts emit options.
620 * @param buf the output buffer.
621 * @param error_on_excess Raise an error if the space in the buffer is insufficient.
622 * @return a substr trimmed to the result in the output buffer. If the buffer is
623 * insufficient (when error_on_excess is false), the string pointer of the
624 * result will be set to null, and the length will report the required buffer size. */
625inline substr emit_yaml(Tree const& t, id_type id, EmitOptions const& opts, substr buf, bool error_on_excess=true)
626{
627 EmitterBuf em(opts, buf);
628 return em.emit_as(EMIT_YAML, t, id, error_on_excess);
629}
630/** (2) like (1), but use default emit options */
631inline substr emit_yaml(Tree const& t, id_type id, substr buf, bool error_on_excess=true)
632{
633 EmitterBuf em(buf);
634 return em.emit_as(EMIT_YAML, t, id, error_on_excess);
635}
636/** (1) emit JSON to the given buffer. Return a substr trimmed to the emitted JSON.
637 * @param t the tree to emit.
638 * @param id the node where to start emitting.
639 * @param opts emit options.
640 * @param buf the output buffer.
641 * @param error_on_excess Raise an error if the space in the buffer is insufficient.
642 * @return a substr trimmed to the result in the output buffer. If the buffer is
643 * insufficient (when error_on_excess is false), the string pointer of the
644 * result will be set to null, and the length will report the required buffer size. */
645inline substr emit_json(Tree const& t, id_type id, EmitOptions const& opts, substr buf, bool error_on_excess=true)
646{
647 EmitterBuf em(opts, buf);
648 return em.emit_as(EMIT_JSON, t, id, error_on_excess);
649}
650/** (2) like (1), but use default emit options */
651inline substr emit_json(Tree const& t, id_type id, substr buf, bool error_on_excess=true)
652{
653 EmitterBuf em(buf);
654 return em.emit_as(EMIT_JSON, t, id, error_on_excess);
655}
656
657
658// emit from root -------------------------
659
660/** (1) emit YAML to the given buffer. Return a substr trimmed to the emitted YAML.
661 * @param t the tree; will be emitted from the root node.
662 * @param opts emit options.
663 * @param buf the output buffer.
664 * @param error_on_excess Raise an error if the space in the buffer is insufficient.
665 * @return a substr trimmed to the result in the output buffer. If the buffer is
666 * insufficient (when error_on_excess is false), the string pointer of the
667 * result will be set to null, and the length will report the required buffer size. */
668inline substr emit_yaml(Tree const& t, EmitOptions const& opts, substr buf, bool error_on_excess=true)
669{
670 EmitterBuf em(opts, buf);
671 return em.emit_as(EMIT_YAML, t, error_on_excess);
672}
673/** (2) like (1), but use default emit options */
674inline substr emit_yaml(Tree const& t, substr buf, bool error_on_excess=true)
675{
676 EmitterBuf em(buf);
677 return em.emit_as(EMIT_YAML, t, error_on_excess);
678}
679/** (1) emit JSON to the given buffer. Return a substr trimmed to the emitted JSON.
680 * @param t the tree; will be emitted from the root node.
681 * @param opts emit options.
682 * @param buf the output buffer.
683 * @param error_on_excess Raise an error if the space in the buffer is insufficient.
684 * @return a substr trimmed to the result in the output buffer. If the buffer is
685 * insufficient (when error_on_excess is false), the string pointer of the
686 * result will be set to null, and the length will report the required buffer size. */
687inline substr emit_json(Tree const& t, EmitOptions const& opts, substr buf, bool error_on_excess=true)
688{
689 EmitterBuf em(opts, buf);
690 return em.emit_as(EMIT_JSON, t, error_on_excess);
691}
692/** (2) like (1), but use default emit options */
693inline substr emit_json(Tree const& t, substr buf, bool error_on_excess=true)
694{
695 EmitterBuf em(buf);
696 return em.emit_as(EMIT_JSON, t, error_on_excess);
697}
698
699
700// emit from ConstNodeRef ------------------------
701
702/** (1) emit YAML to the given buffer. Return a substr trimmed to the emitted YAML.
703 * @param r the starting node.
704 * @param buf the output buffer.
705 * @param opts emit options.
706 * @param error_on_excess Raise an error if the space in the buffer is insufficient.
707 * @return a substr trimmed to the result in the output buffer. If the buffer is
708 * insufficient (when error_on_excess is false), the string pointer of the
709 * result will be set to null, and the length will report the required buffer size. */
710inline substr emit_yaml(ConstNodeRef const& r, EmitOptions const& opts, substr buf, bool error_on_excess=true)
711{
712 if(!r.readable())
713 return {};
714 EmitterBuf em(opts, buf);
715 return em.emit_as(EMIT_YAML, r, error_on_excess);
716}
717/** (2) like (1), but use default emit options */
718inline substr emit_yaml(ConstNodeRef const& r, substr buf, bool error_on_excess=true)
719{
720 if(!r.readable())
721 return {};
722 EmitterBuf em(buf);
723 return em.emit_as(EMIT_YAML, r, error_on_excess);
724}
725/** (1) emit JSON to the given buffer. Return a substr trimmed to the emitted JSON.
726 * @param r the starting node.
727 * @param buf the output buffer.
728 * @param opts emit options.
729 * @param error_on_excess Raise an error if the space in the buffer is insufficient.
730 * @return a substr trimmed to the result in the output buffer. If the buffer is
731 * insufficient (when error_on_excess is false), the string pointer of the
732 * result will be set to null, and the length will report the required buffer size. */
733inline substr emit_json(ConstNodeRef const& r, EmitOptions const& opts, substr buf, bool error_on_excess=true)
734{
735 if(!r.readable())
736 return {};
737 EmitterBuf em(opts, buf);
738 return em.emit_as(EMIT_JSON, r, error_on_excess);
739}
740/** (2) like (1), but use default emit options */
741inline substr emit_json(ConstNodeRef const& r, substr buf, bool error_on_excess=true)
742{
743 if(!r.readable())
744 return {};
745 EmitterBuf em(buf);
746 return em.emit_as(EMIT_JSON, r, error_on_excess);
747}
748
749
750//-----------------------------------------------------------------------------
751
752/** @defgroup doc_emit_to_container Emit to resizeable container
753 *
754 * @{
755 */
756
757// emit from tree and node id ---------------------------
758
759/** (1) emit+resize: emit YAML to the given `std::string`/`std::vector`-like
760 * container, resizing it as needed to fit the emitted YAML. If @p append is
761 * set to true, the emitted YAML is appended at the end of the container.
762 *
763 * @return a substr trimmed to the emitted YAML (excluding the initial contents, when appending) */
764template<class CharOwningContainer>
765substr emitrs_yaml(Tree const& t, id_type id, EmitOptions const& opts, CharOwningContainer * cont, bool append=false)
766{
767 size_t startpos = append ? cont->size() : 0u;
768 cont->resize(cont->capacity()); // otherwise the first emit would be certain to fail
769 substr buf = to_substr(*cont).sub(startpos);
770 substr ret = emit_yaml(t, id, opts, buf, /*error_on_excess*/false);
771 if(ret.str == nullptr && ret.len > 0)
772 {
773 cont->resize(startpos + ret.len);
774 buf = to_substr(*cont).sub(startpos);
775 ret = emit_yaml(t, id, opts, buf, /*error_on_excess*/true);
776 }
777 else
778 {
779 cont->resize(startpos + ret.len);
780 }
781 return ret;
782}
783/** (2) like (1), but use default emit options */
784template<class CharOwningContainer>
785substr emitrs_yaml(Tree const& t, id_type id, CharOwningContainer * cont, bool append=false)
786{
787 return emitrs_yaml(t, id, EmitOptions{}, cont, append);
788}
789/** (1) emit+resize: emit JSON to the given `std::string`/`std::vector`-like
790 * container, resizing it as needed to fit the emitted JSON. If @p append is
791 * set to true, the emitted YAML is appended at the end of the container.
792 *
793 * @return a substr trimmed to the emitted JSON (excluding the initial contents, when appending) */
794template<class CharOwningContainer>
795substr emitrs_json(Tree const& t, id_type id, EmitOptions const& opts, CharOwningContainer * cont, bool append=false)
796{
797 const size_t startpos = append ? cont->size() : 0u;
798 cont->resize(cont->capacity()); // otherwise the first emit would be certain to fail
799 substr buf = to_substr(*cont).sub(startpos);
800 EmitterBuf em(opts, buf);
801 substr ret = emit_json(t, id, opts, buf, /*error_on_excess*/false);
802 if(ret.str == nullptr && ret.len > 0)
803 {
804 cont->resize(startpos + ret.len);
805 buf = to_substr(*cont).sub(startpos);
806 ret = emit_json(t, id, opts, buf, /*error_on_excess*/true);
807 }
808 else
809 {
810 cont->resize(startpos + ret.len);
811 }
812 return ret;
813}
814/** (2) like (1), but use default emit options */
815template<class CharOwningContainer>
816substr emitrs_json(Tree const& t, id_type id, CharOwningContainer * cont, bool append=false)
817{
818 return emitrs_json(t, id, EmitOptions{}, cont, append);
819}
820
821
822/** (3) emit+resize: YAML to a newly-created `std::string`/`std::vector`-like container. */
823template<class CharOwningContainer>
824CharOwningContainer emitrs_yaml(Tree const& t, id_type id, EmitOptions const& opts={})
825{
826 CharOwningContainer c;
827 emitrs_yaml(t, id, opts, &c);
828 return c;
829}
830/** (3) emit+resize: JSON to a newly-created `std::string`/`std::vector`-like container. */
831template<class CharOwningContainer>
832CharOwningContainer emitrs_json(Tree const& t, id_type id, EmitOptions const& opts={})
833{
834 CharOwningContainer c;
835 emitrs_json(t, id, opts, &c);
836 return c;
837}
838
839
840// emit from root -------------------------
841
842/** (1) emit+resize: YAML to the given `std::string`/`std::vector`-like
843 * container, resizing it as needed to fit the emitted YAML.
844 * @return a substr trimmed to the new emitted contents. */
845template<class CharOwningContainer>
846substr emitrs_yaml(Tree const& t, EmitOptions const& opts, CharOwningContainer * cont, bool append=false)
847{
848 if(t.empty())
849 return {};
850 return emitrs_yaml(t, t.root_id(), opts, cont, append);
851}
852/** (2) like (1), but use default emit options */
853template<class CharOwningContainer>
854substr emitrs_yaml(Tree const& t, CharOwningContainer * cont, bool append=false)
855{
856 if(t.empty())
857 return {};
858 return emitrs_yaml(t, t.root_id(), EmitOptions{}, cont, append);
859}
860/** (1) emit+resize: JSON to the given `std::string`/`std::vector`-like
861 * container, resizing it as needed to fit the emitted JSON.
862 * @return a substr trimmed to the new emitted contents. */
863template<class CharOwningContainer>
864substr emitrs_json(Tree const& t, EmitOptions const& opts, CharOwningContainer * cont, bool append=false)
865{
866 if(t.empty())
867 return {};
868 return emitrs_json(t, t.root_id(), opts, cont, append);
869}
870/** (2) like (1), but use default emit options */
871template<class CharOwningContainer>
872substr emitrs_json(Tree const& t, CharOwningContainer * cont, bool append=false)
873{
874 if(t.empty())
875 return {};
876 return emitrs_json(t, t.root_id(), EmitOptions{}, cont, append);
877}
878
879
880/** (3) emit+resize: YAML to a newly-created `std::string`/`std::vector`-like container. */
881template<class CharOwningContainer>
882CharOwningContainer emitrs_yaml(Tree const& t, EmitOptions const& opts={})
883{
884 CharOwningContainer c;
885 if(t.empty())
886 return c;
887 emitrs_yaml(t, t.root_id(), opts, &c);
888 return c;
889}
890/** (3) emit+resize: JSON to a newly-created `std::string`/`std::vector`-like container. */
891template<class CharOwningContainer>
892CharOwningContainer emitrs_json(Tree const& t, EmitOptions const& opts={})
893{
894 CharOwningContainer c;
895 if(t.empty())
896 return c;
897 emitrs_json(t, t.root_id(), opts, &c);
898 return c;
899}
900
901
902// emit from ConstNodeRef ------------------------
903
904
905/** (1) emit+resize: YAML to the given `std::string`/`std::vector`-like container,
906 * resizing it as needed to fit the emitted YAML.
907 * @return a substr trimmed to the new emitted contents */
908template<class CharOwningContainer>
909substr emitrs_yaml(ConstNodeRef const& n, EmitOptions const& opts, CharOwningContainer * cont, bool append=false)
910{
911 if(!n.readable())
912 return {};
913 return emitrs_yaml(*n.tree(), n.id(), opts, cont, append);
914}
915/** (2) like (1), but use default emit options */
916template<class CharOwningContainer>
917substr emitrs_yaml(ConstNodeRef const& n, CharOwningContainer * cont, bool append=false)
918{
919 if(!n.readable())
920 return {};
921 return emitrs_yaml(*n.tree(), n.id(), EmitOptions{}, cont, append);
922}
923/** (1) emit+resize: JSON to the given `std::string`/`std::vector`-like container,
924 * resizing it as needed to fit the emitted JSON.
925 * @return a substr trimmed to the new emitted contents */
926template<class CharOwningContainer>
927substr emitrs_json(ConstNodeRef const& n, EmitOptions const& opts, CharOwningContainer * cont, bool append=false)
928{
929 if(!n.readable())
930 return {};
931 return emitrs_json(*n.tree(), n.id(), opts, cont, append);
932}
933/** (2) like (1), but use default emit options */
934template<class CharOwningContainer>
935substr emitrs_json(ConstNodeRef const& n, CharOwningContainer * cont, bool append=false)
936{
937 if(!n.readable())
938 return {};
939 return emitrs_json(*n.tree(), n.id(), EmitOptions{}, cont, append);
940}
941
942
943/** (3) emit+resize: YAML to a newly-created `std::string`/`std::vector`-like container. */
944template<class CharOwningContainer>
945CharOwningContainer emitrs_yaml(ConstNodeRef const& n, EmitOptions const& opts={})
946{
947 if(!n.readable())
948 return {};
949 CharOwningContainer c;
950 emitrs_yaml(*n.tree(), n.id(), opts, &c);
951 return c;
952}
953/** (3) emit+resize: JSON to a newly-created `std::string`/`std::vector`-like container. */
954template<class CharOwningContainer>
955CharOwningContainer emitrs_json(ConstNodeRef const& n, EmitOptions const& opts={})
956{
957 if(!n.readable())
958 return {};
959 CharOwningContainer c;
960 emitrs_json(*n.tree(), n.id(), opts, &c);
961 return c;
962}
963
964
965/** @} */
966
967
968//-----------------------------------------------------------------------------
969
970/** @cond dev */
971
972#define RYML_DEPRECATE_EMIT \
973 RYML_DEPRECATED("use emit_yaml() instead. " \
974 "See https://github.com/biojppm/rapidyaml/issues/120")
975#define RYML_DEPRECATE_EMITRS \
976 RYML_DEPRECATED("use emitrs_yaml() instead. " \
977 "See https://github.com/biojppm/rapidyaml/issues/120")
978
979// workaround for Qt emit which is a macro;
980// see https://github.com/biojppm/rapidyaml/issues/120.
981// emit is defined in qobjectdefs.h (as an empty define).
982#ifdef emit
983#define RYML_TMP_EMIT_
984#undef emit
985#endif
986
987RYML_DEPRECATE_EMIT inline size_t emit(Tree const& t, id_type id, FILE *f)
988{
989 return emit_yaml(t, id, f);
990}
991RYML_DEPRECATE_EMIT inline size_t emit(Tree const& t, FILE *f=nullptr)
992{
993 return emit_yaml(t, f);
994}
995RYML_DEPRECATE_EMIT inline size_t emit(ConstNodeRef const& r, FILE *f=nullptr)
996{
997 return emit_yaml(r, f);
998}
999
1000RYML_DEPRECATE_EMIT inline substr emit(Tree const& t, id_type id, substr buf, bool error_on_excess=true)
1001{
1002 return emit_yaml(t, id, buf, error_on_excess);
1003}
1004RYML_DEPRECATE_EMIT inline substr emit(Tree const& t, substr buf, bool error_on_excess=true)
1005{
1006 return emit_yaml(t, buf, error_on_excess);
1007}
1008RYML_DEPRECATE_EMIT inline substr emit(ConstNodeRef const& r, substr buf, bool error_on_excess=true)
1009{
1010 return emit_yaml(r, buf, error_on_excess);
1011}
1012
1013#ifdef RYML_TMP_EMIT_
1014#define emit
1015#undef RYML_TMP_EMIT_
1016#endif
1017
1018template<class CharOwningContainer>
1019RYML_DEPRECATE_EMITRS substr emitrs(Tree const& t, id_type id, CharOwningContainer * cont)
1020{
1021 return emitrs_yaml(t, id, cont);
1022}
1023template<class CharOwningContainer>
1024RYML_DEPRECATE_EMITRS CharOwningContainer emitrs(Tree const& t, id_type id)
1025{
1026 return emitrs_yaml<CharOwningContainer>(t, id);
1027}
1028template<class CharOwningContainer>
1029RYML_DEPRECATE_EMITRS substr emitrs(Tree const& t, CharOwningContainer * cont)
1030{
1031 return emitrs_yaml(t, cont);
1032}
1033template<class CharOwningContainer>
1034RYML_DEPRECATE_EMITRS CharOwningContainer emitrs(Tree const& t)
1035{
1036 return emitrs_yaml<CharOwningContainer>(t);
1037}
1038template<class CharOwningContainer>
1039RYML_DEPRECATE_EMITRS substr emitrs(ConstNodeRef const& n, CharOwningContainer * cont)
1040{
1041 return emitrs_yaml(n, cont);
1042}
1043template<class CharOwningContainer>
1044RYML_DEPRECATE_EMITRS CharOwningContainer emitrs(ConstNodeRef const& n)
1045{
1046 return emitrs_yaml<CharOwningContainer>(n);
1047}
1048
1049/** @endcond */
1050
1051
1052} // namespace yml
1053} // namespace c4
1054
1055// NOLINTEND(modernize-avoid-c-style-cast)
1056C4_SUPPRESS_WARNING_GCC_CLANG_POP
1057
1058#undef RYML_DEPRECATE_EMIT
1059#undef RYML_DEPRECATE_EMITRS
1060
1061#include "c4/yml/emit.def.hpp" // NOLINT
1062
1063#endif /* _C4_YML_EMIT_HPP_ */
Holds a pointer to an existing tree, and a node id.
Definition node.hpp:827
id_type id() const noexcept
Definition node.hpp:899
Tree const * tree() const noexcept
Definition node.hpp:898
bool readable() const noexcept
because a ConstNodeRef cannot be used to write to the tree, readable() has the same meaning as !...
Definition node.hpp:884
A stateful emitter, for use with a writer such as WriterBuf, WriterFile, or WriterOStream.
Definition emit.hpp:134
id_type root_id() const
Get the id of the root node. The tree must not be empty.
Definition tree.hpp:337
bool empty() const
Definition tree.hpp:284
Definitions for emit functions.
EmitOptionFlags_e json_error_flags() const noexcept
Definition emit.hpp:91
id_type max_depth() const noexcept
get the max depth for emitted trees (to prevent a stack overflow)
Definition emit.hpp:212
substr emit_as(EmitType_e type, ConstNodeRef const &n, bool error_on_excess=true)
emit starting at the given node
Definition emit.hpp:195
EmitOptions const & options() const noexcept
get the emit options for this object
Definition emit.hpp:205
bool emit_nonroot_key() const noexcept
Definition emit.hpp:82
void max_depth(id_type max_depth) noexcept
set the max depth for emitted trees (to prevent a stack overflow)
Definition emit.hpp:210
as_yaml(Tree const &t, EmitOptions const &opts={})
Definition emit.hpp:577
Tree const * tree
Definition emit.hpp:574
void options(EmitOptions opts) noexcept
set the emit options for this object
Definition emit.hpp:207
substr emit_as(EmitType_e type, Tree const &t, bool error_on_excess=true)
emit starting at the root node
Definition emit.hpp:188
id_type max_depth() const noexcept
Definition emit.hpp:104
Tree const * tree
Definition emit.hpp:553
as_yaml(Tree const &t, id_type id, EmitOptions const &opts={})
Definition emit.hpp:578
Emitter(WriterArgs &&...args)
Construct the emitter and its internal Writer state, using default emit options.
Definition emit.hpp:157
EmitOptions options
Definition emit.hpp:576
Emitter(EmitOptions const &opts, WriterArgs &&...args)
Construct the emitter and its internal Writer state.
Definition emit.hpp:143
substr emit_as(EmitType_e type, Tree const &t, id_type id, bool error_on_excess)
emit!
Definition emit.def.hpp:20
EmitOptions & max_depth(id_type d) noexcept
Definition emit.hpp:105
EmitOptions options
Definition emit.hpp:555
as_json(Tree const &t, EmitOptions const &opts={})
Definition emit.hpp:556
EmitOptions & indent_flow_ml(bool enabled) noexcept
Definition emit.hpp:89
as_yaml(ConstNodeRef const &n, EmitOptions const &opts={})
Definition emit.hpp:579
bool indent_flow_ml() const noexcept
Definition emit.hpp:88
EmitOptions & emit_nonroot_key(bool enabled) noexcept
Definition emit.hpp:83
bool emit_nonroot_dash() const noexcept
Definition emit.hpp:85
as_json(ConstNodeRef const &n, EmitOptions const &opts={})
Definition emit.hpp:558
EmitOptions & emit_nonroot_dash(bool enabled) noexcept
Definition emit.hpp:86
static constexpr const id_type max_depth_default
Definition emit.hpp:106
as_json(Tree const &t, id_type id, EmitOptions const &opts={})
Definition emit.hpp:557
id_type node
Definition emit.hpp:575
id_type node
Definition emit.hpp:554
EmitOptions & json_error_flags(EmitOptionFlags_e d) noexcept
Definition emit.hpp:92
substr emitrs_json(Tree const &t, id_type id, EmitOptions const &opts, CharOwningContainer *cont, bool append=false)
(1) emit+resize: emit JSON to the given std::string/std::vector-like container, resizing it as needed...
Definition emit.hpp:795
substr emitrs_yaml(Tree const &t, id_type id, EmitOptions const &opts, CharOwningContainer *cont, bool append=false)
(1) emit+resize: emit YAML to the given std::string/std::vector-like container, resizing it as needed...
Definition emit.hpp:765
size_t emit_yaml(Tree const &t, id_type id, EmitOptions const &opts, FILE *f)
(1) emit YAML to the given file, starting at the given node.
Definition emit.hpp:416
size_t emit_json(Tree const &t, id_type id, EmitOptions const &opts, FILE *f)
(1) emit JSON to the given file, starting at the given node.
Definition emit.hpp:429
Emitter< WriterBuf > EmitterBuf
Definition emit.hpp:40
Emitter< WriterFile > EmitterFile
Definition emit.hpp:39
Emitter< WriterOStream< OStream > > EmitterOStream
Definition emit.hpp:38
EmitType_e
Specifies the type of content to emit.
Definition emit.hpp:48
@ EMIT_YAML
emit YAML
Definition emit.hpp:49
@ EMIT_JSON
emit JSON
Definition emit.hpp:50
uint32_t type_bits
the integral type necessary to cover all the bits for NodeType_e
Definition node_type.hpp:30
@ KEY_DQUO
mark key scalar as double quoted "
Definition node_type.hpp:69
@ VAL_FOLDED
mark val scalar as multiline, block folded >
Definition node_type.hpp:66
@ VAL_SQUO
mark val scalar as single quoted '
Definition node_type.hpp:68
@ KEY_STYLE
mask of all the scalar styles for key (not container styles!)
Definition node_type.hpp:92
@ VAL_PLAIN
mark val scalar as plain scalar (unquoted, even when multiline)
Definition node_type.hpp:72
@ VAL_DQUO
mark val scalar as double quoted "
Definition node_type.hpp:70
@ KEY_SQUO
mark key scalar as single quoted '
Definition node_type.hpp:67
@ VAL_LITERAL
mark val scalar as multiline, block literal |
Definition node_type.hpp:64
@ KEY_LITERAL
mark key scalar as multiline, block literal |
Definition node_type.hpp:63
@ KEY_PLAIN
mark key scalar as plain scalar (unquoted, even when multiline)
Definition node_type.hpp:71
@ KEY_FOLDED
mark key scalar as multiline, block folded >
Definition node_type.hpp:65
Key< K > key(K &&k)
Definition node.hpp:43
substr to_substr(char(&s)[N]) noexcept
Definition substr.hpp:2377
basic_substring< char > substr
a mutable string view
Definition substr.hpp:2356
basic_substring< const char > csubstr
an immutable string view
Definition substr.hpp:2357
RYML_ID_TYPE id_type
The type of a node id in the YAML tree; to override the default type, define the macro RYML_ID_TYPE t...
Definition common.hpp:249
@ NONE
an index to none
Definition common.hpp:256
(Undefined by default) Use shorter error message from checks/asserts: do not show the check condition...
Definition common.cpp:14
Node classes.
bool begins_with(const C c) const noexcept
true if the first character of the string is c
Definition substr.hpp:851
size_t len
the length of the substring
Definition substr.hpp:218
C * str
a restricted pointer to the first character of the substring
Definition substr.hpp:216
A lightweight object containing options to be used when emitting.
Definition emit.hpp:60
wraps a NodeType_e element with some syntactic sugar and predicates
mark a tree or node to be emitted as yaml when using operator<<, with options.
Definition emit.hpp:552
mark a tree or node to be emitted as yaml when using operator<< .
Definition emit.hpp:573