valkey-rb 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +43 -0
  3. data/.rubocop_todo.yml +22 -0
  4. data/README.md +34 -0
  5. data/Rakefile +23 -0
  6. data/lib/valkey/bindings.rb +173 -0
  7. data/lib/valkey/commands/bitmap_commands.rb +86 -0
  8. data/lib/valkey/commands/cluster_commands.rb +259 -0
  9. data/lib/valkey/commands/connection_commands.rb +318 -0
  10. data/lib/valkey/commands/function_commands.rb +255 -0
  11. data/lib/valkey/commands/generic_commands.rb +454 -0
  12. data/lib/valkey/commands/geo_commands.rb +87 -0
  13. data/lib/valkey/commands/hash_commands.rb +586 -0
  14. data/lib/valkey/commands/hyper_log_log_commands.rb +51 -0
  15. data/lib/valkey/commands/json_commands.rb +389 -0
  16. data/lib/valkey/commands/list_commands.rb +348 -0
  17. data/lib/valkey/commands/module_commands.rb +125 -0
  18. data/lib/valkey/commands/pubsub_commands.rb +237 -0
  19. data/lib/valkey/commands/scripting_commands.rb +217 -0
  20. data/lib/valkey/commands/server_commands.rb +961 -0
  21. data/lib/valkey/commands/set_commands.rb +220 -0
  22. data/lib/valkey/commands/sorted_set_commands.rb +756 -0
  23. data/lib/valkey/commands/stream_commands.rb +636 -0
  24. data/lib/valkey/commands/string_commands.rb +359 -0
  25. data/lib/valkey/commands/transaction_commands.rb +175 -0
  26. data/lib/valkey/commands/vector_search_commands.rb +271 -0
  27. data/lib/valkey/commands.rb +69 -0
  28. data/lib/valkey/errors.rb +41 -0
  29. data/lib/valkey/libglide_ffi.dylib +0 -0
  30. data/lib/valkey/libglide_ffi.so +0 -0
  31. data/lib/valkey/pipeline.rb +20 -0
  32. data/lib/valkey/protobuf/command_request_pb.rb +30 -0
  33. data/lib/valkey/protobuf/connection_request_pb.rb +28 -0
  34. data/lib/valkey/protobuf/response_pb.rb +18 -0
  35. data/lib/valkey/pubsub_callback.rb +10 -0
  36. data/lib/valkey/request_error_type.rb +10 -0
  37. data/lib/valkey/request_type.rb +436 -0
  38. data/lib/valkey/response_type.rb +20 -0
  39. data/lib/valkey/utils.rb +253 -0
  40. data/lib/valkey/version.rb +5 -0
  41. data/lib/valkey.rb +477 -0
  42. metadata +119 -0
