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>,
{
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 "));
}
}