diff options
| author | ras0219 <533828+ras0219@users.noreply.github.com> | 2020-10-12 10:22:47 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-10-12 10:22:47 -0700 |
| commit | d1ba685e975db41d767af5676ca2669a5e7e6ad9 (patch) | |
| tree | e6dcb36f131e638fd5307a304a8fb5f2ce072758 /toolsrc/include | |
| parent | d6b5fbfef10e9476cdb8d1a90503f4f6504e63d9 (diff) | |
| download | vcpkg-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.h | 34 | ||||
| -rw-r--r-- | toolsrc/include/vcpkg/base/fwd/span.h | 3 | ||||
| -rw-r--r-- | toolsrc/include/vcpkg/base/json.h | 377 | ||||
| -rw-r--r-- | toolsrc/include/vcpkg/base/jsonreader.h | 347 | ||||
| -rw-r--r-- | toolsrc/include/vcpkg/base/span.h | 2 | ||||
| -rw-r--r-- | toolsrc/include/vcpkg/base/strings.h | 11 | ||||
| -rw-r--r-- | toolsrc/include/vcpkg/base/view.h | 6 | ||||
| -rw-r--r-- | toolsrc/include/vcpkg/configuration.h | 25 | ||||
| -rw-r--r-- | toolsrc/include/vcpkg/configurationdeserializer.h | 66 | ||||
| -rw-r--r-- | toolsrc/include/vcpkg/registries.h | 34 |
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(); } |
