Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/Byte Vec Deserialize Wrapper #68

Merged
merged 14 commits into from Dec 10, 2020
8 changes: 4 additions & 4 deletions .github/workflows/codecov.yml
Expand Up @@ -16,10 +16,10 @@ jobs:
# Note: If you need to combine the coverage info of multiple
# feature sets, you need a `.tarpaulin.toml` config file, see
# the link above for those docs.
- uses: actions-rs/tarpaulin@v0.1
with:
run-types: Tests,Lib,Bins
args: --ignore-tests --all-features
- name: Install cargo-tarpaulin
run: "cargo install cargo-tarpaulin -f"
- name: Run cargo-tarpaulin
run: "cargo tarpaulin --out Xml --verbose --all-features --run-types Tests,Lib,Bins --ignore-tests"
# Note: closed-source code needs to provide a token,
# but open source code does not.
- name: Upload to codecov.io
Expand Down
121 changes: 116 additions & 5 deletions serde_at/src/de/mod.rs
Expand Up @@ -4,10 +4,11 @@ use core::str::FromStr;
use core::{fmt, str};

use serde::de::{self, Visitor};
use serde::{export, Deserialize};

use self::enum_::VariantAccess;
use self::map::MapAccess;
use self::seq::SeqAccess;
use self::seq::{SeqAccess, SeqByteAccess};

mod enum_;
mod map;
Expand All @@ -16,6 +17,93 @@ mod seq;
/// Deserialization result
pub type Result<T> = core::result::Result<T, Error>;

/// Wrapper type to allow deserializing a number of chars as a char vector
///
/// Example:
/// ```
/// use heapless::{consts, String};
/// use serde_at::{to_string, Bytes, SerializeOptions};
/// use serde_derive::Serialize;
///
/// #[derive(Debug, Deserialize, PartialEq)]
/// struct CommandStruct{
/// id: u8,
/// vec: CharVec<consts::U7>
/// value: i32,
/// }
///
/// let incoming: CommandStruct = from_str("+CCID: 4,IMP_MSG,-12")
///
/// let expected = CommandStruct{
/// id: 4,
/// vec : CharVec(heapless::Vec::from_slice(&['I', 'M', 'P', '_', 'M', 'S', 'G']).unwrap()),
/// value: -12,
/// }
///
/// assert_eq!(incoming, Ok(expected));
/// ```
#[derive(Debug, PartialEq)]
pub struct CharVec<T: heapless::ArrayLength<char>>(heapless::Vec<char, T>);

impl<T> CharVec<T>
where
T: heapless::ArrayLength<char>,
{
#[must_use]
pub fn new() -> Self {
Self(heapless::Vec::<char, T>::new())
}
}
impl<T> Default for CharVec<T>
where
T: heapless::ArrayLength<char>,
{
fn default() -> Self {
Self::new()
}
}
impl<'de, N> Deserialize<'de> for CharVec<N>
where
N: heapless::ArrayLength<char>,
{
fn deserialize<D>(deserializer: D) -> export::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct ValueVisitor<'de, N>(core::marker::PhantomData<(&'de (), char, N)>);

impl<'de, N> de::Visitor<'de> for ValueVisitor<'de, N>
where
N: heapless::ArrayLength<char>,
{
type Value = CharVec<N>;

fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a sequence")
}

fn visit_seq<A>(self, mut seq: A) -> export::Result<Self::Value, A::Error>
where
A: serde::de::SeqAccess<'de>,
{
let mut values = heapless::Vec::new();

while let Some(value) = seq.next_element()? {
if values.push(value).is_err() {
return Err(<A::Error as serde::de::Error>::invalid_length(
values.capacity() + 1,
&self,
));
}
}

Ok(CharVec(values))
}
}
deserializer.deserialize_bytes(ValueVisitor(core::marker::PhantomData))
}
}

