diff --git a/msgpacker/Cargo.toml b/msgpacker/Cargo.toml index 1d5c518..1071d61 100644 --- a/msgpacker/Cargo.toml +++ b/msgpacker/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "msgpacker" -version = "0.3.0" +version = "0.3.1" authors = ["Victor Lopez "] categories = ["compression", "encoding", "parser-implementations"] edition = "2021" diff --git a/msgpacker/src/map/map_ref.rs b/msgpacker/src/map/map_ref.rs index d8bdb42..3dcc815 100644 --- a/msgpacker/src/map/map_ref.rs +++ b/msgpacker/src/map/map_ref.rs @@ -24,6 +24,10 @@ impl<'a> MapEntryRef<'a> { &self.val } + pub(crate) fn into_inner(self) -> (MessageRef<'a>, MessageRef<'a>) { + (self.key, self.val) + } + /// Access the internal key and value as a tuple of references pub const fn inner(&self) -> (&MessageRef<'a>, &MessageRef<'a>) { (&self.key, &self.val) diff --git a/msgpacker/src/message_ref.rs b/msgpacker/src/message_ref.rs index bbc6f5a..b994d2b 100644 --- a/msgpacker/src/message_ref.rs +++ b/msgpacker/src/message_ref.rs @@ -102,6 +102,27 @@ impl<'a> MessageRef<'a> { as_value_ref!(as_map, &'a [MapEntryRef], Map); as_value_ref!(as_extension, &'a ExtensionRef, Extension); + /// Attempt to match every item of the message as map, and then convert it to key/value pairs + pub fn try_collect_map(self) -> Option> + where + M: FromIterator<(K, V)>, + K: TryFrom, Error = io::Error>, + V: TryFrom, Error = io::Error>, + { + let m = match self { + MessageRef::Map(m) => m, + _ => return None, + }; + + let map = m + .into_iter() + .map(|entry| entry.into_inner()) + .map(|(key, value)| K::try_from(key).and_then(|k| V::try_from(value).map(|v| (k, v)))) + .collect(); + + Some(map) + } + /// Return `true` if the message is nil pub const fn is_nil(&self) -> bool { matches!(self, Self::Nil) @@ -882,3 +903,16 @@ impl<'a> SizeableMessage for MessageRef<'a> { } } } + +impl<'a, K, V> FromIterator<(K, V)> for MessageRef<'a> +where + K: Into>, + V: Into>, +{ + fn from_iter>(iter: T) -> Self { + iter.into_iter() + .map(|(key, value)| MapEntryRef::new(key.into(), value.into())) + .collect::>() + .into() + } +} diff --git a/msgpacker/tests/pack_unpack.rs b/msgpacker/tests/pack_unpack.rs index 6facaa4..4db2955 100644 --- a/msgpacker/tests/pack_unpack.rs +++ b/msgpacker/tests/pack_unpack.rs @@ -1,5 +1,6 @@ use msgpacker::prelude::*; +use std::collections::HashMap; use std::io::{self, Cursor, Seek}; use std::time::Duration; @@ -139,3 +140,22 @@ fn pack_unpack() -> io::Result<()> { Ok(()) } + +#[test] +fn pack_unpack_map_works() { + let data = vec![ + (10u32, String::from("test 1")), + (38u32, String::from("")), + (187u32, String::from("another test")), + ]; + + let map = data.clone().into_iter().collect::>(); + let msg = MessageRef::from_iter(map.iter().map(|(k, v)| (*k, v))); + + let map_p = msg + .try_collect_map() + .expect("message is not a map") + .expect("failed to parse items"); + + assert_eq!(map, map_p); +}