1use crate::error::Error as TError;
2use crate::Node::{ARRAY, BULK_STRING, ERROR, INTEGER, SIMPLE_STRING, SIZE, UNKNOWN};
3
4#[derive(Debug, PartialEq)]
78pub enum Value {
79 Nil,
81 Integer(i64),
83 Error(String),
85 String(String),
87 Array(Vec<Value>),
89}
90
91#[derive(Debug)]
92struct Input<'a> {
93 source: &'a str,
95 position: usize,
97}
98
99type InnerResult<'a> = (ValueResult<'a>, usize);
100
101pub type ValueResult<'a> = Result<Value, <Value as TryFrom<&'a str>>::Error>;
103
104impl TryFrom<&str> for Value {
105 type Error = TError;
106
107 fn try_from(source: &str) -> ValueResult {
108 Value::internal_try_from(Input {
109 position: 0,
110 source,
111 })
112 .0
113 }
114}
115
116impl Value {
117 fn internal_try_from(input: Input) -> InnerResult {
118 match input.source.chars().next() {
119 Some('*') => Value::extract_array(input),
120 Some('-') => Value::extract_error(input),
121 Some(':') => Value::extract_integer(input),
122 Some('$') => Value::extract_bulk_string(input),
123 Some('+') => Value::extract_simple_string(input),
124 _ => (Err(TError::of_unexpected(UNKNOWN, input.position)), 0),
125 }
126 }
127
128 fn extract_array(input: Input) -> InnerResult {
129 let integer_input = Input {
130 position: input.position,
131 ..input
132 };
133 match Value::extract_integer(integer_input) {
134 (Ok(Value::Integer(len)), size) => {
135 let mut values = vec![];
136 let len = len as usize;
137 let mut offset = size;
138
139 while values.len() < len {
140 let next_input = Input {
141 position: input.position + offset,
142 source: &input.source[offset..input.source.len()],
143 };
144
145 if "" == next_input.source {
146 return (Err(TError::of_size(ARRAY, offset)), offset);
147 }
148
149 match Value::internal_try_from(next_input) {
150 (Ok(value), size) => {
151 values.push(value);
152 offset += size;
153 }
154 r#else => return r#else,
155 }
156 }
157
158 if len == values.len() {
159 (Ok(Value::Array(values)), offset)
160 } else {
161 (Err(TError::of_size(ARRAY, offset + 1)), offset + 1)
162 }
163 }
164 r#else => return r#else,
165 }
166 }
167
168 fn extract_error(input: Input) -> InnerResult {
169 match Value::extract_simple_string(input) {
170 (Ok(Value::String(message)), size) => (Ok(Value::Error(message)), size),
171 r#else => r#else,
172 }
173 }
174
175 fn extract_integer(input: Input) -> InnerResult {
176 let node = match &input.source[0..1] {
178 ":" => INTEGER,
179 _ => SIZE,
180 };
181 let position = input.position + 1;
182
183 if let Some(i) = input.source.find("\r\n") {
184 return match input.source[1..i].parse::<i64>().ok() {
185 Some(value) => (Ok(Value::Integer(value)), i + 2),
186 _ => (Err(TError::of_type(node, position)), position),
187 };
188 }
189
190 (
191 Err(TError::of_unexpected(node, input.source.len())),
192 input.source.len(),
193 )
194 }
195
196 fn extract_bulk_string(input: Input) -> InnerResult {
197 if input.source.starts_with("$-1\r\n") {
198 return (Ok(Value::Nil), 5);
199 }
200
201 match Self::extract_integer(Input { ..input }) {
202 (Ok(Value::Integer(size)), _) => {
203 let start = 1 + size.to_string().len() + 2;
204 let end = start + size as usize;
205
206 return if input.source[end..input.source.len()].starts_with("\r\n") {
207 (
208 Ok(Value::String(input.source[start..end].to_string())),
209 end + 2,
210 )
211 } else if end < input.source.len() {
212 let position = input.position + end;
213 (Err(TError::of_size(BULK_STRING, position)), position)
214 } else {
215 let position = input.position + end + 1;
216 (Err(TError::of_size(BULK_STRING, position)), position)
217 };
218 }
219 (Err(error), size) => (Err(error), size),
220 _ => (
221 Err(TError::of_unexpected(BULK_STRING, input.position + 1)),
222 input.position + 1,
223 ),
224 }
225 }
226
227 fn extract_simple_string(input: Input) -> InnerResult {
228 let node = match &input.source[0..1] {
229 "+" => SIMPLE_STRING,
230 _ => ERROR,
231 };
232 let mut position = input.position + 1;
233
234 if let Some(i) = input.source.find("\r\n") {
235 match input.source.find('\r').filter(|&p| p < i)
237 .or_else(|| input.source.find('\n').filter(|&p| p < i))
238 {
240 Some(shift) => position = input.position + shift,
241 _ => return (Ok(Value::String(input.source[1..i].into())), i + 2),
242 }
243 }
244
245 (Err(TError::of_unexpected(node, position)), position)
246 }
247}
248
249#[cfg(test)]
250mod tests {
251 use crate::Node::{ARRAY, BULK_STRING, INTEGER, SIMPLE_STRING, SIZE};
252
253 use super::super::{Error, Value};
254
255 #[test]
256 fn value_implement_try_from_resp_nil() {
257 assert_eq!("$-1\r\n".try_into(), Ok(Value::Nil));
258 }
259
260 #[test]
261 fn value_implement_try_from() {
262 let _value: Result<Value, Error> = "".try_into();
263 }
264
265 #[test]
266 fn value_implement_try_from_resp_array() {
267 assert_eq!("*0\r\n".try_into(), Ok(Value::Array(vec![])));
268 assert_eq!(
269 "*5\r\n$-1\r\n:447\r\n-Oh oh!\r\n+Hourly\r\n$26\r\nSi vis pacem,\r\npara bellum\r\n"
270 .try_into(),
271 Ok(Value::Array(vec![
272 Value::Nil,
273 Value::Integer(447),
274 Value::Error("Oh oh!".into()),
275 Value::String("Hourly".into()),
276 Value::String("Si vis pacem,\r\npara bellum".into()),
277 ]))
278 );
279 }
280
281 #[test]
282 fn value_implement_try_from_resp_nested_array() {
283 let got = "*2\r\n*3\r\n+A\r\n+B\r\n+C\r\n*3\r\n:1\r\n:2\r\n:3\r\n".try_into()
284 as Result<Value, Error>;
285 let expected = Ok(Value::Array(vec![
286 Value::Array(vec![
287 Value::String("A".into()),
288 Value::String("B".into()),
289 Value::String("C".into()),
290 ]),
291 Value::Array(vec![
292 Value::Integer(1),
293 Value::Integer(2),
294 Value::Integer(3),
295 ]),
296 ])) as Result<Value, Error>;
297
298 assert_eq!(got, expected);
299 }
300
301 #[test]
302 fn value_implement_try_from_resp_array_with_mismatching_size() {
303 assert_eq!(
304 "*2\r\n$-1\r\n".try_into() as Result<Value, Error>,
305 Err(Error::of_size(ARRAY, 9))
306 );
307 }
308
309 #[test]
310 fn value_implement_try_from_resp_error() {
311 assert_eq!("-My bad\r\n".try_into(), Ok(Value::Error("My bad".into())));
312 }
313
314 #[test]
315 fn value_implement_try_from_resp_integer() {
316 assert_eq!(":10\r\n".try_into(), Ok(Value::Integer(10i64)));
317 }
318
319 #[test]
320 fn value_implement_try_from_resp_integer_with_invalid_integer() {
321 assert_eq!(
322 ":Yikes\r\n".try_into() as Result<Value, Error>,
323 Err(Error::of_type(INTEGER, 1))
324 );
325 assert_eq!(
326 ":0".try_into() as Result<Value, Error>,
327 Err(Error::of_unexpected(INTEGER, 2))
328 );
329 }
330
331 #[test]
332 fn value_implement_try_from_resp_bulk_string() {
333 assert_eq!(
334 "$4\r\nOops\r\n".try_into(),
335 Ok(Value::String("Oops".into()))
336 );
337 assert_eq!(
338 "$7\r\nOh\r\nOh!\r\n".try_into(),
339 Ok(Value::String("Oh\r\nOh!".into()))
340 );
341 }
342
343 #[test]
344 fn value_implement_try_from_resp_bulk_string_with_mismatching_len() {
345 assert_eq!(
346 "$5\r\nOops\r\n".try_into() as Result<Value, Error>,
347 Err(Error::of_size(BULK_STRING, 9))
348 );
349 assert_eq!(
350 "$3\r\nOops\r\n".try_into() as Result<Value, Error>,
351 Err(Error::of_size(BULK_STRING, 7))
352 );
353 }
354
355 #[test]
356 fn value_implement_try_from_resp_simple_string() {
357 assert_eq!(
358 "+Anatomy\r\n".try_into(),
359 Ok(Value::String("Anatomy".into()))
360 );
361 }
362
363 #[test]
364 fn value_implement_try_from_resp_simple_string_with_line_feed_or_carriage_return_in_value() {
365 assert_eq!(
366 "+Top\nBottom\r\n".try_into() as Result<Value, Error>,
367 Err(Error::of_unexpected(SIMPLE_STRING, 4))
368 );
369 assert_eq!(
370 "+Top\rBottom\r\n".try_into() as Result<Value, Error>,
371 Err(Error::of_unexpected(SIMPLE_STRING, 4))
372 );
373 }
374
375 #[test]
376 fn value_implement_try_from_resp_with_invalid_size_type() {
377 assert_eq!(
378 "*!\r\n$-1\r\n".try_into() as Result<Value, Error>,
379 Err(Error::of_type(SIZE, 1))
380 );
381 assert_eq!(
382 "$!\r\n$-1\r\n".try_into() as Result<Value, Error>,
383 Err(Error::of_type(SIZE, 1))
384 );
385 }
386}