cutelyst 4.3.0
A C++ Web Framework built on top of Qt, using the simple approach of Catalyst (Perl) framework.
validatorfilesize.cpp
1/*
2 * SPDX-FileCopyrightText: (C) 2018-2023 Matthias Fehring <mf@huessenbergnetz.de>
3 * SPDX-License-Identifier: BSD-3-Clause
4 */
5
6#include "validatorfilesize_p.h"
7
8#include <cmath>
9#include <limits>
10
11using namespace Cutelyst;
12
14 Option option,
15 const QVariant &min,
16 const QVariant &max,
17 const ValidatorMessages &messages,
18 const QString &defValKey)
19 : ValidatorRule(*new ValidatorFileSizePrivate(field, option, min, max, messages, defValKey))
20{
21}
22
24
26 double min,
27 double max,
29 const QLocale &locale,
30 double *fileSize)
31{
32 bool valid = true;
33
34 const QString str = value.simplified();
35 QString digitPart;
36 QString symbolPart;
37 bool decimalPointFound = false;
38 const QString decimalPoint(locale.decimalPoint());
39 int multiplier = 0;
40 bool binary = false;
41 bool byteSignFound = false;
42 ValidatorFileSizePrivate::StartsWith startsWith{ValidatorFileSizePrivate::StartsWith::NotSet};
43
44 for (const QChar &ch : str) {
45 if (valid) {
46 const char16_t uc = ch.toUpper().unicode();
47 if (((uc >= ValidatorRulePrivate::ascii_0) && (uc <= ValidatorRulePrivate::ascii_9)) ||
48 (ch == decimalPoint)) {
49 if (startsWith == ValidatorFileSizePrivate::StartsWith::NotSet) {
50 startsWith = ValidatorFileSizePrivate::StartsWith::DigitPart;
51 }
52 if (ch == decimalPoint) {
53 if (decimalPointFound) {
54 qCDebug(C_VALIDATOR).nospace()
55 << "ValidatorFileSize: Validation failed for " << value << ": "
56 << "two decimal seperators in a row";
57 valid = false;
58 break;
59 } else {
60 decimalPointFound = true;
61 }
62 }
63 if ((symbolPart.isEmpty() &&
64 (startsWith == ValidatorFileSizePrivate::StartsWith::DigitPart)) ||
65 (!symbolPart.isEmpty() &&
66 (startsWith == ValidatorFileSizePrivate::StartsWith::SymbolPart))) {
67 digitPart.append(ch);
68 } else {
69 qCDebug(C_VALIDATOR).nospace() << "ValidatorFileSize: Validation failed for "
70 << value << ": " << "symbol inside digit part";
71 valid = false;
72 break;
73 }
74 } else if ((uc != ValidatorRulePrivate::asciiTab) &&
75 (uc != ValidatorRulePrivate::asciiSpace)) { // not a digit or decimal point
76 // and not a space or tab
77 if (startsWith == ValidatorFileSizePrivate::StartsWith::NotSet) {
78 startsWith = ValidatorFileSizePrivate::StartsWith::SymbolPart;
79 }
80 if ((digitPart.isEmpty() &&
81 (startsWith == ValidatorFileSizePrivate::StartsWith::SymbolPart)) ||
82 (!digitPart.isEmpty() &&
83 (startsWith == ValidatorFileSizePrivate::StartsWith::DigitPart))) {
84 switch (uc) {
85 case ValidatorFileSizePrivate::ascii_K:
86 {
87 if (multiplier > 0) {
88 valid = false;
89 qCDebug(C_VALIDATOR).nospace()
90 << "ValdatorFileSize: Validation failed for " << value << ": "
91 << "unit symbol K already found";
92 } else {
93 multiplier = 1;
94 symbolPart.append(ch);
95 }
96 } break;
97 case ValidatorFileSizePrivate::ascii_M:
98 {
99 if (multiplier > 0) {
100 valid = false;
101 qCDebug(C_VALIDATOR).nospace()
102 << "ValdatorFileSize: Validation failed for " << value << ": "
103 << "unit symbol M already found";
104 } else {
105 multiplier = 2;
106 symbolPart.append(ch);
107 }
108 } break;
109 case ValidatorFileSizePrivate::ascii_G:
110 {
111 if (multiplier > 0) {
112 valid = false;
113 qCDebug(C_VALIDATOR).nospace()
114 << "ValdatorFileSize: Validation failed for " << value << ": "
115 << "unit symbol G already found";
116 } else {
117 multiplier = 3;
118 symbolPart.append(ch);
119 }
120 } break;
121 case ValidatorFileSizePrivate::ascii_T:
122 {
123 if (multiplier > 0) {
124 valid = false;
125 qCDebug(C_VALIDATOR).nospace()
126 << "ValdatorFileSize: Validation failed for " << value << ": "
127 << "unit symbol T already found";
128 } else {
129 multiplier = 4;
130 symbolPart.append(ch);
131 }
132 } break;
133 case ValidatorFileSizePrivate::ascii_P:
134 {
135 if (multiplier > 0) {
136 valid = false;
137 qCDebug(C_VALIDATOR).nospace()
138 << "ValdatorFileSize: Validation failed for " << value << ": "
139 << "unit symbol P already found";
140 } else {
141 multiplier = 5;
142 symbolPart.append(ch);
143 }
144 } break;
145 case ValidatorFileSizePrivate::ascii_E:
146 {
147 if (multiplier > 0) {
148 valid = false;
149 qCDebug(C_VALIDATOR).nospace()
150 << "ValdatorFileSize: Validation failed for " << value << ": "
151 << "unit symbol E already found";
152 } else {
153 multiplier = 6;
154 symbolPart.append(ch);
155 }
156 } break;
157 case ValidatorRulePrivate::ascii_Z:
158 {
159 if (multiplier > 0) {
160 valid = false;
161 qCDebug(C_VALIDATOR).nospace()
162 << "ValdatorFileSize: Validation failed for " << value << ": "
163 << "unit symbol Z already found";
164 } else {
165 multiplier = 7;
166 symbolPart.append(ch);
167 }
168 } break;
169 case ValidatorFileSizePrivate::ascii_Y:
170 {
171 if (multiplier > 0) {
172 valid = false;
173 qCDebug(C_VALIDATOR).nospace()
174 << "ValdatorFileSize: Validation failed for " << value << ": "
175 << "unit symbol Y already found";
176 } else {
177 multiplier = 8;
178 symbolPart.append(ch);
179 }
180 } break;
181 case ValidatorFileSizePrivate::ascii_I:
182 {
183 if ((multiplier == 0) || binary) {
184 valid = false;
185 qCDebug(C_VALIDATOR).nospace()
186 << "ValdatorFileSize: Validation failed for " << value << ": "
187 << "binary indicator I already found or no unit symbol given "
188 "before";
189 } else {
190 binary = true;
191 symbolPart.append(ch);
192 }
193 } break;
194 case ValidatorFileSizePrivate::ascii_B:
195 {
196 if (byteSignFound) {
197 valid = false;
198 qCDebug(C_VALIDATOR).nospace()
199 << "ValdatorFileSize: Validation failed for " << value << ": "
200 << "byte symbol B already found";
201 } else {
202 byteSignFound = true;
203 symbolPart.append(ch);
204 }
205 } break;
206 case ValidatorRulePrivate::asciiTab:
207 case ValidatorRulePrivate::asciiSpace:
208 break;
209 default:
210 valid = false;
211 qCDebug(C_VALIDATOR).nospace()
212 << "ValdatorFileSize: Validation failed for " << value << ": "
213 << "invalid character in symbol part";
214 break;
215 }
216 } else {
217 valid = false;
218 break;
219 }
220 }
221 } else {
222 break;
223 }
224 }
225
226 if ((option == OnlyBinary) && !binary) {
227 valid = false;
228 } else if ((option == OnlyDecimal) && binary) {
229 valid = false;
230 } else if (option == ForceBinary) {
231 binary = true;
232 } else if (option == ForceDecimal) {
233 binary = false;
234 }
235
236 if (valid) {
237 bool ok = false;
238 double size = locale.toDouble(digitPart, &ok);
239 if (!ok) {
240 valid = false;
241 } else {
242 if (multiplier > 0) {
243 const double _mult =
244 binary ? std::exp2(multiplier * 10) : std::pow(10.0, multiplier * 3);
245 size *= _mult;
246 }
247 if ((min >= 1.0) && (size < min)) {
248 valid = false;
249 }
250 if ((max >= 1.0) && (size > max)) {
251 valid = false;
252 }
253 if (valid && fileSize) {
254 *fileSize = size;
255 }
256 }
257 }
258
259 return valid;
260}
261
263{
264 ValidatorReturnType result;
265
266 Q_D(const ValidatorFileSize);
267
268 const QString v = value(params);
269
270 if (!v.isEmpty()) {
271
272 double min = -1;
273 double max = -1;
274 bool ok = true;
275 if (d->min.isValid()) {
276 min = d->extractDouble(c, params, d->min, &ok);
277 if (!ok) {
278 qCWarning(C_VALIDATOR).noquote()
279 << debugString(c) << "Invalid minimum size comparison data";
281 c, static_cast<int>(ValidatorRulePrivate::ErrorType::InvalidMin));
282 }
283 }
284
285 if (ok && d->max.isValid()) {
286 max = d->extractDouble(c, params, d->max, &ok);
287 if (!ok) {
288 qCWarning(C_VALIDATOR).noquote()
289 << debugString(c) << "Invalid maximum size comparison data";
291 c, static_cast<int>(ValidatorRulePrivate::ErrorType::InvalidMax));
292 }
293 }
294
295 if (ok) {
296 double size = 0;
297 if (ValidatorFileSize::validate(v, min, max, d->option, c->locale(), &size)) {
298 if (size < static_cast<double>(std::numeric_limits<qulonglong>::max())) {
299 result.value.setValue<qulonglong>(static_cast<qulonglong>(size + 0.5));
300 } else {
301 result.value.setValue(size);
302 }
303 } else {
304 result.errorMessage = validationError(c);
305 qCWarning(C_VALIDATOR).noquote()
306 << debugString(c) << v << "is not a valid data size string";
307 }
308 }
309
310 } else {
311 defaultValue(c, &result);
312 }
313
314 return result;
315}
316
318{
319 Q_D(const ValidatorFileSize);
320 Q_UNUSED(errorData)
321 const QString _label = label(c);
322 if (d->min.isValid() || d->max.isValid()) {
323 if (_label.isEmpty()) {
324 //% "Invalid file size or file size not within the allowed limits."
325 return c->qtTrId("cutelyst-valfilesize-genvalerr-minmax");
326 } else {
327 //% "The value in the “%1” field is either not a valid file size or "
328 //% "not within the allowed limits."
329 return c->qtTrId("cutelyst-valfilesize-genvalerr-minmax-label").arg(_label);
330 }
331 } else {
332 if (_label.isEmpty()) {
333 //% "Invalid file size."
334 return c->qtTrId("cutelyst-valfilesize-genvalerr");
335 } else {
336 //% "The “%1” field does not contain a valid file size."
337 return c->qtTrId("cutelyst-valfilesize-genvalerr-label").arg(_label);
338 }
339 }
340}
341
343{
344 const QString _label = label(c);
345
346 const auto errorType = static_cast<ValidatorRulePrivate::ErrorType>(errorData.toInt());
347
348 if (_label.isEmpty()) {
349 switch (errorType) {
350 case ValidatorRulePrivate::ErrorType::InvalidMin:
351 //% "The minimum file size comparison value is not valid."
352 return c->qtTrId("cutelyst-valfilesize-genvaldataerr-min");
353 case ValidatorRulePrivate::ErrorType::InvalidMax:
354 //% "The maximum file size comparison value is not valid."
355 return c->qtTrId("cutelyst-valfilesize-genvaldataerr-max");
356 case ValidatorRulePrivate::ErrorType::InvalidType:
357 // NOLINTNEXTLINE(cppcoreguidelines-avoid-do-while)
358 Q_UNREACHABLE();
359 return {};
360 }
361 } else {
362 switch (errorType) {
363 case ValidatorRulePrivate::ErrorType::InvalidMin:
364 //% "The minimum file size comparison value for the “%1” field is not valid."
365 return c->qtTrId("cutelyst-valfilesize-genvaldataerr-min-label").arg(_label);
366 case ValidatorRulePrivate::ErrorType::InvalidMax:
367 //% "The maximum file size comparison value for the “%1” field is not valid."
368 return c->qtTrId("cutelyst-valfilesize-genvaldataerr-max-label").arg(_label);
369 case ValidatorRulePrivate::ErrorType::InvalidType:
370 // NOLINTNEXTLINE(cppcoreguidelines-avoid-do-while)
371 Q_UNREACHABLE();
372 return {};
373 }
374 }
375
376#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
377 // NOLINTNEXTLINE(cppcoreguidelines-avoid-do-while)
378 Q_UNREACHABLE_RETURN({});
379#else
380 return {};
381#endif
382}
383
385{
386 Q_ASSERT(c);
387 const QString pattern = c->locale().textDirection() == Qt::LeftToRight
388 ? QStringLiteral("^\\d+[%1]?\\d*\\s*[KkMmGgTt]?[Ii]?[Bb]?")
389 .arg(c->locale().decimalPoint())
390 : QStringLiteral("[KkMmGgTt]?[Ii]?[Bb]?\\s*\\d+[%1]?\\d*")
391 .arg(c->locale().decimalPoint());
392 c->setStash(stashKey, pattern);
393}
The Cutelyst Context.
Definition context.h:42
QLocale locale() const noexcept
Definition context.cpp:460
void setStash(const QString &key, const QVariant &value)
Definition context.cpp:212
QString qtTrId(const char *id, int n=-1) const
Definition context.h:656
Checks if the input field contains a valid file size string like 1.5 GB.
~ValidatorFileSize() override
Deconstructs the file size validator.
QString genericValidationDataError(Context *c, const QVariant &errorData) const override
Option
Options for ValidatorFileSize.
QString genericValidationError(Context *c, const QVariant &errorData=QVariant()) const override
static void inputPattern(Context *c, const QString &stashKey=QStringLiteral("fileSizePattern"))
ValidatorFileSize(const QString &field, Option option=NoOption, const QVariant &min=QVariant(), const QVariant &max=QVariant(), const ValidatorMessages &messages=ValidatorMessages(), const QString &defValKey=QString())
Base class for all validator rules.
QString validationError(Context *c, const QVariant &errorData={}) const
QString label(Context *c) const
QString validationDataError(Context *c, const QVariant &errorData={}) const
void defaultValue(Context *c, ValidatorReturnType *result) const
QString value(const ParamsMultiMap &params) const
QString debugString(Context *c) const
static bool validate(const QString &value, double min=-1, double max=-1, Option option=NoOption, const QLocale &locale=QLocale(), double *fileSize=nullptr)
Returns true if value is a valid file size string.
The Cutelyst namespace holds all public Cutelyst API.
QString decimalPoint() const const
Qt::LayoutDirection textDirection() const const
double toDouble(QStringView s, bool *ok) const const
QString & append(QChar ch)
QString arg(Args &&... args) const const
bool isEmpty() const const
QString simplified() const const
LeftToRight
void setValue(QVariant &&value)
int toInt(bool *ok) const const
Stores custom error messages and the input field label.
Contains the result of a single input parameter validation.