Crunch
A Message Definition Language for Getting Things Right
Loading...
Searching...
No Matches
crunch_validators.hpp
1#pragma once
2
3#include <cmath>
4#include <concepts>
5#include <crunch/core/crunch_types.hpp>
6#include <optional>
7
8namespace Crunch {
9
16template <typename V, typename T>
17concept Validator = requires(T value, FieldId field_id) {
18 { V::Check(value, field_id) } -> std::same_as<std::optional<Error>>;
19};
20
24struct None {
25 template <typename T>
26 [[nodiscard]] static constexpr auto Check(T, FieldId) noexcept
27 -> std::optional<Error> {
28 return std::nullopt;
29 }
30};
31
35struct Required {
36 [[nodiscard]] static constexpr std::optional<Error> check_presence(
37 bool set, FieldId id) noexcept {
38 if (!set) {
39 return Error::validation(id, "field is required but not set");
40 }
41 return std::nullopt;
42 }
43};
44
48struct Optional {
49 [[nodiscard]] static constexpr std::optional<Error> check_presence(
50 bool, FieldId) noexcept {
51 return std::nullopt;
52 }
53};
54
58struct True {
59 template <typename T>
60 requires std::same_as<T, bool>
61 [[nodiscard]] static constexpr auto Check(T value,
62 FieldId field_id) noexcept
63 -> std::optional<Error> {
64 if (value) {
65 return std::nullopt;
66 }
67 return Error::validation(field_id, "must be true");
68 }
69};
70
74struct False {
75 template <typename T>
76 requires std::same_as<T, bool>
77 [[nodiscard]] static constexpr auto Check(T value,
78 FieldId field_id) noexcept
79 -> std::optional<Error> {
80 if (!value) {
81 return std::nullopt;
82 }
83 return Error::validation(field_id, "must be false");
84 }
85};
86
90struct IsFinite {
91 template <typename T>
92 requires std::floating_point<T>
93 [[nodiscard]] static constexpr auto Check(T value,
94 FieldId field_id) noexcept
95 -> std::optional<Error> {
96 if (std::isfinite(value)) {
97 return std::nullopt;
98 }
99 return Error::validation(field_id, "must be finite");
100 }
101};
102
109template <auto Target, auto Tolerance>
110struct Around {
111 template <typename T>
112 requires(std::floating_point<T> || std::integral<T>) &&
113 (!std::is_same_v<T, bool>)
114 [[nodiscard]] static constexpr auto Check(T value,
115 FieldId field_id) noexcept
116 -> std::optional<Error> {
117 if (std::abs(value - Target) <= Tolerance) {
118 return std::nullopt;
119 }
120 return Error::validation(field_id, "must be around target");
121 }
122};
123
127template <typename T>
129 std::same_as<T, Required> || std::same_as<T, Optional>;
130
132struct Positive {
133 template <typename T>
134 requires(std::signed_integral<T> || std::floating_point<T>) &&
135 (!std::is_same_v<T, bool>)
136 [[nodiscard]] static constexpr auto Check(T value,
137 FieldId field_id) noexcept
138 -> std::optional<Error> {
139 if (value >= 0) {
140 return std::nullopt;
141 }
142 return Error::validation(field_id, "must be >= 0");
143 }
144};
145
147struct Negative {
148 template <typename T>
149 requires(std::signed_integral<T> || std::floating_point<T>) &&
150 (!std::is_same_v<T, bool>)
151 [[nodiscard]] static constexpr auto Check(T value,
152 FieldId field_id) noexcept
153 -> std::optional<Error> {
154 if (value < 0) {
155 return std::nullopt;
156 }
157 return Error::validation(field_id, "must be < 0");
158 }
159};
160
162struct NotZero {
163 template <typename T>
164 requires(std::floating_point<T> || std::integral<T>) &&
165 (!std::is_same_v<T, bool>)
166 [[nodiscard]] static constexpr auto Check(T value,
167 FieldId field_id) noexcept
168 -> std::optional<Error> {
169 if (value != 0) {
170 return std::nullopt;
171 }
172 return Error::validation(field_id, "must be != 0");
173 }
174};
175
177struct Even {
178 template <typename T>
179 requires std::integral<T> && (!std::is_same_v<T, bool>)
180 [[nodiscard]] static constexpr auto Check(T value,
181 FieldId field_id) noexcept
182 -> std::optional<Error> {
183 if (value % 2 == 0) {
184 return std::nullopt;
185 }
186 return Error::validation(field_id, "must be even");
187 }
188};
189
191struct Odd {
192 template <typename T>
193 requires std::integral<T> && (!std::is_same_v<T, bool>)
194 [[nodiscard]] static constexpr auto Check(T value,
195 FieldId field_id) noexcept
196 -> std::optional<Error> {
197 if (value % 2 != 0) {
198 return std::nullopt;
199 }
200 return Error::validation(field_id, "must be odd");
201 }
202};
203
205template <auto Threshold>
206struct LessThan {
207 template <typename T>
208 requires(std::floating_point<T> || std::integral<T>) &&
209 (!std::is_same_v<T, bool>)
210 [[nodiscard]] static constexpr auto Check(T value,
211 FieldId field_id) noexcept
212 -> std::optional<Error> {
213 if (value < Threshold) {
214 return std::nullopt;
215 }
216 return Error::validation(field_id, "must be < threshold");
217 }
218};
219
221template <auto Threshold>
223 template <typename T>
224 requires(std::floating_point<T> || std::integral<T>) &&
225 (!std::is_same_v<T, bool>)
226 [[nodiscard]] static constexpr auto Check(T value,
227 FieldId field_id) noexcept
228 -> std::optional<Error> {
229 if (value > Threshold) {
230 return std::nullopt;
231 }
232 return Error::validation(field_id, "must be > threshold");
233 }
234};
235
238template <auto Threshold>
240 template <typename T>
241 requires(std::floating_point<T> || std::integral<T>) &&
242 (!std::is_same_v<T, bool>)
243 [[nodiscard]] static constexpr auto Check(T value,
244 FieldId field_id) noexcept
245 -> std::optional<Error> {
246 if (value <= Threshold) {
247 return std::nullopt;
248 }
249 return Error::validation(field_id, "must be <= threshold");
250 }
251};
252
255template <auto Threshold>
257 template <typename T>
258 requires(std::floating_point<T> || std::integral<T>) &&
259 (!std::is_same_v<T, bool>)
260 [[nodiscard]] static constexpr auto Check(T value,
261 FieldId field_id) noexcept
262 -> std::optional<Error> {
263 if (value >= Threshold) {
264 return std::nullopt;
265 }
266 return Error::validation(field_id, "must be >= threshold");
267 }
268};
269
271template <auto Threshold>
272struct EqualTo {
273 template <typename T>
274 requires(std::floating_point<T> || std::integral<T> ||
275 std::is_enum_v<T>) &&
276 (!std::is_same_v<T, bool>)
277 [[nodiscard]] static constexpr auto Check(T value,
278 FieldId field_id) noexcept
279 -> std::optional<Error> {
280 if (value == Threshold) {
281 return std::nullopt;
282 }
283 return Error::validation(field_id, "must equal threshold");
284 }
285};
286
288template <auto Threshold>
290 template <typename T>
291 requires(std::floating_point<T> || std::integral<T> ||
292 std::is_enum_v<T>) &&
293 (!std::is_same_v<T, bool>)
294 [[nodiscard]] static constexpr auto Check(T value,
295 FieldId field_id) noexcept
296 -> std::optional<Error> {
297 if (value != Threshold) {
298 return std::nullopt;
299 }
300 return Error::validation(field_id, "must not equal threshold");
301 }
302};
303
305template <auto... Values>
306struct OneOf {
307 template <typename T>
308 requires(std::floating_point<T> || std::integral<T> ||
309 std::is_enum_v<T>) &&
310 (!std::is_same_v<T, bool>)
311 [[nodiscard]] static constexpr auto Check(T value,
312 FieldId field_id) noexcept
313 -> std::optional<Error> {
314 if (((value == Values) || ...)) {
315 return std::nullopt;
316 }
317 return Error::validation(field_id, "must be one of allowed values");
318 }
319};
320
323template <std::size_t N>
324struct Length {
325 template <typename T>
326 requires requires(const T& t) { std::size(t); }
327 [[nodiscard]] static constexpr auto Check(const T& value,
328 FieldId field_id) noexcept
329 -> std::optional<Error> {
330 if (std::size(value) == N) {
331 return std::nullopt;
332 }
333 return Error::validation(field_id, "length mismatch");
334 }
335};
336
338template <std::size_t N>
340 template <typename T>
341 requires requires(const T& t) { std::size(t); }
342 [[nodiscard]] static constexpr auto Check(const T& value,
343 FieldId field_id) noexcept
344 -> std::optional<Error> {
345 if (std::size(value) >= N) {
346 return std::nullopt;
347 }
348 return Error::validation(field_id, "length must be at least N");
349 }
350};
351
353template <std::size_t N>
355 template <typename T>
356 requires requires(const T& t) { std::size(t); }
357 [[nodiscard]] static constexpr auto Check(const T& value,
358 FieldId field_id) noexcept
359 -> std::optional<Error> {
360 if (std::size(value) <= N) {
361 return std::nullopt;
362 }
363 return Error::validation(field_id, "length must be at most N");
364 }
365};
366
368struct Unique {
369 template <typename T>
370 requires requires(const T& t) {
371 { t.begin() } -> std::forward_iterator;
372 { t.end() } -> std::forward_iterator;
373 }
374 [[nodiscard]] static constexpr auto Check(const T& value,
375 FieldId field_id) noexcept
376 -> std::optional<Error> {
377 // Simple O(N^2) check to avoid sorting/allocating
378 auto end = value.end();
379 for (auto it = value.begin(); it != end; ++it) {
380 for (auto it2 = std::next(it); it2 != end; ++it2) {
381 if (*it == *it2) {
382 return Error::validation(field_id,
383 "elements must be unique");
384 }
385 }
386 }
387 return std::nullopt;
388 }
389};
390
393 template <typename T>
394 requires std::convertible_to<T, std::string_view>
395 [[nodiscard]] static constexpr auto Check(T value,
396 FieldId field_id) noexcept
397 -> std::optional<Error> {
398 std::string_view sv{value};
399 if (!sv.empty() && sv.back() == '\0') {
400 return std::nullopt;
401 }
402 return Error::validation(field_id, "must count null terminator");
403 }
404};
405
406/*
407 * @brief Helper for string literal NTTP.
408 * @note I though that strings could be used as NTTPs but
409 * it seems we need this wrapper to make it work.
410 */
411template <std::size_t N>
413 char buf[N]{};
414 constexpr explicit FixedString(const char (&str)[N]) {
415 for (std::size_t i = 0; i < N; ++i) {
416 buf[i] = str[i];
417 }
418 }
419 constexpr std::string_view view() const { return {buf, N - 1}; }
420};
421
423template <FixedString S>
425 template <typename T>
426 requires std::convertible_to<T, std::string_view>
427 [[nodiscard]] static constexpr auto Check(T value,
428 FieldId field_id) noexcept
429 -> std::optional<Error> {
430 if (std::string_view{value} == S.view()) {
431 return std::nullopt;
432 }
433 return Error::validation(field_id, "must equal expected string");
434 }
435};
436
438template <FixedString S>
440 template <typename T>
441 requires std::convertible_to<T, std::string_view>
442 [[nodiscard]] static constexpr auto Check(T value,
443 FieldId field_id) noexcept
444 -> std::optional<Error> {
445 if (std::string_view{value} != S.view()) {
446 return std::nullopt;
447 }
448 return Error::validation(field_id, "must not equal forbidden string");
449 }
450};
451
452} // namespace Crunch
Concept for validators that check field presence semantics.
Definition: crunch_validators.hpp:128
Concept defining a value validator.
Definition: crunch_validators.hpp:17
The public API for Crunch.
Definition: crunch_endian.hpp:10
int32_t FieldId
Unique identifier for a field within a Crunch message.
Definition: crunch_types.hpp:11
Validates that a floating-point value is within a tolerance of a target.
Definition: crunch_validators.hpp:110
Validates that a value equals a compile-time threshold.
Definition: crunch_validators.hpp:272
static constexpr Error validation(FieldId id, const char(&msg)[N]) noexcept
Creates an error representing a validation failure.
Definition: crunch_types.hpp:97
Validates that an integral value is even.
Definition: crunch_validators.hpp:177
Validates that a boolean value is false.
Definition: crunch_validators.hpp:74
Definition: crunch_validators.hpp:412
Validates that a value is greater than or equal to a compile-time threshold.
Definition: crunch_validators.hpp:256
Validates that a value is greater than a compile-time threshold.
Definition: crunch_validators.hpp:222
Validates that a floating-point value is finite (not NaN or Inf).
Definition: crunch_validators.hpp:90
Validates that a container has at least N elements.
Definition: crunch_validators.hpp:339
Validates that a container has at most N elements.
Definition: crunch_validators.hpp:354
Validates that a string-like or container value has a specific length.
Definition: crunch_validators.hpp:324
Validates that a value is less than or equal to a compile-time threshold.
Definition: crunch_validators.hpp:239
Validates that a value is less than a compile-time threshold.
Definition: crunch_validators.hpp:206
Validates that a value is strictly negative (< 0).
Definition: crunch_validators.hpp:147
Validates nothing (always succeeds).
Definition: crunch_validators.hpp:24
Validates that a value does not equal a compile-time threshold.
Definition: crunch_validators.hpp:289
Validates that a value is not zero.
Definition: crunch_validators.hpp:162
Validates that a string does not contain embedded nulls.
Definition: crunch_validators.hpp:392
Validates that an integral value is odd.
Definition: crunch_validators.hpp:191
Validates that a value is one of a set of compile-time values.
Definition: crunch_validators.hpp:306
Presence validator allowing an optional field (can be unset).
Definition: crunch_validators.hpp:48
Validates that a value is non-negative (>= 0).
Definition: crunch_validators.hpp:132
Presence validator enforcing that a field MUST be set.
Definition: crunch_validators.hpp:35
Validates that a string equals a compile-time string.
Definition: crunch_validators.hpp:424
Validates that a string does not equal a compile-time string.
Definition: crunch_validators.hpp:439
Validates that a boolean value is true.
Definition: crunch_validators.hpp:58
Validates that a container has unique elements.
Definition: crunch_validators.hpp:368