emit.hpp (15910B)
1 #ifndef _C4_YML_EMIT_HPP_ 2 #define _C4_YML_EMIT_HPP_ 3 4 #ifndef _C4_YML_WRITER_HPP_ 5 #include "./writer.hpp" 6 #endif 7 8 #ifndef _C4_YML_TREE_HPP_ 9 #include "./tree.hpp" 10 #endif 11 12 #ifndef _C4_YML_NODE_HPP_ 13 #include "./node.hpp" 14 #endif 15 16 17 #define RYML_DEPRECATE_EMIT \ 18 RYML_DEPRECATED("use emit_yaml() instead. See https://github.com/biojppm/rapidyaml/issues/120") 19 #ifdef emit 20 #error "emit is defined, likely from a Qt include. This will cause a compilation error. See https://github.com/biojppm/rapidyaml/issues/120" 21 #endif 22 #define RYML_DEPRECATE_EMITRS \ 23 RYML_DEPRECATED("use emitrs_yaml() instead. See https://github.com/biojppm/rapidyaml/issues/120") 24 25 26 //----------------------------------------------------------------------------- 27 //----------------------------------------------------------------------------- 28 //----------------------------------------------------------------------------- 29 30 namespace c4 { 31 namespace yml { 32 33 template<class Writer> class Emitter; 34 35 template<class OStream> 36 using EmitterOStream = Emitter<WriterOStream<OStream>>; 37 using EmitterFile = Emitter<WriterFile>; 38 using EmitterBuf = Emitter<WriterBuf>; 39 40 typedef enum { 41 EMIT_YAML = 0, 42 EMIT_JSON = 1 43 } EmitType_e; 44 45 46 /** mark a tree or node to be emitted as json */ 47 struct as_json 48 { 49 Tree const* tree; 50 size_t node; 51 as_json(Tree const& t) : tree(&t), node(t.empty() ? NONE : t.root_id()) {} 52 as_json(Tree const& t, size_t id) : tree(&t), node(id) {} 53 as_json(ConstNodeRef const& n) : tree(n.tree()), node(n.id()) {} 54 }; 55 56 57 //----------------------------------------------------------------------------- 58 //----------------------------------------------------------------------------- 59 //----------------------------------------------------------------------------- 60 61 template<class Writer> 62 class Emitter : public Writer 63 { 64 public: 65 66 using Writer::Writer; 67 68 /** emit! 69 * 70 * When writing to a buffer, returns a substr of the emitted YAML. 71 * If the given buffer has insufficient space, the returned span will 72 * be null and its size will be the needed space. No writes are done 73 * after the end of the buffer. 74 * 75 * When writing to a file, the returned substr will be null, but its 76 * length will be set to the number of bytes written. */ 77 substr emit_as(EmitType_e type, Tree const& t, size_t id, bool error_on_excess); 78 /** emit starting at the root node */ 79 substr emit_as(EmitType_e type, Tree const& t, bool error_on_excess=true); 80 /** emit the given node */ 81 substr emit_as(EmitType_e type, ConstNodeRef const& n, bool error_on_excess=true); 82 83 private: 84 85 Tree const* C4_RESTRICT m_tree; 86 87 void _emit_yaml(size_t id); 88 void _do_visit_flow_sl(size_t id, size_t ilevel=0); 89 void _do_visit_flow_ml(size_t id, size_t ilevel=0, size_t do_indent=1); 90 void _do_visit_block(size_t id, size_t ilevel=0, size_t do_indent=1); 91 void _do_visit_block_container(size_t id, size_t next_level, size_t do_indent); 92 void _do_visit_json(size_t id); 93 94 private: 95 96 void _write(NodeScalar const& C4_RESTRICT sc, NodeType flags, size_t level); 97 void _write_json(NodeScalar const& C4_RESTRICT sc, NodeType flags); 98 99 void _write_doc(size_t id); 100 void _write_scalar(csubstr s, bool was_quoted); 101 void _write_scalar_json(csubstr s, bool as_key, bool was_quoted); 102 void _write_scalar_literal(csubstr s, size_t level, bool as_key, bool explicit_indentation=false); 103 void _write_scalar_folded(csubstr s, size_t level, bool as_key); 104 void _write_scalar_squo(csubstr s, size_t level); 105 void _write_scalar_dquo(csubstr s, size_t level); 106 void _write_scalar_plain(csubstr s, size_t level); 107 108 void _write_tag(csubstr tag) 109 { 110 if(!tag.begins_with('!')) 111 this->Writer::_do_write('!'); 112 this->Writer::_do_write(tag); 113 } 114 115 enum : type_bits { 116 _keysc = (KEY|KEYREF|KEYANCH|KEYQUO|_WIP_KEY_STYLE) | ~(VAL|VALREF|VALANCH|VALQUO|_WIP_VAL_STYLE), 117 _valsc = ~(KEY|KEYREF|KEYANCH|KEYQUO|_WIP_KEY_STYLE) | (VAL|VALREF|VALANCH|VALQUO|_WIP_VAL_STYLE), 118 _keysc_json = (KEY) | ~(VAL), 119 _valsc_json = ~(KEY) | (VAL), 120 }; 121 122 C4_ALWAYS_INLINE void _writek(size_t id, size_t level) { _write(m_tree->keysc(id), m_tree->_p(id)->m_type.type & ~_valsc, level); } 123 C4_ALWAYS_INLINE void _writev(size_t id, size_t level) { _write(m_tree->valsc(id), m_tree->_p(id)->m_type.type & ~_keysc, level); } 124 125 C4_ALWAYS_INLINE void _writek_json(size_t id) { _write_json(m_tree->keysc(id), m_tree->_p(id)->m_type.type & ~(VAL)); } 126 C4_ALWAYS_INLINE void _writev_json(size_t id) { _write_json(m_tree->valsc(id), m_tree->_p(id)->m_type.type & ~(KEY)); } 127 128 }; 129 130 131 //----------------------------------------------------------------------------- 132 //----------------------------------------------------------------------------- 133 //----------------------------------------------------------------------------- 134 135 /** emit YAML to the given file. A null file defaults to stdout. 136 * Return the number of bytes written. */ 137 inline size_t emit_yaml(Tree const& t, size_t id, FILE *f) 138 { 139 EmitterFile em(f); 140 return em.emit_as(EMIT_YAML, t, id, /*error_on_excess*/true).len; 141 } 142 RYML_DEPRECATE_EMIT inline size_t emit(Tree const& t, size_t id, FILE *f) 143 { 144 return emit_yaml(t, id, f); 145 } 146 147 /** emit JSON to the given file. A null file defaults to stdout. 148 * Return the number of bytes written. */ 149 inline size_t emit_json(Tree const& t, size_t id, FILE *f) 150 { 151 EmitterFile em(f); 152 return em.emit_as(EMIT_JSON, t, id, /*error_on_excess*/true).len; 153 } 154 155 156 /** emit YAML to the given file. A null file defaults to stdout. 157 * Return the number of bytes written. 158 * @overload */ 159 inline size_t emit_yaml(Tree const& t, FILE *f=nullptr) 160 { 161 EmitterFile em(f); 162 return em.emit_as(EMIT_YAML, t, /*error_on_excess*/true).len; 163 } 164 RYML_DEPRECATE_EMIT inline size_t emit(Tree const& t, FILE *f=nullptr) 165 { 166 return emit_yaml(t, f); 167 } 168 169 /** emit JSON to the given file. A null file defaults to stdout. 170 * Return the number of bytes written. 171 * @overload */ 172 inline size_t emit_json(Tree const& t, FILE *f=nullptr) 173 { 174 EmitterFile em(f); 175 return em.emit_as(EMIT_JSON, t, /*error_on_excess*/true).len; 176 } 177 178 179 /** emit YAML to the given file. A null file defaults to stdout. 180 * Return the number of bytes written. 181 * @overload */ 182 inline size_t emit_yaml(ConstNodeRef const& r, FILE *f=nullptr) 183 { 184 EmitterFile em(f); 185 return em.emit_as(EMIT_YAML, r, /*error_on_excess*/true).len; 186 } 187 RYML_DEPRECATE_EMIT inline size_t emit(ConstNodeRef const& r, FILE *f=nullptr) 188 { 189 return emit_yaml(r, f); 190 } 191 192 /** emit JSON to the given file. A null file defaults to stdout. 193 * Return the number of bytes written. 194 * @overload */ 195 inline size_t emit_json(ConstNodeRef const& r, FILE *f=nullptr) 196 { 197 EmitterFile em(f); 198 return em.emit_as(EMIT_JSON, r, /*error_on_excess*/true).len; 199 } 200 201 202 //----------------------------------------------------------------------------- 203 204 /** emit YAML to an STL-like ostream */ 205 template<class OStream> 206 inline OStream& operator<< (OStream& s, Tree const& t) 207 { 208 EmitterOStream<OStream> em(s); 209 em.emit_as(EMIT_YAML, t); 210 return s; 211 } 212 213 /** emit YAML to an STL-like ostream 214 * @overload */ 215 template<class OStream> 216 inline OStream& operator<< (OStream& s, ConstNodeRef const& n) 217 { 218 EmitterOStream<OStream> em(s); 219 em.emit_as(EMIT_YAML, n); 220 return s; 221 } 222 223 /** emit json to an STL-like stream */ 224 template<class OStream> 225 inline OStream& operator<< (OStream& s, as_json const& j) 226 { 227 EmitterOStream<OStream> em(s); 228 em.emit_as(EMIT_JSON, *j.tree, j.node, true); 229 return s; 230 } 231 232 233 //----------------------------------------------------------------------------- 234 235 236 /** emit YAML to the given buffer. Return a substr trimmed to the emitted YAML. 237 * @param error_on_excess Raise an error if the space in the buffer is insufficient. 238 * @overload */ 239 inline substr emit_yaml(Tree const& t, size_t id, substr buf, bool error_on_excess=true) 240 { 241 EmitterBuf em(buf); 242 return em.emit_as(EMIT_YAML, t, id, error_on_excess); 243 } 244 RYML_DEPRECATE_EMIT inline substr emit(Tree const& t, size_t id, substr buf, bool error_on_excess=true) 245 { 246 return emit_yaml(t, id, buf, error_on_excess); 247 } 248 249 /** emit JSON to the given buffer. Return a substr trimmed to the emitted JSON. 250 * @param error_on_excess Raise an error if the space in the buffer is insufficient. 251 * @overload */ 252 inline substr emit_json(Tree const& t, size_t id, substr buf, bool error_on_excess=true) 253 { 254 EmitterBuf em(buf); 255 return em.emit_as(EMIT_JSON, t, id, error_on_excess); 256 } 257 258 259 /** emit YAML to the given buffer. Return a substr trimmed to the emitted YAML. 260 * @param error_on_excess Raise an error if the space in the buffer is insufficient. 261 * @overload */ 262 inline substr emit_yaml(Tree const& t, substr buf, bool error_on_excess=true) 263 { 264 EmitterBuf em(buf); 265 return em.emit_as(EMIT_YAML, t, error_on_excess); 266 } 267 RYML_DEPRECATE_EMIT inline substr emit(Tree const& t, substr buf, bool error_on_excess=true) 268 { 269 return emit_yaml(t, buf, error_on_excess); 270 } 271 272 /** emit JSON to the given buffer. Return a substr trimmed to the emitted JSON. 273 * @param error_on_excess Raise an error if the space in the buffer is insufficient. 274 * @overload */ 275 inline substr emit_json(Tree const& t, substr buf, bool error_on_excess=true) 276 { 277 EmitterBuf em(buf); 278 return em.emit_as(EMIT_JSON, t, error_on_excess); 279 } 280 281 282 /** emit YAML to the given buffer. Return a substr trimmed to the emitted YAML. 283 * @param error_on_excess Raise an error if the space in the buffer is insufficient. 284 * @overload 285 */ 286 inline substr emit_yaml(ConstNodeRef const& r, substr buf, bool error_on_excess=true) 287 { 288 EmitterBuf em(buf); 289 return em.emit_as(EMIT_YAML, r, error_on_excess); 290 } 291 RYML_DEPRECATE_EMIT inline substr emit(ConstNodeRef const& r, substr buf, bool error_on_excess=true) 292 { 293 return emit_yaml(r, buf, error_on_excess); 294 } 295 296 /** emit JSON to the given buffer. Return a substr trimmed to the emitted JSON. 297 * @param error_on_excess Raise an error if the space in the buffer is insufficient. 298 * @overload 299 */ 300 inline substr emit_json(ConstNodeRef const& r, substr buf, bool error_on_excess=true) 301 { 302 EmitterBuf em(buf); 303 return em.emit_as(EMIT_JSON, r, error_on_excess); 304 } 305 306 307 //----------------------------------------------------------------------------- 308 309 /** emit+resize: emit YAML to the given std::string/std::vector-like 310 * container, resizing it as needed to fit the emitted YAML. */ 311 template<class CharOwningContainer> 312 substr emitrs_yaml(Tree const& t, size_t id, CharOwningContainer * cont) 313 { 314 substr buf = to_substr(*cont); 315 substr ret = emit_yaml(t, id, buf, /*error_on_excess*/false); 316 if(ret.str == nullptr && ret.len > 0) 317 { 318 cont->resize(ret.len); 319 buf = to_substr(*cont); 320 ret = emit_yaml(t, id, buf, /*error_on_excess*/true); 321 } 322 return ret; 323 } 324 template<class CharOwningContainer> 325 RYML_DEPRECATE_EMITRS substr emitrs(Tree const& t, size_t id, CharOwningContainer * cont) 326 { 327 return emitrs_yaml(t, id, cont); 328 } 329 330 /** emit+resize: emit JSON to the given std::string/std::vector-like 331 * container, resizing it as needed to fit the emitted JSON. */ 332 template<class CharOwningContainer> 333 substr emitrs_json(Tree const& t, size_t id, CharOwningContainer * cont) 334 { 335 substr buf = to_substr(*cont); 336 substr ret = emit_json(t, id, buf, /*error_on_excess*/false); 337 if(ret.str == nullptr && ret.len > 0) 338 { 339 cont->resize(ret.len); 340 buf = to_substr(*cont); 341 ret = emit_json(t, id, buf, /*error_on_excess*/true); 342 } 343 return ret; 344 } 345 346 347 /** emit+resize: emit YAML to the given std::string/std::vector-like 348 * container, resizing it as needed to fit the emitted YAML. */ 349 template<class CharOwningContainer> 350 CharOwningContainer emitrs_yaml(Tree const& t, size_t id) 351 { 352 CharOwningContainer c; 353 emitrs_yaml(t, id, &c); 354 return c; 355 } 356 template<class CharOwningContainer> 357 RYML_DEPRECATE_EMITRS CharOwningContainer emitrs(Tree const& t, size_t id) 358 { 359 CharOwningContainer c; 360 emitrs_yaml(t, id, &c); 361 return c; 362 } 363 364 /** emit+resize: emit JSON to the given std::string/std::vector-like 365 * container, resizing it as needed to fit the emitted JSON. */ 366 template<class CharOwningContainer> 367 CharOwningContainer emitrs_json(Tree const& t, size_t id) 368 { 369 CharOwningContainer c; 370 emitrs_json(t, id, &c); 371 return c; 372 } 373 374 375 /** emit+resize: YAML to the given std::string/std::vector-like 376 * container, resizing it as needed to fit the emitted YAML. */ 377 template<class CharOwningContainer> 378 substr emitrs_yaml(Tree const& t, CharOwningContainer * cont) 379 { 380 if(t.empty()) 381 return {}; 382 return emitrs_yaml(t, t.root_id(), cont); 383 } 384 template<class CharOwningContainer> 385 RYML_DEPRECATE_EMITRS substr emitrs(Tree const& t, CharOwningContainer * cont) 386 { 387 return emitrs_yaml(t, cont); 388 } 389 390 /** emit+resize: JSON to the given std::string/std::vector-like 391 * container, resizing it as needed to fit the emitted JSON. */ 392 template<class CharOwningContainer> 393 substr emitrs_json(Tree const& t, CharOwningContainer * cont) 394 { 395 if(t.empty()) 396 return {}; 397 return emitrs_json(t, t.root_id(), cont); 398 } 399 400 401 /** emit+resize: YAML to the given std::string/std::vector-like container, 402 * resizing it as needed to fit the emitted YAML. */ 403 template<class CharOwningContainer> 404 CharOwningContainer emitrs_yaml(Tree const& t) 405 { 406 CharOwningContainer c; 407 if(t.empty()) 408 return c; 409 emitrs_yaml(t, t.root_id(), &c); 410 return c; 411 } 412 template<class CharOwningContainer> 413 RYML_DEPRECATE_EMITRS CharOwningContainer emitrs(Tree const& t) 414 { 415 return emitrs_yaml<CharOwningContainer>(t); 416 } 417 418 /** emit+resize: JSON to the given std::string/std::vector-like container, 419 * resizing it as needed to fit the emitted JSON. */ 420 template<class CharOwningContainer> 421 CharOwningContainer emitrs_json(Tree const& t) 422 { 423 CharOwningContainer c; 424 if(t.empty()) 425 return c; 426 emitrs_json(t, t.root_id(), &c); 427 return c; 428 } 429 430 431 /** emit+resize: YAML to the given std::string/std::vector-like container, 432 * resizing it as needed to fit the emitted YAML. */ 433 template<class CharOwningContainer> 434 substr emitrs_yaml(ConstNodeRef const& n, CharOwningContainer * cont) 435 { 436 _RYML_CB_CHECK(n.tree()->callbacks(), n.valid()); 437 return emitrs_yaml(*n.tree(), n.id(), cont); 438 } 439 template<class CharOwningContainer> 440 RYML_DEPRECATE_EMITRS substr emitrs(ConstNodeRef const& n, CharOwningContainer * cont) 441 { 442 return emitrs_yaml(n, cont); 443 } 444 445 /** emit+resize: JSON to the given std::string/std::vector-like container, 446 * resizing it as needed to fit the emitted JSON. */ 447 template<class CharOwningContainer> 448 substr emitrs_json(ConstNodeRef const& n, CharOwningContainer * cont) 449 { 450 _RYML_CB_CHECK(n.tree()->callbacks(), n.valid()); 451 return emitrs_json(*n.tree(), n.id(), cont); 452 } 453 454 455 /** emit+resize: YAML to the given std::string/std::vector-like container, 456 * resizing it as needed to fit the emitted YAML. */ 457 template<class CharOwningContainer> 458 CharOwningContainer emitrs_yaml(ConstNodeRef const& n) 459 { 460 _RYML_CB_CHECK(n.tree()->callbacks(), n.valid()); 461 CharOwningContainer c; 462 emitrs_yaml(*n.tree(), n.id(), &c); 463 return c; 464 } 465 template<class CharOwningContainer> 466 RYML_DEPRECATE_EMITRS CharOwningContainer emitrs(ConstNodeRef const& n) 467 { 468 return emitrs_yaml<CharOwningContainer>(n); 469 } 470 471 /** emit+resize: JSON to the given std::string/std::vector-like container, 472 * resizing it as needed to fit the emitted JSON. */ 473 template<class CharOwningContainer> 474 CharOwningContainer emitrs_json(ConstNodeRef const& n) 475 { 476 _RYML_CB_CHECK(n.tree()->callbacks(), n.valid()); 477 CharOwningContainer c; 478 emitrs_json(*n.tree(), n.id(), &c); 479 return c; 480 } 481 482 } // namespace yml 483 } // namespace c4 484 485 #undef RYML_DEPRECATE_EMIT 486 #undef RYML_DEPRECATE_EMITRS 487 488 #include "c4/yml/emit.def.hpp" 489 490 #endif /* _C4_YML_EMIT_HPP_ */