1
- use super :: { parse_redis_value, Value } ;
1
+ use std:: iter:: Iterator ;
2
+
3
+ use crate :: cmd:: { Arg , Cmd } ;
4
+ use crate :: types:: Value ;
2
5
3
6
pub ( crate ) const SLOT_SIZE : usize = 16384 ;
4
7
@@ -10,58 +13,36 @@ pub(crate) enum RoutingInfo {
10
13
Slot ( u16 ) ,
11
14
}
12
15
13
- fn get_arg ( values : & [ Value ] , idx : usize ) -> Option < & [ u8 ] > {
14
- match values. get ( idx) {
15
- Some ( Value :: Data ( ref data) ) => Some ( & data[ ..] ) ,
16
- _ => None ,
17
- }
18
- }
19
-
20
- fn get_command_arg ( values : & [ Value ] , idx : usize ) -> Option < Vec < u8 > > {
21
- get_arg ( values, idx) . map ( |x| x. to_ascii_uppercase ( ) )
22
- }
23
-
24
- fn get_u64_arg ( values : & [ Value ] , idx : usize ) -> Option < u64 > {
25
- get_arg ( values, idx)
26
- . and_then ( |x| std:: str:: from_utf8 ( x) . ok ( ) )
27
- . and_then ( |x| x. parse ( ) . ok ( ) )
28
- }
29
-
30
16
impl RoutingInfo {
31
- pub fn for_packed_command ( cmd : & [ u8 ] ) -> Option < RoutingInfo > {
32
- parse_redis_value ( cmd) . ok ( ) . and_then ( RoutingInfo :: for_value)
33
- }
34
-
35
- pub fn for_value ( value : Value ) -> Option < RoutingInfo > {
36
- let args = match value {
37
- Value :: Bulk ( args) => args,
38
- _ => return None ,
39
- } ;
40
-
41
- match & get_command_arg ( & args, 0 ) ?[ ..] {
17
+ pub ( crate ) fn for_routable < R > ( r : & R ) -> Option < RoutingInfo >
18
+ where
19
+ R : Routable + ?Sized ,
20
+ {
21
+ match & r. command ( ) ?[ ..] {
42
22
b"FLUSHALL" | b"FLUSHDB" | b"SCRIPT" => Some ( RoutingInfo :: AllMasters ) ,
43
23
b"ECHO" | b"CONFIG" | b"CLIENT" | b"SLOWLOG" | b"DBSIZE" | b"LASTSAVE" | b"PING"
44
24
| b"INFO" | b"BGREWRITEAOF" | b"BGSAVE" | b"CLIENT LIST" | b"SAVE" | b"TIME"
45
25
| b"KEYS" => Some ( RoutingInfo :: AllNodes ) ,
46
26
b"SCAN" | b"CLIENT SETNAME" | b"SHUTDOWN" | b"SLAVEOF" | b"REPLICAOF"
47
27
| b"SCRIPT KILL" | b"MOVE" | b"BITOP" => None ,
48
28
b"EVALSHA" | b"EVAL" => {
49
- let key_count = get_u64_arg ( & args, 2 ) ?;
29
+ let key_count = r
30
+ . arg_idx ( 2 )
31
+ . and_then ( |x| std:: str:: from_utf8 ( x) . ok ( ) )
32
+ . and_then ( |x| x. parse :: < u64 > ( ) . ok ( ) ) ?;
50
33
if key_count == 0 {
51
34
Some ( RoutingInfo :: Random )
52
35
} else {
53
- get_arg ( & args , 3 ) . and_then ( RoutingInfo :: for_key)
36
+ r . arg_idx ( 3 ) . and_then ( RoutingInfo :: for_key)
54
37
}
55
38
}
56
- b"XGROUP" | b"XINFO" => get_arg ( & args , 2 ) . and_then ( RoutingInfo :: for_key) ,
39
+ b"XGROUP" | b"XINFO" => r . arg_idx ( 2 ) . and_then ( RoutingInfo :: for_key) ,
57
40
b"XREAD" | b"XREADGROUP" => {
58
- let streams_position = args. iter ( ) . position ( |a| match a {
59
- Value :: Data ( a) => a. eq_ignore_ascii_case ( b"STREAMS" ) ,
60
- _ => false ,
61
- } ) ?;
62
- get_arg ( & args, streams_position + 1 ) . and_then ( RoutingInfo :: for_key)
41
+ let streams_position = r. position ( b"STREAMS" ) ?;
42
+ r. arg_idx ( streams_position + 1 )
43
+ . and_then ( RoutingInfo :: for_key)
63
44
}
64
- _ => match get_arg ( & args , 1 ) {
45
+ _ => match r . arg_idx ( 1 ) {
65
46
Some ( key) => RoutingInfo :: for_key ( key) ,
66
47
None => Some ( RoutingInfo :: Random ) ,
67
48
} ,
@@ -79,6 +60,55 @@ impl RoutingInfo {
79
60
}
80
61
}
81
62
63
+ pub ( crate ) trait Routable {
64
+ // Convenience function to return ascii uppercase version of the
65
+ // the first argument (i.e., the command).
66
+ fn command ( & self ) -> Option < Vec < u8 > > {
67
+ self . arg_idx ( 0 ) . map ( |x| x. to_ascii_uppercase ( ) )
68
+ }
69
+
70
+ // Returns a reference to the data for the argument at `idx`.
71
+ fn arg_idx ( & self , idx : usize ) -> Option < & [ u8 ] > ;
72
+
73
+ // Returns index of argument that matches `candidate`, if it exists
74
+ fn position ( & self , candidate : & [ u8 ] ) -> Option < usize > ;
75
+ }
76
+
77
+ impl Routable for Cmd {
78
+ fn arg_idx ( & self , idx : usize ) -> Option < & [ u8 ] > {
79
+ self . arg_idx ( idx)
80
+ }
81
+
82
+ fn position ( & self , candidate : & [ u8 ] ) -> Option < usize > {
83
+ self . args_iter ( ) . position ( |a| match a {
84
+ Arg :: Simple ( d) => d. eq_ignore_ascii_case ( candidate) ,
85
+ _ => false ,
86
+ } )
87
+ }
88
+ }
89
+
90
+ impl Routable for Value {
91
+ fn arg_idx ( & self , idx : usize ) -> Option < & [ u8 ] > {
92
+ match self {
93
+ Value :: Bulk ( args) => match args. get ( idx) {
94
+ Some ( Value :: Data ( ref data) ) => Some ( & data[ ..] ) ,
95
+ _ => None ,
96
+ } ,
97
+ _ => None ,
98
+ }
99
+ }
100
+
101
+ fn position ( & self , candidate : & [ u8 ] ) -> Option < usize > {
102
+ match self {
103
+ Value :: Bulk ( args) => args. iter ( ) . position ( |a| match a {
104
+ Value :: Data ( d) => d. eq_ignore_ascii_case ( candidate) ,
105
+ _ => false ,
106
+ } ) ,
107
+ _ => None ,
108
+ }
109
+ }
110
+ }
111
+
82
112
#[ derive( Debug ) ]
83
113
pub ( crate ) struct Slot {
84
114
start : u16 ,
@@ -139,7 +169,7 @@ fn get_hashtag(key: &[u8]) -> Option<&[u8]> {
139
169
#[ cfg( test) ]
140
170
mod tests {
141
171
use super :: { get_hashtag, RoutingInfo } ;
142
- use crate :: cmd;
172
+ use crate :: { cmd, parser :: parse_redis_value } ;
143
173
144
174
#[ test]
145
175
fn test_get_hashtag ( ) {
@@ -157,16 +187,69 @@ mod tests {
157
187
lower. arg ( "streams" ) . arg ( "foo" ) . arg ( 0 ) ;
158
188
159
189
assert_eq ! (
160
- RoutingInfo :: for_packed_command ( & upper. get_packed_command ( ) ) . unwrap( ) ,
161
- RoutingInfo :: for_packed_command ( & lower. get_packed_command ( ) ) . unwrap( )
190
+ RoutingInfo :: for_routable ( & upper) . unwrap( ) ,
191
+ RoutingInfo :: for_routable ( & lower) . unwrap( )
162
192
) ;
163
193
164
194
let mut mixed = cmd ( "xReAd" ) ;
165
195
mixed. arg ( "StReAmS" ) . arg ( "foo" ) . arg ( 0 ) ;
166
196
167
197
assert_eq ! (
168
- RoutingInfo :: for_packed_command ( & lower. get_packed_command ( ) ) . unwrap( ) ,
169
- RoutingInfo :: for_packed_command ( & mixed. get_packed_command ( ) ) . unwrap( )
198
+ RoutingInfo :: for_routable ( & lower) . unwrap( ) ,
199
+ RoutingInfo :: for_routable ( & mixed) . unwrap( )
170
200
) ;
171
201
}
202
+
203
+ #[ test]
204
+ fn test_routing_info ( ) {
205
+ let mut test_cmds = vec ! [ ] ;
206
+
207
+ // RoutingInfo::AllMasters
208
+ let mut test_cmd = cmd ( "FLUSHALL" ) ;
209
+ test_cmd. arg ( "" ) ;
210
+ test_cmds. push ( test_cmd) ;
211
+
212
+ // RoutingInfo::AllNodes
213
+ test_cmd = cmd ( "ECHO" ) ;
214
+ test_cmd. arg ( "" ) ;
215
+ test_cmds. push ( test_cmd) ;
216
+
217
+ // Routing key is 2nd arg ("42")
218
+ test_cmd = cmd ( "SET" ) ;
219
+ test_cmd. arg ( "42" ) ;
220
+ test_cmds. push ( test_cmd) ;
221
+
222
+ // Routing key is 3rd arg ("FOOBAR")
223
+ test_cmd = cmd ( "XINFO" ) ;
224
+ test_cmd. arg ( "GROUPS" ) . arg ( "FOOBAR" ) ;
225
+ test_cmds. push ( test_cmd) ;
226
+
227
+ // Routing key is 3rd or 4th arg (3rd = "0" == RoutingInfo::Random)
228
+ test_cmd = cmd ( "EVAL" ) ;
229
+ test_cmd. arg ( "FOO" ) . arg ( "0" ) . arg ( "BAR" ) ;
230
+ test_cmds. push ( test_cmd) ;
231
+
232
+ // Routing key is 3rd or 4th arg (3rd != "0" == RoutingInfo::Slot)
233
+ test_cmd = cmd ( "EVAL" ) ;
234
+ test_cmd. arg ( "FOO" ) . arg ( "4" ) . arg ( "BAR" ) ;
235
+ test_cmds. push ( test_cmd) ;
236
+
237
+ // Routing key position is variable, 3rd arg
238
+ test_cmd = cmd ( "XREAD" ) ;
239
+ test_cmd. arg ( "STREAMS" ) . arg ( "4" ) ;
240
+ test_cmds. push ( test_cmd) ;
241
+
242
+ // Routing key position is variable, 4th arg
243
+ test_cmd = cmd ( "XREAD" ) ;
244
+ test_cmd. arg ( "FOO" ) . arg ( "STREAMS" ) . arg ( "4" ) ;
245
+ test_cmds. push ( test_cmd) ;
246
+
247
+ for cmd in test_cmds {
248
+ let value = parse_redis_value ( & cmd. get_packed_command ( ) ) . unwrap ( ) ;
249
+ assert_eq ! (
250
+ RoutingInfo :: for_routable( & value) . unwrap( ) ,
251
+ RoutingInfo :: for_routable( & cmd) . unwrap( ) ,
252
+ ) ;
253
+ }
254
+ }
172
255
}
0 commit comments