aboutsummaryrefslogtreecommitdiff
path: root/toolsrc/src
diff options
context:
space:
mode:
authorras0219 <533828+ras0219@users.noreply.github.com>2020-10-12 10:22:47 -0700
committerGitHub <noreply@github.com>2020-10-12 10:22:47 -0700
commitd1ba685e975db41d767af5676ca2669a5e7e6ad9 (patch)
treee6dcb36f131e638fd5307a304a8fb5f2ce072758 /toolsrc/src
parentd6b5fbfef10e9476cdb8d1a90503f4f6504e63d9 (diff)
downloadvcpkg-d1ba685e975db41d767af5676ca2669a5e7e6ad9.tar.gz
vcpkg-d1ba685e975db41d767af5676ca2669a5e7e6ad9.zip
[vcpkg] Further JSON error improvements (#13399)
* [vcpkg] Split vcpkg/base/json.h into vcpkg/base/jsonreader.h * [vcpkg] Extract definitions of Configuration-Deserializer (& friends) These types are only used by VcpkgPaths during the initial parse. * [vcpkg] Introduce levenshtein-distance suggestions for json errors * [vcpkg] Fix regression in supports handling * [vcpkg] Fix signed/unsigned mismatch * [vcpkg] Address CR comments * [vcpkg] Address CR comments * Fix compiler error from merge conflict. * [vcpkg] Change parameters of Reader::check_for_unexpected_fields to better match declaration * [vcpkg] Improve errors from features set * [vcpkg] Fix includes * [vcpkg] Reuse code * [vcpkg] Check the "name" field always to maximize error information * [docs] Improve english phrasing in manifests.md * [vcpkg] Correct docs link for manifests Co-authored-by: Robert Schumacher <roschuma@microsoft.com> Co-authored-by: Billy Robert O'Neal III <bion@microsoft.com>
Diffstat (limited to 'toolsrc/src')
-rw-r--r--toolsrc/src/vcpkg-test/manifests.cpp11
-rw-r--r--toolsrc/src/vcpkg-test/strings.cpp15
-rw-r--r--toolsrc/src/vcpkg/base/json.cpp101
-rw-r--r--toolsrc/src/vcpkg/base/strings.cpp49
-rw-r--r--toolsrc/src/vcpkg/configuration.cpp12
-rw-r--r--toolsrc/src/vcpkg/install.cpp5
-rw-r--r--toolsrc/src/vcpkg/platform-expression.cpp2
-rw-r--r--toolsrc/src/vcpkg/registries.cpp25
-rw-r--r--toolsrc/src/vcpkg/sourceparagraph.cpp160
-rw-r--r--toolsrc/src/vcpkg/vcpkgpaths.cpp6
10 files changed, 288 insertions, 98 deletions
diff --git a/toolsrc/src/vcpkg-test/manifests.cpp b/toolsrc/src/vcpkg-test/manifests.cpp
index 0d5c9782a..77f4eb440 100644
--- a/toolsrc/src/vcpkg-test/manifests.cpp
+++ b/toolsrc/src/vcpkg-test/manifests.cpp
@@ -248,6 +248,17 @@ TEST_CASE ("SourceParagraph manifest empty supports", "[manifests]")
REQUIRE_FALSE(m_pgh.has_value());
}
+TEST_CASE ("SourceParagraph manifest non-string supports", "[manifests]")
+{
+ auto m_pgh = test_parse_manifest(R"json({
+ "name": "a",
+ "version-string": "1.0",
+ "supports": true
+ })json",
+ true);
+ REQUIRE_FALSE(m_pgh.has_value());
+}
+
TEST_CASE ("Serialize all the ports", "[manifests]")
{
std::vector<std::string> args_list = {"format-manifest"};
diff --git a/toolsrc/src/vcpkg-test/strings.cpp b/toolsrc/src/vcpkg-test/strings.cpp
index cc45365e6..ecf0524bc 100644
--- a/toolsrc/src/vcpkg-test/strings.cpp
+++ b/toolsrc/src/vcpkg-test/strings.cpp
@@ -56,3 +56,18 @@ TEST_CASE ("find_first_of", "[strings]")
REQUIRE(find_first_of("abcdefg", "bg") == std::string("bcdefg"));
REQUIRE(find_first_of("abcdefg", "gb") == std::string("bcdefg"));
}
+
+TEST_CASE ("edit distance", "[strings]")
+{
+ using vcpkg::Strings::byte_edit_distance;
+ REQUIRE(byte_edit_distance("", "") == 0);
+ REQUIRE(byte_edit_distance("a", "a") == 0);
+ REQUIRE(byte_edit_distance("abcd", "abcd") == 0);
+ REQUIRE(byte_edit_distance("aaa", "aa") == 1);
+ REQUIRE(byte_edit_distance("aa", "aaa") == 1);
+ REQUIRE(byte_edit_distance("abcdef", "bcdefa") == 2);
+ REQUIRE(byte_edit_distance("hello", "world") == 4);
+ REQUIRE(byte_edit_distance("CAPITAL", "capital") == 7);
+ REQUIRE(byte_edit_distance("", "hello") == 5);
+ REQUIRE(byte_edit_distance("world", "") == 5);
+}
diff --git a/toolsrc/src/vcpkg/base/json.cpp b/toolsrc/src/vcpkg/base/json.cpp
index 495a36e32..0355b0c94 100644
--- a/toolsrc/src/vcpkg/base/json.cpp
+++ b/toolsrc/src/vcpkg/base/json.cpp
@@ -1,5 +1,6 @@
#include <vcpkg/base/files.h>
#include <vcpkg/base/json.h>
+#include <vcpkg/base/jsonreader.h>
#include <vcpkg/base/system.debug.h>
#include <vcpkg/base/unicode.h>
@@ -986,6 +987,14 @@ namespace vcpkg::Json
};
}
+ NaturalNumberDeserializer NaturalNumberDeserializer::instance;
+ BooleanDeserializer BooleanDeserializer::instance;
+ ParagraphDeserializer ParagraphDeserializer::instance;
+ IdentifierDeserializer IdentifierDeserializer::instance;
+ IdentifierArrayDeserializer IdentifierArrayDeserializer::instance;
+ PackageNameDeserializer PackageNameDeserializer::instance;
+ PathDeserializer PathDeserializer::instance;
+
bool IdentifierDeserializer::is_ident(StringView sv)
{
static const std::regex BASIC_IDENTIFIER = std::regex(R"([a-z0-9]+(-[a-z0-9]+)*)");
@@ -1282,9 +1291,27 @@ namespace vcpkg::Json
return res;
}
- void Reader::check_for_unexpected_fields(const Object& obj,
- Span<const StringView> valid_fields,
- StringView type_name)
+ void Reader::add_missing_field_error(StringView type, StringView key, StringView key_type)
+ {
+ add_generic_error(type, "missing required field '", key, "' (", key_type, ")");
+ }
+ void Reader::add_expected_type_error(StringView expected_type)
+ {
+ m_errors.push_back(Strings::concat(path(), ": mismatched type: expected ", expected_type));
+ }
+ void Reader::add_extra_field_error(StringView type, StringView field, StringView suggestion)
+ {
+ if (suggestion.size() > 0)
+ {
+ add_generic_error(type, "unexpected field '", field, "\', did you mean \'", suggestion, "\'?");
+ }
+ else
+ {
+ add_generic_error(type, "unexpected field '", field, '\'');
+ }
+ }
+
+ void Reader::check_for_unexpected_fields(const Object& obj, View<StringView> valid_fields, StringView type_name)
{
if (valid_fields.size() == 0)
{
@@ -1292,9 +1319,20 @@ namespace vcpkg::Json
}
auto extra_fields = invalid_json_fields(obj, valid_fields);
- if (!extra_fields.empty())
+ for (auto&& f : extra_fields)
{
- add_extra_fields_error(type_name.to_string(), std::move(extra_fields));
+ auto best_it = valid_fields.begin();
+ auto best_value = Strings::byte_edit_distance(f, *best_it);
+ for (auto i = best_it + 1; i != valid_fields.end(); ++i)
+ {
+ auto v = Strings::byte_edit_distance(f, *i);
+ if (v < best_value)
+ {
+ best_value = v;
+ best_it = i;
+ }
+ }
+ add_extra_field_error(type_name.to_string(), f, *best_it);
}
}
@@ -1311,4 +1349,57 @@ namespace vcpkg::Json
return p;
}
+ Optional<std::vector<std::string>> ParagraphDeserializer::visit_string(Reader&, StringView sv)
+ {
+ std::vector<std::string> out;
+ out.push_back(sv.to_string());
+ return out;
+ }
+
+ Optional<std::vector<std::string>> ParagraphDeserializer::visit_array(Reader& r, const Array& arr)
+ {
+ static StringDeserializer d{"a string"};
+ return r.array_elements(arr, d);
+ }
+
+ Optional<std::string> IdentifierDeserializer::visit_string(Json::Reader& r, StringView sv)
+ {
+ if (!is_ident(sv))
+ {
+ r.add_generic_error(type_name(), "must be lowercase alphanumeric+hyphens and not reserved");
+ }
+ return sv.to_string();
+ }
+
+ Optional<std::vector<std::string>> IdentifierArrayDeserializer::visit_array(Reader& r, const Array& arr)
+ {
+ return r.array_elements(arr, IdentifierDeserializer::instance);
+ }
+
+ bool PackageNameDeserializer::is_package_name(StringView sv)
+ {
+ if (sv.size() == 0)
+ {
+ return false;
+ }
+
+ for (const auto& ident : Strings::split(sv, '.'))
+ {
+ if (!IdentifierDeserializer::is_ident(ident))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ Optional<std::string> PackageNameDeserializer::visit_string(Json::Reader&, StringView sv)
+ {
+ if (!is_package_name(sv))
+ {
+ return nullopt;
+ }
+ return sv.to_string();
+ }
}
diff --git a/toolsrc/src/vcpkg/base/strings.cpp b/toolsrc/src/vcpkg/base/strings.cpp
index 49aaa30bd..a59cdaceb 100644
--- a/toolsrc/src/vcpkg/base/strings.cpp
+++ b/toolsrc/src/vcpkg/base/strings.cpp
@@ -267,6 +267,55 @@ bool Strings::contains(StringView haystack, StringView needle)
return Strings::search(haystack, needle) != haystack.end();
}
+size_t Strings::byte_edit_distance(StringView a, StringView b)
+{
+ static constexpr size_t max_string_size = 100;
+ // For large strings, give up early to avoid performance problems
+ if (a.size() > max_string_size || b.size() > max_string_size)
+ {
+ if (a == b)
+ return 0;
+ else
+ return std::max(a.size(), b.size());
+ }
+ if (a.size() == 0 || b.size() == 0) return std::max(a.size(), b.size());
+
+ auto pa = a.data();
+ auto pb = b.data();
+ size_t sa = a.size();
+ size_t sb = b.size();
+
+ // Levenshtein distance (https://en.wikipedia.org/wiki/Levenshtein_distance)
+ // The first row of the edit distance matrix has been omitted because it's trivial (counting from 0)
+ // Because each subsequent row only depends on the row above, we never need to store the entire matrix
+ char d[max_string_size];
+
+ // Useful invariants:
+ // `sa` is sizeof `pa` using iterator `ia`
+ // `sb` is sizeof `pb` using iterator `ib`
+ // `sa` and `sb` are in (0, `max_string_size`]
+
+ // To avoid dealing with edge effects, `ia` == 0 and `ib` == 0 have been unrolled.
+ // Comparisons are used as the cost for the diagonal action (substitute/leave unchanged)
+ d[0] = pa[0] != pb[0];
+ for (size_t ia = 1; ia < sa; ++ia)
+ d[ia] = std::min<char>(d[ia - 1] + 1, static_cast<char>(ia + (pa[ia] != pb[0])));
+
+ for (size_t ib = 1; ib < sb; ++ib)
+ {
+ // The diagonal information (d[ib-1][ia-1]) is used to compute substitution cost and so must be preserved
+ char diag = d[0];
+ d[0] = std::min<char>(d[0] + 1, static_cast<char>(ib + (pa[0] != pb[ib])));
+ for (size_t ia = 1; ia < sa; ++ia)
+ {
+ auto subst_or_add = std::min<char>(d[ia - 1] + 1, static_cast<char>(diag + (pa[ia] != pb[ib])));
+ diag = d[ia];
+ d[ia] = std::min<char>(d[ia] + 1, subst_or_add);
+ }
+ }
+ return d[sa - 1];
+}
+
namespace vcpkg::Strings
{
namespace
diff --git a/toolsrc/src/vcpkg/configuration.cpp b/toolsrc/src/vcpkg/configuration.cpp
index 9363abe80..f062af51d 100644
--- a/toolsrc/src/vcpkg/configuration.cpp
+++ b/toolsrc/src/vcpkg/configuration.cpp
@@ -1,6 +1,8 @@
+#include <vcpkg/base/jsonreader.h>
#include <vcpkg/base/system.print.h>
#include <vcpkg/configuration.h>
+#include <vcpkg/configurationdeserializer.h>
#include <vcpkg/vcpkgcmdarguments.h>
namespace vcpkg
@@ -13,7 +15,7 @@ namespace vcpkg
{
std::unique_ptr<RegistryImpl> default_registry;
- if (r.optional_object_field(obj, DEFAULT_REGISTRY, default_registry, RegistryImplDeserializer{}))
+ if (r.optional_object_field(obj, DEFAULT_REGISTRY, default_registry, RegistryImplDeserializer::instance))
{
if (!registries_enabled)
{
@@ -26,12 +28,10 @@ namespace vcpkg
}
}
+ static Json::ArrayDeserializer<RegistryDeserializer> array_of_registries{"an array of registries"};
+
std::vector<Registry> regs;
- r.optional_object_field(
- obj,
- REGISTRIES,
- regs,
- Json::ArrayDeserializer<RegistryDeserializer>{"an array of registries", Json::AllowEmpty::Yes});
+ r.optional_object_field(obj, REGISTRIES, regs, array_of_registries);
if (!regs.empty() && !registries_enabled)
{
diff --git a/toolsrc/src/vcpkg/install.cpp b/toolsrc/src/vcpkg/install.cpp
index 99d17f459..a83c6c976 100644
--- a/toolsrc/src/vcpkg/install.cpp
+++ b/toolsrc/src/vcpkg/install.cpp
@@ -794,9 +794,8 @@ namespace vcpkg::Install
if (!maybe_manifest_scf)
{
print_error_message(maybe_manifest_scf.error());
- System::print2(
- "See https://github.com/Microsoft/vcpkg/tree/master/docs/specifications/manifests.md for "
- "more information.\n");
+ System::print2("See https://github.com/Microsoft/vcpkg/tree/master/docs/users/manifests.md for "
+ "more information.\n");
Checks::exit_fail(VCPKG_LINE_INFO);
}
auto& manifest_scf = *maybe_manifest_scf.value_or_exit(VCPKG_LINE_INFO);
diff --git a/toolsrc/src/vcpkg/platform-expression.cpp b/toolsrc/src/vcpkg/platform-expression.cpp
index 1d38dbc05..9132d4be4 100644
--- a/toolsrc/src/vcpkg/platform-expression.cpp
+++ b/toolsrc/src/vcpkg/platform-expression.cpp
@@ -461,7 +461,7 @@ namespace vcpkg::PlatformExpression
ExpectedS<Expr> parse_platform_expression(StringView expression, MultipleBinaryOperators multiple_binary_operators)
{
- auto parser = ExpressionParser(expression, multiple_binary_operators);
+ ExpressionParser parser(expression, multiple_binary_operators);
auto res = parser.parse();
if (auto p = parser.extract_error())
diff --git a/toolsrc/src/vcpkg/registries.cpp b/toolsrc/src/vcpkg/registries.cpp
index ecc63ab3d..f66863068 100644
--- a/toolsrc/src/vcpkg/registries.cpp
+++ b/toolsrc/src/vcpkg/registries.cpp
@@ -1,5 +1,7 @@
#include <vcpkg/base/json.h>
+#include <vcpkg/base/jsonreader.h>
+#include <vcpkg/configurationdeserializer.h>
#include <vcpkg/registries.h>
#include <vcpkg/vcpkgpaths.h>
@@ -40,7 +42,7 @@ namespace vcpkg
constexpr StringLiteral RegistryImplDeserializer::KIND_BUILTIN;
constexpr StringLiteral RegistryImplDeserializer::KIND_DIRECTORY;
- Span<const StringView> RegistryImplDeserializer::valid_fields() const
+ View<StringView> RegistryImplDeserializer::valid_fields() const
{
static const StringView t[] = {KIND, PATH};
return t;
@@ -51,22 +53,22 @@ namespace vcpkg
Optional<std::unique_ptr<RegistryImpl>> RegistryImplDeserializer::visit_object(Json::Reader& r,
const Json::Object& obj)
{
+ static Json::StringDeserializer kind_deserializer{"a registry implementation kind"};
std::string kind;
- r.required_object_field(
- type_name(), obj, KIND, kind, Json::StringDeserializer{"a registry implementation kind"});
+ r.required_object_field(type_name(), obj, KIND, kind, kind_deserializer);
if (kind == KIND_BUILTIN)
{
if (obj.contains(PATH))
{
- r.add_extra_fields_error("a builtin registry", {PATH});
+ r.add_extra_field_error("a builtin registry", PATH);
}
return static_cast<std::unique_ptr<RegistryImpl>>(std::make_unique<BuiltinRegistry>());
}
else if (kind == KIND_DIRECTORY)
{
fs::path path;
- r.required_object_field("a directory registry", obj, PATH, path, Json::PathDeserializer{});
+ r.required_object_field("a directory registry", obj, PATH, path, Json::PathDeserializer::instance);
return static_cast<std::unique_ptr<RegistryImpl>>(std::make_unique<DirectoryRegistry>(std::move(path)));
}
@@ -75,12 +77,13 @@ namespace vcpkg
return nullopt;
}
}
+ RegistryImplDeserializer RegistryImplDeserializer::instance;
StringView RegistryDeserializer::type_name() const { return "a registry"; }
constexpr StringLiteral RegistryDeserializer::PACKAGES;
- Span<const StringView> RegistryDeserializer::valid_fields() const
+ View<StringView> RegistryDeserializer::valid_fields() const
{
static const StringView t[] = {
RegistryImplDeserializer::KIND,
@@ -99,13 +102,11 @@ namespace vcpkg
return nullopt;
}
+ static Json::ArrayDeserializer<Json::PackageNameDeserializer> package_names_deserializer{
+ "an array of package names"};
+
std::vector<std::string> packages;
- r.required_object_field(
- type_name(),
- obj,
- PACKAGES,
- packages,
- Json::ArrayDeserializer<Json::PackageNameDeserializer>{"an array of registries", Json::AllowEmpty::Yes});
+ r.required_object_field(type_name(), obj, PACKAGES, packages, package_names_deserializer);
return Registry{std::move(packages), std::move(impl).value_or_exit(VCPKG_LINE_INFO)};
}
diff --git a/toolsrc/src/vcpkg/sourceparagraph.cpp b/toolsrc/src/vcpkg/sourceparagraph.cpp
index 9300c8e6c..a01f05b28 100644
--- a/toolsrc/src/vcpkg/sourceparagraph.cpp
+++ b/toolsrc/src/vcpkg/sourceparagraph.cpp
@@ -1,5 +1,6 @@
#include <vcpkg/base/checks.h>
#include <vcpkg/base/expected.h>
+#include <vcpkg/base/jsonreader.h>
#include <vcpkg/base/span.h>
#include <vcpkg/base/system.debug.h>
#include <vcpkg/base/system.print.h>
@@ -361,7 +362,7 @@ namespace vcpkg
{
virtual StringView type_name() const override { return "a platform expression"; }
- virtual Optional<PlatformExpression::Expr> visit_string(Json::Reader&, StringView sv) override
+ virtual Optional<PlatformExpression::Expr> visit_string(Json::Reader& r, StringView sv) override
{
auto opt =
PlatformExpression::parse_platform_expression(sv, PlatformExpression::MultipleBinaryOperators::Deny);
@@ -371,11 +372,14 @@ namespace vcpkg
}
else
{
- Debug::print("Failed to parse platform expression: ", opt.error(), "\n");
- return nullopt;
+ r.add_generic_error(type_name(), opt.error());
+ return PlatformExpression::Expr::Empty();
}
}
+
+ static PlatformExprDeserializer instance;
};
+ PlatformExprDeserializer PlatformExprDeserializer::instance;
struct DependencyDeserializer : Json::IDeserializer<Dependency>
{
@@ -398,11 +402,12 @@ namespace vcpkg
return t;
}
- virtual Optional<Dependency> visit_string(Json::Reader&, StringView sv) override
+ virtual Optional<Dependency> visit_string(Json::Reader& r, StringView sv) override
{
if (!Json::PackageNameDeserializer::is_package_name(sv))
{
- return nullopt;
+ r.add_generic_error(type_name(),
+ "must be lowercase alphanumeric+hyphens, split with periods, and not reserved");
}
Dependency dep;
@@ -422,25 +427,39 @@ namespace vcpkg
}
}
- r.required_object_field(type_name(), obj, NAME, dep.name, Json::PackageNameDeserializer{});
- r.optional_object_field(obj,
- FEATURES,
- dep.features,
- Json::ArrayDeserializer<Json::IdentifierDeserializer>{"an array of identifiers",
- Json::AllowEmpty::Yes});
+ static Json::ArrayDeserializer<Json::IdentifierDeserializer> arr_id_d{"an array of identifiers"};
+
+ r.required_object_field(type_name(), obj, NAME, dep.name, Json::PackageNameDeserializer::instance);
+ r.optional_object_field(obj, FEATURES, dep.features, arr_id_d);
bool default_features = true;
- r.optional_object_field(obj, DEFAULT_FEATURES, default_features, Json::BooleanDeserializer{});
+ r.optional_object_field(obj, DEFAULT_FEATURES, default_features, Json::BooleanDeserializer::instance);
if (!default_features)
{
dep.features.push_back("core");
}
- r.optional_object_field(obj, PLATFORM, dep.platform, PlatformExprDeserializer{});
+ r.optional_object_field(obj, PLATFORM, dep.platform, PlatformExprDeserializer::instance);
return dep;
}
+
+ static DependencyDeserializer instance;
};
+ DependencyDeserializer DependencyDeserializer::instance;
+
+ struct DependencyArrayDeserializer final : Json::IDeserializer<std::vector<Dependency>>
+ {
+ virtual StringView type_name() const override { return "an array of dependencies"; }
+
+ virtual Optional<std::vector<Dependency>> visit_array(Json::Reader& r, const Json::Array& arr) override
+ {
+ return r.array_elements(arr, DependencyDeserializer::instance);
+ }
+
+ static DependencyArrayDeserializer instance;
+ };
+ DependencyArrayDeserializer DependencyArrayDeserializer::instance;
constexpr StringLiteral DependencyDeserializer::NAME;
constexpr StringLiteral DependencyDeserializer::FEATURES;
@@ -470,8 +489,6 @@ namespace vcpkg
const Json::Object& obj) override
{
auto feature = std::make_unique<FeatureParagraph>();
- feature->name = std::move(name);
-
for (const auto& el : obj)
{
if (Strings::starts_with(el.first, "$"))
@@ -480,21 +497,18 @@ namespace vcpkg
}
}
- r.required_object_field("a feature", obj, DESCRIPTION, feature->description, Json::ParagraphDeserializer{});
- r.optional_object_field(
- obj,
- DEPENDENCIES,
- feature->dependencies,
- Json::ArrayDeserializer<DependencyDeserializer>{"an array of dependencies", Json::AllowEmpty::Yes});
+ r.required_object_field(
+ type_name(), obj, DESCRIPTION, feature->description, Json::ParagraphDeserializer::instance);
+ r.optional_object_field(obj, DEPENDENCIES, feature->dependencies, DependencyArrayDeserializer::instance);
return std::move(feature);
}
-
- FeatureDeserializer() = default;
- FeatureDeserializer(std::string&& s) : name(std::move(s)) { }
-
- std::string name;
+ static FeatureDeserializer instance;
};
+ FeatureDeserializer FeatureDeserializer::instance;
+ constexpr StringLiteral FeatureDeserializer::NAME;
+ constexpr StringLiteral FeatureDeserializer::DESCRIPTION;
+ constexpr StringLiteral FeatureDeserializer::DEPENDENCIES;
struct ArrayFeatureDeserializer : Json::IDeserializer<std::unique_ptr<FeatureParagraph>>
{
@@ -514,27 +528,31 @@ namespace vcpkg
const Json::Object& obj) override
{
std::string name;
- r.required_object_field(type_name(), obj, FeatureDeserializer::NAME, name, Json::IdentifierDeserializer{});
- return FeatureDeserializer{std::move(name)}.visit_object(r, obj);
+ r.required_object_field(
+ type_name(), obj, FeatureDeserializer::NAME, name, Json::IdentifierDeserializer::instance);
+ auto opt = FeatureDeserializer::instance.visit_object(r, obj);
+ if (auto p = opt.get())
+ {
+ p->get()->name = std::move(name);
+ }
+ return opt;
}
- };
- constexpr StringLiteral FeatureDeserializer::NAME;
- constexpr StringLiteral FeatureDeserializer::DESCRIPTION;
- constexpr StringLiteral FeatureDeserializer::DEPENDENCIES;
+ static Json::ArrayDeserializer<ArrayFeatureDeserializer> array_instance;
+ };
+ Json::ArrayDeserializer<ArrayFeatureDeserializer> ArrayFeatureDeserializer::array_instance{
+ "an array of feature objects"};
struct FeaturesFieldDeserializer : Json::IDeserializer<std::vector<std::unique_ptr<FeatureParagraph>>>
{
- virtual StringView type_name() const override { return "a features field"; }
+ virtual StringView type_name() const override { return "a set of features"; }
virtual Span<const StringView> valid_fields() const override { return {}; }
virtual Optional<std::vector<std::unique_ptr<FeatureParagraph>>> visit_array(Json::Reader& r,
const Json::Array& arr) override
{
- return r.visit_value(arr,
- Json::ArrayDeserializer<ArrayFeatureDeserializer>{"an array of feature objects",
- Json::AllowEmpty::Yes});
+ return ArrayFeatureDeserializer::array_instance.visit_array(r, arr);
}
virtual Optional<std::vector<std::unique_ptr<FeatureParagraph>>> visit_object(Json::Reader& r,
@@ -543,30 +561,31 @@ namespace vcpkg
std::vector<std::unique_ptr<FeatureParagraph>> res;
std::vector<std::string> extra_fields;
- FeatureDeserializer deserializer;
for (const auto& pr : obj)
{
if (!Json::IdentifierDeserializer::is_ident(pr.first))
{
- extra_fields.push_back(pr.first.to_string());
+ r.add_generic_error(type_name(),
+ "unexpected field '",
+ pr.first,
+ "': must be lowercase alphanumeric+hyphens and not reserved");
continue;
}
- deserializer.name.assign(pr.first.begin(), pr.first.end());
- auto field = r.visit_map_field(pr.first, pr.second, deserializer);
- if (auto p = field.get())
+ std::unique_ptr<FeatureParagraph> v;
+ r.visit_in_key(pr.second, pr.first, v, FeatureDeserializer::instance);
+ if (v)
{
- res.push_back(std::move(*p));
+ v->name = pr.first.to_string();
+ res.push_back(std::move(v));
}
}
- if (!extra_fields.empty())
- {
- r.add_extra_fields_error(type_name(), std::move(extra_fields));
- }
-
return std::move(res);
}
+
+ static FeaturesFieldDeserializer instance;
};
+ FeaturesFieldDeserializer FeaturesFieldDeserializer::instance;
static constexpr StringView EXPRESSION_WORDS[] = {
"WITH",
@@ -706,7 +725,10 @@ namespace vcpkg
return sv.to_string();
}
}
+
+ static LicenseExpressionDeserializer instance;
};
+ LicenseExpressionDeserializer LicenseExpressionDeserializer::instance;
struct ManifestDeserializer : Json::IDeserializer<std::unique_ptr<SourceControlFile>>
{
@@ -766,41 +788,41 @@ namespace vcpkg
}
}
+ static Json::StringDeserializer version_deserializer{"a version"};
+ static Json::StringDeserializer url_deserializer{"a url"};
+
constexpr static StringView type_name = "vcpkg.json";
- r.required_object_field(type_name, obj, NAME, spgh->name, Json::IdentifierDeserializer{});
- r.required_object_field(type_name, obj, VERSION, spgh->version, Json::StringDeserializer{"a version"});
- r.optional_object_field(obj, PORT_VERSION, spgh->port_version, Json::NaturalNumberDeserializer{});
- r.optional_object_field(obj, MAINTAINERS, spgh->maintainers, Json::ParagraphDeserializer{});
- r.optional_object_field(obj, DESCRIPTION, spgh->description, Json::ParagraphDeserializer{});
- r.optional_object_field(obj, HOMEPAGE, spgh->homepage, Json::StringDeserializer{"a url"});
- r.optional_object_field(obj, DOCUMENTATION, spgh->documentation, Json::StringDeserializer{"a url"});
- r.optional_object_field(obj, LICENSE, spgh->license, LicenseExpressionDeserializer{});
- r.optional_object_field(
- obj,
- DEPENDENCIES,
- spgh->dependencies,
- Json::ArrayDeserializer<DependencyDeserializer>{"an array of dependencies", Json::AllowEmpty::Yes});
+ r.required_object_field(type_name, obj, NAME, spgh->name, Json::IdentifierDeserializer::instance);
+ r.required_object_field(type_name, obj, VERSION, spgh->version, version_deserializer);
+ r.optional_object_field(obj, PORT_VERSION, spgh->port_version, Json::NaturalNumberDeserializer::instance);
+ r.optional_object_field(obj, MAINTAINERS, spgh->maintainers, Json::ParagraphDeserializer::instance);
+ r.optional_object_field(obj, DESCRIPTION, spgh->description, Json::ParagraphDeserializer::instance);
+ r.optional_object_field(obj, HOMEPAGE, spgh->homepage, url_deserializer);
+ r.optional_object_field(obj, DOCUMENTATION, spgh->documentation, url_deserializer);
+ r.optional_object_field(obj, LICENSE, spgh->license, LicenseExpressionDeserializer::instance);
+ r.optional_object_field(obj, DEPENDENCIES, spgh->dependencies, DependencyArrayDeserializer::instance);
if (obj.contains(DEV_DEPENDENCIES))
{
- System::print2(System::Color::error, "dev_dependencies are not yet supported");
+ System::print2(System::Color::error, DEV_DEPENDENCIES, " are not yet supported");
Checks::exit_fail(VCPKG_LINE_INFO);
}
- r.optional_object_field(obj, SUPPORTS, spgh->supports_expression, PlatformExprDeserializer{});
+ r.optional_object_field(obj, SUPPORTS, spgh->supports_expression, PlatformExprDeserializer::instance);
- r.optional_object_field(obj,
- DEFAULT_FEATURES,
- spgh->default_features,
- Json::ArrayDeserializer<Json::IdentifierDeserializer>{"an array of identifiers",
- Json::AllowEmpty::Yes});
+ r.optional_object_field(
+ obj, DEFAULT_FEATURES, spgh->default_features, Json::IdentifierArrayDeserializer::instance);
- r.optional_object_field(obj, FEATURES, control_file->feature_paragraphs, FeaturesFieldDeserializer{});
+ r.optional_object_field(
+ obj, FEATURES, control_file->feature_paragraphs, FeaturesFieldDeserializer::instance);
canonicalize(*control_file);
return std::move(control_file);
}
+
+ static ManifestDeserializer instance;
};
+ ManifestDeserializer ManifestDeserializer::instance;
constexpr StringLiteral ManifestDeserializer::NAME;
constexpr StringLiteral ManifestDeserializer::VERSION;
@@ -822,7 +844,7 @@ namespace vcpkg
{
Json::Reader reader;
- auto res = reader.visit_value(manifest, ManifestDeserializer{});
+ auto res = reader.visit(manifest, ManifestDeserializer::instance);
if (!reader.errors().empty())
{
diff --git a/toolsrc/src/vcpkg/vcpkgpaths.cpp b/toolsrc/src/vcpkg/vcpkgpaths.cpp
index b76ed19d9..cc51c6a2f 100644
--- a/toolsrc/src/vcpkg/vcpkgpaths.cpp
+++ b/toolsrc/src/vcpkg/vcpkgpaths.cpp
@@ -1,6 +1,7 @@
#include <vcpkg/base/expected.h>
#include <vcpkg/base/files.h>
#include <vcpkg/base/hash.h>
+#include <vcpkg/base/jsonreader.h>
#include <vcpkg/base/system.debug.h>
#include <vcpkg/base/system.process.h>
#include <vcpkg/base/util.h>
@@ -9,6 +10,7 @@
#include <vcpkg/build.h>
#include <vcpkg/commands.h>
#include <vcpkg/configuration.h>
+#include <vcpkg/configurationdeserializer.h>
#include <vcpkg/globalstate.h>
#include <vcpkg/metrics.h>
#include <vcpkg/packagespec.h>
@@ -78,9 +80,9 @@ namespace vcpkg
const fs::path& filepath)
{
Json::Reader reader;
- auto deserializer = ConfigurationDeserializer(args);
+ ConfigurationDeserializer deserializer(args);
- auto parsed_config_opt = reader.visit_value(obj, deserializer);
+ auto parsed_config_opt = reader.visit(obj, deserializer);
if (!reader.errors().empty())
{
System::print2(System::Color::error, "Errors occurred while parsing ", fs::u8string(filepath), "\n");