@@ -0,0 +1,436 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Valkey
4
+ #
5
+ # this module defines constants for request types used in Valkey.
6
+ # Each constant represents a specific command or operation that can be performed.
7
+ #
8
+ module RequestType
9
+ INVALID_REQUEST = 0
10
+ CUSTOM_COMMAND = 1
11
+
12
+ # Bitmap commands
13
+ BIT_COUNT = 101
14
+ BIT_FIELD = 102
15
+ BIT_FIELD_READ_ONLY = 103
16
+ BIT_OP = 104
17
+ BIT_POS = 105
18
+ GET_BIT = 106
19
+ SET_BIT = 107
20
+
21
+ # Cluster commands
22
+ ASKING = 201
23
+ CLUSTER_ADD_SLOTS = 202
24
+ CLUSTER_ADD_SLOTS_RANGE = 203
25
+ CLUSTER_BUMP_EPOCH = 204
26
+ CLUSTER_COUNT_FAILURE_REPORTS = 205
27
+ CLUSTER_COUNT_KEYS_IN_SLOT = 206
28
+ CLUSTER_DEL_SLOTS = 207
29
+ CLUSTER_DEL_SLOTS_RANGE = 208
30
+ CLUSTER_FAILOVER = 209
31
+ CLUSTER_FLUSH_SLOTS = 210
32
+ CLUSTER_FORGET = 211
33
+ CLUSTER_GET_KEYS_IN_SLOT = 212
34
+ CLUSTER_INFO = 213
35
+ CLUSTER_KEY_SLOT = 214
36
+ CLUSTER_LINKS = 215
37
+ CLUSTER_MEET = 216
38
+ CLUSTER_MY_ID = 217
39
+ CLUSTER_MY_SHARD_ID = 218
40
+ CLUSTER_NODES = 219
41
+ CLUSTER_REPLICAS = 220
42
+ CLUSTER_REPLICATE = 221
43
+ CLUSTER_RESET = 222
44
+ CLUSTER_SAVE_CONFIG = 223
45
+ CLUSTER_SET_CONFIG_EPOCH = 224
46
+ CLUSTER_SETSLOT = 225
47
+ CLUSTER_SHARDS = 226
48
+ CLUSTER_SLAVES = 227
49
+ CLUSTER_SLOTS = 228
50
+ READ_ONLY = 229
51
+ READ_WRITE = 230
52
+
53
+ # Connection Management commands
54
+ AUTH = 301
55
+ CLIENT_CACHING = 302
56
+ CLIENT_GET_NAME = 303
57
+ CLIENT_GET_REDIR = 304
58
+ CLIENT_ID = 305
59
+ CLIENT_INFO = 306
60
+ CLIENT_KILL_SIMPLE = 307
61
+ CLIENT_KILL = 308
62
+ CLIENT_LIST = 309
63
+ CLIENT_NO_EVICT = 310
64
+ CLIENT_NO_TOUCH = 311
65
+ CLIENT_PAUSE = 312
66
+ CLIENT_REPLY = 313
67
+ CLIENT_SET_INFO = 314
68
+ CLIENT_SET_NAME = 315
69
+ CLIENT_TRACKING = 316
70
+ CLIENT_TRACKING_INFO = 317
71
+ CLIENT_UNBLOCK = 318
72
+ CLIENT_UNPAUSE = 319
73
+ ECHO = 320
74
+ HELLO = 321
75
+ PING = 322
76
+ QUIT = 323 # deprecated in 7.2.0
77
+ RESET = 324
78
+ SELECT = 325
79
+
80
+ # Generic commands
81
+ COPY = 401
82
+ DEL = 402
83
+ DUMP = 403
84
+ EXISTS = 404
85
+ EXPIRE = 405
86
+ EXPIRE_AT = 406
87
+ EXPIRE_TIME = 407
88
+ KEYS = 408
89
+ MIGRATE = 409
90
+ MOVE = 410
91
+ OBJECT_ENCODING = 411
92
+ OBJECT_FREQ = 412
93
+ OBJECT_IDLE_TIME = 413
94
+ OBJECT_REF_COUNT = 414
95
+ PERSIST = 415
96
+ PEXPIRE = 416
97
+ PEXPIRE_AT = 417
98
+ PEXPIRE_TIME = 418
99
+ PTTL = 419
100
+ RANDOM_KEY = 420
101
+ RENAME = 421
102
+ RENAME_NX = 422
103
+ RESTORE = 423
104
+ SCAN = 424
105
+ SORT = 425
106
+ SORT_READ_ONLY = 426
107
+ TOUCH = 427
108
+ TTL = 428
109
+ TYPE = 429
110
+ UNLINK = 430
111
+ WAIT = 431
112
+ WAIT_AOF = 432
113
+
114
+ # Geospatial indices commands
115
+ GEO_ADD = 501
116
+ GEO_DIST = 502
117
+ GEO_HASH = 503
118
+ GEO_POS = 504
119
+ GEO_RADIUS = 505 # deprecated in 6.2.0
120
+ GEO_RADIUS_READ_ONLY = 506 # deprecated in 6.2.0
121
+ GEO_RADIUS_BY_MEMBER = 507 # deprecated in 6.2.0
122
+ GEO_RADIUS_BY_MEMBER_READ_ONLY = 508 # deprecated in 6.2.0
123
+ GEO_SEARCH = 509
124
+ GEO_SEARCH_STORE = 510
125
+
126
+ # Hash commands
127
+ HDEL = 601
128
+ HEXISTS = 602
129
+ HGET = 603
130
+ HGET_ALL = 604
131
+ HINCR_BY = 605
132
+ HINCR_BY_FLOAT = 606
133
+ HKEYS = 607
134
+ HLEN = 608
135
+ HMGET = 609
136
+ HMSET = 610
137
+ HRAND_FIELD = 611
138
+ HSCAN = 612
139
+ HSET = 613
140
+ HSET_NX = 614
141
+ HSTRLEN = 615
142
+ HVALS = 616
143
+ HSETEX = 617
144
+ HGETEX = 618
145
+ HEXPIRE = 619
146
+ HEXPIREAT = 620
147
+ HPEXPIRE = 621
148
+ HPEXPIREAT = 622
149
+ HPERSIST = 623
150
+ HTTL = 624
151
+ HPTTL = 625
152
+ HEXPIRETIME = 626
153
+ HPEXPIRETIME = 627
154
+
155
+ # HyperLogLog commands
156
+ PFADD = 701
157
+ PFCOUNT = 702
158
+ PFMERGE = 703
159
+
160
+ # List commands
161
+ BLMOVE = 801
162
+ BLMPOP = 802
163
+ BLPOP = 803
164
+ BRPOP = 804
165
+ BRPOPLPUSH = 805 # deprecated in 6.2.0
166
+ LINDEX = 806
167
+ LINSERT = 807
168
+ LLEN = 808
169
+ LMOVE = 809
170
+ LMPOP = 810
171
+ LPOP = 811
172
+ LPOS = 812
173
+ LPUSH = 813
174
+ LPUSHX = 814
175
+ LRANGE = 815
176
+ LREM = 816
177
+ LSET = 817
178
+ LTRIM = 818
179
+ RPOP = 819
180
+ RPOPLPUSH = 820 # deprecated in 6.2.0
181
+ RPUSH = 821
182
+ RPUSHX = 822
183
+
184
+ # Pub/Sub commands
185
+ PSUBSCRIBE = 901
186
+ PUBLISH = 902
187
+ PUBSUB_CHANNELS = 903
188
+ PUBSUB_NUM_PAT = 904
189
+ PUBSUB_NUM_SUB = 905
190
+ PUBSUB_SHARD_CHANNELS = 906
191
+ PUBSUB_SHARD_NUM_SUB = 907
192
+ PUNSUBSCRIBE = 908
193
+ SPUBLISH = 909
194
+ SSUBSCRIBE = 910
195
+ SUBSCRIBE = 911
196
+ SUNSUBSCRIBE = 912
197
+ UNSUBSCRIBE = 913
198
+
199
+ # Scripting and Functions commands
200
+ EVAL = 1001
201
+ EVAL_READ_ONLY = 1002
202
+ EVAL_SHA = 1003
203
+ EVAL_SHA_READ_ONLY = 1004
204
+ FCALL = 1005
205
+ FCALL_READ_ONLY = 1006
206
+ FUNCTION_DELETE = 1007
207
+ FUNCTION_DUMP = 1008
208
+ FUNCTION_FLUSH = 1009
209
+ FUNCTION_KILL = 1010
210
+ FUNCTION_LIST = 1011
211
+ FUNCTION_LOAD = 1012
212
+ FUNCTION_RESTORE = 1013
213
+ FUNCTION_STATS = 1014
214
+ SCRIPT_DEBUG = 1015
215
+ SCRIPT_EXISTS = 1016
216
+ SCRIPT_FLUSH = 1017
217
+ SCRIPT_KILL = 1018
218
+ SCRIPT_LOAD = 1019
219
+ SCRIPT_SHOW = 1020
220
+
221
+ # Server management commands
222
+ ACL_CAT = 1101
223
+ ACL_DEL_USER = 1102
224
+ ACL_DRY_RUN = 1103
225
+ ACL_GEN_PASS = 1104
226
+ ACL_GET_USER = 1105
227
+ ACL_LIST = 1106
228
+ ACL_LOAD = 1107
229
+ ACL_LOG = 1108
230
+ ACL_SAVE = 1109
231
+ ACL_SET_USER = 1110
232
+ ACL_USERS = 1111
233
+ ACL_WHOAMI = 1112
234
+ BG_REWRITE_AOF = 1113
235
+ BG_SAVE = 1114
236
+ COMMAND_ = 1115 # Command - renamed to avoid collisions
237
+ COMMAND_COUNT = 1116
238
+ COMMAND_DOCS = 1117
239
+ COMMAND_GET_KEYS = 1118
240
+ COMMAND_GET_KEYS_AND_FLAGS = 1119
241
+ COMMAND_INFO = 1120
242
+ COMMAND_LIST = 1121
243
+ CONFIG_GET = 1122
244
+ CONFIG_RESET_STAT = 1123
245
+ CONFIG_REWRITE = 1124
246
+ CONFIG_SET = 1125
247
+ DB_SIZE = 1126
248
+ FAIL_OVER = 1127
249
+ FLUSH_ALL = 1128
250
+ FLUSH_DB = 1129
251
+ INFO = 1130
252
+ LAST_SAVE = 1131
253
+ LATENCY_DOCTOR = 1132
254
+ LATENCY_GRAPH = 1133
255
+ LATENCY_HISTOGRAM = 1134
256
+ LATENCY_HISTORY = 1135
257
+ LATENCY_LATEST = 1136
258
+ LATENCY_RESET = 1137
259
+ LOLWUT = 1138
260
+ MEMORY_DOCTOR = 1139
261
+ MEMORY_MALLOC_STATS = 1140
262
+ MEMORY_PURGE = 1141
263
+ MEMORY_STATS = 1142
264
+ MEMORY_USAGE = 1143
265
+ MODULE_LIST = 1144
266
+ MODULE_LOAD = 1145
267
+ MODULE_LOAD_EX = 1146
268
+ MODULE_UNLOAD = 1147
269
+ MONITOR = 1148
270
+ PSYNC = 1149
271
+ REPL_CONF = 1150
272
+ REPLICA_OF = 1151
273
+ RESTORE_ASKING = 1152
274
+ ROLE = 1153
275
+ SAVE = 1154
276
+ SHUT_DOWN = 1155
277
+ SLAVE_OF = 1156
278
+ SLOWLOG_GET = 1157
279
+ SLOWLOG_LEN = 1158
280
+ SLOWLOG_RESET = 1159
281
+ SWAP_DB = 1160
282
+ SYNC = 1161
283
+ TIME = 1162
284
+
285
+ # Set commands
286
+ SADD = 1201
287
+ SCARD = 1202
288
+ SDIFF = 1203
289
+ SDIFF_STORE = 1204
290
+ SINTER = 1205
291
+ SINTER_CARD = 1206
292
+ SINTER_STORE = 1207
293
+ SISMEMBER = 1208
294
+ SMEMBERS = 1209
295
+ SMISMEMBER = 1210
296
+
297
+ # Set commands
298
+ S_MOVE = 1211
299
+ S_POP = 1212
300
+ S_RAND_MEMBER = 1213
301
+ S_REM = 1214
302
+ S_SCAN = 1215
303
+ S_UNION = 1216
304
+ S_UNION_STORE = 1217
305
+
306
+ # Sorted set commands
307
+ BZ_MPOP = 1301
308
+ BZ_POP_MAX = 1302
309
+ BZ_POP_MIN = 1303
310
+ Z_ADD = 1304
311
+ Z_CARD = 1305
312
+ Z_COUNT = 1306
313
+ Z_DIFF = 1307
314
+ Z_DIFF_STORE = 1308
315
+ Z_INCR_BY = 1309
316
+ Z_INTER = 1310
317
+ Z_INTER_CARD = 1311
318
+ Z_INTER_STORE = 1312
319
+ Z_LEX_COUNT = 1313
320
+ Z_MPOP = 1314
321
+ Z_MSCORE = 1315
322
+ Z_POP_MAX = 1316
323
+ Z_POP_MIN = 1317
324
+ Z_RAND_MEMBER = 1318
325
+ Z_RANGE = 1319
326
+ Z_RANGE_BY_LEX = 1320
327
+ Z_RANGE_BY_SCORE = 1321
328
+ Z_RANGE_STORE = 1322
329
+ Z_RANK = 1323
330
+ Z_REM = 1324
331
+ Z_REM_RANGE_BY_LEX = 1325
332
+ Z_REM_RANGE_BY_RANK = 1326
333
+ Z_REM_RANGE_BY_SCORE = 1327
334
+ Z_REV_RANGE = 1328
335
+ Z_REV_RANGE_BY_LEX = 1329
336
+ Z_REV_RANGE_BY_SCORE = 1330
337
+ Z_REV_RANK = 1331
338
+ Z_SCAN = 1332
339
+ Z_SCORE = 1333
340
+ Z_UNION = 1334
341
+ Z_UNION_STORE = 1335
342
+
343
+ # Stream commands
344
+ X_ACK = 1401
345
+ X_ADD = 1402
346
+ X_AUTO_CLAIM = 1403
347
+ X_CLAIM = 1404
348
+ X_DEL = 1405
349
+ X_GROUP_CREATE = 1406
350
+ X_GROUP_CREATE_CONSUMER = 1407
351
+ X_GROUP_DEL_CONSUMER = 1408
352
+ X_GROUP_DESTROY = 1409
353
+ X_GROUP_SET_ID = 1410
354
+ X_INFO_CONSUMERS = 1411
355
+ X_INFO_GROUPS = 1412
356
+ X_INFO_STREAM = 1413
357
+ X_LEN = 1414
358
+ X_PENDING = 1415
359
+ X_RANGE = 1416
360
+ X_READ = 1417
361
+ X_READ_GROUP = 1418
362
+ X_REV_RANGE = 1419
363
+ X_SET_ID = 1420
364
+ X_TRIM = 1421
365
+
366
+ # String commands
367
+ APPEND = 1501
368
+ DECR = 1502
369
+ DECR_BY = 1503
370
+ GET = 1504
371
+ GET_DEL = 1505
372
+ GET_EX = 1506
373
+ GET_RANGE = 1507
374
+ GET_SET = 1508 # deprecated in 6.2.0
375
+ INCR = 1509
376
+ INCR_BY = 1510
377
+ INCR_BY_FLOAT = 1511
378
+ LCS = 1512
379
+ MGET = 1513
380
+ MSET = 1514
381
+ MSET_NX = 1515
382
+ PSET_EX = 1516 # deprecated in 2.6.12
383
+ SET = 1517
384
+ SET_EX = 1518 # deprecated in 2.6.12
385
+ SET_NX = 1519 # deprecated in 2.6.12
386
+ SET_RANGE = 1520
387
+ STRLEN = 1521
388
+ SUBSTR = 1522
389
+
390
+ # Transaction commands
391
+ DISCARD = 1601
392
+ EXEC = 1602
393
+ MULTI = 1603
394
+ UNWATCH = 1604
395
+ WATCH = 1605
396
+
397
+ # JSON commands
398
+ JSON_ARR_APPEND = 2001
399
+ JSON_ARR_INDEX = 2002
400
+ JSON_ARR_INSERT = 2003
401
+ JSON_ARR_LEN = 2004
402
+ JSON_ARR_POP = 2005
403
+ JSON_ARR_TRIM = 2006
404
+ JSON_CLEAR = 2007
405
+ JSON_DEBUG = 2008
406
+ JSON_DEL = 2009
407
+ JSON_FORGET = 2010
408
+ JSON_GET = 2011
409
+ JSON_MGET = 2012
410
+ JSON_NUM_INCR_BY = 2013
411
+ JSON_NUM_MULT_BY = 2014
412
+ JSON_OBJ_KEYS = 2015
413
+ JSON_OBJ_LEN = 2016
414
+ JSON_RESP = 2017
415
+ JSON_SET = 2018
416
+ JSON_STR_APPEND = 2019
417
+ JSON_STR_LEN = 2020
418
+ JSON_TOGGLE = 2021
419
+ JSON_TYPE = 2022
420
+
421
+ # Vector Search commands
422
+ FT_LIST = 2101
423
+ FT_AGGREGATE = 2102
424
+ FT_ALIAS_ADD = 2103
425
+ FT_ALIAS_DEL = 2104
426
+ FT_ALIAS_LIST = 2105
427
+ FT_ALIAS_UPDATE = 2106
428
+ FT_CREATE = 2107
429
+ FT_DROP_INDEX = 2108
430
+ FT_EXPLAIN = 2109
431
+ FT_EXPLAIN_CLI = 2110
432
+ FT_INFO = 2111
433
+ FT_PROFILE = 2112
434
+ FT_SEARCH = 2113
435
+ end
436
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Valkey
4
+ #
5
+ # this module defines constants for response types used in Valkey.
6
+ # Each constant represents a specific type of response that can be received from the server.
7
+ #
8
+ module ResponseType
9
+ NULL = 0
10
+ INT = 1
11
+ FLOAT = 2
12
+ BOOL = 3
13
+ STRING = 4
14
+ ARRAY = 5
15
+ MAP = 6
16
+ SETS = 7
17
+ OK = 8
18
+ ERROR = 9
19
+ end
20
+ end
@@ -0,0 +1,253 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Valkey
4
+ # Valkey Utils module
5
+ #
6
+ # This module provides utility functions for transforming and processing
7
+ # data structures commonly used in Valkey commands.
8
+ #
9
+ # It includes methods for converting values to boolean, hash, or float,
10
+ # as well as methods for handling specific Valkey command responses.
11
+ #
12
+ module Utils
13
+ Boolify = lambda { |value|
14
+ return value if value.is_a?(TrueClass) || value.is_a?(FalseClass)
15
+
16
+ value != 0 unless value.nil?
17
+ }
18
+
19
+ BoolifySet = lambda { |value|
20
+ case value
21
+ when "OK"
22
+ true
23
+ when nil
24
+ false
25
+ else
26
+ value
27
+ end
28
+ }
29
+
30
+ Hashify = lambda { |value|
31
+ if value.respond_to?(:each_slice)
32
+ value.each_slice(2).to_h
33
+ else
34
+ value
35
+ end
36
+ }
37
+
38
+ Pairify = lambda { |value|
39
+ if value.respond_to?(:each_slice)
40
+ value.each_slice(2).to_a
41
+ else
42
+ value
43
+ end
44
+ }
45
+
46
+ Floatify = lambda { |value|
47
+ case value
48
+ when "inf"
49
+ Float::INFINITY
50
+ when "-inf"
51
+ -Float::INFINITY
52
+ when String
53
+ Float(value)
54
+ else
55
+ value
56
+ end
57
+ }
58
+
59
+ FloatifyPair = lambda { |(first, score)|
60
+ [first, Floatify.call(score)]
61
+ }
62
+
63
+ FloatifyPairs = lambda { |value|
64
+ return value unless value.respond_to?(:each_slice)
65
+
66
+ value.each_slice(2).map(&FloatifyPair)
67
+ }
68
+
69
+ HashifyInfo = lambda { |reply|
70
+ lines = reply.split("\r\n").grep_v(/^(#|$)/)
71
+ lines.map! { |line| line.split(':', 2) }
72
+ lines.compact!
73
+ lines.to_h
74
+ }
75
+
76
+ HashifyStreams = lambda { |reply|
77
+ case reply
78
+ when nil
79
+ {}
80
+ else
81
+ reply.transform_values { |entries| HashifyStreamEntries.call(entries) }
82
+ end
83
+ }
84
+
85
+ EMPTY_STREAM_RESPONSE = [nil].freeze
86
+ private_constant :EMPTY_STREAM_RESPONSE
87
+
88
+ HashifyStreamEntries = lambda { |reply|
89
+ return [] if reply.nil?
90
+
91
+ return [] if !reply.is_a?(Array) || reply.empty?
92
+
93
+ # Reply format: [[entry_id, [field1, value1, field2, value2, ...]], ...]
94
+ # Match redis-rb: return flat arrays [["id", ["field", "value", ...]], ...]
95
+ # Check if first element is a pair [entry_id, values_array]
96
+ first_elem = reply.first
97
+ if first_elem.is_a?(Array) && first_elem.length == 2
98
+ # Already in pair format: [[entry_id, [fields...]], ...]
99
+ reply.compact.map do |entry_id, values|
100
+ # Return flat array format like redis-rb, not hash
101
+ values_array = if values.nil?
102
+ []
103
+ elsif values.is_a?(Array)
104
+ values
105
+ else
106
+ []
107
+ end
108
+ [entry_id, values_array]
109
+ end
110
+ else
111
+ # Flat array format: [entry_id1, [field1, value1, ...], entry_id2, [field2, value2, ...], ...]
112
+ reply.compact.each_slice(2).map do |entry_id, values|
113
+ # Return flat array format like redis-rb, not hash
114
+ values_array = if values.nil?
115
+ []
116
+ elsif values.is_a?(Array)
117
+ values
118
+ else
119
+ []
120
+ end
121
+ [entry_id, values_array]
122
+ end
123
+ end
124
+ }
125
+
126
+ HashifyStreamAutoclaim = lambda { |reply|
127
+ {
128
+ 'next' => reply[0],
129
+ 'entries' => if reply[1].nil?
130
+ []
131
+ elsif reply[1].is_a?(Array)
132
+ # Reply[1] is already an array of entries: [[id, [field, value, ...]], ...]
133
+ # Use HashifyStreamEntries to convert them properly
134
+ HashifyStreamEntries.call(reply[1])
135
+ else
136
+ []
137
+ end
138
+ }
139
+ }
140
+
141
+ HashifyStreamAutoclaimJustId = lambda { |reply|
142
+ {
143
+ 'next' => reply[0],
144
+ 'entries' => reply[1]
145
+ }
146
+ }
147
+
148
+ HashifyStreamPendings = lambda { |reply|
149
+ {
150
+ 'size' => reply[0],
151
+ 'min_entry_id' => reply[1],
152
+ 'max_entry_id' => reply[2],
153
+ 'consumers' => reply[3].nil? ? {} : reply[3].to_h
154
+ }
155
+ }
156
+
157
+ HashifyStreamPendingDetails = lambda { |reply|
158
+ reply.map do |arr|
159
+ {
160
+ 'entry_id' => arr[0],
161
+ 'consumer' => arr[1],
162
+ 'elapsed' => arr[2],
163
+ 'count' => arr[3]
164
+ }
165
+ end
166
+ }
167
+
168
+ HashifyClusterNodeInfo = lambda { |str|
169
+ arr = str.split(' ')
170
+ {
171
+ 'node_id' => arr[0],
172
+ 'ip_port' => arr[1],
173
+ 'flags' => arr[2].split(','),
174
+ 'master_node_id' => arr[3],
175
+ 'ping_sent' => arr[4],
176
+ 'pong_recv' => arr[5],
177
+ 'config_epoch' => arr[6],
178
+ 'link_state' => arr[7],
179
+ 'slots' => arr[8].nil? ? nil : Range.new(*arr[8].split('-'))
180
+ }
181
+ }
182
+
183
+ HashifyClusterSlots = lambda { |reply|
184
+ reply.map do |arr|
185
+ first_slot, last_slot = arr[0..1]
186
+ master = { 'ip' => arr[2][0], 'port' => arr[2][1], 'node_id' => arr[2][2] }
187
+ replicas = arr[3..].map { |r| { 'ip' => r[0], 'port' => r[1], 'node_id' => r[2] } }
188
+ {
189
+ 'start_slot' => first_slot,
190
+ 'end_slot' => last_slot,
191
+ 'master' => master,
192
+ 'replicas' => replicas
193
+ }
194
+ end
195
+ }
196
+
197
+ HashifyClusterNodes = lambda { |reply|
198
+ reply.split(/[\r\n]+/).map { |str| HashifyClusterNodeInfo.call(str) }
199
+ }
200
+
201
+ HashifyClusterSlaves = lambda { |reply|
202
+ reply.map { |str| HashifyClusterNodeInfo.call(str) }
203
+ }
204
+
205
+ Noop = ->(reply) { reply }
206
+
207
+ # Parse Redis URL format: redis://[:password@]host[:port][/db]
208
+ # Also supports: rediss:// (SSL)
209
+ #
210
+ # @param [String] url Redis URL to parse
211
+ # @return [Hash] Parsed connection options with keys: :host, :port, :password, :username, :db, :ssl
212
+ # @example
213
+ # parse_redis_url('redis://:secret@localhost:6379/15')
214
+ # # => { host: 'localhost', port: 6379, password: 'secret', db: 15, ssl: false }
215
+ #
216
+ # parse_redis_url('rediss://user:secret@localhost:6380/0')
217
+ # # => { host: 'localhost', port: 6380, username: 'user', password: 'secret', db: 0, ssl: true }
218
+ def self.parse_redis_url(url)
219
+ return {} unless url.is_a?(String) && !url.empty?
220
+
221
+ # Match redis:// or rediss:// URLs
222
+ # Format: redis[s]://[username:password@]host[:port][/db][?param=value]
223
+ # Supports: redis://host, redis://user:pass@host, redis://:pass@host
224
+ # The regex handles:
225
+ # - No auth: redis://host...
226
+ # - Username and password: redis://user:pass@host...
227
+ # - Password only: redis://:pass@host...
228
+ match = url.match(%r{\A(redis|rediss)://(?:([^:@]*):([^@]+)@)?([^:/]+)(?::(\d+))?(?:/(\d+))?(?:\?.*)?\z})
229
+
230
+ return {} unless match
231
+
232
+ scheme = match[1]
233
+ username = match[2]
234
+ password = match[3]
235
+ host = match[4]
236
+ port = match[5]&.to_i
237
+ db = match[6]&.to_i
238
+ ssl = scheme == "rediss"
239
+
240
+ result = {
241
+ host: host,
242
+ port: port || 6379,
243
+ ssl: ssl
244
+ }
245
+
246
+ result[:username] = username if username && !username.empty?
247
+ result[:password] = password if password && !password.empty?
248
+ result[:db] = db if db
249
+
250
+ result
251
+ end
252
+ end
253
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Valkey
4
+ VERSION = "0.3.5"
5
+ end