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 }