#include #include "emitterutils.h" #include "indentation.h" // IWYU pragma: keep #include "yaml-cpp/emitter.h" #include "yaml-cpp/emitterdef.h" #include "yaml-cpp/emittermanip.h" #include "yaml-cpp/exceptions.h" // IWYU pragma: keep namespace YAML { class Binary; struct _Null; Emitter::Emitter() : m_pState(new EmitterState), m_stream{} {} Emitter::Emitter(std::ostream& stream) : m_pState(new EmitterState), m_stream(stream) {} Emitter::~Emitter() = default; const char* Emitter::c_str() const { return m_stream.str(); } std::size_t Emitter::size() const { return m_stream.pos(); } // state checking bool Emitter::good() const { return m_pState->good(); } const std::string Emitter::GetLastError() const { return m_pState->GetLastError(); } // global setters bool Emitter::SetOutputCharset(EMITTER_MANIP value) { return m_pState->SetOutputCharset(value, FmtScope::Global); } bool Emitter::SetStringFormat(EMITTER_MANIP value) { return m_pState->SetStringFormat(value, FmtScope::Global); } bool Emitter::SetBoolFormat(EMITTER_MANIP value) { bool ok = false; if (m_pState->SetBoolFormat(value, FmtScope::Global)) ok = true; if (m_pState->SetBoolCaseFormat(value, FmtScope::Global)) ok = true; if (m_pState->SetBoolLengthFormat(value, FmtScope::Global)) ok = true; return ok; } bool Emitter::SetNullFormat(EMITTER_MANIP value) { return m_pState->SetNullFormat(value, FmtScope::Global); } bool Emitter::SetIntBase(EMITTER_MANIP value) { return m_pState->SetIntFormat(value, FmtScope::Global); } bool Emitter::SetSeqFormat(EMITTER_MANIP value) { return m_pState->SetFlowType(GroupType::Seq, value, FmtScope::Global); } bool Emitter::SetMapFormat(EMITTER_MANIP value) { bool ok = false; if (m_pState->SetFlowType(GroupType::Map, value, FmtScope::Global)) ok = true; if (m_pState->SetMapKeyFormat(value, FmtScope::Global)) ok = true; return ok; } bool Emitter::SetIndent(std::size_t n) { return m_pState->SetIndent(n, FmtScope::Global); } bool Emitter::SetPreCommentIndent(std::size_t n) { return m_pState->SetPreCommentIndent(n, FmtScope::Global); } bool Emitter::SetPostCommentIndent(std::size_t n) { return m_pState->SetPostCommentIndent(n, FmtScope::Global); } bool Emitter::SetFloatPrecision(std::size_t n) { return m_pState->SetFloatPrecision(n, FmtScope::Global); } bool Emitter::SetDoublePrecision(std::size_t n) { return m_pState->SetDoublePrecision(n, FmtScope::Global); } void Emitter::RestoreGlobalModifiedSettings() { m_pState->RestoreGlobalModifiedSettings(); } // SetLocalValue // . Either start/end a group, or set a modifier locally Emitter& Emitter::SetLocalValue(EMITTER_MANIP value) { if (!good()) return *this; switch (value) { case BeginDoc: EmitBeginDoc(); break; case EndDoc: EmitEndDoc(); break; case BeginSeq: EmitBeginSeq(); break; case EndSeq: EmitEndSeq(); break; case BeginMap: EmitBeginMap(); break; case EndMap: EmitEndMap(); break; case Key: case Value: // deprecated (these can be deduced by the parity of nodes in a map) break; case TagByKind: EmitKindTag(); break; case Newline: EmitNewline(); break; default: m_pState->SetLocalValue(value); break; } return *this; } Emitter& Emitter::SetLocalIndent(const _Indent& indent) { m_pState->SetIndent(indent.value, FmtScope::Local); return *this; } Emitter& Emitter::SetLocalPrecision(const _Precision& precision) { if (precision.floatPrecision >= 0) m_pState->SetFloatPrecision(precision.floatPrecision, FmtScope::Local); if (precision.doublePrecision >= 0) m_pState->SetDoublePrecision(precision.doublePrecision, FmtScope::Local); return *this; } // EmitBeginDoc void Emitter::EmitBeginDoc() { if (!good()) return; if (m_pState->CurGroupType() != GroupType::NoType) { m_pState->SetError("Unexpected begin document"); return; } if (m_pState->HasAnchor() || m_pState->HasTag()) { m_pState->SetError("Unexpected begin document"); return; } if (m_stream.col() > 0) m_stream << "\n"; m_stream << "---\n"; m_pState->StartedDoc(); } // EmitEndDoc void Emitter::EmitEndDoc() { if (!good()) return; if (m_pState->CurGroupType() != GroupType::NoType) { m_pState->SetError("Unexpected begin document"); return; } if (m_pState->HasAnchor() || m_pState->HasTag()) { m_pState->SetError("Unexpected begin document"); return; } if (m_stream.col() > 0) m_stream << "\n"; m_stream << "...\n"; } // EmitBeginSeq void Emitter::EmitBeginSeq() { if (!good()) return; PrepareNode(m_pState->NextGroupType(GroupType::Seq)); m_pState->StartedGroup(GroupType::Seq); } // EmitEndSeq void Emitter::EmitEndSeq() { if (!good()) return; FlowType::value originalType = m_pState->CurGroupFlowType(); if (m_pState->CurGroupChildCount() == 0) m_pState->ForceFlow(); if (m_pState->CurGroupFlowType() == FlowType::Flow) { if (m_stream.comment()) m_stream << "\n"; m_stream << IndentTo(m_pState->CurIndent()); if (originalType == FlowType::Block) { m_stream << "["; } else { if (m_pState->CurGroupChildCount() == 0 && !m_pState->HasBegunNode()) m_stream << "["; } m_stream << "]"; } m_pState->EndedGroup(GroupType::Seq); } // EmitBeginMap void Emitter::EmitBeginMap() { if (!good()) return; PrepareNode(m_pState->NextGroupType(GroupType::Map)); m_pState->StartedGroup(GroupType::Map); } // EmitEndMap void Emitter::EmitEndMap() { if (!good()) return; FlowType::value originalType = m_pState->CurGroupFlowType(); if (m_pState->CurGroupChildCount() == 0) m_pState->ForceFlow(); if (m_pState->CurGroupFlowType() == FlowType::Flow) { if (m_stream.comment()) m_stream << "\n"; m_stream << IndentTo(m_pState->CurIndent()); if (originalType == FlowType::Block) { m_stream << "{"; } else { if (m_pState->CurGroupChildCount() == 0 && !m_pState->HasBegunNode()) m_stream << "{"; } m_stream << "}"; } m_pState->EndedGroup(GroupType::Map); } // EmitNewline void Emitter::EmitNewline() { if (!good()) return; PrepareNode(EmitterNodeType::NoType); m_stream << "\n"; m_pState->SetNonContent(); } bool Emitter::CanEmitNewline() const { return true; } // Put the stream in a state so we can simply write the next node // E.g., if we're in a sequence, write the "- " void Emitter::PrepareNode(EmitterNodeType::value child) { switch (m_pState->CurGroupNodeType()) { case EmitterNodeType::NoType: PrepareTopNode(child); break; case EmitterNodeType::FlowSeq: FlowSeqPrepareNode(child); break; case EmitterNodeType::BlockSeq: BlockSeqPrepareNode(child); break; case EmitterNodeType::FlowMap: FlowMapPrepareNode(child); break; case EmitterNodeType::BlockMap: BlockMapPrepareNode(child); break; case EmitterNodeType::Property: case EmitterNodeType::Scalar: assert(false); break; } } void Emitter::PrepareTopNode(EmitterNodeType::value child) { if (child == EmitterNodeType::NoType) return; if (m_pState->CurGroupChildCount() > 0 && m_stream.col() > 0) EmitBeginDoc(); switch (child) { case EmitterNodeType::NoType: break; case EmitterNodeType::Property: case EmitterNodeType::Scalar: case EmitterNodeType::FlowSeq: case EmitterNodeType::FlowMap: // TODO: if we were writing null, and // we wanted it blank, we wouldn't want a space SpaceOrIndentTo(m_pState->HasBegunContent(), 0); break; case EmitterNodeType::BlockSeq: case EmitterNodeType::BlockMap: if (m_pState->HasBegunNode()) m_stream << "\n"; break; } } void Emitter::FlowSeqPrepareNode(EmitterNodeType::value child) { const std::size_t lastIndent = m_pState->LastIndent(); if (!m_pState->HasBegunNode()) { if (m_stream.comment()) m_stream << "\n"; m_stream << IndentTo(lastIndent); if (m_pState->CurGroupChildCount() == 0) m_stream << "["; else m_stream << ","; } switch (child) { case EmitterNodeType::NoType: break; case EmitterNodeType::Property: case EmitterNodeType::Scalar: case EmitterNodeType::FlowSeq: case EmitterNodeType::FlowMap: SpaceOrIndentTo( m_pState->HasBegunContent() || m_pState->CurGroupChildCount() > 0, lastIndent); break; case EmitterNodeType::BlockSeq: case EmitterNodeType::BlockMap: assert(false); break; } } void Emitter::BlockSeqPrepareNode(EmitterNodeType::value child) { const std::size_t curIndent = m_pState->CurIndent(); const std::size_t nextIndent = curIndent + m_pState->CurGroupIndent(); if (child == EmitterNodeType::NoType) return; if (!m_pState->HasBegunContent()) { if (m_pState->CurGroupChildCount() > 0 || m_stream.comment()) { m_stream << "\n"; } m_stream << IndentTo(curIndent); m_stream << "-"; } switch (child) { case EmitterNodeType::NoType: break; case EmitterNodeType::Property: case EmitterNodeType::Scalar: case EmitterNodeType::FlowSeq: case EmitterNodeType::FlowMap: SpaceOrIndentTo(m_pState->HasBegunContent(), nextIndent); break; case EmitterNodeType::BlockSeq: m_stream << "\n"; break; case EmitterNodeType::BlockMap: if (m_pState->HasBegunContent() || m_stream.comment()) m_stream << "\n"; break; } } void Emitter::FlowMapPrepareNode(EmitterNodeType::value child) { if (m_pState->CurGroupChildCount() % 2 == 0) { if (m_pState->GetMapKeyFormat() == LongKey) m_pState->SetLongKey(); if (m_pState->CurGroupLongKey()) FlowMapPrepareLongKey(child); else FlowMapPrepareSimpleKey(child); } else { if (m_pState->CurGroupLongKey()) FlowMapPrepareLongKeyValue(child); else FlowMapPrepareSimpleKeyValue(child); } } void Emitter::FlowMapPrepareLongKey(EmitterNodeType::value child) { const std::size_t lastIndent = m_pState->LastIndent(); if (!m_pState->HasBegunNode()) { if (m_stream.comment()) m_stream << "\n"; m_stream << IndentTo(lastIndent); if (m_pState->CurGroupChildCount() == 0) m_stream << "{ ?"; else m_stream << ", ?"; } switch (child) { case EmitterNodeType::NoType: break; case EmitterNodeType::Property: case EmitterNodeType::Scalar: case EmitterNodeType::FlowSeq: case EmitterNodeType::FlowMap: SpaceOrIndentTo( m_pState->HasBegunContent() || m_pState->CurGroupChildCount() > 0, lastIndent); break; case EmitterNodeType::BlockSeq: case EmitterNodeType::BlockMap: assert(false); break; } } void Emitter::FlowMapPrepareLongKeyValue(EmitterNodeType::value child) { const std::size_t lastIndent = m_pState->LastIndent(); if (!m_pState->HasBegunNode()) { if (m_stream.comment()) m_stream << "\n"; m_stream << IndentTo(lastIndent); m_stream << ":"; } switch (child) { case EmitterNodeType::NoType: break; case EmitterNodeType::Property: case EmitterNodeType::Scalar: case EmitterNodeType::FlowSeq: case EmitterNodeType::FlowMap: SpaceOrIndentTo( m_pState->HasBegunContent() || m_pState->CurGroupChildCount() > 0, lastIndent); break; case EmitterNodeType::BlockSeq: case EmitterNodeType::BlockMap: assert(false); break; } } void Emitter::FlowMapPrepareSimpleKey(EmitterNodeType::value child) { const std::size_t lastIndent = m_pState->LastIndent(); if (!m_pState->HasBegunNode()) { if (m_stream.comment()) m_stream << "\n"; m_stream << IndentTo(lastIndent); if (m_pState->CurGroupChildCount() == 0) m_stream << "{"; else m_stream << ","; } switch (child) { case EmitterNodeType::NoType: break; case EmitterNodeType::Property: case EmitterNodeType::Scalar: case EmitterNodeType::FlowSeq: case EmitterNodeType::FlowMap: SpaceOrIndentTo( m_pState->HasBegunContent() || m_pState->CurGroupChildCount() > 0, lastIndent); break; case EmitterNodeType::BlockSeq: case EmitterNodeType::BlockMap: assert(false); break; } } void Emitter::FlowMapPrepareSimpleKeyValue(EmitterNodeType::value child) { const std::size_t lastIndent = m_pState->LastIndent(); if (!m_pState->HasBegunNode()) { if (m_stream.comment()) m_stream << "\n"; m_stream << IndentTo(lastIndent); if (m_pState->HasAlias()) { m_stream << " "; } m_stream << ":"; } switch (child) { case EmitterNodeType::NoType: break; case EmitterNodeType::Property: case EmitterNodeType::Scalar: case EmitterNodeType::FlowSeq: case EmitterNodeType::FlowMap: SpaceOrIndentTo( m_pState->HasBegunContent() || m_pState->CurGroupChildCount() > 0, lastIndent); break; case EmitterNodeType::BlockSeq: case EmitterNodeType::BlockMap: assert(false); break; } } void Emitter::BlockMapPrepareNode(EmitterNodeType::value child) { if (m_pState->CurGroupChildCount() % 2 == 0) { if (m_pState->GetMapKeyFormat() == LongKey) m_pState->SetLongKey(); if (child == EmitterNodeType::BlockSeq || child == EmitterNodeType::BlockMap || child == EmitterNodeType::Property) m_pState->SetLongKey(); if (m_pState->CurGroupLongKey()) BlockMapPrepareLongKey(child); else BlockMapPrepareSimpleKey(child); } else { if (m_pState->CurGroupLongKey()) BlockMapPrepareLongKeyValue(child); else BlockMapPrepareSimpleKeyValue(child); } } void Emitter::BlockMapPrepareLongKey(EmitterNodeType::value child) { const std::size_t curIndent = m_pState->CurIndent(); const std::size_t childCount = m_pState->CurGroupChildCount(); if (child == EmitterNodeType::NoType) return; if (!m_pState->HasBegunContent()) { if (childCount > 0) { m_stream << "\n"; } if (m_stream.comment()) { m_stream << "\n"; } m_stream << IndentTo(curIndent); m_stream << "?"; } switch (child) { case EmitterNodeType::NoType: break; case EmitterNodeType::Property: case EmitterNodeType::Scalar: case EmitterNodeType::FlowSeq: case EmitterNodeType::FlowMap: SpaceOrIndentTo(true, curIndent + 1); break; case EmitterNodeType::BlockSeq: case EmitterNodeType::BlockMap: if (m_pState->HasBegunContent()) m_stream << "\n"; break; } } void Emitter::BlockMapPrepareLongKeyValue(EmitterNodeType::value child) { const std::size_t curIndent = m_pState->CurIndent(); if (child == EmitterNodeType::NoType) return; if (!m_pState->HasBegunContent()) { m_stream << "\n"; m_stream << IndentTo(curIndent); m_stream << ":"; } switch (child) { case EmitterNodeType::NoType: break; case EmitterNodeType::Property: case EmitterNodeType::Scalar: case EmitterNodeType::FlowSeq: case EmitterNodeType::FlowMap: SpaceOrIndentTo(true, curIndent + 1); break; case EmitterNodeType::BlockSeq: case EmitterNodeType::BlockMap: if (m_pState->HasBegunContent()) m_stream << "\n"; SpaceOrIndentTo(true, curIndent + 1); break; } } void Emitter::BlockMapPrepareSimpleKey(EmitterNodeType::value child) { const std::size_t curIndent = m_pState->CurIndent(); const std::size_t childCount = m_pState->CurGroupChildCount(); if (child == EmitterNodeType::NoType) return; if (!m_pState->HasBegunNode()) { if (childCount > 0) { m_stream << "\n"; } } switch (child) { case EmitterNodeType::NoType: break; case EmitterNodeType::Property: case EmitterNodeType::Scalar: case EmitterNodeType::FlowSeq: case EmitterNodeType::FlowMap: SpaceOrIndentTo(m_pState->HasBegunContent(), curIndent); break; case EmitterNodeType::BlockSeq: case EmitterNodeType::BlockMap: break; } } void Emitter::BlockMapPrepareSimpleKeyValue(EmitterNodeType::value child) { const std::size_t curIndent = m_pState->CurIndent(); const std::size_t nextIndent = curIndent + m_pState->CurGroupIndent(); if (!m_pState->HasBegunNode()) { if (m_pState->HasAlias()) { m_stream << " "; } m_stream << ":"; } switch (child) { case EmitterNodeType::NoType: break; case EmitterNodeType::Property: case EmitterNodeType::Scalar: case EmitterNodeType::FlowSeq: case EmitterNodeType::FlowMap: SpaceOrIndentTo(true, nextIndent); break; case EmitterNodeType::BlockSeq: case EmitterNodeType::BlockMap: m_stream << "\n"; break; } } // SpaceOrIndentTo // . Prepares for some more content by proper spacing void Emitter::SpaceOrIndentTo(bool requireSpace, std::size_t indent) { if (m_stream.comment()) m_stream << "\n"; if (m_stream.col() > 0 && requireSpace) m_stream << " "; m_stream << IndentTo(indent); } void Emitter::PrepareIntegralStream(std::stringstream& stream) const { switch (m_pState->GetIntFormat()) { case Dec: stream << std::dec; break; case Hex: stream << "0x"; stream << std::hex; break; case Oct: stream << "0"; stream << std::oct; break; default: assert(false); } } void Emitter::StartedScalar() { m_pState->StartedScalar(); } // ******************************************************************************************* // overloads of Write StringEscaping::value GetStringEscapingStyle(const EMITTER_MANIP emitterManip) { switch (emitterManip) { case EscapeNonAscii: return StringEscaping::NonAscii; case EscapeAsJson: return StringEscaping::JSON; default: return StringEscaping::None; break; } } Emitter& Emitter::Write(const std::string& str) { if (!good()) return *this; StringEscaping::value stringEscaping = GetStringEscapingStyle(m_pState->GetOutputCharset()); const StringFormat::value strFormat = Utils::ComputeStringFormat(str, m_pState->GetStringFormat(), m_pState->CurGroupFlowType(), stringEscaping == StringEscaping::NonAscii); if (strFormat == StringFormat::Literal || str.size() > 1024) m_pState->SetMapKeyFormat(YAML::LongKey, FmtScope::Local); PrepareNode(EmitterNodeType::Scalar); switch (strFormat) { case StringFormat::Plain: m_stream << str; break; case StringFormat::SingleQuoted: Utils::WriteSingleQuotedString(m_stream, str); break; case StringFormat::DoubleQuoted: Utils::WriteDoubleQuotedString(m_stream, str, stringEscaping); break; case StringFormat::Literal: Utils::WriteLiteralString(m_stream, str, m_pState->CurIndent() + m_pState->GetIndent()); break; } StartedScalar(); return *this; } std::size_t Emitter::GetFloatPrecision() const { return m_pState->GetFloatPrecision(); } std::size_t Emitter::GetDoublePrecision() const { return m_pState->GetDoublePrecision(); } const char* Emitter::ComputeFullBoolName(bool b) const { const EMITTER_MANIP mainFmt = (m_pState->GetBoolLengthFormat() == ShortBool ? YesNoBool : m_pState->GetBoolFormat()); const EMITTER_MANIP caseFmt = m_pState->GetBoolCaseFormat(); switch (mainFmt) { case YesNoBool: switch (caseFmt) { case UpperCase: return b ? "YES" : "NO"; case CamelCase: return b ? "Yes" : "No"; case LowerCase: return b ? "yes" : "no"; default: break; } break; case OnOffBool: switch (caseFmt) { case UpperCase: return b ? "ON" : "OFF"; case CamelCase: return b ? "On" : "Off"; case LowerCase: return b ? "on" : "off"; default: break; } break; case TrueFalseBool: switch (caseFmt) { case UpperCase: return b ? "TRUE" : "FALSE"; case CamelCase: return b ? "True" : "False"; case LowerCase: return b ? "true" : "false"; default: break; } break; default: break; } return b ? "y" : "n"; // should never get here, but it can't hurt to give // these answers } const char* Emitter::ComputeNullName() const { switch (m_pState->GetNullFormat()) { case LowerNull: return "null"; case UpperNull: return "NULL"; case CamelNull: return "Null"; case TildeNull: // fallthrough default: return "~"; } } Emitter& Emitter::Write(bool b) { if (!good()) return *this; PrepareNode(EmitterNodeType::Scalar); const char* name = ComputeFullBoolName(b); if (m_pState->GetBoolLengthFormat() == ShortBool) m_stream << name[0]; else m_stream << name; StartedScalar(); return *this; } Emitter& Emitter::Write(char ch) { if (!good()) return *this; PrepareNode(EmitterNodeType::Scalar); Utils::WriteChar(m_stream, ch, GetStringEscapingStyle(m_pState->GetOutputCharset())); StartedScalar(); return *this; } Emitter& Emitter::Write(const _Alias& alias) { if (!good()) return *this; if (m_pState->HasAnchor() || m_pState->HasTag()) { m_pState->SetError(ErrorMsg::INVALID_ALIAS); return *this; } PrepareNode(EmitterNodeType::Scalar); if (!Utils::WriteAlias(m_stream, alias.content)) { m_pState->SetError(ErrorMsg::INVALID_ALIAS); return *this; } StartedScalar(); m_pState->SetAlias(); return *this; } Emitter& Emitter::Write(const _Anchor& anchor) { if (!good()) return *this; if (m_pState->HasAnchor()) { m_pState->SetError(ErrorMsg::INVALID_ANCHOR); return *this; } PrepareNode(EmitterNodeType::Property); if (!Utils::WriteAnchor(m_stream, anchor.content)) { m_pState->SetError(ErrorMsg::INVALID_ANCHOR); return *this; } m_pState->SetAnchor(); return *this; } Emitter& Emitter::Write(const _Tag& tag) { if (!good()) return *this; if (m_pState->HasTag()) { m_pState->SetError(ErrorMsg::INVALID_TAG); return *this; } PrepareNode(EmitterNodeType::Property); bool success = false; if (tag.type == _Tag::Type::Verbatim) success = Utils::WriteTag(m_stream, tag.content, true); else if (tag.type == _Tag::Type::PrimaryHandle) success = Utils::WriteTag(m_stream, tag.content, false); else success = Utils::WriteTagWithPrefix(m_stream, tag.prefix, tag.content); if (!success) { m_pState->SetError(ErrorMsg::INVALID_TAG); return *this; } m_pState->SetTag(); return *this; } void Emitter::EmitKindTag() { Write(LocalTag("")); } Emitter& Emitter::Write(const _Comment& comment) { if (!good()) return *this; PrepareNode(EmitterNodeType::NoType); if (m_stream.col() > 0) m_stream << Indentation(m_pState->GetPreCommentIndent()); Utils::WriteComment(m_stream, comment.content, m_pState->GetPostCommentIndent()); m_pState->SetNonContent(); return *this; } Emitter& Emitter::Write(const _Null& /*null*/) { if (!good()) return *this; PrepareNode(EmitterNodeType::Scalar); m_stream << ComputeNullName(); StartedScalar(); return *this; } Emitter& Emitter::Write(const Binary& binary) { Write(SecondaryTag("binary")); if (!good()) return *this; PrepareNode(EmitterNodeType::Scalar); Utils::WriteBinary(m_stream, binary); StartedScalar(); return *this; } } // namespace YAML