atat: ATI response cannot be parsed as String, but can as Bytes

I noticed the following when trying out atat. When defining ATI response using heapless::Bytes it parses correctly.

#[derive(Debug, Clone, AtatResp)]
pub struct ATIResponse
{
    pub info: Bytes<64>,
}

The response:

ATIResponse { info: b'Quectel\r\nEC25\r\nRevision: EC25EFAR06A09M4G' }

But when using heapless::String it results in Error::Parse.

#[derive(Debug, Clone, AtatResp)]
pub struct ATIResponse
{
    pub info: String<64>,
}

Perhaps still the same issue as in #86.

About this issue

  • Original URL
  • State: open
  • Created a year ago
  • Comments: 32 (15 by maintainers)

Most upvoted comments

I ran into this problem at work and came up with the following solution. I’m not particularly proud of it but it’s an ok stopgap until something nicer comes along 😃

/// A helper to deserialize unquoted strings in AT responses.
struct UnquotedStringVisitor<const N: usize>;

impl<'de, const N: usize> Visitor<'de> for UnquotedStringVisitor<N> {
    type Value = heapless::String<N>;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("an unquoted string")
    }

    fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
    where
        E: de::Error,
    {
        let s = std::str::from_utf8(v).map_err(serde::de::Error::custom)?;

        if s.len() > N {
            Err(serde::de::Error::custom("Source string too long"))
        } else {
            Ok(s.into())
        }
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct UnquotedHeaplessString<const N: usize>(pub heapless::String<N>);

impl<'de, const N: usize> Deserialize<'de> for UnquotedHeaplessString<N> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let out = deserializer.deserialize_bytes(UnquotedStringVisitor)?;

        Ok(Self(out))
    }
}

impl<const N: usize> From<&str> for UnquotedHeaplessString<N> {
    fn from(s: &str) -> Self {
        Self(s.into())
    }
}

impl<const N: usize> fmt::Display for UnquotedHeaplessString<N> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(&self.0)
    }
}