Branch data Line data Source code
1 : : #pragma once
2 : :
3 : : /*
4 : : MIT License
5 : :
6 : : Copyright (c) 2014-2024 Stephane Cuillerdier (aka aiekick)
7 : :
8 : : Permission is hereby granted, free of charge, to any person obtaining a copy
9 : : of this software and associated documentation files (the "Software"), to deal
10 : : in the Software without restriction, including without limitation the rights
11 : : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 : : copies of the Software, and to permit persons to whom the Software is
13 : : furnished to do so, subject to the following conditions:
14 : :
15 : : The above copyright notice and this permission notice shall be included in all
16 : : copies or substantial portions of the Software.
17 : :
18 : : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 : : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 : : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 : : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 : : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 : : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 : : SOFTWARE.
25 : : */
26 : :
27 : : // ezCron is part of the ezLibs project : https://github.com/aiekick/ezLibs.git
28 : :
29 : : #include <set>
30 : : #include <ctime>
31 : : #include <array>
32 : : #include <string>
33 : : #include <vector>
34 : : #include <cstdint>
35 : : #include <sstream>
36 : : #include <iterator>
37 : : #include <iostream>
38 : :
39 : : ////////////////////////////////////////////////////////////////////////////
40 : : ////////////////////////////////////////////////////////////////////////////
41 : : ////////////////////////////////////////////////////////////////////////////
42 : :
43 : : #ifdef _MSC_VER
44 : : #pragma warning(push)
45 : : #pragma warning(disable : 4244) // Conversion from 'double' to 'float', possible loss of data
46 : : #pragma warning(disable : 4305) // Truncation from 'double' to 'float'
47 : : #elif defined(__GNUC__) || defined(__clang__)
48 : : #pragma GCC diagnostic push
49 : : #pragma GCC diagnostic ignored "-Wconversion"
50 : : #pragma GCC diagnostic ignored "-Wfloat-conversion"
51 : : #endif
52 : :
53 : : ////////////////////////////////////////////////////////////////////////////
54 : : ////////////////////////////////////////////////////////////////////////////
55 : : ////////////////////////////////////////////////////////////////////////////
56 : :
57 : : namespace ez {
58 : : namespace time {
59 : :
60 : : // Supported Crontab format
61 : : // 'mm hh dd MM DD'
62 : : // mm : the minutes from 0 to 59
63 : : // hh : the hour from 0 to 23
64 : : // dd : the day of the month from 1 to 31
65 : : // MM : the month from 1 to 12
66 : : // DD : the day of the week from 0 to 7. 0 and 7 are the sunday
67 : : // For each fields, thoses forms are accepted :
68 : : // * : always valid units (0,1,3,4, etc..)
69 : : // 5,8 : the units 5 and 8
70 : : // 2-5 : the units from 2 to 5 (2, 3, 4, 5)
71 : : // */3 : all the 3 interval units(0, 3, 6, 9, etc..)
72 : :
73 : : class Cron {
74 : : friend class TestCron;
75 : :
76 : : public:
77 : : enum ErrorFlags { //
78 : : NONE = (0),
79 : : INVALID_MINUTE = (1 << 0),
80 : : INVALID_HOUR = (1 << 1),
81 : : INVALID_MONTH_DAY = (1 << 2),
82 : : INVALID_MONTH = (1 << 3),
83 : : INVALID_WEEK_DAY = (1 << 4),
84 : : INVALID_FIELDS_COUNT = (1 << 5),
85 : : INVALID_CHAR = (1 << 6),
86 : : INVALID_RANGE = (1 << 7),
87 : : INVALID_INTERVAL = (1 << 8),
88 : : INVALID_VALUES = (1 << 9),
89 : : INVALID_FIELD = (1 << 10)
90 : : };
91 : : enum class FieldType { //
92 : : VALUE = 0, // Value
93 : : INTERVAL, // Interval. each values
94 : : RANGE, // Range [min:max]
95 : : VALUES, // Values [v0,v1,v2]
96 : : WILDCARD, // Valid value for all
97 : : Count
98 : : };
99 : : enum class FieldIndex { //
100 : : MINUTE = 0,
101 : : HOUR,
102 : : DAY_MONTH,
103 : : MONTH,
104 : : DAY_WEEK,
105 : : Count
106 : : };
107 : : struct Field {
108 : : std::string str; // field str
109 : : FieldIndex index{FieldIndex::MINUTE}; // field index
110 : : int32_t cpos{-1}; // char pos in the cron rule
111 : : FieldType type{FieldType::VALUE}; // field type
112 : : int32_t value{-1}; // the value with type is 'value'
113 : : int32_t interval{-1}; // the interval value when type is 'interval'
114 : : std::pair<int32_t, int32_t> range{std::make_pair(-1, -1)}; // the range when type is 'range'
115 : : std::set<int32_t> values{}; // the valude when type is 'values'
116 : : };
117 : : struct ErrorDetail {
118 : : int32_t flag{};
119 : : int32_t position{-1};
120 : : std::string message;
121 : : };
122 : :
123 : : private:
124 : : int32_t m_errorFlags;
125 : : std::string m_cronRule;
126 : : std::vector<Field> m_fields;
127 : : std::vector<ErrorDetail> m_errorDetails;
128 : :
129 : : public:
130 : : Cron(const std::string& m_cronRule) //
131 : 39 : : m_errorFlags(NONE), m_cronRule(m_cronRule) {
132 : 39 : m_parseExpression();
133 : 39 : }
134 : :
135 : 73 : bool isOk() const {
136 : 73 : return (m_errorFlags == NONE);
137 : 73 : }
138 : :
139 : 2 : const std::string& getCronRule() const {
140 : 2 : return m_cronRule;
141 : 2 : }
142 : :
143 : 28 : int getErrorFlags() const {
144 : 28 : return m_errorFlags;
145 : 28 : }
146 : :
147 : 1 : const std::vector<Field>& getFields() const {
148 : 1 : return m_fields;
149 : 1 : }
150 : :
151 : 4 : bool isTimeToAct() const {
152 : 4 : time_t currentTime = std::time(nullptr);
153 : 4 : return isTimeToAct(currentTime);
154 : 4 : }
155 : :
156 : 34 : bool isTimeToAct(time_t vCurrentTime) const {
157 [ + - ]: 34 : if (isOk()) {
158 : : #ifdef _MSC_VER
159 : : struct tm _tm;
160 : : localtime_s(&_tm, &vCurrentTime);
161 : : auto* currentTm = &_tm;
162 : : #else
163 : 34 : auto* currentTm = std::localtime(&vCurrentTime);
164 : 34 : #endif
165 : 34 : return //
166 [ + + ]: 34 : m_checkTimeForField(FieldIndex::MINUTE, currentTm->tm_min) && //
167 [ + + ]: 34 : m_checkTimeForField(FieldIndex::HOUR, currentTm->tm_hour) && //
168 [ + - ]: 34 : m_checkTimeForField(FieldIndex::DAY_MONTH, currentTm->tm_mday) && //
169 [ + - ]: 34 : m_checkTimeForField(FieldIndex::MONTH, currentTm->tm_mon + 1) && //
170 [ + - ]: 34 : m_checkTimeForField(FieldIndex::DAY_WEEK, currentTm->tm_wday);
171 : 34 : }
172 : 0 : return false;
173 : 34 : }
174 : :
175 : 39 : std::string getErrorMessage() const {
176 : 39 : std::stringstream err;
177 [ + + ]: 39 : if (m_errorDetails.empty()) {
178 : 11 : std::cout << "No errors found.\n";
179 : 28 : } else {
180 : 28 : err << "Errors found in cron rule : " << std::endl;
181 : 28 : err << m_cronRule << std::endl;
182 [ + + ]: 106 : for (auto rev_it = m_errorDetails.rbegin(); rev_it != m_errorDetails.rend(); ++rev_it) {
183 [ + + ]: 78 : if (rev_it->position >= 0) {
184 : 61 : std::string marker_line(m_cronRule.size(), ' ');
185 [ + + ]: 309 : for (auto it = m_errorDetails.begin(); it != m_errorDetails.end(); ++it) {
186 [ + + ]: 248 : if (it->position >= 0) {
187 : 231 : marker_line[it->position] = '|';
188 : 231 : }
189 : 248 : }
190 : 61 : marker_line[rev_it->position] = '^';
191 : 61 : marker_line = marker_line.substr(0, rev_it->position + 1);
192 : 61 : err << marker_line << "--<| " << m_getErrorString(rev_it->flag) << " " << rev_it->message << std::endl;
193 : 61 : }
194 : 78 : }
195 : 28 : }
196 : 39 : return err.str();
197 : 39 : }
198 : :
199 : 0 : std::string getSupportedFormat() const {
200 : 0 : return u8R"(
201 : 0 : Supported Crontab format
202 : 0 : mm hh dd MM DD
203 : 0 : mm : the minutes from 0 to 59
204 : 0 : hh : the hour from 0 to 23
205 : 0 : dd : the day of the month from 1 to 31
206 : 0 : MM : the month from 1 to 12
207 : 0 : DD : the day of the week from 0 to 7. 0 and 7 are the sunday
208 : 0 : For each fields, thoses forms are accepted :
209 : 0 : * : always valid units (0,1,3,4, etc..)
210 : 0 : 5,8 : the units 5 and 8
211 : 0 : 2-5 : the units from 2 to 5 (2, 3, 4, 5)
212 : 0 : */3 : all the 3 interval units (0, 3, 6, 9, etc..)
213 : 0 : )";
214 : 0 : }
215 : :
216 : : private:
217 : : typedef int32_t CharPos;
218 : : typedef std::pair<CharPos, std::string> Token;
219 : 160 : std::vector<Token> m_split(const std::string& vText, const std::string& vDelimiters) {
220 : 160 : std::vector<Token> arr;
221 [ + - ]: 160 : if (!vText.empty()) {
222 : 160 : size_t start = 0;
223 : 160 : std::string token;
224 : 160 : auto end = vText.find_first_of(vDelimiters, start);
225 [ + + ]: 160 : if (end != std::string::npos) {
226 [ + + ]: 221 : while (end != std::string::npos) {
227 : 172 : token = vText.substr(start, end - start);
228 : 172 : arr.push_back(std::make_pair(static_cast<CharPos>(start), token)); // empty token or not
229 : 172 : start = end + 1;
230 : 172 : end = vText.find_first_of(vDelimiters, start);
231 : 172 : }
232 : 49 : token = vText.substr(start);
233 : 49 : arr.push_back(std::make_pair(static_cast<CharPos>(start), token)); // empty token or not
234 : 49 : }
235 : 160 : }
236 : 160 : return arr;
237 : 160 : }
238 : :
239 : 61 : int32_t m_getErrorFlagFromIndex(FieldIndex vIndex) {
240 : 61 : return (1 << static_cast<int32_t>(vIndex));
241 : 61 : }
242 : :
243 : : template <typename U, typename V, typename W>
244 : 99 : void m_addError(U vFlag, V vCharPos, W vIndex, const std::string& vMessage = {}) {
245 : 99 : auto idx = static_cast<size_t>(vIndex);
246 [ + + ][ + + ]: 177 : while (m_errorDetails.size() <= idx) {
[ + + ][ + + ]
[ - + ]
247 : 78 : m_errorDetails.push_back({});
248 : 78 : }
249 : 99 : auto& ed = m_errorDetails.at(idx);
250 : 99 : ed.flag |= static_cast<int32_t>(vFlag);
251 : 99 : auto cp = static_cast<int32_t>(vCharPos);
252 [ + + ][ + + ]: 99 : if (ed.position < 0) {
[ + - ][ + + ]
[ - + ]
253 : 61 : ed.position = cp;
254 [ - + ][ - + ]: 61 : } else if (cp > ed.position) {
[ + + ][ - + ]
[ # # ]
255 : 12 : ed.position = cp;
256 : 12 : }
257 [ - + ][ + + ]: 99 : if (!vMessage.empty()) {
[ - + ][ - + ]
[ - + ]
258 : 2 : ed.message += vMessage;
259 : 2 : }
260 : 99 : m_errorFlags |= ed.flag;
261 : 99 : }
262 : :
263 : 5 : bool m_fieldHaveError(Field& vInOutField) {
264 : 5 : auto idx = static_cast<size_t>(vInOutField.index);
265 [ + + ]: 5 : if (m_errorDetails.size() > idx) {
266 : 1 : return (m_errorDetails.at(idx).position > 0);
267 : 1 : }
268 : 4 : return false;
269 : 5 : }
270 : :
271 : 113 : bool m_parseValue(Field& vInOutField, const std::string& vValue, int32_t vCharPos) {
272 : 113 : try {
273 : 113 : auto wpos = vInOutField.str.find_first_not_of("0123456789/*-,");
274 [ + + ]: 113 : if (wpos != std::string::npos) {
275 : 10 : m_addError(INVALID_CHAR | m_getErrorFlagFromIndex(vInOutField.index), vInOutField.cpos + wpos, vInOutField.index);
276 : 10 : return false;
277 : 10 : }
278 : 103 : auto v = std::stoi(vValue);
279 : : // check min/max
280 : 103 : switch (vInOutField.index) {
281 [ + + ]: 30 : case FieldIndex::MINUTE: {
282 [ - + ][ + + ]: 30 : if (v < 0 || v > 59) {
283 : 3 : m_addError(ErrorFlags::INVALID_MINUTE, vCharPos, vInOutField.index);
284 : 3 : return false;
285 : 3 : }
286 : 30 : } break;
287 [ + + ]: 27 : case FieldIndex::HOUR: {
288 [ - + ][ + + ]: 13 : if (v < 0 || v > 23) {
289 : 3 : m_addError(ErrorFlags::INVALID_HOUR, vCharPos, vInOutField.index);
290 : 3 : return false;
291 : 3 : }
292 : 13 : } break;
293 [ + + ]: 10 : case FieldIndex::DAY_MONTH: {
294 [ - + ][ + + ]: 9 : if (v < 1 || v > 31) {
295 : 3 : m_addError(ErrorFlags::INVALID_MONTH_DAY, vCharPos, vInOutField.index);
296 : 3 : return false;
297 : 3 : }
298 : 9 : } break;
299 [ + + ]: 8 : case FieldIndex::MONTH: {
300 [ - + ][ + + ]: 8 : if (v < 1 || v > 12) {
301 : 3 : m_addError(ErrorFlags::INVALID_MONTH, vCharPos, vInOutField.index);
302 : 3 : return false;
303 : 3 : }
304 : 8 : } break;
305 [ + + ]: 12 : case FieldIndex::DAY_WEEK: {
306 [ - + ][ + + ]: 12 : if (v < 0 || v > 7) {
307 : 3 : m_addError(ErrorFlags::INVALID_WEEK_DAY, vCharPos, vInOutField.index);
308 : 3 : return false;
309 : 3 : }
310 : 12 : } break;
311 [ + + ]: 9 : case FieldIndex::Count:
312 [ - + ]: 1 : default: break;
313 : 103 : }
314 : : // set value
315 : 58 : switch (vInOutField.type) {
316 [ + + ]: 22 : case FieldType::VALUE: {
317 : 22 : vInOutField.value = v;
318 : 22 : } break;
319 [ + + ]: 21 : case FieldType::INTERVAL: {
320 : 21 : vInOutField.interval = v;
321 : 21 : } break;
322 [ + + ]: 8 : case FieldType::RANGE: {
323 [ + + ]: 8 : if (vInOutField.range.first == -1) {
324 : 4 : vInOutField.range.first = v;
325 : 4 : } else {
326 : 4 : vInOutField.range.second = v;
327 : 4 : }
328 : 8 : } break;
329 [ + + ]: 7 : case FieldType::VALUES: {
330 : 7 : vInOutField.values.emplace(v);
331 : 7 : } break;
332 [ - + ]: 0 : case FieldType::WILDCARD:
333 [ - + ]: 0 : case FieldType::Count:
334 [ - + ]: 0 : default: break;
335 : 58 : }
336 : 58 : } catch (...) {
337 : 30 : m_addError(m_getErrorFlagFromIndex(vInOutField.index), vCharPos, vInOutField.index);
338 : 30 : return false;
339 : 30 : }
340 : 58 : return true;
341 : 113 : }
342 : :
343 : 77 : bool m_parseValue(Field& vInOutField, const Token& vToken) {
344 : 77 : return m_parseValue(vInOutField, vToken.second, vToken.first);
345 : 77 : }
346 : :
347 : 195 : bool m_isInterval(Field& vInOutField) {
348 [ + - ]: 195 : if (!vInOutField.str.empty()) {
349 : 195 : auto wpos = vInOutField.str.find("*");
350 [ + + ]: 195 : if (wpos != std::string::npos) {
351 : 136 : auto bad_pos = vInOutField.str.find_first_not_of("0123456789/*");
352 [ - + ]: 136 : if (bad_pos != std::string::npos) {
353 : 0 : m_addError(INVALID_INTERVAL | m_getErrorFlagFromIndex(vInOutField.index), //
354 : 0 : vInOutField.cpos + bad_pos,
355 : 0 : vInOutField.index);
356 : 0 : return true; // we not want to continue the field check
357 : 0 : }
358 [ + + ]: 136 : if (wpos < (vInOutField.str.size() - 1)) { // interval pattern '*/'
359 : 46 : ++wpos;
360 [ + + ]: 46 : if (vInOutField.str[wpos] == '/') {
361 : 36 : ++wpos;
362 : 36 : vInOutField.str = vInOutField.str.substr(wpos);
363 : 36 : vInOutField.type = FieldType::INTERVAL;
364 [ + + ]: 36 : if (!m_parseValue(vInOutField, vInOutField.str, static_cast<int32_t>(wpos))) {
365 : 15 : m_addError(INVALID_INTERVAL, vInOutField.cpos + wpos, vInOutField.index);
366 : 15 : return false; // wildcard, stop field checking
367 : 15 : }
368 : 36 : } else {
369 : 10 : m_addError(INVALID_INTERVAL | m_getErrorFlagFromIndex(vInOutField.index), //
370 : 10 : vInOutField.cpos + wpos,
371 : 10 : vInOutField.index);
372 : 10 : }
373 : 90 : } else { // generic pattern '*'
374 : 90 : vInOutField.type = FieldType::WILDCARD;
375 : 90 : }
376 : 121 : return true; // wildcard, stop field checking
377 : 136 : }
378 : 195 : }
379 : 59 : return false; // no wildcard, continue field checking
380 : 195 : }
381 : :
382 : 74 : bool m_isRange(Field& vInOutField) {
383 [ + + ]: 74 : if (!vInOutField.str.empty()) {
384 [ + + ]: 69 : if (vInOutField.str.front() == '-') {
385 : 2 : m_addError(INVALID_RANGE | m_getErrorFlagFromIndex(vInOutField.index), //
386 : 2 : vInOutField.cpos,
387 : 2 : vInOutField.index);
388 : 2 : return true; // range, stop field checking
389 [ + + ]: 67 : } else if (vInOutField.str.back() == '-') {
390 : 1 : m_addError(INVALID_RANGE | m_getErrorFlagFromIndex(vInOutField.index), //
391 : 1 : vInOutField.cpos + vInOutField.str.size() - 1,
392 : 1 : vInOutField.index);
393 : 1 : return true; // range, stop field checking
394 : 1 : }
395 : 66 : auto tokens = m_split(vInOutField.str, "-");
396 [ + + ]: 66 : if (!tokens.empty()) {
397 : 7 : vInOutField.type = FieldType::RANGE;
398 [ + + ]: 7 : if (tokens.size() != 2) {
399 : 2 : auto err_pos = vInOutField.str.size();
400 [ + + ]: 5 : for (const auto& token : tokens) {
401 [ + + ]: 5 : if (token.second.empty()) {
402 : 1 : err_pos = token.first;
403 : 1 : break;
404 : 1 : }
405 : 5 : }
406 : 2 : m_addError(INVALID_RANGE | m_getErrorFlagFromIndex(vInOutField.index), //
407 : 2 : vInOutField.cpos + err_pos,
408 : 2 : vInOutField.index);
409 : 2 : return true; // range, stop field checking
410 : 2 : }
411 [ + + ]: 10 : for (const auto& token : tokens) {
412 [ + + ]: 10 : if (!m_parseValue(vInOutField, token)) {
413 : 2 : m_addError(INVALID_RANGE, vInOutField.cpos + token.first, vInOutField.index);
414 : 2 : }
415 : 10 : }
416 [ + + ]: 5 : if (!m_fieldHaveError(vInOutField)) {
417 [ + + ]: 4 : if (vInOutField.range.first == vInOutField.range.second) { // 5-5
418 : 1 : m_addError(INVALID_RANGE | m_getErrorFlagFromIndex(vInOutField.index), //
419 : 1 : vInOutField.cpos + tokens[1].first,
420 : 1 : vInOutField.index,
421 : 1 : " End must be different than Start.");
422 [ + + ]: 3 : } else if (vInOutField.range.first > vInOutField.range.second) { // 5-4
423 : 1 : m_addError(INVALID_RANGE | m_getErrorFlagFromIndex(vInOutField.index), //
424 : 1 : vInOutField.cpos + tokens[1].first,
425 : 1 : vInOutField.index,
426 : 1 : " End must be greater than Start.");
427 : 1 : }
428 : 4 : }
429 : 5 : return true; // range, stop field checking
430 : 7 : }
431 : 66 : }
432 : 64 : return false; // no range, continue field checking
433 : 74 : }
434 : :
435 : 64 : bool m_isValues(Field& vInOutField) {
436 [ + + ]: 64 : if (!vInOutField.str.empty()) {
437 [ + + ]: 59 : if (vInOutField.str.front() == ',') {
438 : 3 : m_addError(INVALID_VALUES | m_getErrorFlagFromIndex(vInOutField.index), //
439 : 3 : vInOutField.cpos,
440 : 3 : vInOutField.index);
441 : 3 : return true; // range, stop field checking
442 [ + + ]: 56 : } else if (vInOutField.str.back() == ',') {
443 : 1 : m_addError(INVALID_VALUES | m_getErrorFlagFromIndex(vInOutField.index), //
444 : 1 : vInOutField.cpos + vInOutField.str.size() - 1,
445 : 1 : vInOutField.index);
446 : 1 : return true; // range, stop field checking
447 : 1 : }
448 : 55 : auto tokens = m_split(vInOutField.str, ",");
449 [ + + ]: 55 : if (!tokens.empty()) {
450 : 3 : vInOutField.type = FieldType::VALUES;
451 [ - + ]: 3 : if (tokens.size() == 1) {
452 : 0 : m_addError(INVALID_VALUES | m_getErrorFlagFromIndex(vInOutField.index), //
453 : 0 : vInOutField.cpos + vInOutField.str.size(),
454 : 0 : vInOutField.index);
455 : 0 : return true; // we not want to continue the field check
456 : 0 : }
457 [ + + ]: 10 : for (const auto& token : tokens) {
458 [ + + ]: 10 : if (!m_parseValue(vInOutField, token)) {
459 : 3 : m_addError(INVALID_VALUES, vInOutField.cpos + token.first, vInOutField.index);
460 : 3 : }
461 : 10 : }
462 : 3 : return true; // values, stop field checking
463 : 3 : }
464 : 55 : }
465 : 57 : return false; // no values, continue field checking
466 : 64 : }
467 : :
468 : 195 : void m_parseField(const Token& vToken) {
469 : 195 : Field field;
470 : 195 : field.str = vToken.second;
471 : 195 : field.index = static_cast<FieldIndex>(m_fields.size());
472 : 195 : field.cpos = vToken.first;
473 [ + + ]: 195 : if (!m_isInterval(field)) {
474 [ + + ]: 74 : if (!m_isRange(field)) {
475 [ + + ]: 64 : if (!m_isValues(field)) {
476 : 57 : m_parseValue(field, vToken);
477 : 57 : }
478 : 64 : }
479 : 74 : }
480 : 195 : m_fields.push_back(field);
481 : 195 : }
482 : :
483 : 39 : void m_parseExpression() {
484 : 39 : m_fields.clear();
485 : 39 : auto tokens = m_split(m_cronRule, " ");
486 : 39 : auto count = static_cast<size_t>(FieldIndex::Count);
487 [ + + ]: 39 : if (tokens.size() != count) {
488 : 3 : m_addError(INVALID_FIELDS_COUNT, tokens.back().first, tokens.size() - 1); // put the error on the last available field
489 : 3 : }
490 [ + + ]: 195 : for (const auto& token : tokens) {
491 : 195 : m_parseField(token);
492 : 195 : }
493 : 39 : }
494 : :
495 : 61 : std::string m_getErrorString(int32_t vFlag) const {
496 : 61 : std::stringstream res;
497 [ + + ]: 61 : if (vFlag & INVALID_MINUTE) {
498 : 22 : res << " Invalid minute.";
499 : 22 : }
500 [ + + ]: 61 : if (vFlag & INVALID_HOUR) {
501 : 9 : res << " Invalid hour.";
502 : 9 : }
503 [ + + ]: 61 : if (vFlag & INVALID_MONTH_DAY) {
504 : 9 : res << " Invalid month day.";
505 : 9 : }
506 [ + + ]: 61 : if (vFlag & INVALID_MONTH) {
507 : 9 : res << " Invalid month.";
508 : 9 : }
509 [ + + ]: 61 : if (vFlag & INVALID_WEEK_DAY) {
510 : 9 : res << " Invalid week day.";
511 : 9 : }
512 [ + + ]: 61 : if (vFlag & INVALID_FIELDS_COUNT) {
513 : 3 : res << " Invalid fields count.";
514 : 3 : }
515 [ + + ]: 61 : if (vFlag & INVALID_CHAR) {
516 : 7 : res << " Invalid char.";
517 : 7 : }
518 [ + + ]: 61 : if (vFlag & INVALID_INTERVAL) {
519 : 25 : res << " Invalid interval.";
520 : 25 : }
521 [ + + ]: 61 : if (vFlag & INVALID_RANGE) {
522 : 8 : res << " Invalid range.";
523 : 8 : }
524 [ + + ]: 61 : if (vFlag & INVALID_VALUES) {
525 : 5 : res << " Invalid values.";
526 : 5 : }
527 [ - + ]: 61 : if (vFlag & INVALID_FIELD) {
528 : 0 : res << " Invalid field.";
529 : 0 : }
530 : 61 : return res.str();
531 : 61 : }
532 : :
533 : 91 : bool m_checkTimeForField(FieldIndex vFieldIndex, int32_t vValue) const {
534 : 91 : const auto& field = m_fields.at(static_cast<size_t>(vFieldIndex));
535 : 91 : switch (field.type) {
536 [ + + ]: 28 : case FieldType::WILDCARD: { // '*' is valid for all values
537 : 28 : return true;
538 : 0 : }
539 [ + + ]: 33 : case FieldType::VALUE: { // a == v
540 : 33 : return (field.value == vValue);
541 : 0 : }
542 [ + + ]: 11 : case FieldType::INTERVAL: { // each v
543 : 11 : return ((vValue % field.interval) == 0);
544 : 0 : }
545 [ + + ]: 10 : case FieldType::RANGE: { // a > v > b
546 [ + + ]: 10 : return (vValue >= field.range.first && //
547 [ + + ]: 10 : vValue <= field.range.second);
548 : 0 : }
549 [ + + ]: 9 : case FieldType::VALUES: { // a == v or b == v or ..
550 : 9 : return (field.values.find(vValue) != field.values.end());
551 : 0 : }
552 [ - + ]: 0 : case FieldType::Count:
553 [ - + ]: 0 : default: break;
554 : 91 : }
555 : 0 : return false;
556 : 91 : }
557 : : };
558 : :
559 : : } // namespace time
560 : : } // namespace ez
561 : :
562 : : ////////////////////////////////////////////////////////////////////////////
563 : : ////////////////////////////////////////////////////////////////////////////
564 : : ////////////////////////////////////////////////////////////////////////////
565 : :
566 : : #ifdef _MSC_VER
567 : : #pragma warning(pop)
568 : : #elif defined(__GNUC__) || defined(__clang__)
569 : : #pragma GCC diagnostic pop
570 : : #endif
571 : :
572 : : ////////////////////////////////////////////////////////////////////////////
573 : : ////////////////////////////////////////////////////////////////////////////
574 : : ////////////////////////////////////////////////////////////////////////////
|