Skip to main content

ember_protocol/command/
attributes.rs

1//! Command attribute methods — name, write classification, ACL categories.
2//!
3//! Separated from the enum definition for readability. Each method is a
4//! simple match over all variants.
5
6use super::Command;
7
8impl Command {
9    /// Returns the lowercase command name as a static string.
10    ///
11    /// Used for metrics labels and slow log entries. Zero allocation —
12    /// returns a `&'static str` for every known variant.
13    ///
14    /// The match is explicit rather than derive-generated: a proc macro would
15    /// obscure the string mappings, which are the thing most worth seeing at
16    /// a glance when auditing command names.
17    pub fn command_name(&self) -> &'static str {
18        match self {
19            // connection
20            Command::Ping(_) => "ping",
21            Command::Echo(_) => "echo",
22            Command::Auth { .. } => "auth",
23            Command::Quit => "quit",
24            Command::Monitor => "monitor",
25            Command::ClientId => "client",
26            Command::ClientSetName { .. } => "client",
27            Command::ClientGetName => "client",
28            Command::ClientList => "client",
29
30            // strings
31            Command::Get { .. } => "get",
32            Command::Set { .. } => "set",
33            Command::MGet { .. } => "mget",
34            Command::MSet { .. } => "mset",
35            Command::Incr { .. } => "incr",
36            Command::Decr { .. } => "decr",
37            Command::IncrBy { .. } => "incrby",
38            Command::DecrBy { .. } => "decrby",
39            Command::IncrByFloat { .. } => "incrbyfloat",
40            Command::Append { .. } => "append",
41            Command::Strlen { .. } => "strlen",
42            Command::GetRange { .. } => "getrange",
43            Command::SetRange { .. } => "setrange",
44
45            // key lifecycle
46            Command::Del { .. } => "del",
47            Command::Unlink { .. } => "unlink",
48            Command::Exists { .. } => "exists",
49            Command::Rename { .. } => "rename",
50            Command::Copy { .. } => "copy",
51            Command::ObjectEncoding { .. } => "object",
52            Command::ObjectRefcount { .. } => "object",
53            Command::Keys { .. } => "keys",
54            Command::Scan { .. } => "scan",
55            Command::SScan { .. } => "sscan",
56            Command::HScan { .. } => "hscan",
57            Command::ZScan { .. } => "zscan",
58            Command::Type { .. } => "type",
59            Command::Expire { .. } => "expire",
60            Command::Ttl { .. } => "ttl",
61            Command::Persist { .. } => "persist",
62            Command::Pttl { .. } => "pttl",
63            Command::Pexpire { .. } => "pexpire",
64
65            // server
66            Command::DbSize => "dbsize",
67            Command::Info { .. } => "info",
68            Command::Time => "time",
69            Command::LastSave => "lastsave",
70            Command::Role => "role",
71            Command::BgSave => "bgsave",
72            Command::BgRewriteAof => "bgrewriteaof",
73            Command::FlushDb { .. } => "flushdb",
74            Command::ConfigGet { .. } => "config",
75            Command::ConfigSet { .. } => "config",
76            Command::ConfigRewrite => "config",
77            Command::Multi => "multi",
78            Command::Exec => "exec",
79            Command::Discard => "discard",
80            Command::Watch { .. } => "watch",
81            Command::Unwatch => "unwatch",
82
83            // list
84            Command::LPush { .. } => "lpush",
85            Command::RPush { .. } => "rpush",
86            Command::LPop { .. } => "lpop",
87            Command::RPop { .. } => "rpop",
88            Command::LRange { .. } => "lrange",
89            Command::LLen { .. } => "llen",
90            Command::BLPop { .. } => "blpop",
91            Command::BRPop { .. } => "brpop",
92            Command::LIndex { .. } => "lindex",
93            Command::LSet { .. } => "lset",
94            Command::LTrim { .. } => "ltrim",
95            Command::LInsert { .. } => "linsert",
96            Command::LRem { .. } => "lrem",
97            Command::LPos { .. } => "lpos",
98
99            // sorted set
100            Command::ZAdd { .. } => "zadd",
101            Command::ZRem { .. } => "zrem",
102            Command::ZScore { .. } => "zscore",
103            Command::ZRank { .. } => "zrank",
104            Command::ZRevRank { .. } => "zrevrank",
105            Command::ZCard { .. } => "zcard",
106            Command::ZRange { .. } => "zrange",
107            Command::ZRevRange { .. } => "zrevrange",
108            Command::ZCount { .. } => "zcount",
109            Command::ZIncrBy { .. } => "zincrby",
110            Command::ZRangeByScore { .. } => "zrangebyscore",
111            Command::ZRevRangeByScore { .. } => "zrevrangebyscore",
112            Command::ZPopMin { .. } => "zpopmin",
113            Command::ZPopMax { .. } => "zpopmax",
114
115            // hash
116            Command::HSet { .. } => "hset",
117            Command::HGet { .. } => "hget",
118            Command::HGetAll { .. } => "hgetall",
119            Command::HDel { .. } => "hdel",
120            Command::HExists { .. } => "hexists",
121            Command::HLen { .. } => "hlen",
122            Command::HIncrBy { .. } => "hincrby",
123            Command::HKeys { .. } => "hkeys",
124            Command::HVals { .. } => "hvals",
125            Command::HMGet { .. } => "hmget",
126
127            // set
128            Command::SAdd { .. } => "sadd",
129            Command::SRem { .. } => "srem",
130            Command::SMembers { .. } => "smembers",
131            Command::SIsMember { .. } => "sismember",
132            Command::SCard { .. } => "scard",
133            Command::SUnion { .. } => "sunion",
134            Command::SInter { .. } => "sinter",
135            Command::SDiff { .. } => "sdiff",
136            Command::SUnionStore { .. } => "sunionstore",
137            Command::SInterStore { .. } => "sinterstore",
138            Command::SDiffStore { .. } => "sdiffstore",
139            Command::SRandMember { .. } => "srandmember",
140            Command::SPop { .. } => "spop",
141            Command::SMisMember { .. } => "smismember",
142
143            // cluster
144            Command::ClusterInfo => "cluster_info",
145            Command::ClusterNodes => "cluster_nodes",
146            Command::ClusterSlots => "cluster_slots",
147            Command::ClusterKeySlot { .. } => "cluster_keyslot",
148            Command::ClusterMyId => "cluster_myid",
149            Command::ClusterSetSlotImporting { .. } => "cluster_setslot",
150            Command::ClusterSetSlotMigrating { .. } => "cluster_setslot",
151            Command::ClusterSetSlotNode { .. } => "cluster_setslot",
152            Command::ClusterSetSlotStable { .. } => "cluster_setslot",
153            Command::ClusterMeet { .. } => "cluster_meet",
154            Command::ClusterAddSlots { .. } => "cluster_addslots",
155            Command::ClusterAddSlotsRange { .. } => "cluster_addslotsrange",
156            Command::ClusterDelSlots { .. } => "cluster_delslots",
157            Command::ClusterForget { .. } => "cluster_forget",
158            Command::ClusterReplicate { .. } => "cluster_replicate",
159            Command::ClusterFailover { .. } => "cluster_failover",
160            Command::ClusterCountKeysInSlot { .. } => "cluster_countkeysinslot",
161            Command::ClusterGetKeysInSlot { .. } => "cluster_getkeysinslot",
162            Command::Migrate { .. } => "migrate",
163            Command::Restore { .. } => "restore",
164            Command::Asking => "asking",
165
166            // slow log
167            Command::SlowLogGet { .. } => "slowlog",
168            Command::SlowLogLen => "slowlog",
169            Command::SlowLogReset => "slowlog",
170
171            // pub/sub
172            Command::Subscribe { .. } => "subscribe",
173            Command::Unsubscribe { .. } => "unsubscribe",
174            Command::PSubscribe { .. } => "psubscribe",
175            Command::PUnsubscribe { .. } => "punsubscribe",
176            Command::Publish { .. } => "publish",
177            Command::PubSubChannels { .. } => "pubsub",
178            Command::PubSubNumSub { .. } => "pubsub",
179            Command::PubSubNumPat => "pubsub",
180
181            // vector
182            Command::VAdd { .. } => "vadd",
183            Command::VAddBatch { .. } => "vadd_batch",
184            Command::VSim { .. } => "vsim",
185            Command::VRem { .. } => "vrem",
186            Command::VGet { .. } => "vget",
187            Command::VCard { .. } => "vcard",
188            Command::VDim { .. } => "vdim",
189            Command::VInfo { .. } => "vinfo",
190
191            // protobuf
192            Command::ProtoRegister { .. } => "proto.register",
193            Command::ProtoSet { .. } => "proto.set",
194            Command::ProtoGet { .. } => "proto.get",
195            Command::ProtoType { .. } => "proto.type",
196            Command::ProtoSchemas => "proto.schemas",
197            Command::ProtoDescribe { .. } => "proto.describe",
198            Command::ProtoGetField { .. } => "proto.getfield",
199            Command::ProtoSetField { .. } => "proto.setfield",
200            Command::ProtoDelField { .. } => "proto.delfield",
201
202            // acl
203            Command::AclWhoAmI => "acl",
204            Command::AclList => "acl",
205            Command::AclUsers => "acl",
206            Command::AclGetUser { .. } => "acl",
207            Command::AclDelUser { .. } => "acl",
208            Command::AclSetUser { .. } => "acl",
209            Command::AclCat { .. } => "acl",
210
211            Command::RandomKey => "randomkey",
212            Command::Touch { .. } => "touch",
213            Command::Sort { .. } => "sort",
214
215            Command::Unknown(_) => "unknown",
216        }
217    }
218
219    /// Returns `true` if this command mutates state.
220    ///
221    /// Used by the replica write-rejection layer: any command matching this
222    /// predicate is redirected to the primary via MOVED rather than executed
223    /// locally on a read-only replica.
224    pub fn is_write(&self) -> bool {
225        matches!(
226            self,
227            // string mutations
228            Command::Set { .. }
229                | Command::MSet { .. }
230                | Command::Append { .. }
231                | Command::Incr { .. }
232                | Command::Decr { .. }
233                | Command::IncrBy { .. }
234                | Command::DecrBy { .. }
235                | Command::IncrByFloat { .. }
236            // key lifecycle
237                | Command::Del { .. }
238                | Command::Unlink { .. }
239                | Command::Rename { .. }
240                | Command::Copy { .. }
241                | Command::Expire { .. }
242                | Command::Pexpire { .. }
243                | Command::Persist { .. }
244            // list
245                | Command::LPush { .. }
246                | Command::RPush { .. }
247                | Command::LPop { .. }
248                | Command::RPop { .. }
249                | Command::BLPop { .. }
250                | Command::BRPop { .. }
251                | Command::LSet { .. }
252                | Command::LTrim { .. }
253                | Command::LInsert { .. }
254                | Command::LRem { .. }
255            // sorted set
256                | Command::ZAdd { .. }
257                | Command::ZRem { .. }
258                | Command::ZIncrBy { .. }
259                | Command::ZPopMin { .. }
260                | Command::ZPopMax { .. }
261            // hash
262                | Command::HSet { .. }
263                | Command::HDel { .. }
264                | Command::HIncrBy { .. }
265            // set
266                | Command::SAdd { .. }
267                | Command::SRem { .. }
268                | Command::SUnionStore { .. }
269                | Command::SInterStore { .. }
270                | Command::SDiffStore { .. }
271                | Command::SPop { .. }
272            // server / persistence
273                | Command::FlushDb { .. }
274                | Command::ConfigSet { .. }
275                | Command::Exec
276                | Command::BgRewriteAof
277                | Command::BgSave
278                | Command::Restore { .. }
279            // vector
280                | Command::VAdd { .. }
281                | Command::VAddBatch { .. }
282                | Command::VRem { .. }
283            // protobuf
284                | Command::ProtoRegister { .. }
285                | Command::ProtoSet { .. }
286                | Command::ProtoSetField { .. }
287                | Command::ProtoDelField { .. }
288            // acl mutations
289                | Command::AclSetUser { .. }
290                | Command::AclDelUser { .. }
291        ) || matches!(self, Command::Sort { store: Some(_), .. })
292    }
293
294    /// Returns the ACL category bitmask for this command.
295    ///
296    /// Each bit corresponds to a category (read, write, string, list, etc.)
297    /// as defined in `ember-server/src/acl.rs`. Used by the permission check
298    /// to determine whether a user has access to a command.
299    ///
300    /// The bit values match the constants in the acl module:
301    /// READ=1<<0, WRITE=1<<1, STRING=1<<2, LIST=1<<3, SET=1<<4,
302    /// SORTEDSET=1<<5, HASH=1<<6, KEYSPACE=1<<7, SERVER=1<<8,
303    /// CONNECTION=1<<9, TRANSACTION=1<<10, PUBSUB=1<<11, FAST=1<<12,
304    /// SLOW=1<<13, ADMIN=1<<14, DANGEROUS=1<<15, CLUSTER=1<<16
305    pub fn acl_categories(&self) -> u64 {
306        // bitmask constants (duplicated from acl.rs to avoid cross-crate dep)
307        const READ: u64 = 1 << 0;
308        const WRITE: u64 = 1 << 1;
309        const STRING: u64 = 1 << 2;
310        const LIST: u64 = 1 << 3;
311        const SET: u64 = 1 << 4;
312        const SORTEDSET: u64 = 1 << 5;
313        const HASH: u64 = 1 << 6;
314        const KEYSPACE: u64 = 1 << 7;
315        const SERVER: u64 = 1 << 8;
316        const CONNECTION: u64 = 1 << 9;
317        const TRANSACTION: u64 = 1 << 10;
318        const PUBSUB: u64 = 1 << 11;
319        const FAST: u64 = 1 << 12;
320        const SLOW: u64 = 1 << 13;
321        const ADMIN: u64 = 1 << 14;
322        const DANGEROUS: u64 = 1 << 15;
323        const CLUSTER: u64 = 1 << 16;
324
325        match self {
326            // connection
327            Command::Ping(_) | Command::Echo(_) => CONNECTION | FAST,
328            Command::Auth { .. } | Command::Quit => CONNECTION | FAST,
329            Command::ClientId | Command::ClientGetName | Command::ClientSetName { .. } => {
330                CONNECTION | SLOW
331            }
332            Command::ClientList => CONNECTION | ADMIN | SLOW,
333            Command::Monitor => CONNECTION | ADMIN | SLOW,
334
335            // string — reads
336            Command::Get { .. }
337            | Command::MGet { .. }
338            | Command::Strlen { .. }
339            | Command::GetRange { .. } => READ | STRING | FAST,
340
341            // string — writes
342            Command::Set { .. }
343            | Command::MSet { .. }
344            | Command::Append { .. }
345            | Command::SetRange { .. } => WRITE | STRING | SLOW,
346            Command::Incr { .. }
347            | Command::Decr { .. }
348            | Command::IncrBy { .. }
349            | Command::DecrBy { .. }
350            | Command::IncrByFloat { .. } => WRITE | STRING | FAST,
351
352            // keyspace — reads
353            Command::Exists { .. } | Command::Type { .. } | Command::Touch { .. } => {
354                READ | KEYSPACE | FAST
355            }
356            Command::RandomKey => READ | KEYSPACE | FAST,
357            Command::Keys { .. } => READ | KEYSPACE | DANGEROUS | SLOW,
358            Command::Sort { .. } => WRITE | SET | SORTEDSET | LIST | SLOW,
359            Command::Scan { .. }
360            | Command::SScan { .. }
361            | Command::HScan { .. }
362            | Command::ZScan { .. } => READ | KEYSPACE | SLOW,
363            Command::Ttl { .. }
364            | Command::Pttl { .. }
365            | Command::ObjectEncoding { .. }
366            | Command::ObjectRefcount { .. } => READ | KEYSPACE | FAST,
367
368            // keyspace — writes
369            Command::Del { .. } | Command::Unlink { .. } => WRITE | KEYSPACE | FAST,
370            Command::Rename { .. } | Command::Copy { .. } => WRITE | KEYSPACE | SLOW,
371            Command::Expire { .. } | Command::Pexpire { .. } | Command::Persist { .. } => {
372                WRITE | KEYSPACE | FAST
373            }
374
375            // list — reads
376            Command::LRange { .. } | Command::LLen { .. } => READ | LIST | SLOW,
377            Command::LIndex { .. } => READ | LIST | FAST,
378            Command::LPos { .. } => READ | LIST | SLOW,
379
380            // list — writes
381            Command::LPush { .. }
382            | Command::RPush { .. }
383            | Command::LPop { .. }
384            | Command::RPop { .. }
385            | Command::LSet { .. } => WRITE | LIST | FAST,
386            Command::LTrim { .. } | Command::LInsert { .. } | Command::LRem { .. } => {
387                WRITE | LIST | SLOW
388            }
389            Command::BLPop { .. } | Command::BRPop { .. } => WRITE | LIST | SLOW,
390
391            // sorted set — reads
392            Command::ZScore { .. }
393            | Command::ZRank { .. }
394            | Command::ZRevRank { .. }
395            | Command::ZCard { .. }
396            | Command::ZCount { .. } => READ | SORTEDSET | FAST,
397            Command::ZRange { .. }
398            | Command::ZRevRange { .. }
399            | Command::ZRangeByScore { .. }
400            | Command::ZRevRangeByScore { .. } => READ | SORTEDSET | SLOW,
401
402            // sorted set — writes
403            Command::ZAdd { .. }
404            | Command::ZRem { .. }
405            | Command::ZIncrBy { .. }
406            | Command::ZPopMin { .. }
407            | Command::ZPopMax { .. } => WRITE | SORTEDSET | SLOW,
408
409            // hash — reads
410            Command::HGet { .. } | Command::HExists { .. } | Command::HLen { .. } => {
411                READ | HASH | FAST
412            }
413            Command::HGetAll { .. }
414            | Command::HKeys { .. }
415            | Command::HVals { .. }
416            | Command::HMGet { .. } => READ | HASH | SLOW,
417
418            // hash — writes
419            Command::HSet { .. } | Command::HDel { .. } | Command::HIncrBy { .. } => {
420                WRITE | HASH | FAST
421            }
422
423            // set — reads
424            Command::SIsMember { .. } | Command::SCard { .. } | Command::SMisMember { .. } => {
425                READ | SET | FAST
426            }
427            Command::SMembers { .. } | Command::SRandMember { .. } => READ | SET | SLOW,
428            Command::SUnion { .. } | Command::SInter { .. } | Command::SDiff { .. } => {
429                READ | SET | SLOW
430            }
431
432            // set — writes
433            Command::SAdd { .. } | Command::SRem { .. } | Command::SPop { .. } => {
434                WRITE | SET | FAST
435            }
436            Command::SUnionStore { .. }
437            | Command::SInterStore { .. }
438            | Command::SDiffStore { .. } => WRITE | SET | SLOW,
439
440            // server
441            Command::DbSize => SERVER | KEYSPACE | READ | FAST,
442            Command::Info { .. } => SERVER | SLOW,
443            Command::Time | Command::LastSave | Command::Role => SERVER | FAST,
444            Command::BgSave | Command::BgRewriteAof => SERVER | ADMIN | SLOW,
445            Command::FlushDb { .. } => KEYSPACE | WRITE | ADMIN | DANGEROUS | SLOW,
446            Command::ConfigGet { .. } => SERVER | ADMIN | SLOW,
447            Command::ConfigSet { .. } | Command::ConfigRewrite => SERVER | ADMIN | SLOW,
448            Command::SlowLogGet { .. } | Command::SlowLogLen | Command::SlowLogReset => {
449                SERVER | ADMIN | SLOW
450            }
451
452            // transactions
453            Command::Multi | Command::Exec | Command::Discard => TRANSACTION | FAST,
454            Command::Watch { .. } | Command::Unwatch => TRANSACTION | FAST,
455
456            // cluster
457            Command::ClusterInfo
458            | Command::ClusterNodes
459            | Command::ClusterSlots
460            | Command::ClusterKeySlot { .. }
461            | Command::ClusterMyId => CLUSTER | SLOW,
462            Command::ClusterSetSlotImporting { .. }
463            | Command::ClusterSetSlotMigrating { .. }
464            | Command::ClusterSetSlotNode { .. }
465            | Command::ClusterSetSlotStable { .. }
466            | Command::ClusterMeet { .. }
467            | Command::ClusterAddSlots { .. }
468            | Command::ClusterAddSlotsRange { .. }
469            | Command::ClusterDelSlots { .. }
470            | Command::ClusterForget { .. }
471            | Command::ClusterReplicate { .. }
472            | Command::ClusterFailover { .. }
473            | Command::ClusterCountKeysInSlot { .. }
474            | Command::ClusterGetKeysInSlot { .. } => CLUSTER | ADMIN | SLOW,
475            Command::Migrate { .. } | Command::Restore { .. } => CLUSTER | KEYSPACE | WRITE | SLOW,
476            Command::Asking => CLUSTER | FAST,
477
478            // pub/sub
479            Command::Subscribe { .. }
480            | Command::Unsubscribe { .. }
481            | Command::PSubscribe { .. }
482            | Command::PUnsubscribe { .. } => PUBSUB | SLOW,
483            Command::Publish { .. } => PUBSUB | FAST,
484            Command::PubSubChannels { .. }
485            | Command::PubSubNumSub { .. }
486            | Command::PubSubNumPat => PUBSUB | SLOW,
487
488            // vector
489            Command::VAdd { .. } | Command::VAddBatch { .. } | Command::VRem { .. } => WRITE | SLOW,
490            Command::VSim { .. }
491            | Command::VGet { .. }
492            | Command::VCard { .. }
493            | Command::VDim { .. }
494            | Command::VInfo { .. } => READ | SLOW,
495
496            // protobuf
497            Command::ProtoRegister { .. } => WRITE | SERVER | SLOW,
498            Command::ProtoSet { .. }
499            | Command::ProtoSetField { .. }
500            | Command::ProtoDelField { .. } => WRITE | STRING | SLOW,
501            Command::ProtoGet { .. }
502            | Command::ProtoType { .. }
503            | Command::ProtoSchemas
504            | Command::ProtoDescribe { .. }
505            | Command::ProtoGetField { .. } => READ | STRING | SLOW,
506
507            // ACL commands
508            Command::AclWhoAmI => CONNECTION | FAST,
509            Command::AclList | Command::AclUsers | Command::AclGetUser { .. } => {
510                SERVER | ADMIN | SLOW
511            }
512            Command::AclSetUser { .. } | Command::AclDelUser { .. } => SERVER | ADMIN | SLOW,
513            Command::AclCat { .. } => SERVER | SLOW,
514
515            Command::Unknown(_) => 0,
516        }
517    }
518
519    /// Returns the primary key for this command, if there is one.
520    ///
521    /// Used to calculate the hash slot for MOVED redirects on replicas.
522    /// For multi-key commands, returns the first key.
523    pub fn primary_key(&self) -> Option<&str> {
524        match self {
525            Command::Get { key }
526            | Command::Set { key, .. }
527            | Command::Incr { key }
528            | Command::Decr { key }
529            | Command::IncrBy { key, .. }
530            | Command::DecrBy { key, .. }
531            | Command::IncrByFloat { key, .. }
532            | Command::Append { key, .. }
533            | Command::Strlen { key }
534            | Command::GetRange { key, .. }
535            | Command::SetRange { key, .. }
536            | Command::Persist { key }
537            | Command::Expire { key, .. }
538            | Command::Pexpire { key, .. }
539            | Command::Ttl { key }
540            | Command::Pttl { key }
541            | Command::Type { key }
542            | Command::Rename { key, .. }
543            | Command::ObjectEncoding { key }
544            | Command::ObjectRefcount { key }
545            | Command::LPush { key, .. }
546            | Command::RPush { key, .. }
547            | Command::LPop { key }
548            | Command::RPop { key }
549            | Command::LRange { key, .. }
550            | Command::LLen { key }
551            | Command::LIndex { key, .. }
552            | Command::LSet { key, .. }
553            | Command::LTrim { key, .. }
554            | Command::LInsert { key, .. }
555            | Command::LRem { key, .. }
556            | Command::LPos { key, .. }
557            | Command::ZAdd { key, .. }
558            | Command::ZRem { key, .. }
559            | Command::ZScore { key, .. }
560            | Command::ZRank { key, .. }
561            | Command::ZRange { key, .. }
562            | Command::ZCard { key }
563            | Command::HSet { key, .. }
564            | Command::HGet { key, .. }
565            | Command::HGetAll { key }
566            | Command::HDel { key, .. }
567            | Command::HExists { key, .. }
568            | Command::HLen { key }
569            | Command::HIncrBy { key, .. }
570            | Command::HKeys { key }
571            | Command::HVals { key }
572            | Command::HMGet { key, .. }
573            | Command::SAdd { key, .. }
574            | Command::SRem { key, .. }
575            | Command::SMembers { key }
576            | Command::SIsMember { key, .. }
577            | Command::SCard { key }
578            | Command::SScan { key, .. }
579            | Command::SRandMember { key, .. }
580            | Command::SPop { key, .. }
581            | Command::SMisMember { key, .. }
582            | Command::HScan { key, .. }
583            | Command::ZScan { key, .. }
584            | Command::VAdd { key, .. }
585            | Command::VAddBatch { key, .. }
586            | Command::VSim { key, .. }
587            | Command::VRem { key, .. }
588            | Command::VGet { key, .. }
589            | Command::VCard { key }
590            | Command::VDim { key }
591            | Command::VInfo { key }
592            | Command::ProtoSet { key, .. }
593            | Command::ProtoGet { key }
594            | Command::ProtoType { key }
595            | Command::ProtoGetField { key, .. }
596            | Command::ProtoSetField { key, .. }
597            | Command::ProtoDelField { key, .. }
598            | Command::Restore { key, .. }
599            | Command::Sort { key, .. } => Some(key),
600            Command::Copy { source, .. } => Some(source),
601            Command::Del { keys }
602            | Command::Unlink { keys }
603            | Command::Exists { keys }
604            | Command::Touch { keys }
605            | Command::MGet { keys }
606            | Command::BLPop { keys, .. }
607            | Command::BRPop { keys, .. }
608            | Command::SUnion { keys }
609            | Command::SInter { keys }
610            | Command::SDiff { keys } => keys.first().map(String::as_str),
611            Command::SUnionStore { dest, .. }
612            | Command::SInterStore { dest, .. }
613            | Command::SDiffStore { dest, .. } => Some(dest),
614            Command::MSet { pairs } => pairs.first().map(|(k, _)| k.as_str()),
615            _ => None,
616        }
617    }
618}