diff --git a/src/cascadia/TerminalApp/JsonUtilsNew.h b/src/cascadia/TerminalApp/JsonUtilsNew.h index 36189b4afa..03e9826f4b 100644 --- a/src/cascadia/TerminalApp/JsonUtilsNew.h +++ b/src/cascadia/TerminalApp/JsonUtilsNew.h @@ -20,9 +20,15 @@ Author(s): namespace winrt { - // If we don't use winrt, nobody will include the ConversionTrait for winrt::guid. - // If nobody includes it, this forward declaration will suffice. + // If we don't use winrt, nobody will include the ConversionTraits for winrt stuff. + // If nobody includes it, these forward declarations will suffice. struct guid; + struct hstring; + namespace Windows::Foundation + { + template + struct IReference; + } } namespace TerminalApp::JsonUtils @@ -45,12 +51,21 @@ namespace TerminalApp::JsonUtils struct DeduceOptional { using Type = typename std::decay::type; + static constexpr bool IsOptional = false; }; template struct DeduceOptional> { using Type = typename std::decay::type; + static constexpr bool IsOptional = true; + }; + + template + struct DeduceOptional<::winrt::Windows::Foundation::IReference> + { + using Type = typename std::decay::type; + static constexpr bool IsOptional = true; }; } @@ -105,7 +120,9 @@ namespace TerminalApp::JsonUtils template struct ConversionTrait { - // FromJson, CanConvert are not defined so as to cause a compile error (which forces a specialization) + // Forward-declare these so the linker can pick up specializations from elsewhere! + T FromJson(const Json::Value&); + bool CanConvert(const Json::Value& json); }; template<> @@ -136,6 +153,18 @@ namespace TerminalApp::JsonUtils } }; +#ifdef WINRT_BASE_H + template<> + struct ConversionTrait : public ConversionTrait + { + // Leverage the wstring converter's validation + winrt::hstring FromJson(const Json::Value& json) + { + return winrt::hstring{ til::u8u16(Detail::GetStringView(json)) }; + } + }; +#endif + template<> struct ConversionTrait { @@ -248,13 +277,14 @@ namespace TerminalApp::JsonUtils } const auto string{ Detail::GetStringView(json) }; - return (string.length() == 7 || string.length() == 3) && string.front() == '#'; + return (string.length() == 7 || string.length() == 4) && string.front() == '#'; } }; template struct EnumMapper { + using BaseEnumMapper = EnumMapper; using pair_type = std::pair; T FromJson(const Json::Value& json) { @@ -284,6 +314,13 @@ namespace TerminalApp::JsonUtils template struct FlagMapper : public EnumMapper { + private: + // Hide BaseEnumMapper so FlagMapper's consumers cannot see + // it. + using BaseEnumMapper = EnumMapper::BaseEnumMapper; + + public: + using BaseFlagMapper = FlagMapper; static constexpr T AllSet{ static_cast(~0u) }; static constexpr T AllClear{ static_cast(0u) }; @@ -291,7 +328,7 @@ namespace TerminalApp::JsonUtils { if (json.isString()) { - return EnumMapper::FromJson(json); + return BaseEnumMapper::FromJson(json); } else if (json.isArray()) { @@ -299,7 +336,7 @@ namespace TerminalApp::JsonUtils T value{}; for (const auto& element : json) { - const auto newFlag{ EnumMapper::FromJson(element) }; + const auto newFlag{ BaseEnumMapper::FromJson(element) }; if (++seen > 1 && ((newFlag == AllClear && value != AllClear) || (value == AllClear && newFlag != AllClear))) @@ -318,7 +355,7 @@ namespace TerminalApp::JsonUtils bool CanConvert(const Json::Value& json) { - return EnumMapper::CanConvert(json) || json.isArray(); + return BaseEnumMapper::CanConvert(json) || json.isArray(); } }; @@ -334,6 +371,18 @@ namespace TerminalApp::JsonUtils template bool GetValue(const Json::Value& json, T& target, Converter&& conv) { + if constexpr (Detail::DeduceOptional::IsOptional) + { + // FOR OPTION TYPES + // - If the json object is set to `null`, then + // we'll instead set the target back to the empty optional. + if (json.isNull()) + { + target = T{}; // zero-construct an empty optional + return true; + } + } + if (json) { if (!conv.CanConvert(json)) @@ -347,36 +396,6 @@ namespace TerminalApp::JsonUtils return false; } - // Method Description: - // - Overload on GetValue that will populate a std::optional with a value converted from json - // - If the json value doesn't exist we'll leave the target object unmodified. - // - If the json object is set to `null`, then - // we'll instead set the target back to nullopt. - // Arguments: - // - json: the json object to convert - // - target: the value to populate with the converted result - // Return Value: - // - a boolean indicating whether the optional was changed - // - // GetValue, type-deduced for optional, manual converter - template - bool GetValue(const Json::Value& json, std::optional& target, Converter&& conv) - { - if (json.isNull()) - { - target = std::nullopt; - return true; // null is valid for optionals - } - - std::decay_t local{}; - if (GetValue(json, local, std::forward(conv))) - { - target = std::move(local); - return true; - } - return false; - } - // GetValue, forced return type, manual converter template std::decay_t GetValue(const Json::Value& json, Converter&& conv)