Line data Source code
1 : /*
2 : ___________________________________________
3 : | _ ___ _ |
4 : | | | |__ \ | | |
5 : | | |__ ) |__ _ __ _ ___ _ __ | |_ |
6 : | | '_ \ / // _` |/ _` |/ _ \ '_ \| __| | HTTP/2 AGENT FOR MOCK TESTING
7 : | | | | |/ /| (_| | (_| | __/ | | | |_ | Version 0.0.z
8 : | |_| |_|____\__,_|\__, |\___|_| |_|\__| | https://github.com/testillano/h2agent
9 : | __/ | |
10 : | |___/ |
11 : |___________________________________________|
12 :
13 : Licensed under the MIT License <http://opensource.org/licenses/MIT>.
14 : SPDX-License-Identifier: MIT
15 : Copyright (c) 2021 Eduardo Ramos
16 :
17 : Permission is hereby granted, free of charge, to any person obtaining a copy
18 : of this software and associated documentation files (the "Software"), to deal
19 : in the Software without restriction, including without limitation the rights
20 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
21 : copies of the Software, and to permit persons to whom the Software is
22 : furnished to do so, subject to the following conditions:
23 :
24 : The above copyright notice and this permission notice shall be included in all
25 : copies or substantial portions of the Software.
26 :
27 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
30 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
32 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
33 : SOFTWARE.
34 : */
35 :
36 : #include <cinttypes> // PRIi64, etc.
37 :
38 : #include <ert/tracing/Logger.hpp>
39 :
40 : #include <TypeConverter.hpp>
41 :
42 :
43 : namespace h2agent
44 : {
45 : namespace model
46 : {
47 :
48 14 : void searchReplaceAll(std::string& str,
49 : const std::string& from,
50 : const std::string& to)
51 : {
52 14 : LOGDEBUG(
53 : std::string msg = ert::tracing::Logger::asString("String source to 'search/replace all': %s | from: %s | to: %s", str.c_str(), from.c_str(), to.c_str());
54 : ert::tracing::Logger::debug(msg, ERT_FILE_LOCATION);
55 : );
56 14 : std::string::size_type pos = 0u;
57 31 : while((pos = str.find(from, pos)) != std::string::npos) {
58 17 : str.replace(pos, from.length(), to);
59 17 : pos += to.length();
60 : }
61 :
62 14 : LOGDEBUG(
63 : std::string msg = ert::tracing::Logger::asString("String result of 'search/replace all': %s", str.c_str());
64 : ert::tracing::Logger::debug(msg, ERT_FILE_LOCATION);
65 : );
66 14 : }
67 :
68 246 : void replaceVariables(std::string &str, const std::map<std::string, std::string> &patterns, const std::map<std::string,std::string> &vars, GlobalVariable *gvars) {
69 :
70 246 : if (patterns.empty()) return;
71 10 : if (vars.empty() && gvars->empty()) return;
72 :
73 10 : std::map<std::string,std::string>::const_iterator it;
74 10 : std::unordered_map<std::string,std::string>::const_iterator git;
75 :
76 10 : std::string aux{};
77 :
78 23 : for (auto pit = patterns.begin(); pit != patterns.end(); pit++) {
79 :
80 : // local var has priority over a global var with the same name
81 13 : if (!vars.empty()) {
82 7 : it = vars.find(pit->second);
83 7 : if (it != vars.end()) {
84 6 : searchReplaceAll(str, pit->first, it->second);
85 6 : continue; // all is done
86 : }
87 : }
88 :
89 7 : if (!gvars->empty()) { // this is much more efficient that find() == end() below
90 7 : if (gvars->tryGet(pit->second, aux)) {
91 7 : searchReplaceAll(str, pit->first, aux);
92 : }
93 : }
94 : }
95 10 : }
96 :
97 113 : void TypeConverter::setString(const std::string &str) {
98 113 : clear();
99 113 : s_value_ = str;
100 113 : native_type_ = NativeType::String;
101 113 : LOGDEBUG(ert::tracing::Logger::debug(ert::tracing::Logger::asString("String value: %s", s_value_.c_str()), ERT_FILE_LOCATION));
102 113 : }
103 :
104 11 : void TypeConverter::setInteger(const std::int64_t i) {
105 11 : clear();
106 11 : i_value_ = i;
107 11 : native_type_ = NativeType::Integer;
108 11 : LOGDEBUG(std::string fmt = std::string("Integer value: %") + PRIi64; ert::tracing::Logger::debug(ert::tracing::Logger::asString(fmt.c_str(), i_value_), ERT_FILE_LOCATION));
109 11 : }
110 :
111 5 : void TypeConverter::setUnsigned(const std::uint64_t u) {
112 5 : clear();
113 5 : u_value_ = u;
114 5 : native_type_ = NativeType::Unsigned;
115 5 : LOGDEBUG(std::string fmt = std::string("Unsigned value: %") + PRIu64; ert::tracing::Logger::debug(ert::tracing::Logger::asString(fmt.c_str(), u_value_), ERT_FILE_LOCATION));
116 5 : }
117 :
118 4 : void TypeConverter::setFloat(const double f) {
119 4 : clear();
120 4 : f_value_ = f;
121 4 : native_type_ = NativeType::Float;
122 4 : LOGDEBUG(ert::tracing::Logger::debug(ert::tracing::Logger::asString("Float value: %lf", f_value_), ERT_FILE_LOCATION));
123 4 : }
124 :
125 1 : void TypeConverter::setBoolean(bool boolean) {
126 1 : clear();
127 1 : b_value_ = boolean;
128 1 : native_type_ = NativeType::Boolean;
129 1 : LOGDEBUG(ert::tracing::Logger::debug(ert::tracing::Logger::asString("Boolean value: %s", b_value_ ? "true":"false"), ERT_FILE_LOCATION));
130 1 : }
131 :
132 53 : void TypeConverter::setStringReplacingVariables(const std::string &str, const std::map<std::string, std::string> &patterns, const std::map<std::string,std::string> &vars, GlobalVariable *gvars) {
133 :
134 53 : setString(str);
135 53 : replaceVariables(s_value_, patterns, vars, gvars);
136 53 : }
137 :
138 112 : const std::string &TypeConverter::getString(bool &success) {
139 :
140 112 : success = true; // actually, always true
141 :
142 112 : switch (native_type_) {
143 7 : case NativeType::Object:
144 : {
145 7 : s_value_ = j_value_.dump();
146 7 : break;
147 : }
148 5 : case NativeType::Integer:
149 : {
150 5 : s_value_ = std::to_string(i_value_);
151 5 : break;
152 : }
153 6 : case NativeType::Unsigned:
154 : {
155 6 : s_value_ = std::to_string(u_value_);
156 6 : break;
157 : }
158 2 : case NativeType::Float:
159 : {
160 2 : s_value_ = std::to_string(f_value_); // we should remove trailing decimal zeroes:
161 : // Using stringstream formatter is less efficient than post-processing its result:
162 : // (https://stackoverflow.com/questions/13686482/c11-stdto-stringdouble-no-trailing-zeros)
163 2 : s_value_.erase ( s_value_.find_last_not_of('0') + 1, std::string::npos );
164 2 : s_value_.erase ( s_value_.find_last_not_of('.') + 1, std::string::npos );
165 2 : break;
166 : }
167 1 : case NativeType::Boolean:
168 : {
169 1 : s_value_ = b_value_ ? "true":"false";
170 1 : break;
171 : }
172 91 : case NativeType::String:
173 91 : break;
174 : }
175 :
176 112 : LOGDEBUG(
177 : std::string msg;
178 : if (success) {
179 : msg = ert::tracing::Logger::asString("String value: %s", s_value_.c_str());
180 : }
181 : else {
182 : msg = ert::tracing::Logger::asString("Unable to get string representation for source: %s", asString().c_str());
183 : }
184 :
185 : ert::tracing::Logger::debug(msg, ERT_FILE_LOCATION);
186 : );
187 :
188 112 : return s_value_;
189 : }
190 :
191 16 : std::int64_t TypeConverter::getInteger(bool &success) {
192 :
193 16 : success = true;
194 :
195 16 : switch (native_type_) {
196 1 : case NativeType::Object:
197 : {
198 1 : success = false;
199 1 : break;
200 : }
201 5 : case NativeType::String:
202 : {
203 : try {
204 5 : i_value_ = std::stoll(s_value_);
205 : }
206 1 : catch(std::exception &e)
207 : {
208 1 : std::string msg = ert::tracing::Logger::asString("Error converting string '%s' to long long integer: %s", s_value_.c_str(), e.what());
209 1 : ert::tracing::Logger::error(msg, ERT_FILE_LOCATION);
210 1 : success = false;
211 1 : }
212 5 : break;
213 : }
214 1 : case NativeType::Unsigned:
215 : {
216 1 : i_value_ = std::int64_t(u_value_);
217 1 : break;
218 : }
219 1 : case NativeType::Float:
220 : {
221 1 : i_value_ = std::int64_t(f_value_);
222 1 : break;
223 : }
224 1 : case NativeType::Boolean:
225 : {
226 1 : i_value_ = std::int64_t(b_value_ ? 1:0);
227 1 : break;
228 : }
229 7 : case NativeType::Integer:
230 7 : break;
231 : }
232 :
233 16 : LOGDEBUG(
234 : std::string msg;
235 : if (success) {
236 : std::string fmt = std::string("Integer value: %") + PRIi64;
237 : msg = ert::tracing::Logger::asString(fmt.c_str(), i_value_);
238 : }
239 : else {
240 : msg = ert::tracing::Logger::asString("Unable to get integer representation for source: %s", asString().c_str());
241 : }
242 :
243 : ert::tracing::Logger::debug(msg, ERT_FILE_LOCATION);
244 : );
245 :
246 16 : return i_value_;
247 : }
248 :
249 18 : std::uint64_t TypeConverter::getUnsigned(bool &success) {
250 :
251 18 : success = true;
252 :
253 18 : switch (native_type_) {
254 1 : case NativeType::Object:
255 : {
256 1 : success = false;
257 1 : break;
258 : }
259 9 : case NativeType::String:
260 : {
261 : try {
262 9 : u_value_ = std::stoull(s_value_);
263 : }
264 1 : catch(std::exception &e)
265 : {
266 1 : std::string msg = ert::tracing::Logger::asString("Error converting string '%s' to unsigned long long integer: %s", s_value_.c_str(), e.what());
267 1 : ert::tracing::Logger::error(msg, ERT_FILE_LOCATION);
268 1 : success = false;
269 1 : }
270 9 : break;
271 : }
272 2 : case NativeType::Integer:
273 : {
274 2 : u_value_ = std::uint64_t(i_value_);
275 2 : break;
276 : }
277 2 : case NativeType::Float:
278 : {
279 2 : u_value_ = std::uint64_t(f_value_);
280 2 : break;
281 : }
282 1 : case NativeType::Boolean:
283 : {
284 1 : u_value_ = std::uint64_t(b_value_ ? 1:0);
285 1 : break;
286 : }
287 3 : case NativeType::Unsigned:
288 3 : break;
289 : }
290 :
291 18 : LOGDEBUG(
292 : std::string msg;
293 : if (success) {
294 : std::string fmt = std::string("Unsigned value: %") + PRIu64;
295 : msg = ert::tracing::Logger::asString(fmt.c_str(), u_value_);
296 : }
297 : else {
298 : msg = ert::tracing::Logger::asString("Unable to get unsigned integer representation for source: %s", asString().c_str());
299 : }
300 :
301 : ert::tracing::Logger::debug(msg, ERT_FILE_LOCATION);
302 : );
303 :
304 18 : return u_value_;
305 : }
306 :
307 12 : double TypeConverter::getFloat(bool &success) {
308 :
309 12 : success = true;
310 :
311 12 : switch (native_type_) {
312 1 : case NativeType::Object:
313 : {
314 1 : success = false;
315 1 : break;
316 : }
317 5 : case NativeType::String:
318 : {
319 : try {
320 5 : f_value_ = std::stod(s_value_);
321 : }
322 1 : catch(std::exception &e)
323 : {
324 1 : std::string msg = ert::tracing::Logger::asString("Error converting string '%s' to float number: %s", s_value_.c_str(), e.what());
325 1 : ert::tracing::Logger::error(msg, ERT_FILE_LOCATION);
326 1 : success = false;
327 1 : }
328 5 : break;
329 : }
330 1 : case NativeType::Integer:
331 : {
332 1 : f_value_ = double(i_value_);
333 1 : break;
334 : }
335 1 : case NativeType::Unsigned:
336 : {
337 1 : f_value_ = double(u_value_);
338 1 : break;
339 : }
340 1 : case NativeType::Boolean:
341 : {
342 1 : f_value_ = double(b_value_ ? 1:0);
343 1 : break;
344 : }
345 3 : case NativeType::Float:
346 3 : break;
347 : }
348 :
349 12 : LOGDEBUG(
350 : std::string msg;
351 : if (success) {
352 : msg = ert::tracing::Logger::asString("Float value: %lf", f_value_);
353 : }
354 : else {
355 : msg = ert::tracing::Logger::asString("Unable to get float number representation for source: %s", asString().c_str());
356 : }
357 :
358 : ert::tracing::Logger::debug(msg, ERT_FILE_LOCATION);
359 : );
360 :
361 12 : return f_value_;
362 : }
363 :
364 10 : bool TypeConverter::getBoolean(bool &success) {
365 :
366 10 : success = true;
367 :
368 10 : switch (native_type_) {
369 1 : case NativeType::Object:
370 : {
371 1 : success = false;
372 1 : break;
373 : }
374 3 : case NativeType::String:
375 : {
376 3 : b_value_ = (s_value_.empty() ? false:true);
377 3 : break;
378 : }
379 1 : case NativeType::Integer:
380 : {
381 1 : b_value_ = ((i_value_ != (std::int64_t)0) ? true:false);
382 1 : break;
383 : }
384 1 : case NativeType::Unsigned:
385 : {
386 1 : b_value_ = ((u_value_ != (std::uint64_t)0) ? true:false);
387 1 : break;
388 : }
389 1 : case NativeType::Float:
390 : {
391 1 : b_value_ = ((f_value_ != (double)0) ? true:false);
392 1 : break;
393 : }
394 3 : case NativeType::Boolean:
395 3 : break;
396 : }
397 :
398 10 : LOGDEBUG(
399 : std::string msg;
400 : if (success) {
401 : msg = ert::tracing::Logger::asString("Boolean value: %s", b_value_ ? "true":"false");
402 : }
403 : else {
404 : msg = ert::tracing::Logger::asString("Unable to get boolean representation for source: %s", asString().c_str());
405 : }
406 :
407 : ert::tracing::Logger::debug(msg, ERT_FILE_LOCATION);
408 : );
409 :
410 10 : return b_value_;
411 : }
412 :
413 13 : const nlohmann::json &TypeConverter::getObject(bool &success) {
414 :
415 13 : success = (native_type_ == NativeType::Object);
416 :
417 13 : LOGDEBUG(
418 : std::string msg;
419 : if (success) {
420 : msg = ert::tracing::Logger::asString("Json object value: %s", j_value_.dump().c_str());
421 : }
422 : else {
423 : msg = ert::tracing::Logger::asString("Unable to get json object from source: %s", asString().c_str());
424 : }
425 :
426 : ert::tracing::Logger::debug(msg, ERT_FILE_LOCATION);
427 : );
428 :
429 13 : return j_value_;
430 : }
431 :
432 270 : void TypeConverter::clear() {
433 270 : s_value_ = "";
434 270 : i_value_ = 0;
435 270 : u_value_ = 0;
436 270 : f_value_ = 0;
437 270 : b_value_ = false;
438 270 : j_value_.clear();
439 270 : native_type_ = NativeType::String;
440 270 : }
441 :
442 24 : bool TypeConverter::setObject(const nlohmann::json &jsonSource, const std::string &path) {
443 24 : clear();
444 :
445 24 : LOGDEBUG(
446 : std::string msg = ert::tracing::Logger::asString("Json path: %s | Json object: %s", path.c_str(), jsonSource.dump().c_str());
447 : ert::tracing::Logger::debug(msg, ERT_FILE_LOCATION);
448 : );
449 :
450 24 : if (path.empty()) {
451 10 : j_value_ = jsonSource;
452 : }
453 : else {
454 : try {
455 14 : nlohmann::json::json_pointer j_ptr(path);
456 : // operator[] aborts in debug compilation when the json pointer path is not found.
457 : // It is safer to use at() method which has "bounds checking":
458 : // j_value_ = jsonSource[j_ptr];
459 14 : j_value_ = jsonSource.at(j_ptr);
460 10 : if (j_value_.empty()) return false; // null extracted (path not found)
461 14 : }
462 4 : catch (std::exception& e)
463 : {
464 4 : ert::tracing::Logger::error(e.what(), ERT_FILE_LOCATION);
465 4 : return false;
466 4 : }
467 : }
468 :
469 20 : if (j_value_.is_object()) {
470 10 : native_type_ = NativeType::Object;
471 : }
472 10 : else if (j_value_.is_string()) {
473 3 : s_value_ = j_value_;
474 3 : native_type_ = NativeType::String;
475 : }
476 7 : else if (j_value_.is_number_unsigned()) { // this condition here (before integer), because integer also becomes true for unsigned source
477 3 : u_value_ = j_value_;
478 3 : native_type_ = NativeType::Unsigned;
479 : }
480 4 : else if (j_value_.is_number_integer()) {
481 1 : i_value_ = j_value_;
482 1 : native_type_ = NativeType::Integer;
483 : }
484 3 : else if (j_value_.is_number_float()) {
485 1 : f_value_ = j_value_;
486 1 : native_type_ = NativeType::Float;
487 : }
488 2 : else if (j_value_.is_boolean()) {
489 2 : b_value_ = j_value_;
490 2 : native_type_ = NativeType::Boolean;
491 : }
492 : //else {
493 : // ert::tracing::Logger::error("Unrecognized json pointer value format", ERT_FILE_LOCATION); // this shouldn't happen as all the possible formats are checked above
494 : // return false;
495 : //}
496 :
497 20 : return true;
498 : }
499 :
500 6 : std::string TypeConverter::asString() {
501 :
502 6 : std::stringstream ss;
503 6 : ss << "NativeType (String = 0, Integer, Unsigned, Float, Boolean, Object): " << getNativeType()
504 12 : << " | " << ((getNativeType() == NativeType::String) ? "STRING":"String") << ": " << s_value_
505 6 : << " | " << ((getNativeType() == NativeType::Integer) ? "INTEGER":"Integer") << ": " << i_value_
506 6 : << " | " << ((getNativeType() == NativeType::Unsigned) ? "UNSIGNED":"Unsigned") << ": " << u_value_
507 6 : << " | " << ((getNativeType() == NativeType::Float) ? "FLOAT":"Float") << ": " << f_value_
508 12 : << " | " << ((getNativeType() == NativeType::Boolean) ? "BOOLEAN":"Boolean") << ": " << (b_value_ ? "true":"false")
509 6 : << " | " << ((getNativeType() == NativeType::Object) ? "OBJECT":"Object") << ": " << j_value_.dump();
510 :
511 12 : return (ss.str());
512 6 : }
513 :
514 : }
515 : }
|