1use crate::ValkeyResult;
5use crate::value::FromValkeyValue;
6use crate::value::ValkeyValue;
7use crate::value::to_glide_error;
8
9#[derive(Debug, Clone, PartialEq)]
15#[non_exhaustive]
16pub enum ValueType {
17 None,
19 String,
21 List,
23 Set,
25 ZSet,
27 Hash,
29 Stream,
31 Unknown(String),
33}
34
35impl<T: AsRef<str>> From<T> for ValueType {
36 fn from(s: T) -> Self {
37 match s.as_ref() {
38 "none" => Self::None,
39 "string" => Self::String,
40 "list" => Self::List,
41 "set" => Self::Set,
42 "zset" => Self::ZSet,
43 "hash" => Self::Hash,
44 "stream" => Self::Stream,
45 s => Self::Unknown(s.to_string()),
46 }
47 }
48}
49
50impl From<ValueType> for String {
51 fn from(v: ValueType) -> Self {
52 match v {
53 ValueType::None => "none".to_string(),
54 ValueType::String => "string".to_string(),
55 ValueType::List => "list".to_string(),
56 ValueType::Set => "set".to_string(),
57 ValueType::ZSet => "zset".to_string(),
58 ValueType::Hash => "hash".to_string(),
59 ValueType::Stream => "stream".to_string(),
60 ValueType::Unknown(s) => s,
61 }
62 }
63}
64
65impl FromValkeyValue for ValueType {
66 fn from_owned_valkey_value(value: ValkeyValue) -> ValkeyResult<Self> {
67 match value {
68 ValkeyValue::SimpleString(s) => Ok(s.into()),
69 other => Err(to_glide_error(
70 other,
71 "Value type should be a simple string.",
72 )),
73 }
74 }
75}
76
77#[cfg(test)]
78mod value_type_tests {
79 use super::*;
80
81 #[test]
82 fn value_type_round_trips_known_names() {
83 for name in ["none", "string", "list", "set", "zset", "hash", "stream"] {
84 let value_type = ValueType::from(name);
85 assert!(!matches!(value_type, ValueType::Unknown(_)), "{name}");
86 assert_eq!(String::from(value_type), name);
87 }
88 }
89
90 #[test]
91 fn value_type_keeps_unknown_names() {
92 assert_eq!(
93 ValueType::from("vectorset"),
94 ValueType::Unknown("vectorset".to_string())
95 );
96 assert_eq!(String::from(ValueType::from("vectorset")), "vectorset");
97 }
98
99 #[test]
100 fn value_type_decodes_simple_string_only() {
101 let decoded =
102 ValueType::from_owned_valkey_value(ValkeyValue::SimpleString("zset".into())).unwrap();
103 assert_eq!(decoded, ValueType::ZSet);
104 assert!(
105 ValueType::from_owned_valkey_value(ValkeyValue::BulkString("zset".into())).is_err()
106 );
107 }
108}
109
110#[derive(Debug, PartialEq, Clone)]
117#[non_exhaustive]
118pub enum IntegerReplyOrNoOp {
119 IntegerReply(usize),
121 ExistsButNotRelevant,
123 NotExists,
125}
126
127impl IntegerReplyOrNoOp {
128 pub fn raw(&self) -> isize {
130 match self {
131 Self::IntegerReply(s) => *s as isize,
132 Self::ExistsButNotRelevant => -1,
133 Self::NotExists => -2,
134 }
135 }
136
137 fn eq_signed(&self, other: i64) -> bool {
139 match self {
140 Self::IntegerReply(s) => usize::try_from(other).is_ok_and(|o| o == *s),
141 Self::ExistsButNotRelevant => other == -1,
142 Self::NotExists => other == -2,
143 }
144 }
145}
146
147impl FromValkeyValue for IntegerReplyOrNoOp {
148 fn from_owned_valkey_value(value: ValkeyValue) -> ValkeyResult<Self> {
149 match value {
150 ValkeyValue::Int(-2) => Ok(Self::NotExists),
151 ValkeyValue::Int(-1) => Ok(Self::ExistsButNotRelevant),
152 ValkeyValue::Int(s) => usize::try_from(s).map(Self::IntegerReply).map_err(|_| {
153 to_glide_error(
154 ValkeyValue::Int(s),
155 "Value should be a non-negative integer, -1, or -2.",
156 )
157 }),
158 other => Err(to_glide_error(other, "Value should be an integer.")),
159 }
160 }
161}
162
163impl PartialEq<isize> for IntegerReplyOrNoOp {
164 fn eq(&self, other: &isize) -> bool {
165 i64::try_from(*other).is_ok_and(|o| self.eq_signed(o))
166 }
167}
168
169impl PartialEq<usize> for IntegerReplyOrNoOp {
170 fn eq(&self, other: &usize) -> bool {
171 matches!(self, Self::IntegerReply(s) if s == other)
172 }
173}
174
175impl PartialEq<i32> for IntegerReplyOrNoOp {
176 fn eq(&self, other: &i32) -> bool {
177 self.eq_signed(i64::from(*other))
178 }
179}
180
181impl PartialEq<u32> for IntegerReplyOrNoOp {
182 fn eq(&self, other: &u32) -> bool {
183 matches!(self, Self::IntegerReply(s) if usize::try_from(*other).is_ok_and(|o| o == *s))
184 }
185}
186
187#[cfg(test)]
188mod integer_reply_or_noop_tests {
189 use super::*;
190
191 #[test]
192 fn integer_reply_or_no_op_decodes_sentinels() {
193 assert_eq!(decode(-2), IntegerReplyOrNoOp::NotExists);
194 assert_eq!(decode(-1), IntegerReplyOrNoOp::ExistsButNotRelevant);
195 assert_eq!(decode(0), IntegerReplyOrNoOp::IntegerReply(0));
196 assert_eq!(decode(42), IntegerReplyOrNoOp::IntegerReply(42));
197 assert!(IntegerReplyOrNoOp::from_owned_valkey_value(ValkeyValue::Nil).is_err());
198 }
199
200 #[test]
201 fn integer_reply_or_no_op_rejects_other_negatives() {
202 for i in [-3, i64::MIN] {
203 assert!(IntegerReplyOrNoOp::from_owned_valkey_value(ValkeyValue::Int(i)).is_err());
204 }
205 }
206
207 #[test]
208 fn integer_reply_or_no_op_raw() {
209 assert_eq!(IntegerReplyOrNoOp::NotExists.raw(), -2);
210 assert_eq!(IntegerReplyOrNoOp::ExistsButNotRelevant.raw(), -1);
211 assert_eq!(IntegerReplyOrNoOp::IntegerReply(7).raw(), 7);
212 }
213
214 #[test]
215 fn integer_reply_or_no_op_comparisons() {
216 assert_eq!(IntegerReplyOrNoOp::NotExists, -2isize);
218 assert_eq!(IntegerReplyOrNoOp::ExistsButNotRelevant, -1i32);
219 assert_eq!(IntegerReplyOrNoOp::IntegerReply(7), 7usize);
220 assert_eq!(IntegerReplyOrNoOp::IntegerReply(7), 7u32);
221 assert_ne!(IntegerReplyOrNoOp::NotExists, 2usize);
222
223 let large = IntegerReplyOrNoOp::IntegerReply((1usize << 32) + 7);
225 assert_ne!(large, 7u32);
226 assert_ne!(large, 7i32);
227 assert_eq!(large, (1usize << 32) + 7);
228 assert_eq!(large, (1isize << 32) + 7);
229
230 let max = IntegerReplyOrNoOp::IntegerReply(usize::MAX);
232 assert_ne!(max, -1isize);
233 assert_ne!(max, -1i32);
234 }
235
236 fn decode(i: i64) -> IntegerReplyOrNoOp {
237 IntegerReplyOrNoOp::from_owned_valkey_value(ValkeyValue::Int(i)).unwrap()
238 }
239}