aboutsummaryrefslogtreecommitdiff
path: root/toolsrc/include
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/include
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/include')
-rw-r--r--toolsrc/include/vcpkg/base/fwd/json.h34
-rw-r--r--toolsrc/include/vcpkg/base/fwd/span.h3
-rw-r--r--toolsrc/include/vcpkg/base/json.h377
-rw-r--r--toolsrc/include/vcpkg/base/jsonreader.h347
-rw-r--r--toolsrc/include/vcpkg/base/span.h2
-rw-r--r--toolsrc/include/vcpkg/base/strings.h11
-rw-r--r--toolsrc/include/vcpkg/base/view.h6
-rw-r--r--toolsrc/include/vcpkg/configuration.h25
-rw-r--r--toolsrc/include/vcpkg/configurationdeserializer.h66
-rw-r--r--toolsrc/include/vcpkg/registries.h34
10 files changed, 434 insertions, 471 deletions
diff --git a/toolsrc/include/vcpkg/base/fwd/json.h b/toolsrc/include/vcpkg/base/fwd/json.h
index 73826127c..bb31a5f60 100644
--- a/toolsrc/include/vcpkg/base/fwd/json.h
+++ b/toolsrc/include/vcpkg/base/fwd/json.h
@@ -1,9 +1,5 @@
#pragma once
-#include <vcpkg/base/fwd/optional.h>
-#include <vcpkg/base/fwd/span.h>
-#include <vcpkg/base/fwd/stringview.h>
-
namespace vcpkg::Json
{
struct JsonStyle;
@@ -11,34 +7,4 @@ namespace vcpkg::Json
struct Value;
struct Object;
struct Array;
-
- struct ReaderError;
- struct BasicReaderError;
- struct Reader;
-
- // This is written all the way out so that one can include a subclass in a header
- template<class Type>
- struct IDeserializer
- {
- using type = Type;
- virtual StringView type_name() const = 0;
-
- virtual Span<const StringView> valid_fields() const;
-
- virtual Optional<Type> visit_null(Reader&);
- virtual Optional<Type> visit_boolean(Reader&, bool);
- virtual Optional<Type> visit_integer(Reader& r, int64_t i);
- virtual Optional<Type> visit_number(Reader&, double);
- virtual Optional<Type> visit_string(Reader&, StringView);
- virtual Optional<Type> visit_array(Reader&, const Array&);
- virtual Optional<Type> visit_object(Reader&, const Object&);
-
- protected:
- IDeserializer() = default;
- IDeserializer(const IDeserializer&) = default;
- IDeserializer& operator=(const IDeserializer&) = default;
- IDeserializer(IDeserializer&&) = default;
- IDeserializer& operator=(IDeserializer&&) = default;
- virtual ~IDeserializer() = default;
- };
}
diff --git a/toolsrc/include/vcpkg/base/fwd/span.h b/toolsrc/include/vcpkg/base/fwd/span.h
index af78aa642..9cd45250e 100644
--- a/toolsrc/include/vcpkg/base/fwd/span.h
+++ b/toolsrc/include/vcpkg/base/fwd/span.h
@@ -4,4 +4,7 @@ namespace vcpkg
{
template<class T>
struct Span;
+
+ template<class T>
+ using View = Span<const T>;
}
diff --git a/toolsrc/include/vcpkg/base/json.h b/toolsrc/include/vcpkg/base/json.h
index a16e9ed82..e7f3076f7 100644
--- a/toolsrc/include/vcpkg/base/json.h
+++ b/toolsrc/include/vcpkg/base/json.h
@@ -184,6 +184,7 @@ namespace vcpkg::Json
private:
underlying_t underlying_;
};
+
struct Object
{
private:
@@ -287,381 +288,6 @@ namespace vcpkg::Json
underlying_t underlying_;
};
- VCPKG_MSVC_WARNING(push)
- VCPKG_MSVC_WARNING(disable : 4505)
-
- template<class Type>
- Span<const StringView> IDeserializer<Type>::valid_fields() const
- {
- return {};
- }
-
- template<class Type>
- Optional<Type> IDeserializer<Type>::visit_null(Reader&)
- {
- return nullopt;
- }
- template<class Type>
- Optional<Type> IDeserializer<Type>::visit_boolean(Reader&, bool)
- {
- return nullopt;
- }
- template<class Type>
- Optional<Type> IDeserializer<Type>::visit_integer(Reader& r, int64_t i)
- {
- return this->visit_number(r, static_cast<double>(i));
- }
- template<class Type>
- Optional<Type> IDeserializer<Type>::visit_number(Reader&, double)
- {
- return nullopt;
- }
- template<class Type>
- Optional<Type> IDeserializer<Type>::visit_string(Reader&, StringView)
- {
- return nullopt;
- }
- template<class Type>
- Optional<Type> IDeserializer<Type>::visit_array(Reader&, const Array&)
- {
- return nullopt;
- }
- template<class Type>
- Optional<Type> IDeserializer<Type>::visit_object(Reader&, const Object&)
- {
- return nullopt;
- }
-
- VCPKG_MSVC_WARNING(pop)
-
- struct Reader
- {
- const std::vector<std::string>& errors() const { return m_errors; }
- std::vector<std::string>& errors() { return m_errors; }
-
- void add_missing_field_error(StringView type, StringView key, StringView key_type)
- {
- m_errors.push_back(
- Strings::concat(path(), " (", type, "): ", "missing required field '", key, "' (", key_type, ")"));
- }
- void add_expected_type_error(StringView expected_type)
- {
- m_errors.push_back(Strings::concat(path(), ": mismatched type: expected ", expected_type));
- }
- void add_extra_fields_error(StringView type, std::vector<std::string>&& fields)
- {
- for (auto&& field : fields)
- m_errors.push_back(Strings::concat(path(), " (", type, "): ", "unexpected field '", field, '\''));
- }
-
- std::string path() const noexcept;
-
- private:
- std::vector<std::string> m_errors;
- struct Path
- {
- constexpr Path() = default;
- constexpr Path(int64_t i) : index(i) { }
- constexpr Path(StringView f) : field(f) { }
-
- int64_t index = -1;
- StringView field;
- };
- std::vector<Path> m_path;
-
- template<class Type>
- Optional<Type> internal_visit(const Value& value, IDeserializer<Type>& visitor)
- {
- switch (value.kind())
- {
- case ValueKind::Null: return visitor.visit_null(*this);
- case ValueKind::Boolean: return visitor.visit_boolean(*this, value.boolean());
- case ValueKind::Integer: return visitor.visit_integer(*this, value.integer());
- case ValueKind::Number: return visitor.visit_number(*this, value.number());
- case ValueKind::String: return visitor.visit_string(*this, value.string());
- case ValueKind::Array: return visitor.visit_array(*this, value.array());
- case ValueKind::Object:
- {
- const auto& obj = value.object();
- check_for_unexpected_fields(obj, visitor.valid_fields(), visitor.type_name());
- return visitor.visit_object(*this, obj);
- }
- }
-
- vcpkg::Checks::unreachable(VCPKG_LINE_INFO);
- }
-
- // returns whether the field was found, not whether it was valid
- template<class Type>
- bool internal_field(const Object& obj, StringView key, Type& place, IDeserializer<Type>& visitor)
- {
- auto value = obj.get(key);
- if (!value)
- {
- return false;
- }
-
- m_path.push_back(key);
- Optional<Type> opt = internal_visit(*value, visitor);
-
- if (auto val = opt.get())
- {
- place = std::move(*val);
- }
- else
- {
- add_expected_type_error(visitor.type_name().to_string());
- }
- m_path.pop_back();
- return true;
- }
-
- // checks that an object doesn't contain any fields which both:
- // * don't start with a `$`
- // * are not in `valid_fields`
- // if known_fields.empty(), then it's treated as if all field names are valid
- void check_for_unexpected_fields(const Object& obj, Span<const StringView> valid_fields, StringView type_name);
-
- public:
- template<class Type, class Deserializer>
- void required_object_field(
- StringView type, const Object& obj, StringView key, Type& place, Deserializer&& visitor)
- {
- if (!internal_field(obj, key, place, visitor))
- {
- this->add_missing_field_error(type, key, visitor.type_name());
- }
- }
-
- // returns whether key \in obj
- template<class Type, class Deserializer>
- bool optional_object_field(const Object& obj, StringView key, Type& place, Deserializer&& visitor)
- {
- return internal_field(obj, key, place, visitor);
- }
-
- template<class Type>
- Optional<Type> visit_value(const Value& value, IDeserializer<Type>& visitor)
- {
- return internal_visit(value, visitor);
- }
- template<class Type>
- Optional<Type> visit_value(const Value& value, IDeserializer<Type>&& visitor)
- {
- return visit_value(value, visitor);
- }
-
- template<class Type>
- Optional<Type> visit_value(const Array& value, IDeserializer<Type>& visitor)
- {
- return visitor.visit_array(*this, value);
- }
- template<class Type>
- Optional<Type> visit_value(const Array& value, IDeserializer<Type>&& visitor)
- {
- return visit_value(value, visitor);
- }
-
- template<class Type>
- Optional<Type> visit_value(const Object& value, IDeserializer<Type>& visitor)
- {
- check_for_unexpected_fields(value, visitor.valid_fields(), visitor.type_name());
- return visitor.visit_object(*this, value);
- }
- template<class Type>
- Optional<Type> visit_value(const Object& value, IDeserializer<Type>&& visitor)
- {
- return visit_value(value, visitor);
- }
-
- template<class Type>
- Optional<Type> visit_map_field(StringView key, const Value& value, IDeserializer<Type>& visitor)
- {
- m_path.push_back(key);
- auto res = internal_visit(value, visitor);
- m_path.pop_back();
- return res;
- }
- template<class Type>
- Optional<Type> visit_map_field(StringView key, const Value& value, IDeserializer<Type>&& visitor)
- {
- return visit_map_field(key, value, visitor);
- }
-
- template<class Type>
- Optional<std::vector<Type>> array_elements(const Array& arr, IDeserializer<Type>& visitor)
- {
- std::vector<Type> result;
- m_path.emplace_back();
- for (size_t i = 0; i < arr.size(); ++i)
- {
- m_path.back().index = static_cast<int64_t>(i);
- auto opt = internal_visit(arr[i], visitor);
- if (auto p = opt.get())
- {
- result.push_back(std::move(*p));
- }
- else
- {
- this->add_expected_type_error(visitor.type_name());
- for (++i; i < arr.size(); ++i)
- {
- m_path.back().index = static_cast<int64_t>(i);
- auto opt2 = internal_visit(arr[i], visitor);
- if (!opt2) this->add_expected_type_error(visitor.type_name());
- }
- }
- }
- m_path.pop_back();
- return std::move(result);
- }
- template<class Type>
- Optional<std::vector<Type>> array_elements(const Array& arr, IDeserializer<Type>&& visitor)
- {
- return array_elements(arr, visitor);
- }
- };
-
- struct StringDeserializer final : IDeserializer<std::string>
- {
- virtual StringView type_name() const override { return type_name_; }
- virtual Optional<std::string> visit_string(Reader&, StringView sv) override { return sv.to_string(); }
-
- explicit StringDeserializer(StringView type_name_) : type_name_(type_name_) { }
-
- private:
- StringView type_name_;
- };
-
- struct PathDeserializer final : IDeserializer<fs::path>
- {
- virtual StringView type_name() const override { return "a path"; }
- virtual Optional<fs::path> visit_string(Reader&, StringView sv) override { return fs::u8path(sv); }
- };
-
- struct NaturalNumberDeserializer final : IDeserializer<int>
- {
- virtual StringView type_name() const override { return "a natural number"; }
-
- virtual Optional<int> visit_integer(Reader&, int64_t value) override
- {
- if (value > std::numeric_limits<int>::max() || value < 0)
- {
- return nullopt;
- }
- return static_cast<int>(value);
- }
- };
-
- struct BooleanDeserializer final : IDeserializer<bool>
- {
- virtual StringView type_name() const override { return "a boolean"; }
-
- virtual Optional<bool> visit_boolean(Reader&, bool b) override { return b; }
- };
-
- enum class AllowEmpty : bool
- {
- No,
- Yes,
- };
-
- template<class Underlying>
- struct ArrayDeserializer final : IDeserializer<std::vector<typename Underlying::type>>
- {
- using typename IDeserializer<std::vector<typename Underlying::type>>::type;
-
- virtual StringView type_name() const override { return type_name_; }
-
- ArrayDeserializer(StringView type_name_, AllowEmpty allow_empty, Underlying&& t = {})
- : type_name_(type_name_), underlying_visitor_(static_cast<Underlying&&>(t)), allow_empty_(allow_empty)
- {
- }
-
- virtual Optional<type> visit_array(Reader& r, const Array& arr) override
- {
- if (allow_empty_ == AllowEmpty::No && arr.size() == 0)
- {
- return nullopt;
- }
- return r.array_elements(arr, underlying_visitor_);
- }
-
- private:
- StringView type_name_;
- Underlying underlying_visitor_;
- AllowEmpty allow_empty_;
- };
-
- struct ParagraphDeserializer final : IDeserializer<std::vector<std::string>>
- {
- virtual StringView type_name() const override { return "a string or array of strings"; }
-
- virtual Optional<std::vector<std::string>> visit_string(Reader&, StringView sv) override
- {
- std::vector<std::string> out;
- out.push_back(sv.to_string());
- return out;
- }
-
- virtual Optional<std::vector<std::string>> visit_array(Reader& r, const Array& arr) override
- {
- return r.array_elements(arr, StringDeserializer{"a string"});
- }
- };
-
- struct IdentifierDeserializer final : Json::IDeserializer<std::string>
- {
- virtual StringView type_name() const override { return "an identifier"; }
-
- // [a-z0-9]+(-[a-z0-9]+)*, plus not any of {prn, aux, nul, con, lpt[1-9], com[1-9], core, default}
- static bool is_ident(StringView sv);
-
- virtual Optional<std::string> visit_string(Json::Reader&, StringView sv) override
- {
- if (is_ident(sv))
- {
- return sv.to_string();
- }
- else
- {
- return nullopt;
- }
- }
- };
-
- struct PackageNameDeserializer final : Json::IDeserializer<std::string>
- {
- virtual StringView type_name() const override { return "a package name"; }
-
- static bool 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;
- }
-
- virtual Optional<std::string> visit_string(Json::Reader&, StringView sv) override
- {
- if (!is_package_name(sv))
- {
- return nullopt;
- }
- return sv.to_string();
- }
- };
-
ExpectedT<std::pair<Value, JsonStyle>, std::unique_ptr<Parse::IParseError>> parse_file(
const Files::Filesystem&, const fs::path&, std::error_code& ec) noexcept;
ExpectedT<std::pair<Value, JsonStyle>, std::unique_ptr<Parse::IParseError>> parse(
@@ -671,5 +297,4 @@ namespace vcpkg::Json
std::string stringify(const Value&, JsonStyle style);
std::string stringify(const Object&, JsonStyle style);
std::string stringify(const Array&, JsonStyle style);
-
}
diff --git a/toolsrc/include/vcpkg/base/jsonreader.h b/toolsrc/include/vcpkg/base/jsonreader.h
new file mode 100644
index 000000000..6518501a2
--- /dev/null
+++ b/toolsrc/include/vcpkg/base/jsonreader.h
@@ -0,0 +1,347 @@
+#pragma once
+
+#include <vcpkg/base/fwd/json.h>
+
+#include <vcpkg/base/json.h>
+#include <vcpkg/base/optional.h>
+#include <vcpkg/base/strings.h>
+#include <vcpkg/base/stringview.h>
+#include <vcpkg/base/view.h>
+
+namespace vcpkg::Json
+{
+ struct Reader;
+
+ template<class Type>
+ struct IDeserializer
+ {
+ using type = Type;
+ virtual StringView type_name() const = 0;
+
+ private:
+ friend struct Reader;
+ Optional<Type> visit(Reader&, const Value&);
+ Optional<Type> visit(Reader&, const Object&);
+
+ protected:
+ virtual Optional<Type> visit_null(Reader&);
+ virtual Optional<Type> visit_boolean(Reader&, bool);
+ virtual Optional<Type> visit_integer(Reader& r, int64_t i);
+ virtual Optional<Type> visit_number(Reader&, double);
+ virtual Optional<Type> visit_string(Reader&, StringView);
+ virtual Optional<Type> visit_array(Reader&, const Array&);
+ virtual Optional<Type> visit_object(Reader&, const Object&);
+ virtual View<StringView> valid_fields() const;
+
+ IDeserializer() = default;
+ IDeserializer(const IDeserializer&) = default;
+ IDeserializer& operator=(const IDeserializer&) = default;
+ IDeserializer(IDeserializer&&) = default;
+ IDeserializer& operator=(IDeserializer&&) = default;
+ virtual ~IDeserializer() = default;
+ };
+
+ struct Reader
+ {
+ const std::vector<std::string>& errors() const { return m_errors; }
+ std::vector<std::string>& errors() { return m_errors; }
+
+ void add_missing_field_error(StringView type, StringView key, StringView key_type);
+ void add_expected_type_error(StringView expected_type);
+ void add_extra_field_error(StringView type, StringView fields, StringView suggestion = {});
+ template<class... Args>
+ void add_generic_error(StringView type, Args&&... args)
+ {
+ m_errors.push_back(Strings::concat(path(), " (", type, "): ", args...));
+ }
+
+ std::string path() const noexcept;
+
+ private:
+ template<class Type>
+ friend struct IDeserializer;
+
+ std::vector<std::string> m_errors;
+ struct Path
+ {
+ constexpr Path() = default;
+ constexpr Path(int64_t i) : index(i) { }
+ constexpr Path(StringView f) : field(f) { }
+
+ int64_t index = -1;
+ StringView field;
+ };
+ std::vector<Path> m_path;
+
+ // checks that an object doesn't contain any fields which both:
+ // * don't start with a `$`
+ // * are not in `valid_fields`
+ // if known_fields.empty(), then it's treated as if all field names are valid
+ void check_for_unexpected_fields(const Object& obj, View<StringView> valid_fields, StringView type_name);
+
+ public:
+ template<class Type>
+ void required_object_field(
+ StringView type, const Object& obj, StringView key, Type& place, IDeserializer<Type>& visitor)
+ {
+ if (auto value = obj.get(key))
+ {
+ visit_in_key(*value, key, place, visitor);
+ }
+ else
+ {
+ this->add_missing_field_error(type, key, visitor.type_name());
+ }
+ }
+
+ // value should be the value at key of the currently visited object
+ template<class Type>
+ void visit_in_key(const Value& value, StringView key, Type& place, IDeserializer<Type>& visitor)
+ {
+ m_path.push_back(key);
+ auto opt = visitor.visit(*this, value);
+
+ if (auto p_opt = opt.get())
+ {
+ place = std::move(*p_opt);
+ }
+ else
+ {
+ add_expected_type_error(visitor.type_name());
+ }
+ m_path.pop_back();
+ }
+
+ // returns whether key \in obj
+ template<class Type>
+ bool optional_object_field(const Object& obj, StringView key, Type& place, IDeserializer<Type>& visitor)
+ {
+ if (auto value = obj.get(key))
+ {
+ visit_in_key(*value, key, place, visitor);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ template<class Type>
+ Optional<Type> visit(const Value& value, IDeserializer<Type>& visitor)
+ {
+ return visitor.visit(*this, value);
+ }
+ template<class Type>
+ Optional<Type> visit(const Object& value, IDeserializer<Type>& visitor)
+ {
+ return visitor.visit(*this, value);
+ }
+
+ template<class Type>
+ Optional<std::vector<Type>> array_elements(const Array& arr, IDeserializer<Type>& visitor)
+ {
+ std::vector<Type> result;
+ m_path.emplace_back();
+ for (size_t i = 0; i < arr.size(); ++i)
+ {
+ m_path.back().index = static_cast<int64_t>(i);
+ auto opt = visitor.visit(*this, arr[i]);
+ if (auto p = opt.get())
+ {
+ result.push_back(std::move(*p));
+ }
+ else
+ {
+ this->add_expected_type_error(visitor.type_name());
+ for (++i; i < arr.size(); ++i)
+ {
+ m_path.back().index = static_cast<int64_t>(i);
+ auto opt2 = visitor.visit(*this, arr[i]);
+ if (!opt2) this->add_expected_type_error(visitor.type_name());
+ }
+ }
+ }
+ m_path.pop_back();
+ return std::move(result);
+ }
+ };
+
+ VCPKG_MSVC_WARNING(push);
+ VCPKG_MSVC_WARNING(disable : 4505);
+
+ template<class Type>
+ Optional<Type> IDeserializer<Type>::visit(Reader& r, const Value& value)
+ {
+ switch (value.kind())
+ {
+ case ValueKind::Null: return visit_null(r);
+ case ValueKind::Boolean: return visit_boolean(r, value.boolean());
+ case ValueKind::Integer: return visit_integer(r, value.integer());
+ case ValueKind::Number: return visit_number(r, value.number());
+ case ValueKind::String: return visit_string(r, value.string());
+ case ValueKind::Array: return visit_array(r, value.array());
+ case ValueKind::Object: return visit(r, value.object()); // Call `visit` to get unexpected fields checking
+ default: vcpkg::Checks::unreachable(VCPKG_LINE_INFO);
+ }
+ }
+
+ template<class Type>
+ Optional<Type> IDeserializer<Type>::visit(Reader& r, const Object& obj)
+ {
+ r.check_for_unexpected_fields(obj, valid_fields(), type_name());
+ return visit_object(r, obj);
+ }
+
+ template<class Type>
+ View<StringView> IDeserializer<Type>::valid_fields() const
+ {
+ return {};
+ }
+
+ template<class Type>
+ Optional<Type> IDeserializer<Type>::visit_null(Reader&)
+ {
+ return nullopt;
+ }
+ template<class Type>
+ Optional<Type> IDeserializer<Type>::visit_boolean(Reader&, bool)
+ {
+ return nullopt;
+ }
+ template<class Type>
+ Optional<Type> IDeserializer<Type>::visit_integer(Reader& r, int64_t i)
+ {
+ return this->visit_number(r, static_cast<double>(i));
+ }
+ template<class Type>
+ Optional<Type> IDeserializer<Type>::visit_number(Reader&, double)
+ {
+ return nullopt;
+ }
+ template<class Type>
+ Optional<Type> IDeserializer<Type>::visit_string(Reader&, StringView)
+ {
+ return nullopt;
+ }
+ template<class Type>
+ Optional<Type> IDeserializer<Type>::visit_array(Reader&, const Array&)
+ {
+ return nullopt;
+ }
+ template<class Type>
+ Optional<Type> IDeserializer<Type>::visit_object(Reader&, const Object&)
+ {
+ return nullopt;
+ }
+
+ VCPKG_MSVC_WARNING(pop);
+
+ struct StringDeserializer final : IDeserializer<std::string>
+ {
+ virtual StringView type_name() const override { return type_name_; }
+ virtual Optional<std::string> visit_string(Reader&, StringView sv) override { return sv.to_string(); }
+
+ explicit StringDeserializer(StringLiteral type_name_) : type_name_(type_name_) { }
+
+ private:
+ StringLiteral type_name_;
+ };
+
+ struct PathDeserializer final : IDeserializer<fs::path>
+ {
+ virtual StringView type_name() const override { return "a path"; }
+ virtual Optional<fs::path> visit_string(Reader&, StringView sv) override { return fs::u8path(sv); }
+
+ static PathDeserializer instance;
+ };
+
+ struct NaturalNumberDeserializer final : IDeserializer<int>
+ {
+ virtual StringView type_name() const override { return "a natural number"; }
+
+ virtual Optional<int> visit_integer(Reader&, int64_t value) override
+ {
+ if (value > std::numeric_limits<int>::max() || value < 0)
+ {
+ return nullopt;
+ }
+ return static_cast<int>(value);
+ }
+
+ static NaturalNumberDeserializer instance;
+ };
+
+ struct BooleanDeserializer final : IDeserializer<bool>
+ {
+ virtual StringView type_name() const override { return "a boolean"; }
+
+ virtual Optional<bool> visit_boolean(Reader&, bool b) override { return b; }
+
+ static BooleanDeserializer instance;
+ };
+
+ template<class Underlying>
+ struct ArrayDeserializer final : IDeserializer<std::vector<typename Underlying::type>>
+ {
+ using type = std::vector<typename Underlying::type>;
+
+ virtual StringView type_name() const override { return m_type_name; }
+
+ ArrayDeserializer(StringLiteral type_name_, Underlying&& t = {})
+ : m_type_name(type_name_), m_underlying_visitor(static_cast<Underlying&&>(t))
+ {
+ }
+
+ virtual Optional<type> visit_array(Reader& r, const Array& arr) override
+ {
+ return r.array_elements(arr, m_underlying_visitor);
+ }
+
+ private:
+ StringLiteral m_type_name;
+ Underlying m_underlying_visitor;
+ };
+
+ struct ParagraphDeserializer final : IDeserializer<std::vector<std::string>>
+ {
+ virtual StringView type_name() const override { return "a string or array of strings"; }
+
+ virtual Optional<std::vector<std::string>> visit_string(Reader&, StringView sv) override;
+ virtual Optional<std::vector<std::string>> visit_array(Reader& r, const Array& arr) override;
+
+ static ParagraphDeserializer instance;
+ };
+
+ struct IdentifierDeserializer final : Json::IDeserializer<std::string>
+ {
+ virtual StringView type_name() const override { return "an identifier"; }
+
+ // [a-z0-9]+(-[a-z0-9]+)*, plus not any of {prn, aux, nul, con, lpt[1-9], com[1-9], core, default}
+ static bool is_ident(StringView sv);
+
+ virtual Optional<std::string> visit_string(Json::Reader&, StringView sv) override;
+
+ static IdentifierDeserializer instance;
+ };
+
+ struct IdentifierArrayDeserializer final : Json::IDeserializer<std::vector<std::string>>
+ {
+ virtual StringView type_name() const override { return "an array of identifiers"; }
+
+ virtual Optional<std::vector<std::string>> visit_array(Reader& r, const Array& arr) override;
+
+ static IdentifierArrayDeserializer instance;
+ };
+
+ struct PackageNameDeserializer final : Json::IDeserializer<std::string>
+ {
+ virtual StringView type_name() const override { return "a package name"; }
+
+ static bool is_package_name(StringView sv);
+
+ virtual Optional<std::string> visit_string(Json::Reader&, StringView sv) override;
+
+ static PackageNameDeserializer instance;
+ };
+}
diff --git a/toolsrc/include/vcpkg/base/span.h b/toolsrc/include/vcpkg/base/span.h
index a66205332..91d94ec47 100644
--- a/toolsrc/include/vcpkg/base/span.h
+++ b/toolsrc/include/vcpkg/base/span.h
@@ -1,5 +1,7 @@
#pragma once
+#include <vcpkg/base/fwd/span.h>
+
#include <array>
#include <cstddef>
#include <initializer_list>
diff --git a/toolsrc/include/vcpkg/base/strings.h b/toolsrc/include/vcpkg/base/strings.h
index 0962d6c1e..5113ff2fc 100644
--- a/toolsrc/include/vcpkg/base/strings.h
+++ b/toolsrc/include/vcpkg/base/strings.h
@@ -94,6 +94,13 @@ namespace vcpkg::Strings
return ret;
}
+ template<class... Args>
+ [[nodiscard]] std::string concat(std::string&& first, const Args&... args)
+ {
+ append(first, args...);
+ return std::move(first);
+ }
+
template<class... Args, class = void>
std::string concat_or_view(const Args&... args)
{
@@ -288,4 +295,8 @@ namespace vcpkg::Strings
// base 32 encoding, following IETC RFC 4648
std::string b32_encode(std::uint64_t x) noexcept;
+
+ // Implements https://en.wikipedia.org/wiki/Levenshtein_distance with a "give-up" clause for large strings
+ // Guarantees 0 for equal strings and nonzero for inequal strings.
+ size_t byte_edit_distance(StringView a, StringView b);
}
diff --git a/toolsrc/include/vcpkg/base/view.h b/toolsrc/include/vcpkg/base/view.h
index 491e8351b..12908c4a5 100644
--- a/toolsrc/include/vcpkg/base/view.h
+++ b/toolsrc/include/vcpkg/base/view.h
@@ -1,9 +1,3 @@
#pragma once
#include <vcpkg/base/span.h>
-
-namespace vcpkg
-{
- template<class T>
- using View = Span<const T>;
-}
diff --git a/toolsrc/include/vcpkg/configuration.h b/toolsrc/include/vcpkg/configuration.h
index a4ed38223..4cba88fe5 100644
--- a/toolsrc/include/vcpkg/configuration.h
+++ b/toolsrc/include/vcpkg/configuration.h
@@ -1,9 +1,6 @@
#pragma once
#include <vcpkg/fwd/configuration.h>
-#include <vcpkg/fwd/vcpkgcmdarguments.h>
-
-#include <vcpkg/base/json.h>
#include <vcpkg/registries.h>
@@ -16,26 +13,4 @@ namespace vcpkg
// taken care of in RegistrySet.
RegistrySet registry_set;
};
-
- struct ConfigurationDeserializer final : Json::IDeserializer<Configuration>
- {
- virtual StringView type_name() const override { return "a configuration object"; }
-
- constexpr static StringLiteral DEFAULT_REGISTRY = "default-registry";
- constexpr static StringLiteral REGISTRIES = "registries";
- virtual Span<const StringView> valid_fields() const override
- {
- constexpr static StringView t[] = {DEFAULT_REGISTRY, REGISTRIES};
- return t;
- }
-
- virtual Optional<Configuration> visit_object(Json::Reader& r, const Json::Object& obj) override;
-
- ConfigurationDeserializer(const VcpkgCmdArguments& args);
-
- private:
- bool print_json;
-
- bool registries_enabled;
- };
}
diff --git a/toolsrc/include/vcpkg/configurationdeserializer.h b/toolsrc/include/vcpkg/configurationdeserializer.h
new file mode 100644
index 000000000..83d3d0869
--- /dev/null
+++ b/toolsrc/include/vcpkg/configurationdeserializer.h
@@ -0,0 +1,66 @@
+#pragma once
+
+#include <vcpkg/base/fwd/json.h>
+
+#include <vcpkg/fwd/vcpkgcmdarguments.h>
+
+#include <vcpkg/base/json.h>
+#include <vcpkg/base/optional.h>
+#include <vcpkg/base/stringliteral.h>
+#include <vcpkg/base/stringview.h>
+#include <vcpkg/base/view.h>
+
+#include <vcpkg/configuration.h>
+#include <vcpkg/registries.h>
+
+namespace vcpkg
+{
+ struct RegistryImplDeserializer : Json::IDeserializer<std::unique_ptr<RegistryImpl>>
+ {
+ constexpr static StringLiteral KIND = "kind";
+ constexpr static StringLiteral PATH = "path";
+
+ constexpr static StringLiteral KIND_BUILTIN = "builtin";
+ constexpr static StringLiteral KIND_DIRECTORY = "directory";
+
+ virtual StringView type_name() const override;
+ virtual View<StringView> valid_fields() const override;
+
+ virtual Optional<std::unique_ptr<RegistryImpl>> visit_null(Json::Reader&) override;
+ virtual Optional<std::unique_ptr<RegistryImpl>> visit_object(Json::Reader&, const Json::Object&) override;
+
+ static RegistryImplDeserializer instance;
+ };
+
+ struct RegistryDeserializer final : Json::IDeserializer<Registry>
+ {
+ constexpr static StringLiteral PACKAGES = "packages";
+
+ virtual StringView type_name() const override;
+ virtual View<StringView> valid_fields() const override;
+
+ virtual Optional<Registry> visit_object(Json::Reader&, const Json::Object&) override;
+ };
+
+ struct ConfigurationDeserializer final : Json::IDeserializer<Configuration>
+ {
+ virtual StringView type_name() const override { return "a configuration object"; }
+
+ constexpr static StringLiteral DEFAULT_REGISTRY = "default-registry";
+ constexpr static StringLiteral REGISTRIES = "registries";
+ virtual View<StringView> valid_fields() const override
+ {
+ constexpr static StringView t[] = {DEFAULT_REGISTRY, REGISTRIES};
+ return t;
+ }
+
+ virtual Optional<Configuration> visit_object(Json::Reader& r, const Json::Object& obj) override;
+
+ ConfigurationDeserializer(const VcpkgCmdArguments& args);
+
+ private:
+ bool print_json;
+
+ bool registries_enabled;
+ };
+}
diff --git a/toolsrc/include/vcpkg/registries.h b/toolsrc/include/vcpkg/registries.h
index 9119ba17b..812bce86b 100644
--- a/toolsrc/include/vcpkg/registries.h
+++ b/toolsrc/include/vcpkg/registries.h
@@ -1,11 +1,10 @@
#pragma once
-#include <vcpkg/base/fwd/json.h>
-
#include <vcpkg/fwd/vcpkgpaths.h>
#include <vcpkg/base/files.h>
-#include <vcpkg/base/optional.h>
+#include <vcpkg/base/stringview.h>
+#include <vcpkg/base/view.h>
#include <memory>
#include <string>
@@ -29,7 +28,7 @@ namespace vcpkg
Registry(std::vector<std::string>&&, std::nullptr_t) = delete;
// always ordered lexicographically
- Span<const std::string> packages() const { return packages_; }
+ View<std::string> packages() const { return packages_; }
const RegistryImpl& implementation() const { return *implementation_; }
static std::unique_ptr<RegistryImpl> builtin_registry();
@@ -39,31 +38,6 @@ namespace vcpkg
std::unique_ptr<RegistryImpl> implementation_;
};
- struct RegistryImplDeserializer : Json::IDeserializer<std::unique_ptr<RegistryImpl>>
- {
- constexpr static StringLiteral KIND = "kind";
- constexpr static StringLiteral PATH = "path";
-
- constexpr static StringLiteral KIND_BUILTIN = "builtin";
- constexpr static StringLiteral KIND_DIRECTORY = "directory";
-
- virtual StringView type_name() const override;
- virtual Span<const StringView> valid_fields() const override;
-
- virtual Optional<std::unique_ptr<RegistryImpl>> visit_null(Json::Reader&) override;
- virtual Optional<std::unique_ptr<RegistryImpl>> visit_object(Json::Reader&, const Json::Object&) override;
- };
-
- struct RegistryDeserializer final : Json::IDeserializer<Registry>
- {
- constexpr static StringLiteral PACKAGES = "packages";
-
- virtual StringView type_name() const override;
- virtual Span<const StringView> valid_fields() const override;
-
- virtual Optional<Registry> visit_object(Json::Reader&, const Json::Object&) override;
- };
-
// this type implements the registry fall back logic from the registries RFC:
// A port name maps to one of the non-default registries if that registry declares
// that it is the registry for that port name, else it maps to the default registry
@@ -78,7 +52,7 @@ namespace vcpkg
// Returns the null pointer if there is no registry set up for that name
const RegistryImpl* registry_for_port(StringView port_name) const;
- Span<const Registry> registries() const { return registries_; }
+ View<Registry> registries() const { return registries_; }
const RegistryImpl* default_registry() const { return default_registry_.get(); }