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 }