Clio develop
The XRP Ledger API server.
Loading...
Searching...
No Matches
ConfigConstraints.hpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of clio: https://github.com/XRPLF/clio
4 Copyright (c) 2024, the clio developers.
5
6 Permission to use, copy, modify, and distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#pragma once
21
22#include "rpc/common/APIVersion.hpp"
23#include "util/config/Error.hpp"
24#include "util/config/Types.hpp"
25#include "util/log/Logger.hpp"
26
27#include <fmt/format.h>
28#include <fmt/ranges.h>
29
30#include <algorithm>
31#include <array>
32#include <cstddef>
33#include <cstdint>
34#include <limits>
35#include <optional>
36#include <ostream>
37#include <string>
38#include <string_view>
39#include <variant>
40
41namespace util::config {
42class ValueView;
43class ConfigValue;
44
48static constexpr std::array<char const*, 6> kLOG_LEVELS = {
49 "trace",
50 "debug",
51 "info",
52 "warning",
53 "error",
54 "fatal",
55};
56
60static constexpr std::array<char const*, 5> kLOG_TAGS = {
61 "int",
62 "uint",
63 "null",
64 "none",
65 "uuid",
66};
67
71static constexpr std::array<char const*, 3> kLOAD_CACHE_MODE = {
72 "sync",
73 "async",
74 "none",
75};
76
80static constexpr std::array<char const*, 1> kDATABASE_TYPE = {"cassandra"};
81
85static constexpr std::array<char const*, 2> kPROCESSING_POLICY = {"parallel", "sequent"};
86
91public:
92 constexpr virtual ~Constraint() noexcept = default;
93
100 [[nodiscard]]
101 std::optional<Error>
102 checkConstraint(Value const& val) const
103 {
104 if (auto const maybeError = checkTypeImpl(val); maybeError.has_value())
105 return maybeError;
106 return checkValueImpl(val);
107 }
108
109protected:
119 template <std::size_t ArrSize>
120 constexpr std::string
121 makeErrorMsg(std::string_view key, Value const& value, std::array<char const*, ArrSize> arr) const
122 {
123 // Extract the value from the variant
124 auto const valueStr = std::visit([](auto const& v) { return fmt::format("{}", v); }, value);
125
126 // Create the error message
127 return fmt::format(
128 R"(You provided value "{}". Key "{}"'s value must be one of the following: {})",
129 valueStr,
130 key,
131 fmt::join(arr, ", ")
132 );
133 }
134
141 virtual std::optional<Error>
142 checkTypeImpl(Value const& val) const = 0;
143
150 virtual std::optional<Error>
151 checkValueImpl(Value const& val) const = 0;
152
158 virtual void
159 print(std::ostream& stream) const = 0;
160
168 friend std::ostream&
169 operator<<(std::ostream& stream, Constraint const& cons)
170 {
171 cons.print(stream);
172 return stream;
173 }
174};
175
179class PortConstraint final : public Constraint {
180public:
181 constexpr ~PortConstraint() noexcept override = default;
182
183private:
190 [[nodiscard]] std::optional<Error>
191 checkTypeImpl(Value const& port) const override;
192
199 [[nodiscard]] std::optional<Error>
200 checkValueImpl(Value const& port) const override;
201
207 void
208 print(std::ostream& stream) const override
209 {
210 stream << fmt::format("The minimum value is `{}`. The maximum value is `{}`.", kPORT_MIN, kPORT_MAX);
211 }
212
213 static constexpr uint32_t kPORT_MIN = 1;
214 static constexpr uint32_t kPORT_MAX = 65535;
215};
216
220class ValidIPConstraint final : public Constraint {
221public:
222 constexpr ~ValidIPConstraint() noexcept override = default;
223
224private:
231 [[nodiscard]] std::optional<Error>
232 checkTypeImpl(Value const& ip) const override;
233
240 [[nodiscard]] std::optional<Error>
241 checkValueImpl(Value const& ip) const override;
242
248 void
249 print(std::ostream& stream) const override
250 {
251 stream << "The value must be a valid IP address.";
252 }
253};
254
260template <std::size_t ArrSize>
261class OneOf final : public Constraint {
262public:
269 constexpr OneOf(std::string_view key, std::array<char const*, ArrSize> arr) : key_{key}, arr_{arr}
270 {
271 }
272
273 constexpr ~OneOf() noexcept override = default;
274
275private:
282 [[nodiscard]] std::optional<Error>
283 checkTypeImpl(Value const& val) const override
284 {
285 if (!std::holds_alternative<std::string>(val))
286 return Error{fmt::format(R"(Key "{}"'s value must be a string)", key_)};
287 return std::nullopt;
288 }
289
296 [[nodiscard]] std::optional<Error>
297 checkValueImpl(Value const& val) const override
298 {
299 namespace rg = std::ranges;
300 auto const check = [&val](std::string_view name) { return std::get<std::string>(val) == name; };
301 if (rg::any_of(arr_, check))
302 return std::nullopt;
303
304 return Error{makeErrorMsg(key_, val, arr_)};
305 }
306
312 void
313 print(std::ostream& stream) const override
314 {
315 std::string valuesStream;
316 std::ranges::for_each(arr_, [&valuesStream](std::string const& elem) {
317 valuesStream += fmt::format(" `{}`,", elem);
318 });
319 // replace the last "," with "."
320 valuesStream.back() = '.';
321 stream << fmt::format("The value must be one of the following:{}", valuesStream);
322 }
323
324 std::string_view key_;
325 std::array<char const*, ArrSize> arr_;
326};
327
331template <typename NumType>
332class NumberValueConstraint final : public Constraint {
333public:
340 constexpr NumberValueConstraint(NumType min, NumType max) : min_{min}, max_{max}
341 {
342 }
343
344 constexpr ~NumberValueConstraint() noexcept override = default;
345
346private:
353 [[nodiscard]] std::optional<Error>
354 checkTypeImpl(Value const& num) const override
355 {
356 if (!std::holds_alternative<int64_t>(num))
357 return Error{"Number must be of type integer"};
358 return std::nullopt;
359 }
360
367 [[nodiscard]] std::optional<Error>
368 checkValueImpl(Value const& num) const override
369 {
370 auto const numValue = std::get<int64_t>(num);
371 if (numValue >= static_cast<int64_t>(min_) && numValue <= static_cast<int64_t>(max_))
372 return std::nullopt;
373 return Error{fmt::format("Number must be between {} and {}", min_, max_)};
374 }
375
381 void
382 print(std::ostream& stream) const override
383 {
384 stream << fmt::format("The minimum value is `{}`. The maximum value is `{}`.", min_, max_);
385 }
386
387 NumType min_;
388 NumType max_;
389};
390
394class PositiveDouble final : public Constraint {
395public:
396 constexpr ~PositiveDouble() noexcept override = default;
397
398private:
405 [[nodiscard]] std::optional<Error>
406 checkTypeImpl(Value const& num) const override;
407
414 [[nodiscard]] std::optional<Error>
415 checkValueImpl(Value const& num) const override;
416
422 void
423 print(std::ostream& stream) const override
424 {
425 stream << "The value must be a positive double number.";
426 }
427};
428
432class RpcNameConstraint final : public Constraint {
433private:
440 [[nodiscard]] std::optional<Error>
441 checkTypeImpl(Value const& value) const override;
442
449 [[nodiscard]] std::optional<Error>
450 checkValueImpl(Value const& value) const override;
451
457 void
458 print(std::ostream& stream) const override
459 {
460 stream << "Checks whether provided RPC name is valid";
461 }
462};
463
464static constinit PortConstraint gValidatePort{};
465static constinit ValidIPConstraint gValidateIp{};
466
467static constinit OneOf gValidateChannelName{"channel", Logger::kCHANNELS};
468static constinit OneOf gValidateLogLevelName{"log_level", kLOG_LEVELS};
469static constinit OneOf gValidateCassandraName{"database.type", kDATABASE_TYPE};
470static constinit OneOf gValidateLoadMode{"cache.load", kLOAD_CACHE_MODE};
471static constinit OneOf gValidateLogTag{"log_tag_style", kLOG_TAGS};
472static constinit OneOf gValidateProcessingPolicy{"server.processing_policy", kPROCESSING_POLICY};
473
474static constinit PositiveDouble gValidatePositiveDouble{};
475
476static constinit NumberValueConstraint<uint16_t> gValidateNumMarkers{1, 256};
477static constinit NumberValueConstraint<uint16_t> gValidateNumCursors{0, std::numeric_limits<uint16_t>::max()};
478
479// replication factor can be 0
480static constinit NumberValueConstraint<uint16_t> gValidateReplicationFactor{0, std::numeric_limits<uint16_t>::max()};
481
482static constinit NumberValueConstraint<uint16_t> gValidateUint16{1, std::numeric_limits<uint16_t>::max()};
483
484static constinit NumberValueConstraint<uint32_t> gValidateUint32{1, std::numeric_limits<uint32_t>::max()};
485static constinit NumberValueConstraint<uint32_t> gValidateNonNegativeUint32{0, std::numeric_limits<uint32_t>::max()};
486static constinit NumberValueConstraint<uint32_t> gValidateApiVersion{rpc::kAPI_VERSION_MIN, rpc::kAPI_VERSION_MAX};
487
488static constinit RpcNameConstraint gRpcNameConstraint{};
489} // namespace util::config
An interface to enforce constraints on certain values within ClioConfigDefinition.
Definition ConfigConstraints.hpp:90
friend std::ostream & operator<<(std::ostream &stream, Constraint const &cons)
Custom output stream for constraint.
Definition ConfigConstraints.hpp:169
virtual void print(std::ostream &stream) const =0
Prints to the output stream for this specific constraint.
virtual std::optional< Error > checkValueImpl(Value const &val) const =0
Check if the value is within the constraint.
std::optional< Error > checkConstraint(Value const &val) const
Check if the value meets the specific constraint.
Definition ConfigConstraints.hpp:102
constexpr std::string makeErrorMsg(std::string_view key, Value const &value, std::array< char const *, ArrSize > arr) const
Creates an error message for all constraints that must satisfy certain hard-coded values.
Definition ConfigConstraints.hpp:121
virtual std::optional< Error > checkTypeImpl(Value const &val) const =0
Check if the value is of a correct type for the constraint.
A constraint class to ensure an integer value is between two numbers (inclusive)
Definition ConfigConstraints.hpp:332
constexpr NumberValueConstraint(NumType min, NumType max)
Constructs a constraint where the number must be between min_ and max_.
Definition ConfigConstraints.hpp:340
A constraint class to ensure the provided value is one of the specified values in an array.
Definition ConfigConstraints.hpp:261
constexpr OneOf(std::string_view key, std::array< char const *, ArrSize > arr)
Constructs a constraint where the value must be one of the values in the provided array.
Definition ConfigConstraints.hpp:269
A constraint to ensure the port number is within a valid range.
Definition ConfigConstraints.hpp:179
A constraint to ensure a double number is positive.
Definition ConfigConstraints.hpp:394
A constraint to ensure the value is a valid RPC command name.
Definition ConfigConstraints.hpp:432
A constraint to ensure the IP address is valid.
Definition ConfigConstraints.hpp:220
static constexpr uint32_t kAPI_VERSION_MIN
Minimum API version supported by this build.
Definition APIVersion.hpp:38
static constexpr uint32_t kAPI_VERSION_MAX
Maximum API version supported by this build.
Definition APIVersion.hpp:43
Displays the different errors when parsing user config.
Definition Error.hpp:31