winget-cli

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

Yaml.h (11189B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #pragma once
      4 #include <AppInstallerSHA256.h>
      5 
      6 #include <fstream>
      7 #include <map>
      8 #include <memory>
      9 #include <optional>
     10 #include <stack>
     11 #include <string>
     12 #include <string_view>
     13 #include <vector>
     14 
     15 
     16 namespace AppInstaller::YAML
     17 {
     18     // A location within the stream.
     19     struct Mark
     20     {
     21         Mark() = default;
     22         Mark(size_t l, size_t c) : line(l), column(c) {}
     23 
     24         size_t line = 0;
     25         size_t column = 0;
     26     };
     27 
     28     // An exception from YAML.
     29     struct Exception : public wil::ResultException
     30     {
     31         // The type of error that occurred.
     32         enum class Type
     33         {
     34             None,
     35             Memory,
     36             Reader,
     37             Scanner,
     38             Parser,
     39             Composer,
     40             Writer,
     41             Emitter,
     42             Policy,
     43         };
     44 
     45         // Should only be used for Memory.
     46         Exception(Type type);
     47 
     48         // Should only be used for Reader.
     49         Exception(Type type, const char* problem, size_t offset, int value);
     50 
     51         // Used for Scanner, Parser, and Composer.
     52         Exception(Type type, const char* problem, const Mark& problemMark, const char* context = {}, const Mark& contextMark = {});
     53 
     54         // Used for Writer and Emitter.
     55         Exception(Type type, const char* problem);
     56 
     57         const char* what() const noexcept override;
     58 
     59         const Mark& GetMark() const;
     60 
     61     private:
     62         std::string m_what;
     63         YAML::Mark m_mark;
     64     };
     65 
     66     // A YAML node.
     67     struct Node
     68     {
     69         // The node's type.
     70         enum class Type
     71         {
     72             Invalid,
     73             None,
     74             Scalar,
     75             Sequence,
     76             Mapping
     77         };
     78 
     79         // The node's tag
     80         enum class TagType
     81         {
     82             Unknown,
     83             Null,
     84             Bool,
     85             Str,
     86             Int,
     87             Float,
     88             Timestamp,
     89             Seq,
     90             Map,
     91         };
     92 
     93         Node() : m_type(Type::Invalid), m_tagType(TagType::Unknown) {}
     94         Node(Type type, std::string tag, const Mark& mark);
     95 
     96         // Sets the scalar value of the node.
     97         void SetScalar(std::string value);
     98         void SetScalar(std::string value, bool isQuoted);
     99 
    100         // Adds a child node to the sequence.
    101         template <typename... Args>
    102         Node& AddSequenceNode(Args&&... args)
    103         {
    104             Require(Type::Sequence);
    105             return m_sequence->emplace_back(std::forward<Args>(args)...);
    106         }
    107 
    108         // Merges sequence nodes. If both sequence have the specified key with the same value
    109         // they will get merged together. All elements in sequence must have the key.
    110         void MergeSequenceNode(Node other, std::string_view key, bool caseInsensitive = false);
    111 
    112         // Adds a child node to the mapping.
    113         template <typename... Args>
    114         Node& AddMappingNode(Node&& key, Args&&... args)
    115         {
    116             Require(Type::Mapping);
    117             return m_mapping->emplace(std::move(key), Node(std::forward<Args>(args)...))->second;
    118         }
    119 
    120         // Merge mapping node. If both contain a node with the same key preserve this.
    121         void MergeMappingNode(Node other, bool caseInsensitive = false);
    122 
    123         bool IsDefined() const { return m_type != Type::Invalid; }
    124         bool IsNull() const { return m_type == Type::Invalid || m_type == Type::None || (m_type == Type::Scalar && m_scalar.empty()); }
    125         bool IsScalar() const { return m_type == Type::Scalar; }
    126         bool IsSequence() const { return m_type == Type::Sequence; }
    127         bool IsMap() const { return m_type == Type::Mapping; }
    128         Type GetType() const { return m_type; }
    129         TagType GetTagType() const { return m_tagType; }
    130 
    131         explicit operator bool() const { return IsDefined(); }
    132 
    133         // Gets the scalar value as the requested type.
    134         template <typename T>
    135         T as() const
    136         {
    137             Require(Type::Scalar);
    138             T* t = nullptr;
    139             return as_dispatch(t);
    140         }
    141 
    142         template <typename T>
    143         std::optional<T> try_as() const
    144         {
    145             if (m_type != Type::Scalar)
    146             {
    147                 return {};
    148             }
    149 
    150             T* t = nullptr;
    151             return try_as_dispatch(t);
    152         }
    153 
    154         bool operator<(const Node& other) const;
    155 
    156         // Gets a child node from the mapping by its name.
    157         Node& operator[](std::string_view key);
    158         const Node& operator[](std::string_view key) const;
    159 
    160         // Gets a child node from the mapping by its name case-insensitive.
    161         Node& GetChildNode(std::string_view key);
    162         const Node& GetChildNode(std::string_view key) const;
    163 
    164         // Gets a child node from the sequence by its index.
    165         Node& operator[](size_t index);
    166         const Node& operator[](size_t index) const;
    167 
    168         // Gets the number of child nodes.
    169         size_t size() const;
    170 
    171         // Gets the mark for this node.
    172         const Mark& Mark() const { return m_mark; }
    173 
    174         // Gets the nodes in the sequence.
    175         const std::vector<Node>& Sequence() const;
    176 
    177         // Gets the nodes in the mapping.
    178         const std::multimap<Node, Node>& Mapping() const;
    179 
    180     private:
    181         Node(std::string_view key) : m_type(Type::Scalar), m_scalar(key), m_tagType(TagType::Str) {}
    182 
    183         // Require certain node types to; throwing if the requirement is not met.
    184         void Require(Type type) const;
    185 
    186         // The workers for the as function.
    187         std::string as_dispatch(std::string*) const;
    188         std::optional<std::string> try_as_dispatch(std::string*) const;
    189 
    190         std::wstring as_dispatch(std::wstring*) const;
    191         std::optional<std::wstring> try_as_dispatch(std::wstring*) const;
    192 
    193         int64_t as_dispatch(int64_t*) const;
    194         std::optional<int64_t> try_as_dispatch(int64_t*) const;
    195 
    196         int as_dispatch(int*) const;
    197         std::optional<int> try_as_dispatch(int*) const;
    198 
    199         bool as_dispatch(bool*) const;
    200         std::optional<bool> try_as_dispatch(bool*) const;
    201 
    202         Type m_type;
    203         std::string m_tag;
    204         TagType m_tagType;
    205         YAML::Mark m_mark;
    206         std::string m_scalar;
    207         std::optional<std::vector<Node>> m_sequence;
    208         std::optional<std::multimap<Node, Node>> m_mapping;
    209     };
    210 
    211     // Loads from the input; returns the root node of the first document.
    212     Node Load(std::string_view input);
    213     Node Load(const std::string& input);
    214     Node Load(const std::filesystem::path& input);
    215     Node Load(const std::filesystem::path& input, Utility::SHA256::HashBuffer& hashOut);
    216 
    217     // Any emitter event.
    218     // Not using enum class to enable existing code to function.
    219     enum EmitterEvent
    220     {
    221         BeginSeq,
    222         EndSeq,
    223         BeginMap,
    224         EndMap,
    225         Key,
    226         Value,
    227     };
    228 
    229     // Sets the scalar style to use for the next scalar output.
    230     enum class ScalarStyle
    231     {
    232         Any,
    233         Plain,
    234         SingleQuoted,
    235         DoubleQuoted,
    236         Literal,
    237         Folded,
    238     };
    239 
    240     // A schema header for a document.
    241     struct DocumentSchemaHeader
    242     {
    243         DocumentSchemaHeader() = default;
    244         DocumentSchemaHeader(std::string schemaHeaderString, const Mark& mark) : SchemaHeader(std::move(schemaHeaderString)), Mark(mark) {}
    245 
    246         std::string SchemaHeader;
    247         Mark Mark;
    248         static constexpr std::string_view YamlLanguageServerKey = "yaml-language-server";
    249     };
    250 
    251     struct Document
    252     {
    253         Document() = default;
    254         Document(Node root, DocumentSchemaHeader schemaHeader) : m_root(std::move(root)), m_schemaHeader(std::move(schemaHeader)) {}
    255 
    256         const DocumentSchemaHeader& GetSchemaHeader() const { return m_schemaHeader; }
    257 
    258         // Return r-values for move semantics
    259         Node&& GetRoot() && { return std::move(m_root); }
    260 
    261     private:
    262         Node m_root;
    263         DocumentSchemaHeader m_schemaHeader;
    264     };
    265 
    266     // Forward declaration to allow pImpl in this Emitter.
    267     namespace Wrapper
    268     {
    269         struct Document;
    270     }
    271 
    272     // Loads from the input; returns the root node of the first document.
    273     Document LoadDocument(std::string_view input);
    274     Document LoadDocument(const std::string& input);
    275     Document LoadDocument(const std::filesystem::path& input);
    276     Document LoadDocument(const std::filesystem::path& input, Utility::SHA256::HashBuffer& hashOut);
    277 
    278     // A YAML emitter.
    279     struct Emitter
    280     {
    281         Emitter();
    282 
    283         Emitter(const Emitter&) = delete;
    284         Emitter& operator=(const Emitter&) = delete;
    285 
    286         Emitter(Emitter&&) noexcept;
    287         Emitter& operator=(Emitter&&) noexcept;
    288 
    289         ~Emitter();
    290 
    291         // Emit events and values.
    292         Emitter& operator<<(EmitterEvent event);
    293         Emitter& operator<<(std::string_view value);
    294         Emitter& operator<<(int64_t value);
    295         Emitter& operator<<(int value);
    296         Emitter& operator<<(bool value);
    297 
    298         Emitter& operator<<(ScalarStyle style);
    299 
    300         // Gets the result of the emitter; can only be retrieved once.
    301         std::string str();
    302 
    303         // Gets the result of the emitter to out stream; can only be retrieved once.
    304         void Emit(std::ostream& out);
    305 
    306     private:
    307         // Appends the given node to the current container if applicable.
    308         void AppendNode(int id);
    309 
    310         std::unique_ptr<Wrapper::Document> m_document;
    311 
    312         // If set, stores the last Key that was set.
    313         std::optional<int> m_keyId;
    314 
    315         struct ContainerInfo
    316         {
    317             ContainerInfo(int id, bool map) : Id(id), IsMapping(map) {}
    318 
    319             int Id;
    320             bool IsMapping;
    321         };
    322 
    323         // The stack of containers being emitted.
    324         std::stack<ContainerInfo> m_containers;
    325 
    326         // *** State Machine ***
    327 
    328         // The type of input coming into the emitter.
    329         enum class InputType
    330         {
    331             Scalar,
    332             BeginSeq,
    333             EndSeq,
    334             BeginMap,
    335             EndMap,
    336             Key,
    337             Value,
    338         };
    339 
    340         // If set, defines the type of the next scalar (Key or Value).
    341         std::optional<InputType> m_scalarType;
    342 
    343         // If set, defines the style of the next scalar.
    344         std::optional<ScalarStyle> m_scalarStyle;
    345 
    346         // Converts the input type to a bitmask value.
    347         size_t GetInputBitmask(InputType type);
    348 
    349         // Checks the state of the emitter to ensure that the incoming value is acceptable.
    350         void CheckInput(InputType type);
    351 
    352         // The currently allowed input types.
    353         size_t m_allowedInputs = 0;
    354 
    355         template <InputType... types>
    356         void SetAllowedInputs()
    357         {
    358             m_allowedInputs = (GetInputBitmask(types) | ...);
    359         }
    360 
    361         // Sets the allowed inputs for the container on the top of the stack.
    362         void SetAllowedInputsForContainer();
    363     };
    364 }