#ifndef _C4_YML_EMIT_HPP_ #define _C4_YML_EMIT_HPP_ #ifndef _C4_YML_WRITER_HPP_ #include "./writer.hpp" #endif #ifndef _C4_YML_TREE_HPP_ #include "./tree.hpp" #endif #ifndef _C4_YML_NODE_HPP_ #include "./node.hpp" #endif namespace c4 { namespace yml { template class Emitter; template using EmitterOStream = Emitter>; using EmitterFile = Emitter; using EmitterBuf = Emitter; typedef enum { YAML = 0, JSON = 1 } EmitType_e; //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- template class Emitter : public Writer { public: using Writer::Writer; /** emit! * * When writing to a buffer, returns a substr of the emitted YAML. * If the given buffer has insufficient space, the returned span will * be null and its size will be the needed space. No writes are done * after the end of the buffer. * * When writing to a file, the returned substr will be null, but its * length will be set to the number of bytes written. */ substr emit(EmitType_e type, Tree const& t, size_t id, bool error_on_excess); /** @overload */ substr emit(EmitType_e type, Tree const& t, bool error_on_excess=true) { return emit(type, t, t.root_id(), error_on_excess); } /** @overload */ substr emit(EmitType_e type, NodeRef const& n, bool error_on_excess=true) { return emit(type, *n.tree(), n.id(), error_on_excess); } private: void _do_visit(Tree const& t, size_t id, size_t ilevel=0, size_t do_indent=1); void _do_visit_json(Tree const& t, size_t id); private: void _write(NodeScalar const& sc, NodeType flags, size_t level); void _write_json(NodeScalar const& sc, NodeType flags); void _write_scalar(csubstr s); void _write_scalar_json(csubstr s, bool as_key); void _write_scalar_block(csubstr s, size_t level, bool as_key); void _indent(size_t ilevel) { this->Writer::_do_write(indent_to(ilevel)); } enum { _keysc = (KEY|KEYREF|KEYANCH) | ~(VAL|VALREF|VALANCH), _valsc = ~(KEY|KEYREF|KEYANCH) | (VAL|VALREF|VALANCH), _keysc_json = (KEY) | ~(VAL), _valsc_json = ~(KEY) | (VAL), }; C4_ALWAYS_INLINE void _writek(Tree const& t, size_t id, size_t level) { _write(t.keysc(id), t.type(id) & ~(VAL|VALREF|VALANCH), level); } C4_ALWAYS_INLINE void _writev(Tree const& t, size_t id, size_t level) { _write(t.valsc(id), t.type(id) & ~(KEY|KEYREF|KEYANCH), level); } C4_ALWAYS_INLINE void _writek_json(Tree const& t, size_t id) { _write_json(t.keysc(id), t.type(id) & ~(VAL)); } C4_ALWAYS_INLINE void _writev_json(Tree const& t, size_t id) { _write_json(t.valsc(id), t.type(id) & ~(KEY)); } }; //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- /** emit YAML to the given file. A null file defaults to stdout. * Return the number of bytes written. */ inline size_t emit(Tree const& t, size_t id, FILE *f) { EmitterFile em(f); size_t len = em.emit(YAML, t, id, /*error_on_excess*/true).len; return len; } /** emit JSON to the given file. A null file defaults to stdout. * Return the number of bytes written. */ inline size_t emit_json(Tree const& t, size_t id, FILE *f) { EmitterFile em(f); size_t len = em.emit(JSON, t, id, /*error_on_excess*/true).len; return len; } /** emit YAML to the given file. A null file defaults to stdout. * Return the number of bytes written. * @overload */ inline size_t emit(Tree const& t, FILE *f=nullptr) { return emit(t, t.root_id(), f); } /** emit JSON to the given file. A null file defaults to stdout. * Return the number of bytes written. * @overload */ inline size_t emit_json(Tree const& t, FILE *f=nullptr) { return emit_json(t, t.root_id(), f); } /** emit YAML to the given file. A null file defaults to stdout. * Return the number of bytes written. * @overload */ inline size_t emit(NodeRef const& r, FILE *f=nullptr) { return emit(*r.tree(), r.id(), f); } /** emit JSON to the given file. A null file defaults to stdout. * Return the number of bytes written. * @overload */ inline size_t emit_json(NodeRef const& r, FILE *f=nullptr) { return emit_json(*r.tree(), r.id(), f); } //----------------------------------------------------------------------------- /** emit YAML to an STL-like ostream */ template inline OStream& operator<< (OStream& s, Tree const& t) { EmitterOStream em(s); em.emit(YAML, t.rootref()); return s; } /** emit YAML to an STL-like ostream * @overload */ template inline OStream& operator<< (OStream& s, NodeRef const& n) { EmitterOStream em(s); em.emit(YAML, n); return s; } struct as_json { NodeRef node; as_json(NodeRef const& n) : node(n) {} as_json(Tree const& t) : node(t.rootref()) {} }; template inline OStream& operator<< (OStream& s, as_json const& js) { EmitterOStream em(s); em.emit(JSON, js.node); return s; } //----------------------------------------------------------------------------- /** emit YAML to the given buffer. Return a substr trimmed to the emitted YAML. * Raise an error if the space in the buffer is insufficient. * @overload */ inline substr emit(Tree const& t, size_t id, substr buf, bool error_on_excess=true) { EmitterBuf em(buf); substr result = em.emit(YAML, t, id, error_on_excess); return result; } /** emit JSON to the given buffer. Return a substr trimmed to the emitted JSON. * Raise an error if the space in the buffer is insufficient. * @overload */ inline substr emit_json(Tree const& t, size_t id, substr buf, bool error_on_excess=true) { EmitterBuf em(buf); substr result = em.emit(JSON, t, id, error_on_excess); return result; } /** emit YAML to the given buffer. Return a substr trimmed to the emitted YAML. * Raise an error if the space in the buffer is insufficient. * @overload */ inline substr emit(Tree const& t, substr buf, bool error_on_excess=true) { return emit(t, t.root_id(), buf, error_on_excess); } /** emit JSON to the given buffer. Return a substr trimmed to the emitted JSON. * Raise an error if the space in the buffer is insufficient. * @overload */ inline substr emit_json(Tree const& t, substr buf, bool error_on_excess=true) { return emit_json(t, t.root_id(), buf, error_on_excess); } /** emit YAML to the given buffer. Return a substr trimmed to the emitted YAML. * Raise an error if the space in the buffer is insufficient. * @overload */ inline substr emit(NodeRef const& r, substr buf, bool error_on_excess=true) { return emit(*r.tree(), r.id(), buf, error_on_excess); } /** emit JSON to the given buffer. Return a substr trimmed to the emitted JSON. * Raise an error if the space in the buffer is insufficient. * @overload */ inline substr emit_json(NodeRef const& r, substr buf, bool error_on_excess=true) { return emit_json(*r.tree(), r.id(), buf, error_on_excess); } //----------------------------------------------------------------------------- /** emit+resize: YAML to the given std::string/std::vector-like container, * resizing it as needed to fit the emitted YAML. */ template substr emitrs(Tree const& t, size_t id, CharOwningContainer * cont) { substr buf = to_substr(*cont); substr ret = emit(t, id, buf, /*error_on_excess*/false); if(ret.str == nullptr && ret.len > 0) { cont->resize(ret.len); buf = to_substr(*cont); ret = emit(t, id, buf, /*error_on_excess*/true); } return ret; } /** emit+resize: JSON to the given std::string/std::vector-like container, * resizing it as needed to fit the emitted JSON. */ template substr emitrs_json(Tree const& t, size_t id, CharOwningContainer * cont) { substr buf = to_substr(*cont); substr ret = emit_json(t, id, buf, /*error_on_excess*/false); if(ret.str == nullptr && ret.len > 0) { cont->resize(ret.len); buf = to_substr(*cont); ret = emit_json(t, id, buf, /*error_on_excess*/true); } return ret; } /** emit+resize: YAML to the given std::string/std::vector-like container, * resizing it as needed to fit the emitted YAML. */ template CharOwningContainer emitrs(Tree const& t, size_t id) { CharOwningContainer c; emitrs(t, id, &c); return c; } /** emit+resize: JSON to the given std::string/std::vector-like container, * resizing it as needed to fit the emitted JSON. */ template CharOwningContainer emitrs_json(Tree const& t, size_t id) { CharOwningContainer c; emitrs_json(t, id, &c); return c; } /** emit+resize: YAML to the given std::string/std::vector-like container, * resizing it as needed to fit the emitted YAML. */ template substr emitrs(Tree const& t, CharOwningContainer * cont) { return emitrs(t, t.root_id(), cont); } /** emit+resize: JSON to the given std::string/std::vector-like container, * resizing it as needed to fit the emitted JSON. */ template substr emitrs_json(Tree const& t, CharOwningContainer * cont) { return emitrs_json(t, t.root_id(), cont); } /** emit+resize: YAML to the given std::string/std::vector-like container, * resizing it as needed to fit the emitted YAML. */ template CharOwningContainer emitrs(Tree const& t) { CharOwningContainer c; emitrs(t, t.root_id(), &c); return c; } /** emit+resize: JSON to the given std::string/std::vector-like container, * resizing it as needed to fit the emitted JSON. */ template CharOwningContainer emitrs_json(Tree const& t) { CharOwningContainer c; emitrs_json(t, t.root_id(), &c); return c; } /** emit+resize: YAML to the given std::string/std::vector-like container, * resizing it as needed to fit the emitted YAML. */ template substr emitrs(NodeRef const& n, CharOwningContainer * cont) { return emitrs(*n.tree(), n.id(), cont); } /** emit+resize: JSON to the given std::string/std::vector-like container, * resizing it as needed to fit the emitted JSON. */ template substr emitrs_json(NodeRef const& n, CharOwningContainer * cont) { return emitrs_json(*n.tree(), n.id(), cont); } /** emit+resize: YAML to the given std::string/std::vector-like container, * resizing it as needed to fit the emitted YAML. */ template CharOwningContainer emitrs(NodeRef const& n) { CharOwningContainer c; emitrs(*n.tree(), n.id(), &c); return c; } /** emit+resize: JSON to the given std::string/std::vector-like container, * resizing it as needed to fit the emitted JSON. */ template CharOwningContainer emitrs_json(NodeRef const& n) { CharOwningContainer c; emitrs_json(*n.tree(), n.id(), &c); return c; } } // namespace yml } // namespace c4 #include "./emit.def.hpp" #endif /* _C4_YML_EMIT_HPP_ */