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<std::string_view, 6> kLOG_LEVELS = {
49 "trace",
50 "debug",
51 "info",
52 "warning",
53 "error",
54 "fatal",
55};
56
60static constexpr std::array<std::string_view, 5> kLOG_TAGS = {
61 "int",
62 "uint",
63 "null",
64 "none",
65 "uuid",
66};
67
71static constexpr std::array<std::string_view, 3> kLOAD_CACHE_MODE = {
72 "sync",
73 "async",
74 "none",
75};
76
80static constexpr std::array<std::string_view, 1> kDATABASE_TYPE = {"cassandra"};
81
85static constexpr std::array<std::string_view, 2> kPROCESSING_POLICY = {"parallel", "sequent"};
86
90static constexpr std::array<std::string_view, 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:
125 template <std::size_t ArrSize>
126 constexpr std::string
128 std::string_view key,
129 Value const& value,
130 std::array<std::string_view, ArrSize> arr
131 ) const
132 {
133 // Extract the value from the variant
134 auto const valueStr = std::visit([](auto const& v) { return fmt::format("{}", v); }, value);
135
136 // Create the error message
137 return fmt::format(
138 R"(You provided value "{}". Key "{}"'s value must be one of the following: {})",
139 valueStr,
140 key,
141 fmt::join(arr, ", ")
142 );
143 }
144
151 virtual std::optional<Error>
152 checkTypeImpl(Value const& val) const = 0;
153
160 virtual std::optional<Error>
161 checkValueImpl(Value const& val) const = 0;
162
168 virtual void
169 print(std::ostream& stream) const = 0;
170
178 friend std::ostream&
179 operator<<(std::ostream& stream, Constraint const& cons)
180 {
181 cons.print(stream);
182 return stream;
183 }
184};
185
189class PortConstraint final : public Constraint {
190public:
191 constexpr ~PortConstraint() noexcept override = default;
192
193private:
200 [[nodiscard]] std::optional<Error>
201 checkTypeImpl(Value const& port) const override;
202
209 [[nodiscard]] std::optional<Error>
210 checkValueImpl(Value const& port) const override;
211
217 void
218 print(std::ostream& stream) const override
219 {
220 stream << fmt::format(
221 "The minimum value is `{}`. The maximum value is `{}`.", kPORT_MIN, kPORT_MAX
222 );
223 }
224
225 static constexpr uint32_t kPORT_MIN = 1;
226 static constexpr uint32_t kPORT_MAX = 65535;
227};
228
232class ValidIPConstraint final : public Constraint {
233public:
234 constexpr ~ValidIPConstraint() noexcept override = default;
235
236private:
243 [[nodiscard]] std::optional<Error>
244 checkTypeImpl(Value const& ip) const override;
245
252 [[nodiscard]] std::optional<Error>
253 checkValueImpl(Value const& ip) const override;
254
260 void
261 print(std::ostream& stream) const override
262 {
263 stream << "The value must be a valid IP address.";
264 }
265};
266
273template <std::size_t ArrSize>
274class OneOf final : public Constraint {
275public:
283 constexpr OneOf(std::string_view key, std::array<std::string_view, ArrSize> arr)
284 : key_{key}, arr_{arr}
285 {
286 }
287
288 constexpr ~OneOf() noexcept override = default;
289
290private:
297 [[nodiscard]] std::optional<Error>
298 checkTypeImpl(Value const& val) const override
299 {
300 if (!std::holds_alternative<std::string>(val))
301 return Error{fmt::format(R"(Key "{}"'s value must be a string)", key_)};
302 return std::nullopt;
303 }
304
311 [[nodiscard]] std::optional<Error>
312 checkValueImpl(Value const& val) const override
313 {
314 namespace rg = std::ranges;
315 auto const check = [&val](std::string_view name) {
316 return std::get<std::string>(val) == name;
317 };
318 if (rg::any_of(arr_, check))
319 return std::nullopt;
320
321 return Error{makeErrorMsg(key_, val, arr_)};
322 }
323
329 void
330 print(std::ostream& stream) const override
331 {
332 std::string valuesStream;
333 std::ranges::for_each(arr_, [&valuesStream](std::string_view elem) {
334 valuesStream += fmt::format(" `{}`,", elem);
335 });
336 // replace the last "," with "."
337 valuesStream.back() = '.';
338 stream << fmt::format("The value must be one of the following:{}", valuesStream);
339 }
340
341 std::string_view key_;
342 std::array<std::string_view, ArrSize> arr_;
343};
344
348template <typename NumType>
349class NumberValueConstraint final : public Constraint {
350public:
357 constexpr NumberValueConstraint(NumType min, NumType max) : min_{min}, max_{max}
358 {
359 }
360
361 constexpr ~NumberValueConstraint() noexcept override = default;
362
363private:
370 [[nodiscard]] std::optional<Error>
371 checkTypeImpl(Value const& num) const override
372 {
373 if (!std::holds_alternative<int64_t>(num))
374 return Error{"Number must be of type integer"};
375 return std::nullopt;
376 }
377
384 [[nodiscard]] std::optional<Error>
385 checkValueImpl(Value const& num) const override
386 {
387 auto const numValue = std::get<int64_t>(num);
388 if (numValue >= static_cast<int64_t>(min_) && numValue <= static_cast<int64_t>(max_))
389 return std::nullopt;
390 return Error{fmt::format("Number must be between {} and {}", min_, max_)};
391 }
392
398 void
399 print(std::ostream& stream) const override
400 {
401 stream << fmt::format("The minimum value is `{}`. The maximum value is `{}`.", min_, max_);
402 }
403
404 NumType min_;
405 NumType max_;
406};
407
411class PositiveDouble final : public Constraint {
412public:
413 constexpr ~PositiveDouble() noexcept override = default;
414
415private:
422 [[nodiscard]] std::optional<Error>
423 checkTypeImpl(Value const& num) const override;
424
431 [[nodiscard]] std::optional<Error>
432 checkValueImpl(Value const& num) const override;
433
439 void
440 print(std::ostream& stream) const override
441 {
442 stream << "The value must be a positive double number.";
443 }
444};
445
449class RpcNameConstraint final : public Constraint {
450private:
457 [[nodiscard]] std::optional<Error>
458 checkTypeImpl(Value const& value) const override;
459
466 [[nodiscard]] std::optional<Error>
467 checkValueImpl(Value const& value) const override;
468
474 void
475 print(std::ostream& stream) const override
476 {
477 stream << "Checks whether provided RPC name is valid";
478 }
479};
480
481static constinit PortConstraint gValidatePort{};
482static constinit ValidIPConstraint gValidateIp{};
483
484static constinit OneOf gValidateChannelName{"channel", Logger::kCHANNELS};
485static constinit OneOf gValidateLogLevelName{"log.level", kLOG_LEVELS};
486static constinit OneOf gValidateCassandraName{"database.type", kDATABASE_TYPE};
487static constinit OneOf gValidateLoadMode{"cache.load", kLOAD_CACHE_MODE};
488static constinit OneOf gValidateLogTag{"log.tag_style", kLOG_TAGS};
489static constinit OneOf gValidateProcessingPolicy{"server.processing_policy", kPROCESSING_POLICY};
490static constinit OneOf gValidateProvider{"database.cassandra.provider", kPROVIDER};
491
492static constinit PositiveDouble gValidatePositiveDouble{};
493
494static constinit NumberValueConstraint<uint16_t> gValidateNumMarkers{1, 256};
495static constinit NumberValueConstraint<uint16_t> gValidateNumCursors{
496 0,
497 std::numeric_limits<uint16_t>::max()
498};
499
500// replication factor can be 0
501static constinit NumberValueConstraint<uint16_t> gValidateReplicationFactor{
502 0,
503 std::numeric_limits<uint16_t>::max()
504};
505
506static constinit NumberValueConstraint<uint16_t> gValidateUint16{
507 1,
508 std::numeric_limits<uint16_t>::max()
509};
510
511static constinit NumberValueConstraint<uint32_t> gValidateUint32{
512 1,
513 std::numeric_limits<uint32_t>::max()
514};
515static constinit NumberValueConstraint<uint32_t> gValidateNonNegativeUint32{
516 0,
517 std::numeric_limits<uint32_t>::max()
518};
519static constinit NumberValueConstraint<uint32_t> gValidateApiVersion{
522};
523
524static constinit RpcNameConstraint gRpcNameConstraint{};
525} // 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:179
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.
constexpr std::string makeErrorMsg(std::string_view key, Value const &value, std::array< std::string_view, ArrSize > arr) const
Creates an error message for all constraints that must satisfy certain hard-coded values.
Definition ConfigConstraints.hpp:127
std::optional< Error > checkConstraint(Value const &val) const
Check if the value meets the specific constraint.
Definition ConfigConstraints.hpp:107
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:349
constexpr NumberValueConstraint(NumType min, NumType max)
Constructs a constraint where the number must be between min_ and max_.
Definition ConfigConstraints.hpp:357
A constraint class to ensure the provided value is one of the specified values in an array.
Definition ConfigConstraints.hpp:274
constexpr OneOf(std::string_view key, std::array< std::string_view, ArrSize > arr)
Constructs a constraint where the value must be one of the values in the provided array.
Definition ConfigConstraints.hpp:283
A constraint to ensure the port number is within a valid range.
Definition ConfigConstraints.hpp:189
A constraint to ensure a double number is positive.
Definition ConfigConstraints.hpp:411
A constraint to ensure the value is a valid RPC command name.
Definition ConfigConstraints.hpp:449
A constraint to ensure the IP address is valid.
Definition ConfigConstraints.hpp:232
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