winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

Yaml.cpp (25502B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #include <pch.h>
      4 #include "winget/Yaml.h"
      5 #include "YamlWrapper.h"
      6 #include "AppInstallerErrors.h"
      7 #include "AppInstallerLogging.h"
      8 #include "AppInstallerStrings.h"
      9 
     10 
     11 namespace AppInstaller::YAML
     12 {
     13     using namespace std::string_view_literals;
     14 
     15     namespace
     16     {
     17         Node s_globalInvalidNode;
     18 
     19         static constexpr std::string_view s_nullTag = "tag:yaml.org,2002:null"sv;
     20         static constexpr std::string_view s_boolTag = "tag:yaml.org,2002:bool"sv;
     21         static constexpr std::string_view s_strTag = "tag:yaml.org,2002:str"sv;
     22         static constexpr std::string_view s_intTag = "tag:yaml.org,2002:int"sv;
     23         static constexpr std::string_view s_floatTag = "tag:yaml.org,2002:float"sv;
     24         static constexpr std::string_view s_timestampTag = "tag:yaml.org,2002:timestamp"sv;
     25         static constexpr std::string_view s_seqTag = "tag:yaml.org,2002:seq"sv;
     26         static constexpr std::string_view s_mapTag = "tag:yaml.org,2002:map"sv;
     27 
     28         std::string_view GetExceptionTypeStringView(Exception::Type type)
     29         {
     30             switch (type)
     31             {
     32             case Exception::Type::None:
     33                 return "None"sv;
     34             case Exception::Type::Memory:
     35                 return "Memory"sv;
     36             case Exception::Type::Reader:
     37                 return "Reader"sv;
     38             case Exception::Type::Scanner:
     39                 return "Scanner"sv;
     40             case Exception::Type::Parser:
     41                 return "Parser"sv;
     42             case Exception::Type::Composer:
     43                 return "Composer"sv;
     44             case Exception::Type::Writer:
     45                 return "Writer"sv;
     46             case Exception::Type::Emitter:
     47                 return "Emitter"sv;
     48             case Exception::Type::Policy:
     49                 return "Policy"sv;
     50             }
     51 
     52             return "Unknown"sv;
     53         }
     54 
     55         void OutputExceptionHeader(std::ostringstream& out, Exception::Type type)
     56         {
     57             out << "[YAML:" << GetExceptionTypeStringView(type) << "] ";
     58         }
     59 
     60         void OutputMark(std::ostringstream& out, const Mark& mark)
     61         {
     62             out << "[line " << mark.line << "; col " << mark.column << ']';
     63         }
     64 
     65         Node::TagType ConvertToTagType(const std::string& tag)
     66         {
     67             if (tag == s_strTag)
     68             {
     69                 return Node::TagType::Str;
     70             }
     71             else if (tag == s_seqTag)
     72             {
     73                 return Node::TagType::Seq;
     74             }
     75             else if (tag == s_mapTag)
     76             {
     77                 return Node::TagType::Map;
     78             }
     79             else if (tag == s_boolTag)
     80             {
     81                 return Node::TagType::Bool;
     82             }
     83             else if (tag == s_intTag)
     84             {
     85                 return Node::TagType::Int;
     86             }
     87             else if (tag == s_floatTag)
     88             {
     89                 return Node::TagType::Float;
     90             }
     91             else if (tag == s_timestampTag)
     92             {
     93                 return Node::TagType::Timestamp;
     94             }
     95             else if (tag == s_nullTag)
     96             {
     97                 return Node::TagType::Null;
     98             }
     99 
    100             return Node::TagType::Unknown;
    101         }
    102 
    103         DocumentSchemaHeader ExtractSchemaHeaderFromYaml( const std::string& yamlDocument, size_t rootNodeLine)
    104         {
    105             std::istringstream input(yamlDocument);
    106             std::string line;
    107             size_t currentLine = 1;
    108 
    109             // Search for the schema header string in the comments before the root node.
    110             while (currentLine < rootNodeLine && std::getline(input, line))
    111             {
    112                 std::string comment = Utility::Trim(line);
    113 
    114                 // Check if the line is a comment
    115                 if (!comment.empty() && comment[0] == '#')
    116                 {
    117                     size_t pos = line.find(DocumentSchemaHeader::YamlLanguageServerKey);
    118 
    119                     // Check if the comment contains the schema header string
    120                     if (pos != std::string::npos)
    121                     {
    122                         return DocumentSchemaHeader(std::move(comment), YAML::Mark{ currentLine, pos});
    123                     }
    124                 }
    125 
    126                 currentLine++;
    127             }
    128 
    129             return {};
    130         }
    131     }
    132 
    133     Exception::Exception(Type type) :
    134         wil::ResultException(APPINSTALLER_CLI_ERROR_LIBYAML_ERROR)
    135     {
    136         std::ostringstream out;
    137         OutputExceptionHeader(out, type);
    138 
    139         if (type == Type::Memory)
    140         {
    141             out << "Unable to (re)allocate memory";
    142         }
    143         else
    144         {
    145             out << "An unknown error occurred";
    146         }
    147 
    148         m_what = out.str();
    149     }
    150 
    151     Exception::Exception(Type type, const char* problem, size_t offset, int value) :
    152         wil::ResultException(APPINSTALLER_CLI_ERROR_LIBYAML_ERROR)
    153     {
    154         std::ostringstream out;
    155         OutputExceptionHeader(out, type);
    156 
    157         out << (problem ? problem : "Unexplained error");
    158 
    159         if (value != -1)
    160         {
    161             out << " [" << value << ']';
    162         }
    163 
    164         out << " at " << offset;
    165 
    166         m_what = out.str();
    167     }
    168 
    169     Exception::Exception(Type type, const char* problem, const Mark& problemMark, const char* context, const Mark& contextMark) :
    170         wil::ResultException(APPINSTALLER_CLI_ERROR_LIBYAML_ERROR), m_mark(problemMark)
    171     {
    172         std::ostringstream out;
    173         OutputExceptionHeader(out, type);
    174 
    175         if (context)
    176         {
    177             out << context << ' ';
    178             OutputMark(out, contextMark);
    179             out << ' ' << (problem ? problem : "unexplained error");
    180         }
    181         else
    182         {
    183             out << (problem ? problem : "Unexplained error");
    184         }
    185 
    186         out << ' ';
    187         OutputMark(out, problemMark);
    188 
    189         m_what = out.str();
    190     }
    191 
    192     Exception::Exception(Type type, const char* problem) :
    193         wil::ResultException(APPINSTALLER_CLI_ERROR_LIBYAML_ERROR)
    194     {
    195         std::ostringstream out;
    196         OutputExceptionHeader(out, type);
    197 
    198         out << (problem ? problem : "Unexplained error");
    199 
    200         m_what = out.str();
    201     }
    202 
    203     const char* Exception::what() const noexcept
    204     {
    205         return m_what.c_str();
    206     }
    207 
    208     const Mark& Exception::GetMark() const
    209     {
    210         return m_mark;
    211     }
    212 
    213     Node::Node(Type type, std::string tag, const YAML::Mark& mark) :
    214         m_type(type), m_tag(std::move(tag)), m_mark(mark)
    215     {
    216         if (m_type == Type::Sequence)
    217         {
    218             m_sequence = decltype(m_sequence)::value_type{};
    219         }
    220         else if (m_type == Type::Mapping)
    221         {
    222             m_mapping = decltype(m_mapping)::value_type{};
    223         }
    224 
    225         m_tagType = ConvertToTagType(m_tag);
    226     }
    227 
    228     void Node::SetScalar(std::string value)
    229     {
    230         Require(Type::Scalar);
    231         m_scalar = std::move(value);
    232     }
    233 
    234     void Node::SetScalar(std::string value, bool isQuoted)
    235     {
    236         this->SetScalar(value);
    237 
    238         // For untagged scalar nodes, libyaml always assigns the generic string
    239         // tag. Here we just try our best and assume that if the value is unquoted
    240         // then is not necessarily a string.
    241         // TODO: handle float and timestamps
    242         if (!isQuoted && this->GetTagType() == TagType::Str)
    243         {
    244             // Integer
    245             // 0 | -? [1-9] [0-9]*
    246             auto tryInt = this->try_as<int64_t>();
    247             if (tryInt.has_value())
    248             {
    249                 m_tagType = TagType::Int;
    250                 return;
    251             }
    252 
    253             // Boolean. Either 'true' or 'false'
    254             auto tryBool = this->try_as<bool>();
    255             if (tryBool.has_value())
    256             {
    257                 m_tagType = TagType::Bool;
    258             }
    259         }
    260     }
    261 
    262     bool Node::operator<(const Node& other) const
    263     {
    264         Require(Type::Scalar);
    265         other.Require(Type::Scalar);
    266         return this->m_scalar < other.m_scalar;
    267     }
    268 
    269     Node& Node::operator[](std::string_view key)
    270     {
    271         Require(Type::Mapping);
    272         auto itrs = m_mapping->equal_range(key);
    273 
    274         if (itrs.first == itrs.second)
    275         {
    276             return s_globalInvalidNode;
    277         }
    278 
    279         Node& result = itrs.first->second;
    280 
    281         THROW_HR_IF(APPINSTALLER_CLI_ERROR_YAML_DUPLICATE_MAPPING_KEY, ++itrs.first != itrs.second);
    282 
    283         return result;
    284     }
    285 
    286     const Node& Node::operator[](std::string_view key) const
    287     {
    288         Require(Type::Mapping);
    289         auto itrs = m_mapping->equal_range(key);
    290 
    291         if (itrs.first == itrs.second)
    292         {
    293             return s_globalInvalidNode;
    294         }
    295 
    296         const Node& result = itrs.first->second;
    297 
    298         THROW_HR_IF(APPINSTALLER_CLI_ERROR_YAML_DUPLICATE_MAPPING_KEY, ++itrs.first != itrs.second);
    299 
    300         return result;
    301     }
    302 
    303     // Gets a child node from the mapping by its name.
    304     Node& Node::GetChildNode(std::string_view key)
    305     {
    306         Require(Type::Mapping);
    307 
    308         auto itr = m_mapping->begin();
    309         for (; itr != m_mapping->end(); itr++)
    310         {
    311             if (Utility::CaseInsensitiveEquals(itr->first.m_scalar, key))
    312             {
    313                 break;
    314             }
    315         }
    316 
    317         if (itr == m_mapping->end())
    318         {
    319             return s_globalInvalidNode;
    320         }
    321 
    322         auto firstFound = itr;
    323         for (++itr; itr != m_mapping->end(); itr++)
    324         {
    325             if (Utility::CaseInsensitiveEquals(itr->first.m_scalar, key))
    326             {
    327                 break;
    328             }
    329         }
    330 
    331         THROW_HR_IF(APPINSTALLER_CLI_ERROR_YAML_DUPLICATE_MAPPING_KEY, itr != m_mapping->end());
    332         Node& result = firstFound->second;
    333         return result;
    334     }
    335 
    336     const Node& Node::GetChildNode(std::string_view key) const
    337     {
    338         Require(Type::Mapping);
    339 
    340         auto itr = m_mapping->begin();
    341         for (; itr != m_mapping->end(); itr++)
    342         {
    343             if (Utility::CaseInsensitiveEquals(itr->first.m_scalar, key))
    344             {
    345                 break;
    346             }
    347         }
    348 
    349         if (itr == m_mapping->end())
    350         {
    351             return s_globalInvalidNode;
    352         }
    353 
    354         auto firstFound = itr;
    355         for (++itr; itr != m_mapping->end(); itr++)
    356         {
    357             if (Utility::CaseInsensitiveEquals(itr->first.m_scalar, key))
    358             {
    359                 break;
    360             }
    361         }
    362 
    363         THROW_HR_IF(APPINSTALLER_CLI_ERROR_YAML_DUPLICATE_MAPPING_KEY, itr != m_mapping->end());
    364         const Node& result = firstFound->second;
    365         return result;
    366     }
    367 
    368     Node& Node::operator[](size_t index)
    369     {
    370         Require(Type::Sequence);
    371         return m_sequence.value()[index];
    372     }
    373 
    374     const Node& Node::operator[](size_t index) const
    375     {
    376         Require(Type::Sequence);
    377         return m_sequence.value()[index];
    378     }
    379 
    380     size_t Node::size() const
    381     {
    382         switch (m_type)
    383         {
    384         case Type::Invalid:
    385         case Type::None:
    386         case Type::Scalar:
    387             return 0;
    388         case Type::Sequence:
    389             return m_sequence->size();
    390         case Type::Mapping:
    391             return m_mapping->size();
    392         }
    393 
    394         THROW_HR(E_UNEXPECTED);
    395     }
    396 
    397     const std::vector<Node>& Node::Sequence() const
    398     {
    399         Require(Type::Sequence);
    400         return m_sequence.value();
    401     }
    402 
    403     const std::multimap<Node, Node>& Node::Mapping() const
    404     {
    405         Require(Type::Mapping);
    406         return m_mapping.value();
    407     }
    408 
    409     void Node::Require(Type type) const
    410     {
    411         THROW_HR_IF(APPINSTALLER_CLI_ERROR_YAML_INVALID_OPERATION, m_type != type);
    412     }
    413 
    414     std::string Node::as_dispatch(std::string*) const
    415     {
    416         return m_scalar;
    417     }
    418 
    419     std::optional<std::string> Node::try_as_dispatch(std::string*) const
    420     {
    421         return std::optional{ m_scalar };
    422     }
    423 
    424     std::wstring Node::as_dispatch(std::wstring*) const
    425     {
    426         return Utility::ConvertToUTF16(m_scalar);
    427     }
    428 
    429     std::optional<std::wstring> Node::try_as_dispatch(std::wstring*) const
    430     {
    431         return Utility::TryConvertToUTF16(m_scalar);
    432     }
    433 
    434     int64_t Node::as_dispatch(int64_t*) const
    435     {
    436         return std::stoll(m_scalar);
    437     }
    438 
    439     std::optional<int64_t> Node::try_as_dispatch(int64_t*) const
    440     {
    441         if (m_scalar.empty())
    442         {
    443             return {};
    444         }
    445 
    446         const char* begin = m_scalar.c_str();
    447         char* end = nullptr;
    448         errno = 0;
    449         int64_t result = static_cast<int64_t>(strtoll(begin, &end, 0));
    450 
    451         if (errno == ERANGE || static_cast<size_t>(end - begin) != m_scalar.length())
    452         {
    453             return {};
    454         }
    455 
    456         return result;
    457     }
    458 
    459     int Node::as_dispatch(int*) const
    460     {
    461         // To allow HResult representation
    462         return static_cast<int>(std::stoll(m_scalar, 0, 0));
    463     }
    464 
    465     std::optional<int> Node::try_as_dispatch(int*) const
    466     {
    467         try
    468         {
    469             return std::optional{ static_cast<int>(std::stoll(m_scalar, 0, 0)) };
    470         }
    471         catch (...)
    472         {
    473             return {};
    474         }
    475     }
    476 
    477     bool Node::as_dispatch(bool*) const
    478     {
    479         bool* t = nullptr;
    480         auto tryToBool = this->try_as_dispatch(t);
    481         if (tryToBool.has_value())
    482         {
    483             return tryToBool.value();
    484         }
    485         else
    486         {
    487             THROW_HR(APPINSTALLER_CLI_ERROR_YAML_INVALID_DATA);
    488         }
    489     }
    490 
    491     std::optional<bool> Node::try_as_dispatch(bool*) const
    492     {
    493         if (Utility::CaseInsensitiveEquals(m_scalar, "true"))
    494         {
    495             return std::optional{ true };
    496         }
    497         else if (Utility::CaseInsensitiveEquals(m_scalar, "false"))
    498         {
    499             return std::optional{ false };
    500         }
    501 
    502         return {};
    503     }
    504 
    505     void Node::MergeSequenceNode(Node other, std::string_view key, bool caseInsensitive)
    506     {
    507         Require(Type::Sequence);
    508         other.Require(Type::Sequence);
    509 
    510         auto getKeyValue = [&](const YAML::Node& node) {
    511             auto keyNode = caseInsensitive ? node.GetChildNode(key) : node[key];
    512             if (keyNode.IsNull())
    513             {
    514                 THROW_HR(APPINSTALLER_CLI_ERROR_YAML_INVALID_DATA);
    515             }
    516 
    517             auto keyValue = keyNode.as<std::string>();
    518             return caseInsensitive ? std::string{ Utility::FoldCase(std::string_view{keyValue}) } : keyValue;
    519         };
    520 
    521         std::map<std::string, Node> newSequenceMap;
    522         for (Node& node : m_sequence.value())
    523         {
    524             node.Require(Type::Mapping);
    525             auto keyValue = getKeyValue(node);
    526             newSequenceMap.emplace(std::move(keyValue), std::move(node));
    527         }
    528 
    529         for (Node& node : other.m_sequence.value())
    530         {
    531             node.Require(Type::Mapping);
    532             auto keyValue = getKeyValue(node);
    533             if (newSequenceMap.find(keyValue) == newSequenceMap.end())
    534             {
    535                 newSequenceMap.emplace(std::move(keyValue), std::move(node));
    536             }
    537             else
    538             {
    539                 newSequenceMap[keyValue].MergeMappingNode(node, caseInsensitive);
    540             }
    541         }
    542 
    543         m_sequence.reset();
    544         std::vector<Node> newSequence;
    545         for (const auto& keyValuePair : newSequenceMap)
    546         {
    547             newSequence.push_back(keyValuePair.second);
    548         }
    549 
    550         m_sequence = std::move(newSequence);
    551     }
    552 
    553     void Node::MergeMappingNode(Node other, bool caseInsensitive)
    554     {
    555         Require(Type::Mapping);
    556         other.Require(Type::Mapping);
    557 
    558         std::multimap<Node, Node> uniques;
    559         for (auto& keyValuePair : other.m_mapping.value())
    560         {
    561             if (caseInsensitive)
    562             {
    563                 auto node = GetChildNode(keyValuePair.first.as<std::string>());
    564                 if (node.IsNull())
    565                 {
    566                     uniques.emplace(std::move(keyValuePair));
    567                 }
    568             }
    569             else
    570             {
    571                 if (m_mapping->count(keyValuePair.first) == 0)
    572                 {
    573                     uniques.emplace(std::move(keyValuePair));
    574                 }
    575             }
    576         }
    577 
    578         m_mapping->merge(uniques);
    579     }
    580 
    581     Node Load(std::string_view input)
    582     {
    583         Wrapper::Parser parser(input);
    584         Wrapper::Document document = parser.Load();
    585 
    586         if (document.HasRoot())
    587         {
    588             return document.GetRoot();
    589         }
    590         else
    591         {
    592             return {};
    593         }
    594     }
    595 
    596     Node Load(const std::string& input)
    597     {
    598         return Load(static_cast<std::string_view>(input));
    599     }
    600 
    601     Node Load(std::istream& input, Utility::SHA256::HashBuffer* hashOut)
    602     {
    603         Wrapper::Parser parser(input, hashOut);
    604         Wrapper::Document document = parser.Load();
    605 
    606         if (document.HasRoot())
    607         {
    608             return document.GetRoot();
    609         }
    610         else
    611         {
    612             return {};
    613         }
    614     }
    615 
    616     Node Load(const std::filesystem::path& input, Utility::SHA256::HashBuffer* hashOut)
    617     {
    618         std::ifstream stream(input, std::ios_base::in | std::ios_base::binary);
    619         THROW_LAST_ERROR_IF(stream.fail());
    620         return Load(stream, hashOut);
    621     }
    622 
    623     Node Load(const std::filesystem::path& input)
    624     {
    625         return Load(input, nullptr);
    626     }
    627 
    628     Node Load(const std::filesystem::path& input, Utility::SHA256::HashBuffer& hashOut)
    629     {
    630         return Load(input, &hashOut);
    631     }
    632 
    633     Document LoadDocument(std::string_view input)
    634     {
    635         Wrapper::Parser parser(input);
    636         Wrapper::Document document = parser.Load();
    637 
    638         if (document.HasRoot())
    639         {
    640             const Node root = document.GetRoot();
    641             const DocumentSchemaHeader schemaHeader = ExtractSchemaHeaderFromYaml(parser.GetEncodedInput(), root.Mark().line);
    642 
    643             return { root, schemaHeader };
    644         }
    645         else
    646         {
    647             // Return an empty root and schema header.
    648             return {};
    649         }
    650     }
    651 
    652     Document LoadDocument(const std::string& input)
    653     {
    654         return LoadDocument(static_cast<std::string_view>(input));
    655     }
    656 
    657     Document LoadDocument(std::istream& input, Utility::SHA256::HashBuffer* hashOut)
    658     {
    659         Wrapper::Parser parser(input, hashOut);
    660         Wrapper::Document document = parser.Load();
    661 
    662         if (document.HasRoot())
    663         {
    664             const Node root = document.GetRoot();
    665             const DocumentSchemaHeader schemaHeader = ExtractSchemaHeaderFromYaml(parser.GetEncodedInput(), root.Mark().line);
    666 
    667             return { root, schemaHeader };
    668         }
    669         else
    670         {
    671             // Return an empty root and schema header.
    672             return {};
    673         }
    674     }
    675 
    676     Document LoadDocument(const std::filesystem::path& input, Utility::SHA256::HashBuffer* hashOut)
    677     {
    678         std::ifstream stream(input, std::ios_base::in | std::ios_base::binary);
    679         THROW_LAST_ERROR_IF(stream.fail());
    680         return LoadDocument(stream, hashOut);
    681     }
    682 
    683     Document LoadDocument(const std::filesystem::path& input)
    684     {
    685         return LoadDocument(input, nullptr);
    686     }
    687 
    688     Document LoadDocument(const std::filesystem::path& input, Utility::SHA256::HashBuffer& hashOut)
    689     {
    690         return LoadDocument(input, &hashOut);
    691     }
    692 
    693     Emitter::Emitter() :
    694         m_document(std::make_unique<Wrapper::Document>(true))
    695     {
    696         SetAllowedInputs<InputType::BeginMap, InputType::BeginSeq>();
    697     }
    698 
    699     Emitter::Emitter(Emitter&&) noexcept = default;
    700     Emitter& Emitter::operator=(Emitter&&) noexcept = default;
    701 
    702     Emitter::~Emitter() = default;
    703 
    704     Emitter& Emitter::operator<<(EmitterEvent event)
    705     {
    706         switch (event)
    707         {
    708         case AppInstaller::YAML::BeginSeq:
    709         {
    710             CheckInput(InputType::BeginSeq);
    711             int id = m_document->AddSequence();
    712             AppendNode(id);
    713             m_containers.emplace(id, false);
    714             SetAllowedInputsForContainer();
    715             break;
    716         }
    717         case AppInstaller::YAML::EndSeq:
    718             CheckInput(InputType::EndSeq);
    719             m_containers.pop();
    720             SetAllowedInputsForContainer();
    721             break;
    722         case AppInstaller::YAML::BeginMap:
    723         {
    724             CheckInput(InputType::BeginMap);
    725             int id = m_document->AddMapping();
    726             AppendNode(id);
    727             m_containers.emplace(id, true);
    728             SetAllowedInputsForContainer();
    729             break;
    730         }
    731         case AppInstaller::YAML::EndMap:
    732             CheckInput(InputType::EndMap);
    733             m_containers.pop();
    734             SetAllowedInputsForContainer();
    735             break;
    736         case AppInstaller::YAML::Key:
    737             CheckInput(InputType::Key);
    738             m_scalarType = InputType::Key;
    739             SetAllowedInputs<InputType::Scalar>();
    740             break;
    741         case AppInstaller::YAML::Value:
    742             CheckInput(InputType::Value);
    743             m_scalarType = InputType::Value;
    744             SetAllowedInputs<InputType::Scalar, InputType::BeginMap, InputType::BeginSeq>();
    745             break;
    746         default:
    747             THROW_HR(E_UNEXPECTED);
    748         }
    749 
    750         return *this;
    751     }
    752 
    753     Emitter& Emitter::operator<<(std::string_view value)
    754     {
    755         CheckInput(InputType::Scalar);
    756 
    757         int id = m_document->AddScalar(value, m_scalarStyle.value_or(ScalarStyle::Any));
    758         m_scalarStyle = std::nullopt;
    759 
    760         if (!m_scalarType)
    761         {
    762             // Part of a sequence
    763             AppendNode(id);
    764             // No change to allowed inputs
    765         }
    766         else if (m_scalarType.value() == InputType::Key)
    767         {
    768             m_keyId = id;
    769             m_scalarType = std::nullopt;
    770             SetAllowedInputs<InputType::Value, InputType::BeginMap, InputType::BeginSeq>();
    771         }
    772         else if (m_scalarType.value() == InputType::Value)
    773         {
    774             // Mapping pair complete
    775             AppendNode(id);
    776             m_scalarType = std::nullopt;
    777             SetAllowedInputsForContainer();
    778         }
    779         else
    780         {
    781             THROW_HR(APPINSTALLER_CLI_ERROR_YAML_INVALID_EMITTER_STATE);
    782         }
    783 
    784         return *this;
    785     }
    786 
    787     Emitter& Emitter::operator<<(int64_t value)
    788     {
    789         std::ostringstream stream;
    790         stream << value;
    791         return operator<<(stream.str());
    792     }
    793 
    794     Emitter& Emitter::operator<<(int value)
    795     {
    796         std::ostringstream stream;
    797         stream << value;
    798         return operator<<(stream.str());
    799     }
    800 
    801     Emitter& Emitter::operator<<(bool value)
    802     {
    803         return operator<<(value ? "true"sv : "false"sv);
    804     }
    805 
    806     Emitter& Emitter::operator<<(ScalarStyle style)
    807     {
    808         m_scalarStyle = style;
    809         // Because without this you get a C26815...
    810         (void)0;
    811         return *this;
    812     }
    813 
    814     std::string Emitter::str()
    815     {
    816         std::ostringstream stream;
    817         Wrapper::Emitter emitter(stream);
    818 
    819         emitter.Dump(*m_document);
    820         emitter.Flush();
    821 
    822         return stream.str();
    823     }
    824 
    825     void Emitter::Emit(std::ostream& out)
    826     {
    827         Wrapper::Emitter emitter(out);
    828 
    829         emitter.Dump(*m_document);
    830         emitter.Flush();
    831     }
    832 
    833     void Emitter::AppendNode(int id)
    834     {
    835         if (!m_containers.empty())
    836         {
    837             ContainerInfo& ci = m_containers.top();
    838 
    839             if (ci.IsMapping)
    840             {
    841                 THROW_HR_IF(APPINSTALLER_CLI_ERROR_YAML_INVALID_EMITTER_STATE, !m_keyId);
    842                 m_document->AppendMappingPair(ci.Id, m_keyId.value(), id);
    843                 m_keyId = std::nullopt;
    844             }
    845             else
    846             {
    847                 m_document->AppendSequenceItem(ci.Id, id);
    848             }
    849         }
    850     }
    851 
    852     size_t Emitter::GetInputBitmask(InputType type)
    853     {
    854         return static_cast<size_t>(1) << static_cast<size_t>(type);
    855     }
    856 
    857     void Emitter::CheckInput(InputType type)
    858     {
    859         if ((m_allowedInputs & GetInputBitmask(type)) == 0)
    860         {
    861             AICLI_LOG(YAML, Error, << "Invalid emitter input [0x" <<
    862                 std::hex << std::setw(2) << std::setfill('0') << GetInputBitmask(type) << "], expected one of [0x" <<
    863                 std::hex << std::setw(2) << std::setfill('0') << m_allowedInputs << "]");
    864             THROW_HR(APPINSTALLER_CLI_ERROR_YAML_INVALID_EMITTER_STATE);
    865         }
    866     }
    867 
    868     void Emitter::SetAllowedInputsForContainer()
    869     {
    870         if (m_containers.empty())
    871         {
    872             m_allowedInputs = 0;
    873         }
    874         else
    875         {
    876             if (m_containers.top().IsMapping)
    877             {
    878                 SetAllowedInputs<InputType::Key, InputType::EndMap>();
    879             }
    880             else
    881             {
    882                 SetAllowedInputs<InputType::Scalar, InputType::BeginMap, InputType::BeginSeq, InputType::EndSeq>();
    883             }
    884         }
    885     }
    886 }