Skip to main content

dynomite/msg/
msg_type.rs

1//! Message type discriminant.
2//!
3//! Every datastore-bound request and response carries a `MsgType` tag
4//! that identifies which command or reply class the message belongs
5//! to. The variants are paired one-for-one with the entries in the
6//! reference engine's `MSG_TYPE_CODEC` X-macro: order, count, and
7//! string spellings all line up so the integer indices and the names
8//! returned by [`MsgType::name`] remain compatible across the two
9//! implementations.
10//!
11//! The enum is exhaustive: 178 named variants plus the trailing
12//! `EndIdx` sentinel. Helpers are provided to round-trip integer
13//! indices and to classify a tag as a request or a response.
14
15use core::fmt;
16
17macro_rules! define_msg_types {
18    ($( ($variant:ident, $name:literal) ),+ $(,)?) => {
19        /// Message type discriminant.
20        ///
21        /// Variants enumerate every datastore command and response
22        /// class supported by the engine, in declaration order.
23        ///
24        /// # Examples
25        ///
26        /// ```
27        /// use dynomite::msg::MsgType;
28        ///
29        /// assert_eq!(MsgType::Unknown.as_index(), 0);
30        /// assert_eq!(MsgType::ReqMcGet.name(), "REQ_MC_GET");
31        /// assert!(MsgType::ReqRedisGet.is_request());
32        /// ```
33        #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
34        #[non_exhaustive]
35        pub enum MsgType {
36            $(
37                #[doc = concat!("`", $name, "`")]
38                $variant,
39            )+
40        }
41
42        impl MsgType {
43            const ALL: &'static [MsgType] = &[ $( MsgType::$variant, )+ ];
44            const NAMES: &'static [&'static str] = &[ $( $name, )+ ];
45        }
46    };
47}
48
49define_msg_types![
50    (Unknown, "UNKNOWN"),
51    (ReqMcGet, "REQ_MC_GET"),
52    (ReqMcGets, "REQ_MC_GETS"),
53    (ReqMcDelete, "REQ_MC_DELETE"),
54    (ReqMcCas, "REQ_MC_CAS"),
55    (ReqMcSet, "REQ_MC_SET"),
56    (ReqMcAdd, "REQ_MC_ADD"),
57    (ReqMcReplace, "REQ_MC_REPLACE"),
58    (ReqMcAppend, "REQ_MC_APPEND"),
59    (ReqMcPrepend, "REQ_MC_PREPEND"),
60    (ReqMcIncr, "REQ_MC_INCR"),
61    (ReqMcDecr, "REQ_MC_DECR"),
62    (ReqMcTouch, "REQ_MC_TOUCH"),
63    (ReqMcQuit, "REQ_MC_QUIT"),
64    (RspMcNum, "RSP_MC_NUM"),
65    (RspMcStored, "RSP_MC_STORED"),
66    (RspMcNotStored, "RSP_MC_NOT_STORED"),
67    (RspMcExists, "RSP_MC_EXISTS"),
68    (RspMcNotFound, "RSP_MC_NOT_FOUND"),
69    (RspMcEnd, "RSP_MC_END"),
70    (RspMcValue, "RSP_MC_VALUE"),
71    (RspMcDeleted, "RSP_MC_DELETED"),
72    (RspMcTouched, "RSP_MC_TOUCHED"),
73    (RspMcError, "RSP_MC_ERROR"),
74    (RspMcClientError, "RSP_MC_CLIENT_ERROR"),
75    (RspMcServerError, "RSP_MC_SERVER_ERROR"),
76    (ReqRedisDel, "REQ_REDIS_DEL"),
77    (ReqRedisExists, "REQ_REDIS_EXISTS"),
78    (ReqRedisExpire, "REQ_REDIS_EXPIRE"),
79    (ReqRedisExpireat, "REQ_REDIS_EXPIREAT"),
80    (ReqRedisPexpire, "REQ_REDIS_PEXPIRE"),
81    (ReqRedisPexpireat, "REQ_REDIS_PEXPIREAT"),
82    (ReqRedisPersist, "REQ_REDIS_PERSIST"),
83    (ReqRedisPttl, "REQ_REDIS_PTTL"),
84    (ReqRedisScan, "REQ_REDIS_SCAN"),
85    (ReqRedisSort, "REQ_REDIS_SORT"),
86    (ReqRedisTtl, "REQ_REDIS_TTL"),
87    (ReqRedisType, "REQ_REDIS_TYPE"),
88    (ReqRedisAppend, "REQ_REDIS_APPEND"),
89    (ReqRedisBitcount, "REQ_REDIS_BITCOUNT"),
90    (ReqRedisBitpos, "REQ_REDIS_BITPOS"),
91    (ReqRedisDecr, "REQ_REDIS_DECR"),
92    (ReqRedisDecrby, "REQ_REDIS_DECRBY"),
93    (ReqRedisDump, "REQ_REDIS_DUMP"),
94    (ReqRedisGet, "REQ_REDIS_GET"),
95    (ReqRedisGetbit, "REQ_REDIS_GETBIT"),
96    (ReqRedisGetrange, "REQ_REDIS_GETRANGE"),
97    (ReqRedisGetset, "REQ_REDIS_GETSET"),
98    (ReqRedisIncr, "REQ_REDIS_INCR"),
99    (ReqRedisIncrby, "REQ_REDIS_INCRBY"),
100    (ReqRedisIncrbyfloat, "REQ_REDIS_INCRBYFLOAT"),
101    (ReqRedisMset, "REQ_REDIS_MSET"),
102    (ReqRedisMget, "REQ_REDIS_MGET"),
103    (ReqRedisPsetex, "REQ_REDIS_PSETEX"),
104    (ReqRedisRestore, "REQ_REDIS_RESTORE"),
105    (ReqRedisSet, "REQ_REDIS_SET"),
106    (ReqRedisSetbit, "REQ_REDIS_SETBIT"),
107    (ReqRedisSetex, "REQ_REDIS_SETEX"),
108    (ReqRedisSetnx, "REQ_REDIS_SETNX"),
109    (ReqRedisSetrange, "REQ_REDIS_SETRANGE"),
110    (ReqRedisStrlen, "REQ_REDIS_STRLEN"),
111    (ReqRedisHdel, "REQ_REDIS_HDEL"),
112    (ReqRedisHexists, "REQ_REDIS_HEXISTS"),
113    (ReqRedisHget, "REQ_REDIS_HGET"),
114    (ReqRedisHgetall, "REQ_REDIS_HGETALL"),
115    (ReqRedisHincrby, "REQ_REDIS_HINCRBY"),
116    (ReqRedisHincrbyfloat, "REQ_REDIS_HINCRBYFLOAT"),
117    (ReqRedisHkeys, "REQ_REDIS_HKEYS"),
118    (ReqRedisHlen, "REQ_REDIS_HLEN"),
119    (ReqRedisHmget, "REQ_REDIS_HMGET"),
120    (ReqRedisHmset, "REQ_REDIS_HMSET"),
121    (ReqRedisHset, "REQ_REDIS_HSET"),
122    (ReqRedisHsetnx, "REQ_REDIS_HSETNX"),
123    (ReqRedisHscan, "REQ_REDIS_HSCAN"),
124    (ReqRedisHvals, "REQ_REDIS_HVALS"),
125    (ReqRedisHstrlen, "REQ_REDIS_HSTRLEN"),
126    (ReqRedisKeys, "REQ_REDIS_KEYS"),
127    (ReqRedisInfo, "REQ_REDIS_INFO"),
128    (ReqRedisLindex, "REQ_REDIS_LINDEX"),
129    (ReqRedisLinsert, "REQ_REDIS_LINSERT"),
130    (ReqRedisLlen, "REQ_REDIS_LLEN"),
131    (ReqRedisLpop, "REQ_REDIS_LPOP"),
132    (ReqRedisLpush, "REQ_REDIS_LPUSH"),
133    (ReqRedisLpushx, "REQ_REDIS_LPUSHX"),
134    (ReqRedisLrange, "REQ_REDIS_LRANGE"),
135    (ReqRedisLrem, "REQ_REDIS_LREM"),
136    (ReqRedisLset, "REQ_REDIS_LSET"),
137    (ReqRedisLtrim, "REQ_REDIS_LTRIM"),
138    (ReqRedisPing, "REQ_REDIS_PING"),
139    (ReqRedisQuit, "REQ_REDIS_QUIT"),
140    (ReqRedisRpop, "REQ_REDIS_RPOP"),
141    (ReqRedisRpoplpush, "REQ_REDIS_RPOPLPUSH"),
142    (ReqRedisRpush, "REQ_REDIS_RPUSH"),
143    (ReqRedisRpushx, "REQ_REDIS_RPUSHX"),
144    (ReqRedisSadd, "REQ_REDIS_SADD"),
145    (ReqRedisScard, "REQ_REDIS_SCARD"),
146    (ReqRedisSdiff, "REQ_REDIS_SDIFF"),
147    (ReqRedisSdiffstore, "REQ_REDIS_SDIFFSTORE"),
148    (ReqRedisSinter, "REQ_REDIS_SINTER"),
149    (ReqRedisSinterstore, "REQ_REDIS_SINTERSTORE"),
150    (ReqRedisSismember, "REQ_REDIS_SISMEMBER"),
151    (ReqRedisSlaveof, "REQ_REDIS_SLAVEOF"),
152    (ReqRedisSmembers, "REQ_REDIS_SMEMBERS"),
153    (ReqRedisSmove, "REQ_REDIS_SMOVE"),
154    (ReqRedisSpop, "REQ_REDIS_SPOP"),
155    (ReqRedisSrandmember, "REQ_REDIS_SRANDMEMBER"),
156    (ReqRedisSrem, "REQ_REDIS_SREM"),
157    (ReqRedisSunion, "REQ_REDIS_SUNION"),
158    (ReqRedisSunionstore, "REQ_REDIS_SUNIONSTORE"),
159    (ReqRedisSscan, "REQ_REDIS_SSCAN"),
160    (ReqRedisZadd, "REQ_REDIS_ZADD"),
161    (ReqRedisZcard, "REQ_REDIS_ZCARD"),
162    (ReqRedisZcount, "REQ_REDIS_ZCOUNT"),
163    (ReqRedisZincrby, "REQ_REDIS_ZINCRBY"),
164    (ReqRedisZinterstore, "REQ_REDIS_ZINTERSTORE"),
165    (ReqRedisZlexcount, "REQ_REDIS_ZLEXCOUNT"),
166    (ReqRedisZrange, "REQ_REDIS_ZRANGE"),
167    (ReqRedisZrangebylex, "REQ_REDIS_ZRANGEBYLEX"),
168    (ReqRedisZrangebyscore, "REQ_REDIS_ZRANGEBYSCORE"),
169    (ReqRedisZrank, "REQ_REDIS_ZRANK"),
170    (ReqRedisZrem, "REQ_REDIS_ZREM"),
171    (ReqRedisZremrangebyrank, "REQ_REDIS_ZREMRANGEBYRANK"),
172    (ReqRedisZremrangebylex, "REQ_REDIS_ZREMRANGEBYLEX"),
173    (ReqRedisZremrangebyscore, "REQ_REDIS_ZREMRANGEBYSCORE"),
174    (ReqRedisZrevrange, "REQ_REDIS_ZREVRANGE"),
175    (ReqRedisZrevrangebylex, "REQ_REDIS_ZREVRANGEBYLEX"),
176    (ReqRedisZrevrangebyscore, "REQ_REDIS_ZREVRANGEBYSCORE"),
177    (ReqRedisZrevrank, "REQ_REDIS_ZREVRANK"),
178    (ReqRedisZscore, "REQ_REDIS_ZSCORE"),
179    (ReqRedisZunionstore, "REQ_REDIS_ZUNIONSTORE"),
180    (ReqRedisZscan, "REQ_REDIS_ZSCAN"),
181    (ReqRedisEval, "REQ_REDIS_EVAL"),
182    (ReqRedisEvalsha, "REQ_REDIS_EVALSHA"),
183    (ReqRedisGeoadd, "REQ_REDIS_GEOADD"),
184    (ReqRedisGeoradius, "REQ_REDIS_GEORADIUS"),
185    (ReqRedisGeodist, "REQ_REDIS_GEODIST"),
186    (ReqRedisGeohash, "REQ_REDIS_GEOHASH"),
187    (ReqRedisGeopos, "REQ_REDIS_GEOPOS"),
188    (ReqRedisGeoradiusbymember, "REQ_REDIS_GEORADIUSBYMEMBER"),
189    (ReqRedisUnlink, "REQ_REDIS_UNLINK"),
190    (ReqRedisJsonset, "REQ_REDIS_JSONSET"),
191    (ReqRedisJsonget, "REQ_REDIS_JSONGET"),
192    (ReqRedisJsondel, "REQ_REDIS_JSONDEL"),
193    (ReqRedisJsontype, "REQ_REDIS_JSONTYPE"),
194    (ReqRedisJsonmget, "REQ_REDIS_JSONMGET"),
195    (ReqRedisJsonarrappend, "REQ_REDIS_JSONARRAPPEND"),
196    (ReqRedisJsonarrinsert, "REQ_REDIS_JSONARRINSERT"),
197    (ReqRedisJsonarrlen, "REQ_REDIS_JSONARRLEN"),
198    (ReqRedisJsonobjkeys, "REQ_REDIS_JSONOBJKEYS"),
199    (ReqRedisJsonobjlen, "REQ_REDIS_JSONOBJLEN"),
200    (ReqRedisPfadd, "REQ_REDIS_PFADD"),
201    (ReqRedisPfcount, "REQ_REDIS_PFCOUNT"),
202    (ReqRedisConfig, "REQ_REDIS_CONFIG"),
203    (ReqRedisScript, "REQ_REDIS_SCRIPT"),
204    (ReqRedisScriptLoad, "REQ_REDIS_SCRIPT_LOAD"),
205    (ReqRedisScriptExists, "REQ_REDIS_SCRIPT_EXISTS"),
206    (ReqRedisScriptFlush, "REQ_REDIS_SCRIPT_FLUSH"),
207    (ReqRedisScriptKill, "REQ_REDIS_SCRIPT_KILL"),
208    (RspRedisStatus, "RSP_REDIS_STATUS"),
209    (RspRedisInteger, "RSP_REDIS_INTEGER"),
210    (RspRedisBulk, "RSP_REDIS_BULK"),
211    (RspRedisMultibulk, "RSP_REDIS_MULTIBULK"),
212    (RspRedisError, "RSP_REDIS_ERROR"),
213    (RspRedisErrorErr, "RSP_REDIS_ERROR_ERR"),
214    (RspRedisErrorOom, "RSP_REDIS_ERROR_OOM"),
215    (RspRedisErrorBusy, "RSP_REDIS_ERROR_BUSY"),
216    (RspRedisErrorNoauth, "RSP_REDIS_ERROR_NOAUTH"),
217    (RspRedisErrorLoading, "RSP_REDIS_ERROR_LOADING"),
218    (RspRedisErrorBusykey, "RSP_REDIS_ERROR_BUSYKEY"),
219    (RspRedisErrorMisconf, "RSP_REDIS_ERROR_MISCONF"),
220    (RspRedisErrorNoscript, "RSP_REDIS_ERROR_NOSCRIPT"),
221    (RspRedisErrorReadonly, "RSP_REDIS_ERROR_READONLY"),
222    (RspRedisErrorWrongtype, "RSP_REDIS_ERROR_WRONGTYPE"),
223    (RspRedisErrorExecabort, "RSP_REDIS_ERROR_EXECABORT"),
224    (RspRedisErrorMasterdown, "RSP_REDIS_ERROR_MASTERDOWN"),
225    (RspRedisErrorNoreplicas, "RSP_REDIS_ERROR_NOREPLICAS"),
226    (HackSettingConnConsistency, "HACK_SETTING_CONN_CONSISTENCY"),
227    (Sentinel, "SENTINEL"),
228    (ReqRedisFtCreate, "REQ_REDIS_FT_CREATE"),
229    (ReqRedisFtSearch, "REQ_REDIS_FT_SEARCH"),
230    (ReqRedisFtInfo, "REQ_REDIS_FT_INFO"),
231    (ReqRedisFtList, "REQ_REDIS_FT_LIST"),
232    (ReqRedisFtDropindex, "REQ_REDIS_FT_DROPINDEX"),
233    (ReqRedisFtRegex, "REQ_REDIS_FT_REGEX"),
234    (ReqRedisFtUnknown, "REQ_REDIS_FT_UNKNOWN"),
235    (EndIdx, "END_IDX"),
236];
237
238impl MsgType {
239    /// Number of declared variants, including `Unknown`, `Sentinel`,
240    /// and `EndIdx`.
241    ///
242    /// # Examples
243    ///
244    /// ```
245    /// use dynomite::msg::MsgType;
246    /// assert!(MsgType::COUNT > 100);
247    /// ```
248    pub const COUNT: usize = Self::ALL.len();
249
250    /// Integer index of this variant. Matches the integer value the
251    /// reference engine assigns to the corresponding `MSG_TYPE_CODEC`
252    /// entry.
253    ///
254    /// # Examples
255    ///
256    /// ```
257    /// use dynomite::msg::MsgType;
258    /// assert_eq!(MsgType::Unknown.as_index(), 0);
259    /// assert_eq!(MsgType::ReqMcGet.as_index(), 1);
260    /// ```
261    #[must_use]
262    pub const fn as_index(self) -> u32 {
263        self as u32
264    }
265
266    /// Recover the variant from its integer index. Returns `None`
267    /// when `index` is out of range.
268    ///
269    /// # Examples
270    ///
271    /// ```
272    /// use dynomite::msg::MsgType;
273    ///
274    /// assert_eq!(MsgType::from_index(0), Some(MsgType::Unknown));
275    /// assert_eq!(MsgType::from_index(MsgType::COUNT as u32), None);
276    /// ```
277    #[must_use]
278    pub fn from_index(index: u32) -> Option<MsgType> {
279        Self::ALL.get(index as usize).copied()
280    }
281
282    /// Canonical uppercase name as it appears in the C source.
283    ///
284    /// # Examples
285    ///
286    /// ```
287    /// use dynomite::msg::MsgType;
288    /// assert_eq!(MsgType::ReqRedisGet.name(), "REQ_REDIS_GET");
289    /// assert_eq!(MsgType::EndIdx.name(), "END_IDX");
290    /// ```
291    #[must_use]
292    pub fn name(&self) -> &'static str {
293        Self::NAMES[self.as_index() as usize]
294    }
295
296    /// True when this variant identifies a datastore request
297    /// (`REQ_*`).
298    ///
299    /// # Examples
300    ///
301    /// ```
302    /// use dynomite::msg::MsgType;
303    /// assert!(MsgType::ReqMcGet.is_request());
304    /// assert!(!MsgType::RspMcStored.is_request());
305    /// assert!(!MsgType::Unknown.is_request());
306    /// ```
307    #[must_use]
308    pub fn is_request(&self) -> bool {
309        self.name().starts_with("REQ_")
310    }
311
312    /// True when this variant identifies a datastore response
313    /// (`RSP_*`).
314    ///
315    /// # Examples
316    ///
317    /// ```
318    /// use dynomite::msg::MsgType;
319    /// assert!(MsgType::RspRedisBulk.is_response());
320    /// assert!(!MsgType::ReqMcGet.is_response());
321    /// ```
322    #[must_use]
323    pub fn is_response(&self) -> bool {
324        self.name().starts_with("RSP_")
325    }
326}
327
328impl fmt::Display for MsgType {
329    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
330        f.write_str(self.name())
331    }
332}
333
334#[cfg(test)]
335mod tests {
336    use super::*;
337
338    #[test]
339    fn unknown_is_zero() {
340        assert_eq!(MsgType::Unknown.as_index(), 0);
341        assert_eq!(MsgType::Unknown.name(), "UNKNOWN");
342    }
343
344    #[test]
345    fn end_idx_is_last() {
346        assert_eq!(
347            MsgType::EndIdx.as_index() + 1,
348            u32::try_from(MsgType::COUNT).unwrap(),
349        );
350    }
351
352    #[test]
353    fn from_index_round_trip() {
354        let count_u32 = u32::try_from(MsgType::COUNT).unwrap();
355        for i in 0..count_u32 {
356            let ty = MsgType::from_index(i).unwrap();
357            assert_eq!(ty.as_index(), i);
358        }
359        assert!(MsgType::from_index(count_u32).is_none());
360        assert!(MsgType::from_index(u32::MAX).is_none());
361    }
362
363    #[test]
364    fn classification_partition() {
365        let count_u32 = u32::try_from(MsgType::COUNT).unwrap();
366        for i in 0..count_u32 {
367            let ty = MsgType::from_index(i).unwrap();
368            // Each variant is at most one of request/response.
369            assert!(!(ty.is_request() && ty.is_response()));
370        }
371        assert!(MsgType::ReqMcSet.is_request());
372        assert!(MsgType::RspRedisStatus.is_response());
373        assert!(!MsgType::Sentinel.is_request());
374        assert!(!MsgType::Sentinel.is_response());
375    }
376
377    #[test]
378    fn names_are_unique_uppercase() {
379        let mut seen = std::collections::HashSet::new();
380        for &name in MsgType::NAMES {
381            assert!(name.chars().all(|c| c.is_ascii_uppercase() || c == '_'));
382            assert!(seen.insert(name), "duplicate name: {name}");
383        }
384    }
385}