1use std::collections::HashMap;
14use std::fmt;
15use std::hash::{BuildHasher, Hash};
16use std::io;
17use std::str;
18use std::sync::Arc;
19
20use bytes::{Buf, BufMut, BytesMut};
21
22use tokio_util::codec::{Decoder, Encoder};
23
24use super::error::{self, Error};
25
26#[derive(Clone, Eq, PartialEq)]
31pub enum RespValue {
32 Nil,
33
34 Array(Vec<RespValue>),
36
37 BulkString(Vec<u8>),
40
41 Error(String),
43
44 Integer(i64),
47
48 SimpleString(String),
49}
50
51impl RespValue {
52 #[inline]
53 fn into_result(self) -> Result<RespValue, Error> {
54 match self {
55 RespValue::Error(string) => Err(Error::Remote(string)),
56 x => Ok(x),
57 }
58 }
59
60 pub fn append<T>(mut self, other: impl IntoIterator<Item = T>) -> Self
65 where
66 T: Into<RespValue>,
67 {
68 match self {
69 RespValue::Array(ref mut vals) => {
70 vals.extend(other.into_iter().map(|t| t.into()));
71 }
72 _ => panic!("Can only append to arrays"),
73 }
74 self
75 }
76
77 pub fn push<T: Into<RespValue>>(&mut self, item: T) {
81 match self {
82 RespValue::Array(ref mut vals) => {
83 vals.push(item.into());
84 }
85 _ => panic!("Can only push to arrays"),
86 }
87 }
88}
89
90impl fmt::Debug for RespValue {
91 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92 match self {
93 RespValue::Nil => write!(f, "Nil"),
94 RespValue::Array(vals) => write!(f, "Array({:?})", vals),
95 RespValue::BulkString(bytes) => {
96 if let Ok(string) = str::from_utf8(bytes) {
99 write!(f, "BulkString({:?})", string)
100 } else {
101 write!(f, "BulkString({:?})", bytes)
102 }
103 }
104 RespValue::Error(string) => write!(f, "Error({:?})", string),
105 RespValue::Integer(int) => write!(f, "Integer({:?})", int),
106 RespValue::SimpleString(string) => write!(f, "SimpleString({:?})", string),
107 }
108 }
109}
110
111pub trait FromResp: Sized {
116 #[inline]
119 fn from_resp(resp: RespValue) -> Result<Self, Error> {
120 Self::from_resp_int(resp.into_result()?)
121 }
122
123 fn from_resp_int(resp: RespValue) -> Result<Self, Error>;
124}
125
126impl FromResp for RespValue {
127 #[inline]
128 fn from_resp_int(resp: RespValue) -> Result<RespValue, Error> {
129 Ok(resp)
130 }
131}
132
133impl FromResp for String {
134 #[inline]
135 fn from_resp_int(resp: RespValue) -> Result<String, Error> {
136 match resp {
137 RespValue::BulkString(ref bytes) => Ok(String::from_utf8_lossy(bytes).into_owned()),
138 RespValue::Integer(i) => Ok(i.to_string()),
139 RespValue::SimpleString(string) => Ok(string),
140 _ => Err(error::resp("Cannot convert into a string", resp)),
141 }
142 }
143}
144
145impl FromResp for Arc<str> {
146 #[inline]
147 fn from_resp_int(resp: RespValue) -> Result<Arc<str>, Error> {
148 match resp {
149 RespValue::BulkString(ref bytes) => Ok(String::from_utf8_lossy(bytes).into()),
150 _ => Err(error::resp("Cannot convert into a Arc<str>", resp)),
151 }
152 }
153}
154
155impl FromResp for Vec<u8> {
156 #[inline]
157 fn from_resp_int(resp: RespValue) -> Result<Vec<u8>, Error> {
158 match resp {
159 RespValue::BulkString(bytes) => Ok(bytes),
160 _ => Err(error::resp("Not a bulk string", resp)),
161 }
162 }
163}
164
165impl FromResp for i64 {
166 #[inline]
167 fn from_resp_int(resp: RespValue) -> Result<i64, Error> {
168 match resp {
169 RespValue::Integer(i) => Ok(i),
170 _ => Err(error::resp("Cannot be converted into an i64", resp)),
171 }
172 }
173}
174
175macro_rules! impl_fromresp_integers {
176 ($($int_ty:ident),* $(,)*) => {
177 $(
178 #[allow(clippy::cast_lossless)]
179 impl FromResp for $int_ty {
180 #[inline]
181 fn from_resp_int(resp: RespValue) -> Result<Self, Error> {
182 i64::from_resp_int(resp).and_then(|x| {
183 if x < ($int_ty::min_value() as i64)
186 || ($int_ty::max_value() as i64 > 0
187 && x > ($int_ty::max_value() as i64))
188 {
189 Err(error::resp(
190 concat!(
191 "i64 value cannot be represented as {}",
192 stringify!($int_ty),
193 ),
194 RespValue::Integer(x),
195 ))
196 } else {
197 Ok(x as $int_ty)
198 }
199 })
200 }
201 }
202 )*
203 };
204}
205
206impl_fromresp_integers!(isize, usize, i32, u32, u64);
207
208impl FromResp for bool {
209 #[inline]
210 fn from_resp_int(resp: RespValue) -> Result<bool, Error> {
211 i64::from_resp_int(resp).and_then(|x| match x {
212 0 => Ok(false),
213 1 => Ok(true),
214 _ => Err(error::resp(
215 "i64 value cannot be represented as bool",
216 RespValue::Integer(x),
217 )),
218 })
219 }
220}
221
222impl<T: FromResp> FromResp for Option<T> {
223 #[inline]
224 fn from_resp_int(resp: RespValue) -> Result<Option<T>, Error> {
225 match resp {
226 RespValue::Nil => Ok(None),
227 x => Ok(Some(T::from_resp_int(x)?)),
228 }
229 }
230}
231
232impl<T: FromResp> FromResp for Vec<T> {
233 #[inline]
234 fn from_resp_int(resp: RespValue) -> Result<Vec<T>, Error> {
235 match resp {
236 RespValue::Array(ary) => {
237 let mut ar = Vec::with_capacity(ary.len());
238 for value in ary {
239 ar.push(T::from_resp(value)?);
240 }
241 Ok(ar)
242 }
243 _ => Err(error::resp("Cannot be converted into a vector", resp)),
244 }
245 }
246}
247
248impl<K: FromResp + Hash + Eq, T: FromResp, S: BuildHasher + Default> FromResp for HashMap<K, T, S> {
249 fn from_resp_int(resp: RespValue) -> Result<HashMap<K, T, S>, Error> {
250 match resp {
251 RespValue::Array(ary) => {
252 let mut map = HashMap::with_capacity_and_hasher(ary.len(), S::default());
253 let mut items = ary.into_iter();
254
255 while let Some(k) = items.next() {
256 let key = K::from_resp(k)?;
257 let value = T::from_resp(items.next().ok_or_else(|| {
258 error::resp(
259 "Cannot convert an odd number of elements into a hashmap",
260 "".into(),
261 )
262 })?)?;
263
264 map.insert(key, value);
265 }
266
267 Ok(map)
268 }
269 _ => Err(error::resp("Cannot be converted into a hashmap", resp)),
270 }
271 }
272}
273
274impl FromResp for () {
275 #[inline]
276 fn from_resp_int(resp: RespValue) -> Result<(), Error> {
277 match resp {
278 RespValue::SimpleString(string) => match string.as_ref() {
279 "OK" => Ok(()),
280 _ => Err(Error::Resp(
281 format!("Unexpected value within SimpleString: {}", string),
282 None,
283 )),
284 },
285 _ => Err(error::resp(
286 "Unexpected value, should be encoded as a SimpleString",
287 resp,
288 )),
289 }
290 }
291}
292
293impl<A, B> FromResp for (A, B)
294where
295 A: FromResp,
296 B: FromResp,
297{
298 #[inline]
299 fn from_resp_int(resp: RespValue) -> Result<(A, B), Error> {
300 match resp {
301 RespValue::Array(ary) => {
302 if ary.len() == 2 {
303 let mut ary_iter = ary.into_iter();
304 Ok((
305 A::from_resp(ary_iter.next().expect("No value"))?,
306 B::from_resp(ary_iter.next().expect("No value"))?,
307 ))
308 } else {
309 Err(Error::Resp(
310 format!("Array needs to be 2 elements, is: {}", ary.len()),
311 None,
312 ))
313 }
314 }
315 _ => Err(error::resp(
316 "Unexpected value, should be encoded as an array",
317 resp,
318 )),
319 }
320 }
321}
322
323impl<A, B, C> FromResp for (A, B, C)
324where
325 A: FromResp,
326 B: FromResp,
327 C: FromResp,
328{
329 #[inline]
330 fn from_resp_int(resp: RespValue) -> Result<(A, B, C), Error> {
331 match resp {
332 RespValue::Array(ary) => {
333 if ary.len() == 3 {
334 let mut ary_iter = ary.into_iter();
335 Ok((
336 A::from_resp(ary_iter.next().expect("No value"))?,
337 B::from_resp(ary_iter.next().expect("No value"))?,
338 C::from_resp(ary_iter.next().expect("No value"))?,
339 ))
340 } else {
341 Err(Error::Resp(
342 format!("Array needs to be 3 elements, is: {}", ary.len()),
343 None,
344 ))
345 }
346 }
347 _ => Err(error::resp(
348 "Unexpected value, should be encoded as an array",
349 resp,
350 )),
351 }
352 }
353}
354
355#[macro_export]
385macro_rules! resp_array {
386 ($($e:expr),* $(,)?) => {
387 {
388 $crate::resp::RespValue::Array(vec![
389 $(
390 $e.into(),
391 )*
392 ])
393 }
394 }
395}
396
397macro_rules! into_resp {
398 ($t:ty, $f:ident) => {
399 impl<'a> From<$t> for RespValue {
400 #[inline]
401 fn from(from: $t) -> RespValue {
402 from.$f()
403 }
404 }
405 };
406}
407
408pub trait IntoRespString {
410 fn into_resp_string(self) -> RespValue;
411}
412
413macro_rules! string_into_resp {
414 ($t:ty) => {
415 into_resp!($t, into_resp_string);
416 };
417}
418
419impl IntoRespString for String {
420 #[inline]
421 fn into_resp_string(self) -> RespValue {
422 RespValue::BulkString(self.into_bytes())
423 }
424}
425string_into_resp!(String);
426
427impl<'a> IntoRespString for &'a String {
428 #[inline]
429 fn into_resp_string(self) -> RespValue {
430 RespValue::BulkString(self.as_bytes().into())
431 }
432}
433string_into_resp!(&'a String);
434
435impl<'a> IntoRespString for &'a str {
436 #[inline]
437 fn into_resp_string(self) -> RespValue {
438 RespValue::BulkString(self.as_bytes().into())
439 }
440}
441string_into_resp!(&'a str);
442
443impl<'a> IntoRespString for &'a [u8] {
444 #[inline]
445 fn into_resp_string(self) -> RespValue {
446 RespValue::BulkString(self.to_vec())
447 }
448}
449string_into_resp!(&'a [u8]);
450
451impl IntoRespString for Vec<u8> {
452 #[inline]
453 fn into_resp_string(self) -> RespValue {
454 RespValue::BulkString(self)
455 }
456}
457string_into_resp!(Vec<u8>);
458
459impl IntoRespString for Arc<str> {
460 #[inline]
461 fn into_resp_string(self) -> RespValue {
462 RespValue::BulkString(self.as_bytes().into())
463 }
464}
465string_into_resp!(Arc<str>);
466
467pub trait IntoRespInteger {
468 fn into_resp_integer(self) -> RespValue;
469}
470
471macro_rules! integer_into_resp {
472 ($t:ty) => {
473 into_resp!($t, into_resp_integer);
474 };
475}
476
477impl IntoRespInteger for usize {
478 #[inline]
479 fn into_resp_integer(self) -> RespValue {
480 RespValue::Integer(self as i64)
481 }
482}
483integer_into_resp!(usize);
484
485pub struct RespCodec;
487
488fn write_rn(buf: &mut BytesMut) {
489 buf.put_u8(b'\r');
490 buf.put_u8(b'\n');
491}
492
493fn check_and_reserve(buf: &mut BytesMut, amt: usize) {
494 let remaining_bytes = buf.remaining_mut();
495 if remaining_bytes < amt {
496 buf.reserve(amt);
497 }
498}
499
500fn write_header(symb: u8, len: i64, buf: &mut BytesMut) {
501 let len_as_string = len.to_string();
502 let len_as_bytes = len_as_string.as_bytes();
503 let header_bytes = 1 + len_as_bytes.len() + 2;
504 check_and_reserve(buf, header_bytes);
505 buf.put_u8(symb);
506 buf.extend(len_as_bytes);
507 write_rn(buf);
508}
509
510fn write_simple_string(symb: u8, string: &str, buf: &mut BytesMut) {
511 let bytes = string.as_bytes();
512 let size = 1 + bytes.len() + 2;
513 check_and_reserve(buf, size);
514 buf.put_u8(symb);
515 buf.extend(bytes);
516 write_rn(buf);
517}
518
519impl Encoder<RespValue> for RespCodec {
520 type Error = io::Error;
521
522 fn encode(&mut self, msg: RespValue, buf: &mut BytesMut) -> Result<(), Self::Error> {
523 match msg {
524 RespValue::Nil => {
525 write_header(b'$', -1, buf);
526 }
527 RespValue::Array(ary) => {
528 write_header(b'*', ary.len() as i64, buf);
529 for v in ary {
530 self.encode(v, buf)?;
531 }
532 }
533 RespValue::BulkString(bstr) => {
534 let len = bstr.len();
535 write_header(b'$', len as i64, buf);
536 check_and_reserve(buf, len + 2);
537 buf.extend(bstr);
538 write_rn(buf);
539 }
540 RespValue::Error(ref string) => {
541 write_simple_string(b'-', string, buf);
542 }
543 RespValue::Integer(val) => {
544 write_header(b':', val, buf);
546 }
547 RespValue::SimpleString(ref string) => {
548 write_simple_string(b'+', string, buf);
549 }
550 }
551 Ok(())
552 }
553}
554
555#[inline]
556fn parse_error(message: String) -> Error {
557 Error::Resp(message, None)
558}
559
560fn scan_integer(buf: &mut BytesMut, idx: usize) -> Result<Option<(usize, &[u8])>, Error> {
568 let length = buf.len();
569 let mut at_end = false;
570 let mut pos = idx;
571 loop {
572 if length <= pos {
573 return Ok(None);
574 }
575 match (at_end, buf[pos]) {
576 (true, b'\n') => return Ok(Some((pos + 1, &buf[idx..pos - 1]))),
577 (false, b'\r') => at_end = true,
578 (false, b'0'..=b'9') => (),
579 (false, b'-') => (),
580 (_, val) => {
581 return Err(parse_error(format!(
582 "Unexpected byte in size_string: {}",
583 val
584 )));
585 }
586 }
587 pos += 1;
588 }
589}
590
591fn scan_string(buf: &mut BytesMut, idx: usize) -> Option<(usize, String)> {
592 let length = buf.len();
593 let mut at_end = false;
594 let mut pos = idx;
595 loop {
596 if length <= pos {
597 return None;
598 }
599 match (at_end, buf[pos]) {
600 (true, b'\n') => {
601 let value = String::from_utf8_lossy(&buf[idx..pos - 1]).into_owned();
602 return Some((pos + 1, value));
603 }
604 (true, _) => at_end = false,
605 (false, b'\r') => at_end = true,
606 (false, _) => (),
607 }
608 pos += 1;
609 }
610}
611
612fn decode_raw_integer(buf: &mut BytesMut, idx: usize) -> Result<Option<(usize, i64)>, Error> {
613 match scan_integer(buf, idx) {
614 Ok(None) => Ok(None),
615 Ok(Some((pos, int_str))) => {
616 match str::from_utf8(int_str) {
618 Ok(string) => {
619 match string.parse() {
621 Ok(int) => Ok(Some((pos, int))),
622 Err(_) => Err(parse_error(format!("Not an integer: {}", string))),
623 }
624 }
625 Err(_) => Err(parse_error(format!("Not a valid string: {:?}", int_str))),
626 }
627 }
628 Err(e) => Err(e),
629 }
630}
631
632type DecodeResult = Result<Option<(usize, RespValue)>, Error>;
633
634fn decode_bulk_string(buf: &mut BytesMut, idx: usize) -> DecodeResult {
635 match decode_raw_integer(buf, idx) {
636 Ok(None) => Ok(None),
637 Ok(Some((pos, -1))) => Ok(Some((pos, RespValue::Nil))),
638 Ok(Some((pos, size))) if size >= 0 => {
639 let size = size as usize;
640 let remaining = buf.len() - pos;
641 let required_bytes = size + 2;
642
643 if remaining < required_bytes {
644 return Ok(None);
645 }
646
647 let bulk_string = RespValue::BulkString(buf[pos..(pos + size)].to_vec());
648 Ok(Some((pos + required_bytes, bulk_string)))
649 }
650 Ok(Some((_, size))) => Err(parse_error(format!("Invalid string size: {}", size))),
651 Err(e) => Err(e),
652 }
653}
654
655fn decode_array(buf: &mut BytesMut, idx: usize) -> DecodeResult {
656 match decode_raw_integer(buf, idx) {
657 Ok(None) => Ok(None),
658 Ok(Some((pos, -1))) => Ok(Some((pos, RespValue::Nil))),
659 Ok(Some((pos, size))) if size >= 0 => {
660 let size = size as usize;
661 let mut pos = pos;
662 let mut values = Vec::with_capacity(size);
663 for _ in 0..size {
664 match decode(buf, pos) {
665 Ok(None) => return Ok(None),
666 Ok(Some((new_pos, value))) => {
667 values.push(value);
668 pos = new_pos;
669 }
670 Err(e) => return Err(e),
671 }
672 }
673 Ok(Some((pos, RespValue::Array(values))))
674 }
675 Ok(Some((_, size))) => Err(parse_error(format!("Invalid array size: {}", size))),
676 Err(e) => Err(e),
677 }
678}
679
680fn decode_integer(buf: &mut BytesMut, idx: usize) -> DecodeResult {
681 match decode_raw_integer(buf, idx) {
682 Ok(None) => Ok(None),
683 Ok(Some((pos, int))) => Ok(Some((pos, RespValue::Integer(int)))),
684 Err(e) => Err(e),
685 }
686}
687
688#[allow(clippy::unnecessary_wraps)]
690fn decode_simple_string(buf: &mut BytesMut, idx: usize) -> DecodeResult {
691 match scan_string(buf, idx) {
692 None => Ok(None),
693 Some((pos, string)) => Ok(Some((pos, RespValue::SimpleString(string)))),
694 }
695}
696
697#[allow(clippy::unnecessary_wraps)]
698fn decode_error(buf: &mut BytesMut, idx: usize) -> DecodeResult {
699 match scan_string(buf, idx) {
700 None => Ok(None),
701 Some((pos, string)) => Ok(Some((pos, RespValue::Error(string)))),
702 }
703}
704
705fn decode(buf: &mut BytesMut, idx: usize) -> DecodeResult {
706 let length = buf.len();
707 if length <= idx {
708 return Ok(None);
709 }
710
711 let first_byte = buf[idx];
712 match first_byte {
713 b'$' => decode_bulk_string(buf, idx + 1),
714 b'*' => decode_array(buf, idx + 1),
715 b':' => decode_integer(buf, idx + 1),
716 b'+' => decode_simple_string(buf, idx + 1),
717 b'-' => decode_error(buf, idx + 1),
718 _ => Err(parse_error(format!("Unexpected byte: {}", first_byte))),
719 }
720}
721
722impl Decoder for RespCodec {
723 type Item = RespValue;
724 type Error = Error;
725
726 fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
727 match decode(buf, 0) {
728 Ok(None) => Ok(None),
729 Ok(Some((pos, item))) => {
730 buf.advance(pos);
731 Ok(Some(item))
732 }
733 Err(e) => Err(e),
734 }
735 }
736}
737
738#[cfg(test)]
739mod tests {
740 use std::collections::HashMap;
741
742 use bytes::BytesMut;
743
744 use tokio_util::codec::{Decoder, Encoder};
745
746 use super::{Error, FromResp, RespCodec, RespValue};
747
748 fn obj_to_bytes(obj: RespValue) -> Vec<u8> {
749 let mut bytes = BytesMut::new();
750 let mut codec = RespCodec;
751 codec.encode(obj, &mut bytes).unwrap();
752 bytes.to_vec()
753 }
754
755 #[test]
756 fn test_resp_array_macro() {
757 let resp_object = resp_array!["SET", "x"];
758 let bytes = obj_to_bytes(resp_object);
759 assert_eq!(b"*2\r\n$3\r\nSET\r\n$1\r\nx\r\n", bytes.as_slice());
760
761 let resp_object = resp_array!["RPUSH", "wyz"].append(vec!["a", "b"]);
762 let bytes = obj_to_bytes(resp_object);
763 assert_eq!(
764 &b"*4\r\n$5\r\nRPUSH\r\n$3\r\nwyz\r\n$1\r\na\r\n$1\r\nb\r\n"[..],
765 bytes.as_slice()
766 );
767
768 let vals = vec![String::from("a"), String::from("b")];
769 #[allow(clippy::needless_borrow)]
770 let resp_object = resp_array!["RPUSH", "xyz"].append(vals);
771 let bytes = obj_to_bytes(resp_object);
772 assert_eq!(
773 &b"*4\r\n$5\r\nRPUSH\r\n$3\r\nxyz\r\n$1\r\na\r\n$1\r\nb\r\n"[..],
774 bytes.as_slice()
775 );
776 }
777
778 #[test]
779 fn test_bulk_string() {
780 let resp_object = RespValue::BulkString(b"THISISATEST".to_vec());
781 let mut bytes = BytesMut::new();
782 let mut codec = RespCodec;
783 codec.encode(resp_object.clone(), &mut bytes).unwrap();
784 assert_eq!(b"$11\r\nTHISISATEST\r\n".to_vec(), bytes.to_vec());
785
786 let deserialized = codec.decode(&mut bytes).unwrap().unwrap();
787 assert_eq!(deserialized, resp_object);
788 }
789
790 #[test]
791 fn test_array() {
792 let resp_object = RespValue::Array(vec!["TEST1".into(), "TEST2".into()]);
793 let mut bytes = BytesMut::new();
794 let mut codec = RespCodec;
795 codec.encode(resp_object.clone(), &mut bytes).unwrap();
796 assert_eq!(
797 b"*2\r\n$5\r\nTEST1\r\n$5\r\nTEST2\r\n".to_vec(),
798 bytes.to_vec()
799 );
800
801 let deserialized = codec.decode(&mut bytes).unwrap().unwrap();
802 assert_eq!(deserialized, resp_object);
803 }
804
805 #[test]
806 fn test_nil_string() {
807 let mut bytes = BytesMut::new();
808 bytes.extend_from_slice(&b"$-1\r\n"[..]);
809
810 let mut codec = RespCodec;
811 let deserialized = codec.decode(&mut bytes).unwrap().unwrap();
812 assert_eq!(deserialized, RespValue::Nil);
813 }
814
815 #[test]
816 fn test_integer_overflow() {
817 let resp_object = RespValue::Integer(i64::max_value());
818 let res = i32::from_resp(resp_object);
819 assert!(res.is_err());
820 }
821
822 #[test]
823 fn test_integer_underflow() {
824 let resp_object = RespValue::Integer(-2);
825 let res = u64::from_resp(resp_object);
826 assert!(res.is_err());
827 }
828
829 #[test]
830 fn test_integer_convesion() {
831 let resp_object = RespValue::Integer(50);
832 assert_eq!(u32::from_resp(resp_object).unwrap(), 50);
833 }
834
835 #[test]
836 fn test_hashmap_conversion() {
837 let mut expected = HashMap::new();
838 expected.insert("KEY1".to_string(), "VALUE1".to_string());
839 expected.insert("KEY2".to_string(), "VALUE2".to_string());
840
841 let resp_object = RespValue::Array(vec![
842 "KEY1".into(),
843 "VALUE1".into(),
844 "KEY2".into(),
845 "VALUE2".into(),
846 ]);
847 assert_eq!(
848 HashMap::<String, String>::from_resp(resp_object).unwrap(),
849 expected
850 );
851 }
852
853 #[test]
854 fn test_hashmap_conversion_fails_with_odd_length_array() {
855 let resp_object = RespValue::Array(vec![
856 "KEY1".into(),
857 "VALUE1".into(),
858 "KEY2".into(),
859 "VALUE2".into(),
860 "KEY3".into(),
861 ]);
862 let res = HashMap::<String, String>::from_resp(resp_object);
863
864 match res {
865 Err(Error::Resp(_, _)) => {}
866 _ => panic!("Should not be able to convert an odd number of elements to a hashmap"),
867 }
868 }
869}