winget-cli

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

DscComposableObject.h (10038B)


      1 // Copyright (c) Microsoft Corporation.
      2 // Licensed under the MIT License.
      3 #pragma once
      4 #include <AppInstallerErrors.h>
      5 #include <AppInstallerLanguageUtilities.h>
      6 #include <AppInstallerLogging.h>
      7 #include <winget/LocIndependent.h>
      8 #include "Resources.h"
      9 #include <json/json.h>
     10 #include <optional>
     11 #include <string>
     12 #include <vector>
     13 
     14 using namespace std::string_view_literals;
     15 using namespace AppInstaller::Utility::literals;
     16 
     17 namespace AppInstaller::CLI
     18 {
     19     // Flags that define how to treat properties.
     20     enum DscComposablePropertyFlag
     21     {
     22         None = 0x0,
     23         Required = 0x1,
     24         CopyToOutput = 0x2,
     25     };
     26 
     27     DEFINE_ENUM_FLAG_OPERATORS(DscComposablePropertyFlag);
     28 
     29     namespace details
     30     {
     31         // Gets a property or null if not present.
     32         const Json::Value* GetProperty(const Json::Value& object, std::string_view name);
     33 
     34         // Adds the given property and value to the object, if provided.
     35         void AddProperty(Json::Value& object, std::string_view name, std::optional<Json::Value>&& value);
     36 
     37         // Gets the default schema object.
     38         Json::Value GetBaseSchema(const std::string& title);
     39 
     40         // Adds a property to the schema object.
     41         void AddPropertySchema(
     42             Json::Value& object,
     43             std::string_view name,
     44             DscComposablePropertyFlag flags,
     45             Json::ValueType type,
     46             std::string_view description,
     47             const std::vector<std::string>& enumValues,
     48             const std::optional<std::string>& defaultValue);
     49     }
     50 
     51     template <typename PropertyType>
     52     struct GetJsonTypeValue
     53     {
     54         static_assert(false, "Implement for this type.");
     55     };
     56 
     57     template <>
     58     struct GetJsonTypeValue<bool>
     59     {
     60         static bool Get(const Json::Value& value)
     61         {
     62             return value.asBool();
     63         }
     64 
     65         static Json::ValueType SchemaType()
     66         {
     67             return Json::ValueType::booleanValue;
     68         }
     69     };
     70 
     71     template <>
     72     struct GetJsonTypeValue<std::string>
     73     {
     74         static std::string Get(const Json::Value& value)
     75         {
     76             return value.asString();
     77         }
     78 
     79         static Json::ValueType SchemaType()
     80         {
     81             return Json::ValueType::stringValue;
     82         }
     83     };
     84 
     85     template <>
     86     struct GetJsonTypeValue<Json::Value>
     87     {
     88         static Json::Value Get(const Json::Value& value)
     89         {
     90             return value;
     91         }
     92 
     93         static Json::ValueType SchemaType()
     94         {
     95             return Json::ValueType::objectValue;
     96         }
     97     };
     98 
     99     // Template useful for composing objects for DSC resources.
    100     // Properties should be of the shape:
    101     //
    102     // struct Property
    103     // {
    104     //      using PropertyType = { bool, std::string };
    105     //      static std::string_view Name();
    106     //      static void FromJson(Property*, const Json::Value*);
    107     //      static std::optional<Json::Value> ToJson(const Property*);
    108     //
    109     //      const PropertyType& PROPERTY_NAME() const;
    110     //      void PROPERTY_NAME(const PropertyType&);
    111     // }
    112     template <typename... Property>
    113     struct DscComposableObject : public Property...
    114     {
    115         DscComposableObject() = default;
    116 
    117         DscComposableObject(const std::optional<Json::Value>& input, bool ignoreFieldRequirements = false)
    118         {
    119             THROW_HR_IF(E_POINTER, !input && !ignoreFieldRequirements);
    120             if (input)
    121             {
    122                 FromJson(input.value(), ignoreFieldRequirements);
    123             }
    124         }
    125 
    126         // Read values for each property
    127         void FromJson(const Json::Value& input, bool ignoreFieldRequirements = false)
    128         {
    129             (FoldHelper{}, ..., Property::FromJson(this, details::GetProperty(input, Property::Name()), ignoreFieldRequirements));
    130         }
    131 
    132         // Populate JSON object with properties.
    133         Json::Value ToJson()
    134         {
    135             Json::Value result{ Json::ValueType::objectValue };
    136             (FoldHelper{}, ..., details::AddProperty(result, Property::Name(), Property::ToJson(this)));
    137             return result;
    138         }
    139 
    140         // Copies the appropriate values to a new object for output.
    141         DscComposableObject CopyForOutput() const
    142         {
    143             DscComposableObject result;
    144             (FoldHelper{}, ..., Property::CopyForOutput(this, &result));
    145             return result;
    146         }
    147 
    148         // Get the JSON Schema for this object
    149         static Json::Value Schema(const std::string& title)
    150         {
    151             Json::Value result = details::GetBaseSchema(title);
    152             (FoldHelper{}, ..., details::AddPropertySchema(result, Property::Name(), Property::Flags, GetJsonTypeValue<typename Property::PropertyType>::SchemaType(), Property::Description(), Property::EnumValues(), Property::Default()));
    153             return result;
    154         }
    155     };
    156 
    157     template <typename Derived, typename PropertyTypeT, DscComposablePropertyFlag PropertyFlags>
    158     struct DscComposableProperty
    159     {
    160         using PropertyType = PropertyTypeT;
    161         static constexpr DscComposablePropertyFlag Flags = PropertyFlags;
    162 
    163         static void FromJson(Derived* self, const Json::Value* value, bool ignoreFieldRequirements)
    164         {
    165             if (value && !value->isNull())
    166             {
    167                 self->m_value = GetJsonTypeValue<PropertyType>::Get(*value);
    168             }
    169             else
    170             {
    171                 if (!ignoreFieldRequirements && WI_IsFlagSet(PropertyFlags, DscComposablePropertyFlag::Required))
    172                 {
    173                     THROW_HR_MSG(WINGET_CONFIG_ERROR_MISSING_FIELD, "Required property `%hs` not provided.", Derived::Name().data());
    174                 }
    175                 else
    176                 {
    177                     self->m_value = std::nullopt;
    178                 }
    179             }
    180         }
    181 
    182         static std::optional<Json::Value> ToJson(const Derived* self)
    183         {
    184             if constexpr (WI_IsFlagSet(PropertyFlags, DscComposablePropertyFlag::Required))
    185             {
    186                 THROW_HR_IF(WINGET_CONFIG_ERROR_MISSING_FIELD, !self->m_value);
    187                 return self->m_value.value();
    188             }
    189             else
    190             {
    191                 return self->m_value ? std::optional<Json::Value>{ self->m_value.value() } : std::nullopt;
    192             }
    193         }
    194 
    195         static void CopyForOutput(const Derived* self, Derived* other)
    196         {
    197             if constexpr (WI_IsFlagSet(PropertyFlags, DscComposablePropertyFlag::CopyToOutput))
    198             {
    199                 other->m_value = self->m_value;
    200             }
    201         }
    202 
    203     protected:
    204         std::optional<PropertyType> m_value;
    205     };
    206 
    207 #define WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_IMPL_START(_property_type_, _value_type_, _property_name_, _json_name_, _flags_, _description_, _enum_vals_, _default_) \
    208     struct _property_type_ : public DscComposableProperty<_property_type_, _value_type_, _flags_> \
    209     { \
    210         static std::string_view Name() { return _json_name_; } \
    211         static Resource::LocString Description() { return _description_; } \
    212         static std::vector<std::string> EnumValues() { return std::vector<std::string> _enum_vals_; } \
    213         static std::optional<std::string> Default() { return _default_; } \
    214         std::optional<PropertyType>& _property_name_() { return m_value; } \
    215         const std::optional<PropertyType>& _property_name_() const { return m_value; } \
    216         void _property_name_(std::optional<PropertyType> value) { m_value = std::move(value); } \
    217 
    218 #define WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_IMPL(_property_type_, _value_type_, _property_name_, _json_name_, _flags_, _description_, _enum_vals_, _default_) \
    219     WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_IMPL_START(_property_type_, _value_type_, _property_name_, _json_name_, _flags_, _description_, _enum_vals_, _default_) \
    220     };
    221 
    222 #define WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY(_property_type_, _value_type_, _property_name_, _json_name_, _description_) \
    223     WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_IMPL(_property_type_, _value_type_, _property_name_, _json_name_, DscComposablePropertyFlag::None, _description_, {}, {})
    224 
    225 #define WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_FLAGS(_property_type_, _value_type_, _property_name_, _json_name_, _flags_, _description_) \
    226     WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_IMPL(_property_type_, _value_type_, _property_name_, _json_name_, _flags_, _description_, {}, {})
    227 
    228 #define WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_DEFAULT(_property_type_, _value_type_, _property_name_, _json_name_, _description_, _default_) \
    229     WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_IMPL(_property_type_, _value_type_, _property_name_, _json_name_, DscComposablePropertyFlag::None, _description_, {}, _default_)
    230 
    231 #define WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_ENUM(_property_type_, _value_type_, _property_name_, _json_name_, _description_, _enum_vals_, _default_) \
    232     WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_IMPL(_property_type_, _value_type_, _property_name_, _json_name_, DscComposablePropertyFlag::None, _description_, _enum_vals_, _default_)
    233 
    234 #define WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_ENUM_FLAGS(_property_type_, _value_type_, _property_name_, _json_name_, _flags_, _description_, _enum_vals_, _default_) \
    235     WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_IMPL(_property_type_, _value_type_, _property_name_, _json_name_, _flags_, _description_, _enum_vals_, _default_)
    236 
    237     WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY_IMPL_START(StandardExistProperty, bool, Exist, "_exist", DscComposablePropertyFlag::None, Resource::String::DscResourcePropertyDescriptionExist, {}, {})
    238         bool ShouldExist() const { return m_value.value_or(true); }
    239     };
    240 
    241     WINGET_DSC_DEFINE_COMPOSABLE_PROPERTY(StandardInDesiredStateProperty, bool, InDesiredState, "_inDesiredState", Resource::String::DscResourcePropertyDescriptionInDesiredState);
    242 }