1
+ #include < set>
1
2
#include < string.h>
2
3
#include < stdint.h>
3
4
#include < vector>
4
5
#include < iostream>
5
6
#include < sstream>
6
7
#include < system_error>
7
8
#include < functional>
9
+ #include < boost/algorithm/string.hpp>
8
10
9
11
#include " common/logger.h"
10
12
#include " common/redisreply.h"
11
13
#include " common/dbconnector.h"
12
14
#include " common/rediscommand.h"
15
+ #include " common/stringutility.h"
13
16
14
17
using namespace std ;
18
+ using namespace boost ;
15
19
16
20
namespace swss {
17
21
22
+ static set<string> g_intToBoolCommands = {
23
+ " COPY" ,
24
+ " EXPIRE" ,
25
+ " EXPIREAT" ,
26
+ " PEXPIRE" ,
27
+ " PEXPIREAT" ,
28
+ " HEXISTS" ,
29
+ " MOVE" ,
30
+ " MSETNX" ,
31
+ " PERSIST" ,
32
+ " RENAMENX" ,
33
+ " SISMEMBER" ,
34
+ " SMOVE" ,
35
+ " SETNX"
36
+ };
37
+
38
+ static set<string> g_strToBoolCommands = {
39
+ " AUTH" ,
40
+ " HMSET" ,
41
+ " PSETEX" ,
42
+ " SETEX" ,
43
+ " FLUSHALL" ,
44
+ " FLUSHDB" ,
45
+ " LSET" ,
46
+ " LTRIM" ,
47
+ " MSET" ,
48
+ " PFMERGE" ,
49
+ " ASKING" ,
50
+ " READONLY" ,
51
+ " READWRITE" ,
52
+ " RENAME" ,
53
+ " SAVE" ,
54
+ " SELECT" ,
55
+ " SHUTDOWN" ,
56
+ " SLAVEOF" ,
57
+ " SWAPDB" ,
58
+ " WATCH" ,
59
+ " UNWATCH" ,
60
+ " SET"
61
+ };
62
+
18
63
template <typename FUNC>
19
64
inline void guard (FUNC func, const char * command)
20
65
{
@@ -226,38 +271,35 @@ template<> RedisMessage RedisReply::getReply<RedisMessage>()
226
271
return ret;
227
272
}
228
273
229
-
230
274
string RedisReply::to_string ()
231
275
{
232
- return to_string (getContext ());
276
+ return RedisReply:: to_string (this -> getContext ());
233
277
}
234
278
235
- string RedisReply::to_string (redisReply *reply)
279
+ string RedisReply::to_string (redisReply *reply, string command )
236
280
{
281
+ /*
282
+ Response format need keep as same as redis-py, redis-py using a command to result type mapping to convert result:
283
+ https://github.com/redis/redis-py/blob/bedf3c82a55b4b67eed93f686cb17e82f7ab19cd/redis/client.py#L682
284
+ Redis command result type can be found here:
285
+ https://redis.io/commands/
286
+ Only commands used by scripts in sonic repos are supported by these method.
287
+ For example: 'Info' command not used by any sonic scripts, so the output format will different with redis-py.
288
+ */
237
289
switch (reply->type )
238
290
{
239
291
case REDIS_REPLY_INTEGER:
240
- return std::to_string ( reply->integer );
292
+ return formatReply (command, reply->integer );
241
293
242
294
case REDIS_REPLY_STRING:
243
295
case REDIS_REPLY_ERROR:
244
296
case REDIS_REPLY_STATUS:
245
297
case REDIS_REPLY_NIL:
246
- return string ( reply->str , reply->len );
298
+ return formatReply (command, reply->str , reply->len );
247
299
248
300
case REDIS_REPLY_ARRAY:
249
301
{
250
- stringstream result;
251
- for (size_t i = 0 ; i < reply->elements ; i++)
252
- {
253
- result << to_string (reply->element [i]);
254
-
255
- if (i < reply->elements - 1 )
256
- {
257
- result << endl;
258
- }
259
- }
260
- return result.str ();
302
+ return formatReply (command, reply->element , reply->elements );
261
303
}
262
304
263
305
default :
@@ -266,4 +308,156 @@ string RedisReply::to_string(redisReply *reply)
266
308
}
267
309
}
268
310
311
+ string RedisReply::formatReply (string command, long long integer)
312
+ {
313
+ if (g_intToBoolCommands.find (command) != g_intToBoolCommands.end ())
314
+ {
315
+ if (integer == 1 )
316
+ {
317
+ return string (" True" );
318
+ }
319
+ else if (integer == 0 )
320
+ {
321
+ return string (" False" );
322
+ }
323
+ }
324
+ else if (command == " AUTH" )
325
+ {
326
+ if (integer != 0 )
327
+ {
328
+ return string (" OK" );
329
+ }
330
+ }
331
+
332
+ return std::to_string (integer);
333
+ }
334
+
335
+ string RedisReply::formatReply (string command, const char * str, size_t len)
336
+ {
337
+ string result = string (str, len);
338
+ if (g_strToBoolCommands.find (command) != g_strToBoolCommands.end ()
339
+ && result == " OK" )
340
+ {
341
+ return string (" True" );
342
+ }
343
+
344
+ return result;
345
+ }
346
+
347
+ string RedisReply::formatReply (string command, struct redisReply **element, size_t elements)
348
+ {
349
+ if (command == " HGETALL" )
350
+ {
351
+ return formatDictReply (element, elements);
352
+ }
353
+ else if (command == " SCAN"
354
+ || command == " SSCAN" )
355
+ {
356
+ return formatSscanReply (element, elements);
357
+ }
358
+ else if (command == " HSCAN" )
359
+ {
360
+ return formatHscanReply (element, elements);
361
+ }
362
+ else if (command == " BLPOP"
363
+ || command == " BRPOP" )
364
+ {
365
+ return formatTupleReply (element, elements);
366
+ }
367
+ else
368
+ {
369
+ return formatListReply (element, elements);
370
+ }
371
+ }
372
+
373
+ string RedisReply::formatSscanReply (struct redisReply **element, size_t elements)
374
+ {
375
+ if (elements != 2 )
376
+ {
377
+ throw system_error (make_error_code (errc::io_error),
378
+ " Invalid result" );
379
+ }
380
+
381
+ // format HSCAN result, here is a example:
382
+ // (0, {'test3': 'test3', 'test2': 'test2'})
383
+ ostringstream result;
384
+ result << " (" << element[0 ]->integer << " , " ;
385
+ // format the field mapping part
386
+ result << formatArrayReply (element[1 ]->element , element[1 ]->elements );
387
+ result << " )" ;
388
+
389
+ return result.str ();
390
+ }
391
+
392
+ string RedisReply::formatHscanReply (struct redisReply **element, size_t elements)
393
+ {
394
+ if (elements != 2 )
395
+ {
396
+ throw system_error (make_error_code (errc::io_error),
397
+ " Invalid result" );
398
+ }
399
+
400
+ // format HSCAN result, here is a example:
401
+ // (0, {'test3': 'test3', 'test2': 'test2'})
402
+ ostringstream result;
403
+ result << " (" << element[0 ]->integer << " , " ;
404
+ // format the field mapping part
405
+ result << formatDictReply (element[1 ]->element , element[1 ]->elements );
406
+ result << " )" ;
407
+
408
+ return result.str ();
409
+ }
410
+
411
+ string RedisReply::formatDictReply (struct redisReply **element, size_t elements)
412
+ {
413
+ if (elements%2 != 0 )
414
+ {
415
+ throw system_error (make_error_code (errc::io_error),
416
+ " Invalid result" );
417
+ }
418
+
419
+ // format dictionary, not using json.h because the output format are different, here is a example:
420
+ // {'test3': 'test3', 'test2': 'test2'}
421
+ vector<string> elementvector;
422
+ for (unsigned int i = 0 ; i < elements; i += 2 )
423
+ {
424
+ elementvector.push_back (" '" + to_string (element[i]) + " ': '" + to_string (element[i+1 ]) + " '" );
425
+ }
426
+
427
+ return swss::join (" , " , ' {' , ' }' , elementvector.begin (), elementvector.end ());
428
+ }
429
+
430
+ string RedisReply::formatArrayReply (struct redisReply **element, size_t elements)
431
+ {
432
+ vector<string> elementvector;
433
+ for (unsigned int i = 0 ; i < elements; i++)
434
+ {
435
+ elementvector.push_back (" '" + to_string (element[i]) + " '" );
436
+ }
437
+
438
+ return swss::join (" , " , ' [' , ' ]' , elementvector.begin (), elementvector.end ());
439
+ }
440
+
441
+ string RedisReply::formatListReply (struct redisReply **element, size_t elements)
442
+ {
443
+ vector<string> elementvector;
444
+ for (unsigned int i = 0 ; i < elements; i++)
445
+ {
446
+ elementvector.push_back (to_string (element[i]));
447
+ }
448
+
449
+ return swss::join (" \n " , elementvector.begin (), elementvector.end ());
450
+ }
451
+
452
+ string RedisReply::formatTupleReply (struct redisReply **element, size_t elements)
453
+ {
454
+ vector<string> elementvector;
455
+ for (unsigned int i = 0 ; i < elements; i++)
456
+ {
457
+ elementvector.push_back (" '" + to_string (element[i]) + " '" );
458
+ }
459
+
460
+ return swss::join (" , " , ' (' , ' )' , elementvector.begin (), elementvector.end ());
461
+ }
462
+
269
463
}
0 commit comments