smallish 0.1.0

Lightweight, no-std syntax for configuration and scripting.
Documentation
use serde::de;

use crate::de::deserialize::{Error, SmallishDe};
use crate::types::Located;

#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(crate) struct LocatedHandler<'a, 'de, De> {
    location: Located<'de, ()>,
    de: &'a mut De,
    state: LocatedState,
}

#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
enum LocatedState {
    Source,
    Line,
    Column,
    Offset,
    Value,
    End,
}

impl<'a, 'de, De> LocatedHandler<'a, 'de, De> {
    pub(crate) fn new(location: Located<'de, ()>, de: &'a mut De) -> Self {
        Self {
            location,
            de,
            state: LocatedState::Source,
        }
    }
}

impl<'a, 'de, De> SmallishDe<'de> for &mut LocatedHandler<'a, 'de, De>
where
    for<'b> &'b mut De: SmallishDe<'de>,
{
    #[inline]
    fn base(self) -> impl SmallishDe<'de> {
        self.de.base()
    }
}

impl<'a, 'de, De> de::Deserializer<'de> for &mut LocatedHandler<'a, 'de, De>
where
    for<'b> &'b mut De: SmallishDe<'de>,
{
    type Error = Error;

    fn deserialize_any<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
    where
        V: de::Visitor<'de>,
    {
        self.de.error_without_event(Error::InvalidType)
    }

    serde::forward_to_deserialize_any! {
        bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
        byte_buf unit unit_struct newtype_struct seq tuple
        tuple_struct map struct enum identifier ignored_any
    }

    fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value, Self::Error>
    where
        V: de::Visitor<'de>,
    {
        if let Some(src) = self.location.source {
            visitor.visit_borrowed_bytes(src)
        } else {
            self.de.error_without_event(Error::InvalidType)
        }
    }

    fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
    where
        V: de::Visitor<'de>,
    {
        // only called on Option<&'de [u8]> for source
        if self.location.source.is_some() {
            visitor.visit_some(self)
        } else {
            visitor.visit_none()
        }
    }
}

impl<'a, 'de, De> de::MapAccess<'de> for LocatedHandler<'a, 'de, De>
where
    for<'b> &'b mut De: SmallishDe<'de>,
{
    type Error = Error;

    fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error>
    where
        K: de::DeserializeSeed<'de>,
    {
        let key = match self.state {
            LocatedState::Source => "source",
            LocatedState::Line => "line",
            LocatedState::Column => "column",
            LocatedState::Offset => "offset",
            LocatedState::Value => "value",
            LocatedState::End => {
                return Ok(None);
            }
        };

        let de = de::value::BorrowedStrDeserializer::new(key);
        seed.deserialize(de).map(Some)
    }

    fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Self::Error>
    where
        V: de::DeserializeSeed<'de>,
    {
        match self.state {
            LocatedState::Source => {
                self.state = LocatedState::Line;
                seed.deserialize(self)
            }
            LocatedState::Line => {
                self.state = LocatedState::Column;
                let de = de::value::UsizeDeserializer::new(self.location.line);
                seed.deserialize(de)
            }
            LocatedState::Column => {
                self.state = LocatedState::Offset;
                let de = de::value::UsizeDeserializer::new(self.location.column);
                seed.deserialize(de)
            }
            LocatedState::Offset => {
                self.state = LocatedState::Value;
                let de = de::value::UsizeDeserializer::new(self.location.offset);
                seed.deserialize(de)
            }
            LocatedState::Value => {
                self.state = LocatedState::End;
                seed.deserialize(&mut *self.de)
            }
            LocatedState::End => {
                unreachable!("end of Located");
            }
        }
    }
}

#[cfg(test)]
mod test {
    extern crate alloc;

    #[test]
    fn located_base() {
        use super::Located;
        use crate::{from_slice_escaped, Flavor};
        let v: Located<u8> = from_slice_escaped(Flavor::Value, b" \n   42 \n ", &mut []).unwrap();
        assert_eq!(v.offset, 5);
        assert_eq!(v.line, 2);
        assert_eq!(v.column, 3);
        assert_eq!(v.value, 42);
        assert_eq!(v.source_line(), Some("   42 "));
    }

    #[test]
    fn located_nested() {
        use super::Located;
        use crate::{from_slice_escaped, Flavor};
        let v: alloc::vec::Vec<Located<u8>> =
            from_slice_escaped(Flavor::Value, b" \n   [1 \n 2] \n ", &mut []).unwrap();
        assert_eq!(v[1].offset, 10);
        assert_eq!(v[1].line, 3);
        assert_eq!(v[1].column, 1);
        assert_eq!(v[1].value, 2);
        assert_eq!(v[1].source_line(), Some(" 2] "));
    }

    #[test]
    fn located_escaped_str() {
        use super::Located;
        use crate::{from_slice_escaped, types::Escaped, Flavor};
        let v: Located<Escaped<&str>> =
            from_slice_escaped(Flavor::Value, br#" "\n" "#, &mut []).unwrap();
        assert_eq!(v.offset, 1);
        assert_eq!(v.line, 1);
        assert_eq!(v.column, 1);
        assert_eq!(v.source_line(), Some(r#" "\n" "#));
        assert_eq!(*v.value, "\\n");
    }
    #[test]
    fn located_escaped_bytes() {
        use super::Located;
        use crate::{from_slice_escaped, types::Escaped, Flavor};
        let v: Located<Escaped<&[u8]>> =
            from_slice_escaped(Flavor::Value, br#" b"\n" "#, &mut []).unwrap();
        assert_eq!(v.offset, 1);
        assert_eq!(v.line, 1);
        assert_eq!(v.column, 1);
        assert_eq!(v.source_line(), Some(r#" b"\n" "#));
        assert_eq!(*v.value, b"\\n");
    }

    #[derive(Debug, PartialEq, Eq, serde::Deserialize)]
    struct Struct {
        a: u8,
        b: u8,
    }

    #[derive(Debug, PartialEq, Eq, serde::Deserialize)]
    struct Newtype<'a>(#[serde(borrow)] super::Located<'a, Struct>);
    #[test]
    fn newtype_located() {
        use crate::{from_slice_escaped, Flavor};
        let v: Newtype =
            from_slice_escaped(Flavor::Value, b" \n   {a=1, b=2} \n ", &mut []).unwrap();
        assert_eq!(v.0.offset, 5);
        assert_eq!(v.0.line, 2);
        assert_eq!(v.0.column, 3);
        assert_eq!(v.0.value, Struct { a: 1, b: 2 });
        assert_eq!(v.0.source_line(), Some("   {a=1, b=2} "));
    }

    #[derive(Debug, PartialEq, Eq, serde::Deserialize)]
    enum Enum<'a> {
        Newtype(#[serde(borrow)] super::Located<'a, Struct>),
    }
    #[test]
    fn newtype_variant_located() {
        use crate::{from_slice_escaped, Flavor};
        let v: Enum =
            from_slice_escaped(Flavor::Value, b" \n   Newtype a=1 b=2 \n ", &mut []).unwrap();
        let Enum::Newtype(v) = v;
        assert_eq!(v.offset, 13);
        assert_eq!(v.line, 2);
        assert_eq!(v.column, 11);
        assert_eq!(v.value, Struct { a: 1, b: 2 });
        assert_eq!(v.source_line(), Some("   Newtype a=1 b=2 "));
    }
}