mirror of
https://github.com/microsoft/vcpkg
synced 2024-11-21 16:09:03 -07:00
[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>
This commit is contained in:
parent
d6b5fbfef1
commit
d1ba685e97
21 changed files with 730 additions and 576 deletions
|
@ -28,20 +28,21 @@ In this document, we have basic information on [Writing a Manifest](#writing-a-m
|
|||
the [vcpkg Command Line Interface](#command-line-interface),
|
||||
and a little more information on [CMake](#cmake-integration) integration.
|
||||
|
||||
Check out the [manifest cmake example](../examples/manifest-mode-cmake.md) for an example project using CMake and manifest mode.
|
||||
Check out the [manifest cmake example](../examples/manifest-mode-cmake.md) for an example project using CMake and
|
||||
manifest mode.
|
||||
|
||||
## Writing a Manifest
|
||||
|
||||
A manifest is a JSON-formatted file which lies at the root of your project.
|
||||
A manifest is a JSON-formatted file named `vcpkg.json` which lies at the root of your project.
|
||||
It contains all the information a person needs to know to get dependencies for your project,
|
||||
as well as all the metadata about your project that a person who depends on you might be interested in.
|
||||
|
||||
Every manifest is named `vcpkg.json`, and has a top level object. It is a strict JSON file,
|
||||
and can't contain things like comments or trailing commas - however,
|
||||
you can use field names that start with `$` to write your comments.
|
||||
Manifests follow strict JSON: they can't contain C++-style comments (`//`) nor trailing commas. However
|
||||
you can use field names that start with `$` to write your comments in any object that has a well-defined set of keys.
|
||||
These comment fields are not allowed in any objects which permit user-defined keys (such as `"features"`).
|
||||
|
||||
All of the fields that `vcpkg.json` supports are as follows; the most important ones are [`"name"`](#name),
|
||||
the [version fields](#version-fields), and [`"dependencies"`](#dependencies):
|
||||
Each manifest contains a top level object with the fields documented below; the most important ones are
|
||||
[`"name"`](#name), the [version fields](#version-fields), and [`"dependencies"`](#dependencies):
|
||||
|
||||
### `"name"`
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,4 +4,7 @@ namespace vcpkg
|
|||
{
|
||||
template<class T>
|
||||
struct Span;
|
||||
|
||||
template<class T>
|
||||
using View = Span<const T>;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
347
toolsrc/include/vcpkg/base/jsonreader.h
Normal file
347
toolsrc/include/vcpkg/base/jsonreader.h
Normal file
|
@ -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;
|
||||
};
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <vcpkg/base/fwd/span.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <initializer_list>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
#include <vcpkg/base/span.h>
|
||||
|
||||
namespace vcpkg
|
||||
{
|
||||
template<class T>
|
||||
using View = Span<const T>;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
66
toolsrc/include/vcpkg/configurationdeserializer.h
Normal file
66
toolsrc/include/vcpkg/configurationdeserializer.h
Normal file
|
@ -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;
|
||||
};
|
||||
}
|
|
@ -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(); }
|
||||
|
||||
|
|
|
@ -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"};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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)};
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
{
|
||||
|
|
|
@ -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");
|
||||
|
|
Loading…
Reference in a new issue