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