-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for bool, strings and arrays in Configuration settings #3135
Changes from 3 commits
d7f9cc0
772ade9
e3b459b
911f272
a9913f5
85b0a33
013ed69
2f6832b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,4 +8,4 @@ StringTrue: 'true' | |
BooleanFalse: false | ||
StringFalse: 'false' | ||
|
||
LocalTag: !mytag value | ||
LocalTag: !myTag value |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -175,6 +175,32 @@ namespace AppInstaller::Utility | |
return result; | ||
} | ||
|
||
std::optional<std::wstring> TryConvertToUTF16(std::string_view input, UINT codePage) | ||
{ | ||
if (input.empty()) | ||
{ | ||
return {}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This returns an empty optional, which isn't what I would expect. It should return an optional with an empty string in it. |
||
} | ||
|
||
int utf16CharCount = MultiByteToWideChar(codePage, 0, input.data(), wil::safe_cast<int>(input.length()), nullptr, 0); | ||
if (utf16CharCount == 0) | ||
{ | ||
return {}; | ||
} | ||
|
||
// Since the string view should not contain the null char, the result won't either. | ||
// This allows us to use the resulting size value directly in the string constructor. | ||
std::wstring result(wil::safe_cast<size_t>(utf16CharCount), L'\0'); | ||
|
||
int utf16CharsWritten = MultiByteToWideChar(codePage, 0, input.data(), wil::safe_cast<int>(input.length()), &result[0], wil::safe_cast<int>(result.size())); | ||
if (utf16CharCount != utf16CharsWritten) | ||
{ | ||
return {}; | ||
} | ||
|
||
return std::optional{ result }; | ||
} | ||
|
||
std::u32string ConvertToUTF32(std::string_view input) | ||
{ | ||
if (input.empty()) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -128,6 +128,14 @@ namespace AppInstaller::YAML | |
return as_dispatch(t); | ||
} | ||
|
||
template <typename T> | ||
std::optional<T> try_as() const | ||
{ | ||
Require(Type::Scalar); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will throw if the node isn't a scalar. Should it just return an empty optional instead? |
||
T* t = nullptr; | ||
return try_as_dispatch(t); | ||
} | ||
|
||
bool operator<(const Node& other) const; | ||
|
||
// Gets a child node from the mapping by its name. | ||
|
@@ -158,10 +166,19 @@ namespace AppInstaller::YAML | |
|
||
// The workers for the as function. | ||
std::string as_dispatch(std::string*) const; | ||
std::optional<std::string> try_as_dispatch(std::string*) const; | ||
|
||
std::wstring as_dispatch(std::wstring*) const; | ||
std::optional<std::wstring> try_as_dispatch(std::wstring*) const; | ||
|
||
int64_t as_dispatch(int64_t*) const; | ||
std::optional<int64_t> try_as_dispatch(int64_t*) const; | ||
|
||
int as_dispatch(int*) const; | ||
std::optional<int> try_as_dispatch(int*) const; | ||
|
||
bool as_dispatch(bool*) const; | ||
std::optional<bool> try_as_dispatch(bool*) const; | ||
|
||
Type m_type; | ||
std::string m_tag; | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -207,22 +207,19 @@ namespace AppInstaller::YAML | |||||
{ | ||||||
// Integer | ||||||
// 0 | -? [1-9] [0-9]* | ||||||
try | ||||||
auto tryInt = this->try_as<int64_t>(); | ||||||
if (tryInt.has_value()) | ||||||
{ | ||||||
this->as<int64_t>(); | ||||||
m_tagType = TagType::Int; | ||||||
return; | ||||||
} | ||||||
catch (...) | ||||||
{ | ||||||
} | ||||||
|
||||||
// Either 'true' or 'false' | ||||||
// Avoid THROW_HR to don't log. | ||||||
if (Utility::CaseInsensitiveEquals(m_scalar, "true") || | ||||||
Utility::CaseInsensitiveEquals(m_scalar, "false")) | ||||||
else | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would continue to use the early return pattern rather than an |
||||||
{ | ||||||
m_tagType = TagType::Bool; | ||||||
// Boolean. Either 'true' or 'false' | ||||||
auto tryBool = this->try_as<bool>(); | ||||||
if (tryBool.has_value()) | ||||||
{ | ||||||
m_tagType = TagType::Bool; | ||||||
} | ||||||
} | ||||||
} | ||||||
} | ||||||
|
@@ -319,38 +316,84 @@ namespace AppInstaller::YAML | |||||
return m_scalar; | ||||||
} | ||||||
|
||||||
std::optional<std::string> Node::try_as_dispatch(std::string*) const | ||||||
{ | ||||||
return std::optional{ m_scalar }; | ||||||
} | ||||||
|
||||||
std::wstring Node::as_dispatch(std::wstring*) const | ||||||
{ | ||||||
return Utility::ConvertToUTF16(m_scalar); | ||||||
} | ||||||
|
||||||
std::optional<std::wstring> Node::try_as_dispatch(std::wstring*) const | ||||||
{ | ||||||
return Utility::TryConvertToUTF16(m_scalar); | ||||||
} | ||||||
|
||||||
int64_t Node::as_dispatch(int64_t*) const | ||||||
{ | ||||||
return std::stoll(m_scalar); | ||||||
} | ||||||
|
||||||
std::optional<int64_t> Node::try_as_dispatch(int64_t*) const | ||||||
{ | ||||||
try | ||||||
{ | ||||||
return std::optional{ std::stoll(m_scalar) }; | ||||||
} | ||||||
catch(...) | ||||||
{ | ||||||
return {}; | ||||||
} | ||||||
} | ||||||
|
||||||
int Node::as_dispatch(int*) const | ||||||
{ | ||||||
// To allow HResult representation | ||||||
return static_cast<int>(std::stoll(m_scalar, 0, 0)); | ||||||
} | ||||||
|
||||||
bool Node::as_dispatch(bool*) const | ||||||
std::optional<int> Node::try_as_dispatch(int*) const | ||||||
{ | ||||||
if (Utility::CaseInsensitiveEquals(m_scalar, "true")) | ||||||
try | ||||||
{ | ||||||
return true; | ||||||
return std::optional{ static_cast<int>(std::stoll(m_scalar, 0, 0)) }; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} | ||||||
else if (Utility::CaseInsensitiveEquals(m_scalar, "false")) | ||||||
catch (...) | ||||||
{ | ||||||
return {}; | ||||||
} | ||||||
} | ||||||
|
||||||
bool Node::as_dispatch(bool*) const | ||||||
{ | ||||||
bool* t = nullptr; | ||||||
auto tryToBool = this->try_as_dispatch(t); | ||||||
if (tryToBool.has_value()) | ||||||
{ | ||||||
return false; | ||||||
return tryToBool.value(); | ||||||
} | ||||||
else | ||||||
{ | ||||||
THROW_HR(APPINSTALLER_CLI_ERROR_YAML_INVALID_DATA); | ||||||
} | ||||||
} | ||||||
|
||||||
std::optional<bool> Node::try_as_dispatch(bool*) const | ||||||
{ | ||||||
if (Utility::CaseInsensitiveEquals(m_scalar, "true")) | ||||||
{ | ||||||
return std::optional{ true }; | ||||||
} | ||||||
else if (Utility::CaseInsensitiveEquals(m_scalar, "false")) | ||||||
{ | ||||||
return std::optional{ false }; | ||||||
} | ||||||
|
||||||
return {}; | ||||||
} | ||||||
|
||||||
Node Load(std::string_view input) | ||||||
{ | ||||||
Wrapper::Parser parser(input); | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These keys are strings and won't sort numerically.