/// This type represents all possible errors that can occur when deserializing AT Command strings
#[allow(clippy::pub_enum_variant_names)]
#[derive(Debug, PartialEq)]
Expand Down Expand Up @@ -373,11 +461,13 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> {
deserialize_fromstr!(self, visitor, f64, visit_f64, b"0123456789+-.eE")
}

fn deserialize_char<V>(self, _visitor: V) -> Result<V::Value>
fn deserialize_char<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
unreachable!()
let peek = self.parse_whitespace().ok_or(Error::EofWhileParsingValue)?;
self.eat_char();
unizippro marked this conversation as resolved.
Show resolved Hide resolved
visitor.visit_char(peek as char)
}

fn deserialize_str<V>(self, visitor: V) -> Result<V::Value>
Expand Down Expand Up @@ -410,11 +500,11 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> {
}

/// Unsupported
fn deserialize_bytes<V>(self, _visitor: V) -> Result<V::Value>
fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
unreachable!()
Ok(visitor.visit_seq(SeqByteAccess::new(self))?)
}

/// Unsupported
Expand Down Expand Up @@ -629,6 +719,7 @@ where

#[cfg(test)]
mod tests {
use super::CharVec;
use heapless::{consts, String, Vec};
use serde_derive::Deserialize;

Expand All @@ -654,6 +745,9 @@ mod tests {
#[derive(Clone, Debug, PartialEq, Deserialize)]
struct Handle(pub usize);

#[derive(Clone, Debug, PartialEq, Deserialize)]
struct CharHandle(pub char);

#[test]
fn simple_struct() {
assert_eq!(
Expand Down Expand Up @@ -737,8 +831,25 @@ mod tests {
);
}

#[test]
fn char_test() {
assert_eq!(crate::from_str("+CCID: B"), Ok(CharHandle('B')));
}

#[test]
fn newtype_struct() {
assert_eq!(crate::from_str("+CCID: 15"), Ok(Handle(15)));
}

#[test]
fn char_vec_struct() {
let expectation: CharVec<consts::U4> = CharVec::new();
assert_eq!(
CharVec(heapless::Vec::<char, consts::U4>::new()),
expectation
);
let expectation: CharVec<consts::U4> =
CharVec(heapless::Vec::from_slice(&['I', 'M', 'P', '_']).unwrap());
assert_eq!(crate::from_str("+CCID: IMP_"), Ok(expectation));
}
}
29 changes: 29 additions & 0 deletions serde_at/src/de/seq.rs
Expand Up @@ -41,3 +41,32 @@ impl<'a, 'de> de::SeqAccess<'de> for SeqAccess<'a, 'de> {
Ok(Some(seed.deserialize(&mut *self.de)?))
}
}

#[allow(clippy::module_name_repetitions)]
unizippro marked this conversation as resolved.
Show resolved Hide resolved
pub struct SeqByteAccess<'a, 'b> {
unizippro marked this conversation as resolved.
Show resolved Hide resolved
de: &'a mut Deserializer<'b>,
}

impl<'a, 'b> SeqByteAccess<'a, 'b> {
pub(crate) fn new(de: &'a mut Deserializer<'b>) -> Self {
SeqByteAccess { de }
}
}

impl<'a, 'de> de::SeqAccess<'de> for SeqByteAccess<'a, 'de> {
type Error = Error;

fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
where
T: de::DeserializeSeed<'de>,
{
match self.de.parse_whitespace() {
Some(b',') | None => {
return Ok(None);
}
Some(_) => {}
};

Ok(Some(seed.deserialize(&mut *self.de)?))
}
}
2 changes: 1 addition & 1 deletion serde_at/src/lib.rs
Expand Up @@ -12,7 +12,7 @@ pub mod ser;
pub use serde;

#[doc(inline)]
pub use self::de::{from_slice, from_str};
pub use self::de::{from_slice, from_str, CharVec};
#[doc(inline)]
pub use self::ser::{to_string, to_vec, Bytes, SerializeOptions};

Expand Down