mirror of
https://github.com/XRPLF/clio.git
synced 2026-08-23 13:40:52 +00:00
225 lines
7.0 KiB
C++
225 lines
7.0 KiB
C++
#pragma once
|
|
|
|
#include "rpc/Errors.hpp"
|
|
#include "rpc/common/Concepts.hpp"
|
|
#include "rpc/common/Specs.hpp"
|
|
#include "rpc/common/Types.hpp"
|
|
|
|
#include <boost/json/value.hpp>
|
|
#include <fmt/format.h>
|
|
|
|
#include <cstddef>
|
|
#include <functional>
|
|
#include <initializer_list>
|
|
#include <optional>
|
|
#include <string_view>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
namespace rpc::meta {
|
|
|
|
/**
|
|
* @brief A meta-processor that acts as a spec for a sub-object/section.
|
|
*/
|
|
class Section final {
|
|
std::vector<FieldSpec> specs_;
|
|
|
|
public:
|
|
/**
|
|
* @brief Construct new section validator from a list of specs.
|
|
*
|
|
* @param specs List of specs @ref FieldSpec
|
|
*/
|
|
explicit Section(std::initializer_list<FieldSpec> specs) : specs_{specs}
|
|
{
|
|
}
|
|
|
|
/**
|
|
* @brief Verify that the JSON value representing the section is valid according to the given
|
|
* specs.
|
|
*
|
|
* @param value The JSON value representing the outer object
|
|
* @param key The key used to retrieve the section from the outer object
|
|
* @return Possibly an error
|
|
*/
|
|
[[nodiscard]] MaybeError
|
|
verify(boost::json::value& value, std::string_view key) const;
|
|
};
|
|
|
|
/**
|
|
* @brief A meta-processor that specifies a list of specs to run against the object at the given
|
|
* index in the array.
|
|
*/
|
|
class ValidateArrayAt final {
|
|
std::size_t idx_;
|
|
std::vector<FieldSpec> specs_;
|
|
|
|
public:
|
|
/**
|
|
* @brief Constructs a processor that validates the specified element of a JSON array.
|
|
*
|
|
* @param idx The index inside the array to validate
|
|
* @param specs The specifications to validate against
|
|
*/
|
|
ValidateArrayAt(std::size_t idx, std::initializer_list<FieldSpec> specs)
|
|
: idx_{idx}, specs_{specs}
|
|
{
|
|
}
|
|
|
|
/**
|
|
* @brief Verify that the JSON array element at given index is valid according the stored specs.
|
|
*
|
|
* @param value The JSON value representing the outer object
|
|
* @param key The key used to retrieve the array from the outer object
|
|
* @return Possibly an error
|
|
*/
|
|
[[nodiscard]] MaybeError
|
|
verify(boost::json::value& value, std::string_view key) const;
|
|
};
|
|
|
|
/**
|
|
* @brief A meta-processor that specifies a list of requirements to run against when the type
|
|
* matches the template parameter.
|
|
*/
|
|
template <typename Type>
|
|
class IfType final {
|
|
public:
|
|
/**
|
|
* @brief Constructs a validator that validates the specs if the type matches.
|
|
* @param requirements The requirements to validate against
|
|
*/
|
|
template <SomeRequirement... Requirements>
|
|
explicit IfType(Requirements&&... requirements)
|
|
: processor_(
|
|
[... r = std::forward<Requirements>(
|
|
requirements
|
|
)](boost::json::value& j, std::string_view key) -> MaybeError {
|
|
std::optional<Status> firstFailure = std::nullopt;
|
|
|
|
// the check logic is the same as fieldspec
|
|
(
|
|
[&j, &key, &firstFailure, req = &r]() {
|
|
if (firstFailure)
|
|
return;
|
|
|
|
if (auto const res = req->verify(j, key); not res)
|
|
firstFailure = res.error();
|
|
}(),
|
|
...);
|
|
|
|
if (firstFailure)
|
|
return Error{firstFailure.value()};
|
|
|
|
return {};
|
|
}
|
|
)
|
|
{
|
|
}
|
|
|
|
IfType(IfType const&) = default;
|
|
IfType(IfType&&) = default;
|
|
|
|
/**
|
|
* @brief Verify that the element is valid according to the stored requirements when type
|
|
* matches.
|
|
*
|
|
* @param value The JSON value representing the outer object
|
|
* @param key The key used to retrieve the element from the outer object
|
|
* @return Possibly an error
|
|
*/
|
|
[[nodiscard]] MaybeError
|
|
verify(boost::json::value& value, std::string_view key) const
|
|
{
|
|
if (not value.is_object() or not value.as_object().contains(key))
|
|
return {}; // ignore. field does not exist, let 'required' fail instead
|
|
|
|
if (not rpc::validation::checkType<Type>(value.as_object().at(key)))
|
|
return {}; // ignore if type does not match
|
|
|
|
return processor_(value, key);
|
|
}
|
|
|
|
private:
|
|
std::function<MaybeError(boost::json::value&, std::string_view)> processor_;
|
|
};
|
|
|
|
/**
|
|
* @brief A meta-processor that wraps a validator and produces a custom error in case the wrapped
|
|
* validator fails.
|
|
*/
|
|
template <typename RequirementOrModifierType>
|
|
requires SomeRequirement<RequirementOrModifierType> or SomeModifier<RequirementOrModifierType>
|
|
class WithCustomError final {
|
|
RequirementOrModifierType reqOrModifier_;
|
|
Status error_;
|
|
|
|
public:
|
|
/**
|
|
* @brief Constructs a validator that calls the given validator `req` and returns a custom error
|
|
* `err` in case `req` fails.
|
|
*
|
|
* @param reqOrModifier The requirement to validate against
|
|
* @param err The custom error to return in case `req` fails
|
|
*/
|
|
WithCustomError(RequirementOrModifierType reqOrModifier, Status err)
|
|
: reqOrModifier_{std::move(reqOrModifier)}, error_{std::move(err)}
|
|
{
|
|
}
|
|
|
|
/**
|
|
* @brief Runs the stored validator and produces a custom error if the wrapped validator fails.
|
|
*
|
|
* @param value The JSON value representing the outer object
|
|
* @param key The key used to retrieve the element from the outer object
|
|
* @return Possibly an error
|
|
*/
|
|
[[nodiscard]] MaybeError
|
|
verify(boost::json::value const& value, std::string_view key) const
|
|
requires SomeRequirement<RequirementOrModifierType>
|
|
{
|
|
if (auto const res = reqOrModifier_.verify(value, key); not res)
|
|
return Error{error_};
|
|
|
|
return {};
|
|
}
|
|
|
|
/**
|
|
* @brief Runs the stored validator and produces a custom error if the wrapped validator fails.
|
|
* This is an overload for the requirement which can modify the value. Such as IfType.
|
|
*
|
|
* @param value The JSON value representing the outer object, this value can be modified by the
|
|
* requirement inside
|
|
* @param key The key used to retrieve the element from the outer object
|
|
* @return Possibly an error
|
|
*/
|
|
[[nodiscard]] MaybeError
|
|
verify(boost::json::value& value, std::string_view key) const
|
|
requires SomeRequirement<RequirementOrModifierType>
|
|
{
|
|
if (auto const res = reqOrModifier_.verify(value, key); not res)
|
|
return Error{error_};
|
|
|
|
return {};
|
|
}
|
|
|
|
/**
|
|
* @brief Runs the stored modifier and produces a custom error if the wrapped modifier fails.
|
|
*
|
|
* @param value The JSON value representing the outer object. This value can be modified by the
|
|
* modifier.
|
|
* @param key The key used to retrieve the element from the outer object
|
|
* @return Possibly an error
|
|
*/
|
|
MaybeError
|
|
modify(boost::json::value& value, std::string_view key) const
|
|
requires SomeModifier<RequirementOrModifierType>
|
|
|
|
{
|
|
if (auto const res = reqOrModifier_.modify(value, key); not res)
|
|
return Error{error_};
|
|
return {};
|
|
}
|
|
};
|
|
|
|
} // namespace rpc::meta
|