diff --git a/universal/src/formats/yaml/value.cpp b/universal/src/formats/yaml/value.cpp index 2a218a1fc06a..2a44425f9a59 100644 --- a/universal/src/formats/yaml/value.cpp +++ b/universal/src/formats/yaml/value.cpp @@ -11,52 +11,6 @@ #include "exttypes.hpp" #include "string_view_support.hpp" -namespace { - -// Helper structure for YAML conversions. YAML has built in conversion logic and -// an `T Node::as(U default_value)` function that uses it. We provide -// `IsConvertibleChecker{}` as a `default_value`, and if the -// `IsConvertibleChecker` was converted to T (an implicit conversion operator -// was called), then the conversion failed. -template -struct IsConvertibleChecker { - bool& convertible; - - operator T() const { - convertible = false; - return {}; - } -}; - -} // anonymous namespace - -namespace YAML { - -// Reverting harm done by https://github.com/jbeder/yaml-cpp/commit/96f5c887f373ac483844c51cfc9a3621002314f0 -// to detect if the conversion is successful without throwing an exception -template -requires std::integral || std::floating_point -struct as_if > { - explicit as_if(const Node& node_in) - : node(node_in) - {} - const Node& node; - - T operator()(const IsConvertibleChecker& fallback) const { - if (!node.m_pNode) { - return fallback; - } - - T t; - if (convert::decode(node, t)) { - return t; - } - return fallback; - } -}; - -} // namespace YAML - USERVER_NAMESPACE_BEGIN namespace formats::yaml { @@ -182,21 +136,19 @@ bool Value::IsConvertibleToArithmetic() const { return false; } - bool ok = true; - value_pimpl_->as(IsConvertibleChecker{ok}); - return ok && !IsExplicitlyTypedString(*value_pimpl_); + T t{}; + return YAML::convert::decode(*value_pimpl_, t) && !IsExplicitlyTypedString(*value_pimpl_); } template T Value::ValueAsArithmetic() const { CheckNotMissing(); - bool ok = true; - auto res = value_pimpl_->as(IsConvertibleChecker{ok}); - if (!ok || IsExplicitlyTypedString(*value_pimpl_)) { + T t{}; + if (!YAML::convert::decode(*value_pimpl_, t) || IsExplicitlyTypedString(*value_pimpl_)) { throw TypeMismatchException(*value_pimpl_, compiler::GetTypeName(), path_.ToStringView()); } - return res; + return t; } bool Value::IsNull() const noexcept { diff --git a/universal/src/formats/yaml/value_test.cpp b/universal/src/formats/yaml/value_test.cpp index 4689072b32a4..57264187fea8 100644 --- a/universal/src/formats/yaml/value_test.cpp +++ b/universal/src/formats/yaml/value_test.cpp @@ -146,4 +146,23 @@ TEST(FormatsYaml, NodesTags) { EXPECT_EQ(yaml["field_tag_prefix"].GetTag(), "tag:example,2024:foo"); } +TEST(FormatsYaml, IsBoolFromParsedScalar) { + auto f = formats::yaml::FromString("false"); + auto t = formats::yaml::FromString("true"); + + EXPECT_TRUE(f.IsBool()); + EXPECT_TRUE(t.IsBool()); + EXPECT_FALSE(f.As()); + EXPECT_TRUE(t.As()); + + EXPECT_FALSE(formats::yaml::FromString("\"false\"").IsBool()); + EXPECT_FALSE(formats::yaml::FromString("hello").IsBool()); +} + +TEST(FormatsYaml, IsIntFromParsedScalar) { + auto v = formats::yaml::FromString("42"); + EXPECT_TRUE(v.IsInt()); + EXPECT_EQ(v.As(), 42); +} + USERVER_NAMESPACE_END diff --git a/universal/src/yaml_config/merge_schemas_test.cpp b/universal/src/yaml_config/merge_schemas_test.cpp index 2cf3db66ff65..fc0cfbf12252 100644 --- a/universal/src/yaml_config/merge_schemas_test.cpp +++ b/universal/src/yaml_config/merge_schemas_test.cpp @@ -205,4 +205,16 @@ TEST(MergeSchemas, RequiredParentOnlyNoChildRequired) { EXPECT_EQ(*schema.required, (std::unordered_set{"parent_option"})); } +TEST(YamlConfigSchema, AdditionalPropertiesFalse) { + auto schema = yaml_config::impl::SchemaFromString(R"( +type: object +description: test +additionalProperties: false +properties: {} +)"); + ASSERT_TRUE(schema.additional_properties.has_value()); + ASSERT_TRUE(std::holds_alternative(schema.additional_properties.value())); + EXPECT_FALSE(std::get(schema.additional_properties.value())); +} + USERVER_NAMESPACE_END