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
90static constexpr std::array<char const*, 2> kPROVIDER = {"cassandra", "aws_keyspace"};
91
96public:
97 constexpr virtual ~Constraint() noexcept = default;
98
105 [[nodiscard]]
106 std::optional<Error>
107 checkConstraint(Value const& val) const
108 {
109 if (auto const maybeError = checkTypeImpl(val); maybeError.has_value())
110 return maybeError;
111 return checkValueImpl(val);
112 }
113
114protected:
124 template <std::size_t ArrSize>
125 constexpr std::string
126 makeErrorMsg(std::string_view key, Value const& value, std::array<char const*, ArrSize> arr) const
127 {
128 // Extract the value from the variant
129 auto const valueStr = std::visit([](auto const& v) { return fmt::format("{}", v); }, value);
130
131 // Create the error message
132 return fmt::format(
133 R"(You provided value "{}". Key "{}"'s value must be one of the following: {})",
134 valueStr,
135 key,
136 fmt::join(arr, ", ")
137 );
138 }
139
146 virtual std::optional<Error>
147 checkTypeImpl(Value const& val) const = 0;
148
155 virtual std::optional<Error>
156 checkValueImpl(Value const& val) const = 0;
157
163 virtual void
164 print(std::ostream& stream) const = 0;
165
173 friend std::ostream&
174 operator<<(std::ostream& stream, Constraint const& cons)
175 {
176 cons.print(stream);
177 return stream;
178 }
179};
180
184class PortConstraint final : public Constraint {
185public:
186 constexpr ~PortConstraint() noexcept override = default;
187
188private:
195 [[nodiscard]] std::optional<Error>
196 checkTypeImpl(Value const& port) const override;
197
204 [[nodiscard]] std::optional<Error>
205 checkValueImpl(Value const& port) const override;
206
212 void
213 print(std::ostream& stream) const override
214 {
215 stream << fmt::format("The minimum value is `{}`. The maximum value is `{}`.", kPORT_MIN, kPORT_MAX);
216 }
217
218 static constexpr uint32_t kPORT_MIN = 1;
219 static constexpr uint32_t kPORT_MAX = 65535;
220};
221
225class ValidIPConstraint final : public Constraint {
226public:
227 constexpr ~ValidIPConstraint() noexcept override = default;
228
229private:
236 [[nodiscard]] std::optional<Error>
237 checkTypeImpl(Value const& ip) const override;
238
245 [[nodiscard]] std::optional<Error>
246 checkValueImpl(Value const& ip) const override;
247
253 void
254 print(std::ostream& stream) const override
255 {
256 stream << "The value must be a valid IP address.";
257 }
258};
259
265template <std::size_t ArrSize>
266class OneOf final : public Constraint {
267public:
274 constexpr OneOf(std::string_view key, std::array<char const*, ArrSize> arr) : key_{key}, arr_{arr}
275 {
276 }
277
278 constexpr ~OneOf() noexcept override = default;
279
280private:
287 [[nodiscard]] std::optional<Error>
288 checkTypeImpl(Value const& val) const override
289 {
290 if (!std::holds_alternative<std::string>(val))
291 return Error{fmt::format(R"(Key "{}"'s value must be a string)", key_)};
292 return std::nullopt;
293 }
294
301 [[nodiscard]] std::optional<Error>
302 checkValueImpl(Value const& val) const override
303 {
304 namespace rg = std::ranges;
305 auto const check = [&val](std::string_view name) { return std::get<std::string>(val) == name; };
306 if (rg::any_of(arr_, check))
307 return std::nullopt;
308
309 return Error{makeErrorMsg(key_, val, arr_)};
310 }
311
317 void
318 print(std::ostream& stream) const override
319 {
320 std::string valuesStream;
321 std::ranges::for_each(arr_, [&valuesStream](std::string const& elem) {
322 valuesStream += fmt::format(" `{}`,", elem);
323 });
324 // replace the last "," with "."
325 valuesStream.back() = '.';
326 stream << fmt::format("The value must be one of the following:{}", valuesStream);
327 }
328
329 std::string_view key_;
330 std::array<char const*, ArrSize> arr_;
331};
332
336template <typename NumType>
337class NumberValueConstraint final : public Constraint {
338public:
345 constexpr NumberValueConstraint(NumType min, NumType max) : min_{min}, max_{max}
346 {
347 }
348
349 constexpr ~NumberValueConstraint() noexcept override = default;
350
351private:
358 [[nodiscard]] std::optional<Error>
359 checkTypeImpl(Value const& num) const override
360 {
361 if (!std::holds_alternative<int64_t>(num))
362 return Error{"Number must be of type integer"};
363 return std::nullopt;
364 }
365
372 [[nodiscard]] std::optional<Error>
373 checkValueImpl(Value const& num) const override
374 {
375 auto const numValue = std::get<int64_t>(num);
376 if (numValue >= static_cast<int64_t>(min_) && numValue <= static_cast<int64_t>(max_))
377 return std::nullopt;
378 return Error{fmt::format("Number must be between {} and {}", min_, max_)};
379 }
380
386 void
387 print(std::ostream& stream) const override
388 {
389 stream << fmt::format("The minimum value is `{}`. The maximum value is `{}`.", min_, max_);
390 }
391
392 NumType min_;
393 NumType max_;
394};
395
399class PositiveDouble final : public Constraint {
400public:
401 constexpr ~PositiveDouble() noexcept override = default;
402
403private:
410 [[nodiscard]] std::optional<Error>
411 checkTypeImpl(Value const& num) const override;
412
419 [[nodiscard]] std::optional<Error>
420 checkValueImpl(Value const& num) const override;
421
427 void
428 print(std::ostream& stream) const override
429 {
430 stream << "The value must be a positive double number.";
431 }
432};
433
437class RpcNameConstraint final : public Constraint {
438private:
445 [[nodiscard]] std::optional<Error>
446 checkTypeImpl(Value const& value) const override;
447
454 [[nodiscard]] std::optional<Error>
455 checkValueImpl(Value const& value) const override;
456
462 void
463 print(std::ostream& stream) const override
464 {
465 stream << "Checks whether provided RPC name is valid";
466 }
467};
468
469static constinit PortConstraint gValidatePort{};
470static constinit ValidIPConstraint gValidateIp{};
471
472static constinit OneOf gValidateChannelName{"channel", Logger::kCHANNELS};
473static constinit OneOf gValidateLogLevelName{"log.level", kLOG_LEVELS};
474static constinit OneOf gValidateCassandraName{"database.type", kDATABASE_TYPE};
475static constinit OneOf gValidateLoadMode{"cache.load", kLOAD_CACHE_MODE};
476static constinit OneOf gValidateLogTag{"log.tag_style", kLOG_TAGS};
477static constinit OneOf gValidateProcessingPolicy{"server.processing_policy", kPROCESSING_POLICY};
478static constinit OneOf gValidateProvider{"database.cassandra.provider", kPROVIDER};
479
480static constinit PositiveDouble gValidatePositiveDouble{};
481
482static constinit NumberValueConstraint<uint16_t> gValidateNumMarkers{1, 256};
483static constinit NumberValueConstraint<uint16_t> gValidateNumCursors{0, std::numeric_limits<uint16_t>::max()};
484
485// replication factor can be 0
486static constinit NumberValueConstraint<uint16_t> gValidateReplicationFactor{0, std::numeric_limits<uint16_t>::max()};
487
488static constinit NumberValueConstraint<uint16_t> gValidateUint16{1, std::numeric_limits<uint16_t>::max()};
489
490static constinit NumberValueConstraint<uint32_t> gValidateUint32{1, std::numeric_limits<uint32_t>::max()};
491static constinit NumberValueConstraint<uint32_t> gValidateNonNegativeUint32{0, std::numeric_limits<uint32_t>::max()};
493
494static constinit RpcNameConstraint gRpcNameConstraint{};
495} // namespace util::config
Represents the config values for Json/Yaml config.
Definition ConfigValue.hpp:47
An interface to enforce constraints on certain values within ClioConfigDefinition.
Definition ConfigConstraints.hpp:95
friend std::ostream & operator<<(std::ostream &stream, Constraint const &cons)
Custom output stream for constraint.
Definition ConfigConstraints.hpp:174
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:107
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:126
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:337
constexpr NumberValueConstraint(NumType min, NumType max)
Constructs a constraint where the number must be between min_ and max_.
Definition ConfigConstraints.hpp:345
A constraint class to ensure the provided value is one of the specified values in an array.
Definition ConfigConstraints.hpp:266
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:274
A constraint to ensure the port number is within a valid range.
Definition ConfigConstraints.hpp:184
A constraint to ensure a double number is positive.
Definition ConfigConstraints.hpp:399
A constraint to ensure the value is a valid RPC command name.
Definition ConfigConstraints.hpp:437
A constraint to ensure the IP address is valid.
Definition ConfigConstraints.hpp:225
Provides view into ConfigValues that represents values in Clio Config.
Definition ValueView.hpp:46
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