mirror of
https://github.com/XRPLF/clio.git
synced 2025-11-20 11:45:53 +00:00
496 lines
16 KiB
C++
496 lines
16 KiB
C++
//------------------------------------------------------------------------------
|
|
/*
|
|
This file is part of clio: https://github.com/XRPLF/clio
|
|
Copyright (c) 2024, the clio developers.
|
|
|
|
Permission to use, copy, modify, and distribute this software for any
|
|
purpose with or without fee is hereby granted, provided that the above
|
|
copyright notice and this permission notice appear in all copies.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
//==============================================================================
|
|
|
|
#pragma once
|
|
|
|
#include "rpc/common/APIVersion.hpp"
|
|
#include "util/config/Error.hpp"
|
|
#include "util/config/Types.hpp"
|
|
#include "util/log/Logger.hpp"
|
|
|
|
#include <fmt/format.h>
|
|
#include <fmt/ranges.h>
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <limits>
|
|
#include <optional>
|
|
#include <ostream>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <variant>
|
|
|
|
namespace util::config {
|
|
class ValueView;
|
|
class ConfigValue;
|
|
|
|
/**
|
|
* @brief specific values that are accepted for logger levels in config.
|
|
*/
|
|
static constexpr std::array<std::string_view, 6> kLOG_LEVELS = {
|
|
"trace",
|
|
"debug",
|
|
"info",
|
|
"warning",
|
|
"error",
|
|
"fatal",
|
|
};
|
|
|
|
/**
|
|
* @brief specific values that are accepted for logger tag style in config.
|
|
*/
|
|
static constexpr std::array<std::string_view, 5> kLOG_TAGS = {
|
|
"int",
|
|
"uint",
|
|
"null",
|
|
"none",
|
|
"uuid",
|
|
};
|
|
|
|
/**
|
|
* @brief specific values that are accepted for cache loading in config.
|
|
*/
|
|
static constexpr std::array<std::string_view, 3> kLOAD_CACHE_MODE = {
|
|
"sync",
|
|
"async",
|
|
"none",
|
|
};
|
|
|
|
/**
|
|
* @brief specific values that are accepted for database type in config.
|
|
*/
|
|
static constexpr std::array<std::string_view, 1> kDATABASE_TYPE = {"cassandra"};
|
|
|
|
/**
|
|
* @brief specific values that are accepted for server's processing_policy in config.
|
|
*/
|
|
static constexpr std::array<std::string_view, 2> kPROCESSING_POLICY = {"parallel", "sequent"};
|
|
|
|
/**
|
|
* @brief specific values that are accepted for database provider in config.
|
|
*/
|
|
static constexpr std::array<std::string_view, 2> kPROVIDER = {"cassandra", "aws_keyspace"};
|
|
|
|
/**
|
|
* @brief An interface to enforce constraints on certain values within ClioConfigDefinition.
|
|
*/
|
|
class Constraint {
|
|
public:
|
|
constexpr virtual ~Constraint() noexcept = default;
|
|
|
|
/**
|
|
* @brief Check if the value meets the specific constraint.
|
|
*
|
|
* @param val The value to be checked
|
|
* @return An Error object if the constraint is not met, nullopt otherwise
|
|
*/
|
|
[[nodiscard]]
|
|
std::optional<Error>
|
|
checkConstraint(Value const& val) const
|
|
{
|
|
if (auto const maybeError = checkTypeImpl(val); maybeError.has_value())
|
|
return maybeError;
|
|
return checkValueImpl(val);
|
|
}
|
|
|
|
protected:
|
|
/**
|
|
* @brief Creates an error message for all constraints that must satisfy certain hard-coded values.
|
|
*
|
|
* @tparam arrSize, the size of the array of hardcoded values
|
|
* @param key The key to the value
|
|
* @param value The value the user provided
|
|
* @param arr The array with hard-coded values to add to error message
|
|
* @return The error message specifying what the value of key must be
|
|
*/
|
|
template <std::size_t ArrSize>
|
|
constexpr std::string
|
|
makeErrorMsg(std::string_view key, Value const& value, std::array<std::string_view, ArrSize> arr) const
|
|
{
|
|
// Extract the value from the variant
|
|
auto const valueStr = std::visit([](auto const& v) { return fmt::format("{}", v); }, value);
|
|
|
|
// Create the error message
|
|
return fmt::format(
|
|
R"(You provided value "{}". Key "{}"'s value must be one of the following: {})",
|
|
valueStr,
|
|
key,
|
|
fmt::join(arr, ", ")
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @brief Check if the value is of a correct type for the constraint.
|
|
*
|
|
* @param val The value type to be checked
|
|
* @return An Error object if the constraint is not met, nullopt otherwise
|
|
*/
|
|
virtual std::optional<Error>
|
|
checkTypeImpl(Value const& val) const = 0;
|
|
|
|
/**
|
|
* @brief Check if the value is within the constraint.
|
|
*
|
|
* @param val The value type to be checked
|
|
* @return An Error object if the constraint is not met, nullopt otherwise
|
|
*/
|
|
virtual std::optional<Error>
|
|
checkValueImpl(Value const& val) const = 0;
|
|
|
|
/**
|
|
* @brief Prints to the output stream for this specific constraint.
|
|
*
|
|
* @param stream The output stream
|
|
*/
|
|
virtual void
|
|
print(std::ostream& stream) const = 0;
|
|
|
|
/**
|
|
* @brief Custom output stream for constraint
|
|
*
|
|
* @param stream The output stream
|
|
* @param cons The constraint
|
|
* @return The same ostream we were given
|
|
*/
|
|
friend std::ostream&
|
|
operator<<(std::ostream& stream, Constraint const& cons)
|
|
{
|
|
cons.print(stream);
|
|
return stream;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @brief A constraint to ensure the port number is within a valid range.
|
|
*/
|
|
class PortConstraint final : public Constraint {
|
|
public:
|
|
constexpr ~PortConstraint() noexcept override = default;
|
|
|
|
private:
|
|
/**
|
|
* @brief Check if the type of the value is correct for this specific constraint.
|
|
*
|
|
* @param port The type to be checked
|
|
* @return An Error object if the constraint is not met, nullopt otherwise
|
|
*/
|
|
[[nodiscard]] std::optional<Error>
|
|
checkTypeImpl(Value const& port) const override;
|
|
|
|
/**
|
|
* @brief Check if the value is within the constraint.
|
|
*
|
|
* @param port The value to be checked
|
|
* @return An Error object if the constraint is not met, nullopt otherwise
|
|
*/
|
|
[[nodiscard]] std::optional<Error>
|
|
checkValueImpl(Value const& port) const override;
|
|
|
|
/**
|
|
* @brief Prints to the output stream for this specific constraint.
|
|
*
|
|
* @param stream The output stream
|
|
*/
|
|
void
|
|
print(std::ostream& stream) const override
|
|
{
|
|
stream << fmt::format("The minimum value is `{}`. The maximum value is `{}`.", kPORT_MIN, kPORT_MAX);
|
|
}
|
|
|
|
static constexpr uint32_t kPORT_MIN = 1;
|
|
static constexpr uint32_t kPORT_MAX = 65535;
|
|
};
|
|
|
|
/**
|
|
* @brief A constraint to ensure the IP address is valid.
|
|
*/
|
|
class ValidIPConstraint final : public Constraint {
|
|
public:
|
|
constexpr ~ValidIPConstraint() noexcept override = default;
|
|
|
|
private:
|
|
/**
|
|
* @brief Check if the type of the value is correct for this specific constraint.
|
|
*
|
|
* @param ip The type to be checked.
|
|
* @return An optional Error if the constraint is not met, std::nullopt otherwise
|
|
*/
|
|
[[nodiscard]] std::optional<Error>
|
|
checkTypeImpl(Value const& ip) const override;
|
|
|
|
/**
|
|
* @brief Check if the value is within the constraint.
|
|
*
|
|
* @param ip The value to be checked
|
|
* @return An Error object if the constraint is not met, nullopt otherwise
|
|
*/
|
|
[[nodiscard]] std::optional<Error>
|
|
checkValueImpl(Value const& ip) const override;
|
|
|
|
/**
|
|
* @brief Prints to the output stream for this specific constraint.
|
|
*
|
|
* @param stream The output stream
|
|
*/
|
|
void
|
|
print(std::ostream& stream) const override
|
|
{
|
|
stream << "The value must be a valid IP address.";
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @brief A constraint class to ensure the provided value is one of the specified values in an array.
|
|
*
|
|
* @tparam arrSize The size of the array containing the valid values for the constraint
|
|
*/
|
|
template <std::size_t ArrSize>
|
|
class OneOf final : public Constraint {
|
|
public:
|
|
/**
|
|
* @brief Constructs a constraint where the value must be one of the values in the provided array.
|
|
*
|
|
* @param key The key of the ConfigValue that has this constraint
|
|
* @param arr The value that has this constraint must be of the values in arr
|
|
*/
|
|
constexpr OneOf(std::string_view key, std::array<std::string_view, ArrSize> arr) : key_{key}, arr_{arr}
|
|
{
|
|
}
|
|
|
|
constexpr ~OneOf() noexcept override = default;
|
|
|
|
private:
|
|
/**
|
|
* @brief Check if the type of the value is correct for this specific constraint.
|
|
*
|
|
* @param val The type to be checked
|
|
* @return An Error object if the constraint is not met, nullopt otherwise
|
|
*/
|
|
[[nodiscard]] std::optional<Error>
|
|
checkTypeImpl(Value const& val) const override
|
|
{
|
|
if (!std::holds_alternative<std::string>(val))
|
|
return Error{fmt::format(R"(Key "{}"'s value must be a string)", key_)};
|
|
return std::nullopt;
|
|
}
|
|
|
|
/**
|
|
* @brief Check if the value matches one of the value in the provided array
|
|
*
|
|
* @param val The value to check
|
|
* @return An Error object if the constraint is not met, nullopt otherwise
|
|
*/
|
|
[[nodiscard]] std::optional<Error>
|
|
checkValueImpl(Value const& val) const override
|
|
{
|
|
namespace rg = std::ranges;
|
|
auto const check = [&val](std::string_view name) { return std::get<std::string>(val) == name; };
|
|
if (rg::any_of(arr_, check))
|
|
return std::nullopt;
|
|
|
|
return Error{makeErrorMsg(key_, val, arr_)};
|
|
}
|
|
|
|
/**
|
|
* @brief Prints to the output stream for this specific constraint.
|
|
*
|
|
* @param stream The output stream
|
|
*/
|
|
void
|
|
print(std::ostream& stream) const override
|
|
{
|
|
std::string valuesStream;
|
|
std::ranges::for_each(arr_, [&valuesStream](std::string_view elem) {
|
|
valuesStream += fmt::format(" `{}`,", elem);
|
|
});
|
|
// replace the last "," with "."
|
|
valuesStream.back() = '.';
|
|
stream << fmt::format("The value must be one of the following:{}", valuesStream);
|
|
}
|
|
|
|
std::string_view key_;
|
|
std::array<std::string_view, ArrSize> arr_;
|
|
};
|
|
|
|
/**
|
|
* @brief A constraint class to ensure an integer value is between two numbers (inclusive)
|
|
*/
|
|
template <typename NumType>
|
|
class NumberValueConstraint final : public Constraint {
|
|
public:
|
|
/**
|
|
* @brief Constructs a constraint where the number must be between min_ and max_.
|
|
*
|
|
* @param min the minimum number it can be to satisfy this constraint
|
|
* @param max the maximum number it can be to satisfy this constraint
|
|
*/
|
|
constexpr NumberValueConstraint(NumType min, NumType max) : min_{min}, max_{max}
|
|
{
|
|
}
|
|
|
|
constexpr ~NumberValueConstraint() noexcept override = default;
|
|
|
|
private:
|
|
/**
|
|
* @brief Check if the type of the value is correct for this specific constraint.
|
|
*
|
|
* @param num The type to be checked
|
|
* @return An Error object if the constraint is not met, nullopt otherwise
|
|
*/
|
|
[[nodiscard]] std::optional<Error>
|
|
checkTypeImpl(Value const& num) const override
|
|
{
|
|
if (!std::holds_alternative<int64_t>(num))
|
|
return Error{"Number must be of type integer"};
|
|
return std::nullopt;
|
|
}
|
|
|
|
/**
|
|
* @brief Check if the number is positive.
|
|
*
|
|
* @param num The number to check
|
|
* @return An Error object if the constraint is not met, nullopt otherwise
|
|
*/
|
|
[[nodiscard]] std::optional<Error>
|
|
checkValueImpl(Value const& num) const override
|
|
{
|
|
auto const numValue = std::get<int64_t>(num);
|
|
if (numValue >= static_cast<int64_t>(min_) && numValue <= static_cast<int64_t>(max_))
|
|
return std::nullopt;
|
|
return Error{fmt::format("Number must be between {} and {}", min_, max_)};
|
|
}
|
|
|
|
/**
|
|
* @brief Prints to the output stream for this specific constraint.
|
|
*
|
|
* @param stream The output stream
|
|
*/
|
|
void
|
|
print(std::ostream& stream) const override
|
|
{
|
|
stream << fmt::format("The minimum value is `{}`. The maximum value is `{}`.", min_, max_);
|
|
}
|
|
|
|
NumType min_;
|
|
NumType max_;
|
|
};
|
|
|
|
/**
|
|
* @brief A constraint to ensure a double number is positive
|
|
*/
|
|
class PositiveDouble final : public Constraint {
|
|
public:
|
|
constexpr ~PositiveDouble() noexcept override = default;
|
|
|
|
private:
|
|
/**
|
|
* @brief Check if the type of the value is correct for this specific constraint.
|
|
*
|
|
* @param num The type to be checked
|
|
* @return An Error object if the constraint is not met, nullopt otherwise
|
|
*/
|
|
[[nodiscard]] std::optional<Error>
|
|
checkTypeImpl(Value const& num) const override;
|
|
|
|
/**
|
|
* @brief Check if the number is positive.
|
|
*
|
|
* @param num The number to check
|
|
* @return An Error object if the constraint is not met, nullopt otherwise
|
|
*/
|
|
[[nodiscard]] std::optional<Error>
|
|
checkValueImpl(Value const& num) const override;
|
|
|
|
/**
|
|
* @brief Prints to the output stream for this specific constraint.
|
|
*
|
|
* @param stream The output stream
|
|
*/
|
|
void
|
|
print(std::ostream& stream) const override
|
|
{
|
|
stream << "The value must be a positive double number.";
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @brief A constraint to ensure the value is a valid RPC command name.
|
|
*/
|
|
class RpcNameConstraint final : public Constraint {
|
|
private:
|
|
/**
|
|
* @brief Check if the type of the value is correct for this specific constraint.
|
|
*
|
|
* @param value The type to be checked
|
|
* @return An Error object if the constraint is not met, nullopt otherwise
|
|
*/
|
|
[[nodiscard]] std::optional<Error>
|
|
checkTypeImpl(Value const& value) const override;
|
|
|
|
/**
|
|
* @brief Check if the value is a valid RPC command name.
|
|
*
|
|
* @param value The value to check
|
|
* @return An Error object if the constraint is not met, nullopt otherwise
|
|
*/
|
|
[[nodiscard]] std::optional<Error>
|
|
checkValueImpl(Value const& value) const override;
|
|
|
|
/**
|
|
* @brief Prints to the output stream for this specific constraint.
|
|
*
|
|
* @param stream The output stream
|
|
*/
|
|
void
|
|
print(std::ostream& stream) const override
|
|
{
|
|
stream << "Checks whether provided RPC name is valid";
|
|
}
|
|
};
|
|
|
|
static constinit PortConstraint gValidatePort{};
|
|
static constinit ValidIPConstraint gValidateIp{};
|
|
|
|
static constinit OneOf gValidateChannelName{"channel", Logger::kCHANNELS};
|
|
static constinit OneOf gValidateLogLevelName{"log.level", kLOG_LEVELS};
|
|
static constinit OneOf gValidateCassandraName{"database.type", kDATABASE_TYPE};
|
|
static constinit OneOf gValidateLoadMode{"cache.load", kLOAD_CACHE_MODE};
|
|
static constinit OneOf gValidateLogTag{"log.tag_style", kLOG_TAGS};
|
|
static constinit OneOf gValidateProcessingPolicy{"server.processing_policy", kPROCESSING_POLICY};
|
|
static constinit OneOf gValidateProvider{"database.cassandra.provider", kPROVIDER};
|
|
|
|
static constinit PositiveDouble gValidatePositiveDouble{};
|
|
|
|
static constinit NumberValueConstraint<uint16_t> gValidateNumMarkers{1, 256};
|
|
static constinit NumberValueConstraint<uint16_t> gValidateNumCursors{0, std::numeric_limits<uint16_t>::max()};
|
|
|
|
// replication factor can be 0
|
|
static constinit NumberValueConstraint<uint16_t> gValidateReplicationFactor{0, std::numeric_limits<uint16_t>::max()};
|
|
|
|
static constinit NumberValueConstraint<uint16_t> gValidateUint16{1, std::numeric_limits<uint16_t>::max()};
|
|
|
|
static constinit NumberValueConstraint<uint32_t> gValidateUint32{1, std::numeric_limits<uint32_t>::max()};
|
|
static constinit NumberValueConstraint<uint32_t> gValidateNonNegativeUint32{0, std::numeric_limits<uint32_t>::max()};
|
|
static constinit NumberValueConstraint<uint32_t> gValidateApiVersion{rpc::kAPI_VERSION_MIN, rpc::kAPI_VERSION_MAX};
|
|
|
|
static constinit RpcNameConstraint gRpcNameConstraint{};
|
|
} // namespace util::config
|