cutelyst  4.4.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 
11 using 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.
Definition: validatorrule.h:49