Logo
ApraPipes 1.0
Loading...
Searching...
No Matches
PropertyValidators.h
1// ============================================================
2// File: declarative/PropertyValidators.h
3// Task D2: Property Binding System - Validation
4//
5// Provides validators for property values:
6// - RangeValidator: numeric min/max bounds
7// - RegexValidator: string pattern matching
8// - EnumValidator: allowed string values
9// ============================================================
10
11#pragma once
12
13#include "PropertyMacros.h"
14#include <string>
15#include <vector>
16#include <regex>
17#include <memory>
18#include <map>
19#include <functional>
20#include <sstream>
21#include <limits>
22#include <cmath>
23
24namespace apra {
25
26// ============================================================
27// ValidationResult - outcome of validation
28// ============================================================
30 bool valid = true;
31 std::string error;
32 std::string propertyName;
33
34 static ValidationResult ok() { return {true, "", ""}; }
35 static ValidationResult fail(const std::string& prop, const std::string& msg) {
36 return {false, msg, prop};
37 }
38};
39
40// ============================================================
41// PropertyValidator - base class for validators
42// ============================================================
44public:
45 virtual ~PropertyValidator() = default;
46
47 // Validate a property value, return error message if invalid
49 const std::string& propName,
50 const ScalarPropertyValue& value
51 ) const = 0;
52
53 // Human-readable description of the constraint
54 virtual std::string describe() const = 0;
55};
56
57// ============================================================
58// RangeValidator - validates numeric values are within bounds
59// ============================================================
60template<typename T>
66
67public:
68 RangeValidator(T min, T max, bool minInclusive = true, bool maxInclusive = true)
69 : min_(min), max_(max), minInclusive_(minInclusive), maxInclusive_(maxInclusive) {}
70
72 const std::string& propName,
73 const ScalarPropertyValue& value
74 ) const override {
75 T v;
76
77 // Extract value based on variant type
78 if (auto* i = std::get_if<int64_t>(&value)) {
79 v = static_cast<T>(*i);
80 } else if (auto* d = std::get_if<double>(&value)) {
81 v = static_cast<T>(*d);
82 } else {
83 return ValidationResult::fail(propName,
84 "Expected numeric value for range validation");
85 }
86
87 // Check bounds
88 bool minOk = minInclusive_ ? (v >= min_) : (v > min_);
89 bool maxOk = maxInclusive_ ? (v <= max_) : (v < max_);
90
91 if (!minOk || !maxOk) {
92 std::ostringstream oss;
93 oss << "Value " << v << " out of range "
94 << (minInclusive_ ? "[" : "(")
95 << min_ << ", " << max_
96 << (maxInclusive_ ? "]" : ")");
97 return ValidationResult::fail(propName, oss.str());
98 }
99
100 return ValidationResult::ok();
101 }
102
103 std::string describe() const override {
104 std::ostringstream oss;
105 oss << "range" << (minInclusive_ ? "[" : "(")
106 << min_ << "," << max_
107 << (maxInclusive_ ? "]" : ")");
108 return oss.str();
109 }
110};
111
112// Convenience aliases
115
116// ============================================================
117// RegexValidator - validates strings match a pattern
118// ============================================================
120 std::string pattern_;
121 std::regex regex_;
122 std::string description_;
123
124public:
125 explicit RegexValidator(const std::string& pattern,
126 const std::string& description = "")
127 : pattern_(pattern), description_(description) {
128 try {
129 regex_ = std::regex(pattern);
130 } catch (const std::regex_error& e) {
131 throw std::runtime_error("Invalid regex pattern: " + pattern + " - " + e.what());
132 }
133 }
134
136 const std::string& propName,
137 const ScalarPropertyValue& value
138 ) const override {
139 auto* str = std::get_if<std::string>(&value);
140 if (!str) {
141 return ValidationResult::fail(propName,
142 "Expected string value for regex validation");
143 }
144
145 if (!std::regex_match(*str, regex_)) {
146 std::string msg = "String '" + *str + "' does not match pattern";
147 if (!description_.empty()) {
148 msg += " (" + description_ + ")";
149 } else {
150 msg += ": " + pattern_;
151 }
152 return ValidationResult::fail(propName, msg);
153 }
154
155 return ValidationResult::ok();
156 }
157
158 std::string describe() const override {
159 if (!description_.empty()) {
160 return description_;
161 }
162 return "regex(" + pattern_ + ")";
163 }
164};
165
166// Common regex patterns
167namespace patterns {
168 // IPv4 address
169 inline const char* IPv4 = R"(^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$)";
170
171 // Simple email (not RFC-compliant, but practical)
172 inline const char* Email = R"(^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$)";
173
174 // File path (Unix-style)
175 inline const char* UnixPath = R"(^(/[^/\0]+)+/?$|^/$)";
176
177 // URL
178 inline const char* URL = R"(^https?://[^\s/$.?#].[^\s]*$)";
179
180 // Alphanumeric with underscores
181 inline const char* Identifier = R"(^[a-zA-Z_][a-zA-Z0-9_]*$)";
182}
183
184// ============================================================
185// EnumValidator - validates string is one of allowed values
186// ============================================================
188 std::vector<std::string> allowed_;
190
191public:
192 EnumValidator(std::initializer_list<std::string> values, bool caseSensitive = true)
193 : allowed_(values), caseSensitive_(caseSensitive) {}
194
195 EnumValidator(std::vector<std::string> values, bool caseSensitive = true)
196 : allowed_(std::move(values)), caseSensitive_(caseSensitive) {}
197
199 const std::string& propName,
200 const ScalarPropertyValue& value
201 ) const override {
202 auto* str = std::get_if<std::string>(&value);
203 if (!str) {
204 return ValidationResult::fail(propName,
205 "Expected string value for enum validation");
206 }
207
208 std::string input = *str;
209 if (!caseSensitive_) {
210 std::transform(input.begin(), input.end(), input.begin(), ::tolower);
211 }
212
213 for (const auto& allowed : allowed_) {
214 std::string cmp = allowed;
215 if (!caseSensitive_) {
216 std::transform(cmp.begin(), cmp.end(), cmp.begin(), ::tolower);
217 }
218 if (input == cmp) {
219 return ValidationResult::ok();
220 }
221 }
222
223 std::ostringstream oss;
224 oss << "Value '" << *str << "' not in allowed values: {";
225 for (size_t i = 0; i < allowed_.size(); ++i) {
226 if (i > 0) oss << ", ";
227 oss << allowed_[i];
228 }
229 oss << "}";
230 return ValidationResult::fail(propName, oss.str());
231 }
232
233 std::string describe() const override {
234 std::ostringstream oss;
235 oss << "enum{";
236 for (size_t i = 0; i < allowed_.size(); ++i) {
237 if (i > 0) oss << ",";
238 oss << allowed_[i];
239 }
240 oss << "}";
241 return oss.str();
242 }
243
244 const std::vector<std::string>& allowedValues() const { return allowed_; }
245};
246
247// ============================================================
248// CompositeValidator - combines multiple validators (AND logic)
249// ============================================================
251 std::vector<std::shared_ptr<PropertyValidator>> validators_;
252
253public:
254 void add(std::shared_ptr<PropertyValidator> validator) {
255 validators_.push_back(std::move(validator));
256 }
257
259 const std::string& propName,
260 const ScalarPropertyValue& value
261 ) const override {
262 for (const auto& v : validators_) {
263 auto result = v->validate(propName, value);
264 if (!result.valid) {
265 return result;
266 }
267 }
268 return ValidationResult::ok();
269 }
270
271 std::string describe() const override {
272 std::ostringstream oss;
273 for (size_t i = 0; i < validators_.size(); ++i) {
274 if (i > 0) oss << " AND ";
275 oss << validators_[i]->describe();
276 }
277 return oss.str();
278 }
279};
280
281// ============================================================
282// PropertyValidatorRegistry - stores validators per property
283// ============================================================
285 std::map<std::string, std::shared_ptr<PropertyValidator>> validators_;
286
287public:
288 void registerValidator(const std::string& propName,
289 std::shared_ptr<PropertyValidator> validator) {
290 validators_[propName] = std::move(validator);
291 }
292
293 ValidationResult validate(const std::string& propName,
294 const ScalarPropertyValue& value) const {
295 auto it = validators_.find(propName);
296 if (it == validators_.end()) {
297 return ValidationResult::ok(); // No validator = always valid
298 }
299 return it->second->validate(propName, value);
300 }
301
302 bool hasValidator(const std::string& propName) const {
303 return validators_.find(propName) != validators_.end();
304 }
305
306 std::vector<ValidationResult> validateAll(
307 const std::map<std::string, ScalarPropertyValue>& props
308 ) const {
309 std::vector<ValidationResult> results;
310 for (const auto& [name, value] : props) {
311 auto result = validate(name, value);
312 if (!result.valid) {
313 results.push_back(result);
314 }
315 }
316 return results;
317 }
318};
319
320// ============================================================
321// Helper functions to create validators
322// ============================================================
323
324template<typename T>
325inline std::shared_ptr<RangeValidator<T>> makeRangeValidator(T min, T max) {
326 return std::make_shared<RangeValidator<T>>(min, max);
327}
328
329inline std::shared_ptr<RegexValidator> makeRegexValidator(
330 const std::string& pattern,
331 const std::string& description = ""
332) {
333 return std::make_shared<RegexValidator>(pattern, description);
334}
335
336inline std::shared_ptr<EnumValidator> makeEnumValidator(
337 std::initializer_list<std::string> values,
338 bool caseSensitive = true
339) {
340 return std::make_shared<EnumValidator>(values, caseSensitive);
341}
342
343} // namespace apra
Definition PropertyValidators.h:250
void add(std::shared_ptr< PropertyValidator > validator)
Definition PropertyValidators.h:254
std::vector< std::shared_ptr< PropertyValidator > > validators_
Definition PropertyValidators.h:251
std::string describe() const override
Definition PropertyValidators.h:271
ValidationResult validate(const std::string &propName, const ScalarPropertyValue &value) const override
Definition PropertyValidators.h:258
Definition PropertyValidators.h:187
bool caseSensitive_
Definition PropertyValidators.h:189
std::string describe() const override
Definition PropertyValidators.h:233
EnumValidator(std::initializer_list< std::string > values, bool caseSensitive=true)
Definition PropertyValidators.h:192
EnumValidator(std::vector< std::string > values, bool caseSensitive=true)
Definition PropertyValidators.h:195
const std::vector< std::string > & allowedValues() const
Definition PropertyValidators.h:244
std::vector< std::string > allowed_
Definition PropertyValidators.h:188
ValidationResult validate(const std::string &propName, const ScalarPropertyValue &value) const override
Definition PropertyValidators.h:198
Definition PropertyValidators.h:284
std::vector< ValidationResult > validateAll(const std::map< std::string, ScalarPropertyValue > &props) const
Definition PropertyValidators.h:306
void registerValidator(const std::string &propName, std::shared_ptr< PropertyValidator > validator)
Definition PropertyValidators.h:288
std::map< std::string, std::shared_ptr< PropertyValidator > > validators_
Definition PropertyValidators.h:285
bool hasValidator(const std::string &propName) const
Definition PropertyValidators.h:302
ValidationResult validate(const std::string &propName, const ScalarPropertyValue &value) const
Definition PropertyValidators.h:293
Definition PropertyValidators.h:43
virtual std::string describe() const =0
virtual ValidationResult validate(const std::string &propName, const ScalarPropertyValue &value) const =0
virtual ~PropertyValidator()=default
Definition PropertyValidators.h:61
bool maxInclusive_
Definition PropertyValidators.h:65
T min_
Definition PropertyValidators.h:62
bool minInclusive_
Definition PropertyValidators.h:64
std::string describe() const override
Definition PropertyValidators.h:103
RangeValidator(T min, T max, bool minInclusive=true, bool maxInclusive=true)
Definition PropertyValidators.h:68
T max_
Definition PropertyValidators.h:63
ValidationResult validate(const std::string &propName, const ScalarPropertyValue &value) const override
Definition PropertyValidators.h:71
Definition PropertyValidators.h:119
std::string description_
Definition PropertyValidators.h:122
std::string describe() const override
Definition PropertyValidators.h:158
RegexValidator(const std::string &pattern, const std::string &description="")
Definition PropertyValidators.h:125
std::string pattern_
Definition PropertyValidators.h:120
ValidationResult validate(const std::string &propName, const ScalarPropertyValue &value) const override
Definition PropertyValidators.h:135
std::regex regex_
Definition PropertyValidators.h:121
const char * UnixPath
Definition PropertyValidators.h:175
const char * Email
Definition PropertyValidators.h:172
const char * URL
Definition PropertyValidators.h:178
const char * IPv4
Definition PropertyValidators.h:169
const char * Identifier
Definition PropertyValidators.h:181
Definition FrameTypeRegistrations.h:10
std::shared_ptr< RangeValidator< T > > makeRangeValidator(T min, T max)
Definition PropertyValidators.h:325
std::shared_ptr< EnumValidator > makeEnumValidator(std::initializer_list< std::string > values, bool caseSensitive=true)
Definition PropertyValidators.h:336
std::shared_ptr< RegexValidator > makeRegexValidator(const std::string &pattern, const std::string &description="")
Definition PropertyValidators.h:329
std::variant< int64_t, double, bool, std::string > ScalarPropertyValue
Definition ModuleRegistry.h:30
Definition PropertyValidators.h:29
std::string propertyName
Definition PropertyValidators.h:32
std::string error
Definition PropertyValidators.h:31
bool valid
Definition PropertyValidators.h:30
static ValidationResult ok()
Definition PropertyValidators.h:34
static ValidationResult fail(const std::string &prop, const std::string &msg)
Definition PropertyValidators.h:35