config/fields/checks.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /// @file | ||
| 2 | /// @brief JSON paths and value checks that report configuration errors at a path. | ||
| 3 | #pragma once | ||
| 4 | |||
| 5 | #include "terraingen/config/json.h" | ||
| 6 | #include "terraingen/util/checks.h" | ||
| 7 | |||
| 8 | #include <cmath> | ||
| 9 | #include <cstddef> | ||
| 10 | #include <stdexcept> | ||
| 11 | #include <string> | ||
| 12 | #include <string_view> | ||
| 13 | |||
| 14 | namespace terraingen::config { | ||
| 15 | |||
| 16 | /// The JSON path `path.key` of an object's member, or just `key` when `path` is empty. | ||
| 17 | 28064 | inline std::string Child(const std::string& path, std::string_view key) { | |
| 18 | 84192 | return path.empty() ? std::string(key) : path + "." + std::string(key); | |
| 19 | } | ||
| 20 | |||
| 21 | /// The JSON path `path[index]` of an array's element. | ||
| 22 | 3795 | inline std::string Element(const std::string& path, std::size_t index) { | |
| 23 | 3795 | return path + "[" + std::to_string(index) + "]"; | |
| 24 | } | ||
| 25 | |||
| 26 | /// Checks that a configuration value is positive and finite. | ||
| 27 | /// @throws ConfigError "must be positive" at `path` otherwise. | ||
| 28 | 731 | inline void RequirePositive(double value, const std::string& path) { | |
| 29 | 731 | if (!IsPositive(value)) { | |
| 30 | 33 | throw ConfigError(path, "must be positive"); | |
| 31 | } | ||
| 32 | 720 | } | |
| 33 | |||
| 34 | /// Checks that a configuration value is finite and not negative. | ||
| 35 | /// @throws ConfigError "must not be negative" at `path` otherwise. | ||
| 36 | 103 | inline void RequireNonNegative(double value, const std::string& path) { | |
| 37 | 103 | if (!IsNonNegative(value)) { | |
| 38 | 9 | throw ConfigError(path, "must not be negative"); | |
| 39 | } | ||
| 40 | 100 | } | |
| 41 | |||
| 42 | /// Checks that a configuration value is finite. | ||
| 43 | /// @throws ConfigError "must be finite" at `path` for an infinite or NaN `value`. | ||
| 44 | 6 | inline void RequireFinite(double value, const std::string& path) { | |
| 45 | 6 | if (!std::isfinite(value)) { | |
| 46 | 9 | throw ConfigError(path, "must be finite"); | |
| 47 | } | ||
| 48 | 3 | } | |
| 49 | |||
| 50 | /// Checks that a configuration string, such as a file path, is not empty. | ||
| 51 | /// @throws ConfigError "must not be empty" at `path` otherwise. | ||
| 52 | 522 | inline void RequireNonEmpty(const std::string& value, const std::string& path) { | |
| 53 | 522 | if (value.empty()) { | |
| 54 | 27 | throw ConfigError(path, "must not be empty"); | |
| 55 | } | ||
| 56 | 513 | } | |
| 57 | |||
| 58 | /// Runs a library check or build step `check()` and returns its result, reporting its errors at | ||
| 59 | /// the configuration path `path`. | ||
| 60 | /// @throws ConfigError at `path` with the message of a std::invalid_argument or std::domain_error | ||
| 61 | /// that `check` throws. | ||
| 62 | 3510 | template <typename Check> auto At(const std::string& path, Check check) { | |
| 63 | try { | ||
| 64 | 5137 | return check(); | |
| 65 | 107 | } catch (const std::invalid_argument& error) { | |
| 66 | 150 | throw ConfigError(path, error.what()); | |
| 67 | 14 | } catch (const std::domain_error& error) { | |
| 68 | 21 | throw ConfigError(path, error.what()); | |
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | } // namespace terraingen::config | ||
| 73 |