diff --git a/src/de.rs b/src/de.rs index ffd0d48c2..d0dfe51dc 100644 --- a/src/de.rs +++ b/src/de.rs @@ -5,6 +5,8 @@ use crate::error::{Error, ErrorCode, Result}; use crate::lexical; use crate::number::Number; use crate::read::{self, Fused, Reference}; +#[cfg(feature = "raw_value")] +use crate::Position; use alloc::string::String; use alloc::vec::Vec; #[cfg(feature = "float_roundtrip")] @@ -1757,6 +1759,7 @@ impl<'de, 'a, R: Read<'de>> de::Deserializer<'de> for &'a mut Deserializer { self.deserialize_seq(visitor) } + #[cfg(not(feature = "raw_value"))] fn deserialize_tuple_struct( self, _name: &'static str, @@ -1769,6 +1772,58 @@ impl<'de, 'a, R: Read<'de>> de::Deserializer<'de> for &'a mut Deserializer { self.deserialize_seq(visitor) } + #[cfg(feature = "raw_value")] + fn deserialize_tuple_struct( + self, + name: &'static str, + len: usize, + visitor: V, + ) -> Result + where + V: de::Visitor<'de>, + { + if name == crate::read::TOKEN && len == 2 { + struct PosAccess(u8, Position); + impl<'de> serde::de::SeqAccess<'de> for PosAccess { + type Error = Error; + fn next_element_seed(&mut self, seed: T) -> Result> + where + T: de::DeserializeSeed<'de>, + { + let res = match self.0 { + 0 => { + seed.deserialize(serde::de::value::UsizeDeserializer::new(self.1.line))? + } + 1 => seed + .deserialize(serde::de::value::UsizeDeserializer::new(self.1.column))?, + _ => return Ok(None), + }; + self.0 += 1; + Ok(Some(res)) + } + } + return visitor.visit_seq(PosAccess(0, self.read.position())); + } else if name == crate::positioned::TOKEN && len == 2 { + struct PosAccess<'a, R: 'a>(u8, &'a mut Deserializer); + impl<'de, 'a, R: Read<'de> + 'a> serde::de::SeqAccess<'de> for PosAccess<'a, R> { + type Error = Error; + fn next_element_seed(&mut self, seed: T) -> Result> + where + T: de::DeserializeSeed<'de>, + { + let res = match self.0 { + 0 | 1 => seed.deserialize(&mut *self.1)?, + _ => return Ok(None), + }; + self.0 += 1; + Ok(Some(res)) + } + } + return visitor.visit_seq(PosAccess(0, self)); + } + self.deserialize_seq(visitor) + } + fn deserialize_map(self, visitor: V) -> Result where V: de::Visitor<'de>, diff --git a/src/lib.rs b/src/lib.rs index cf4b83d19..0d90022fc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -417,5 +417,12 @@ mod lexical; mod number; mod read; +#[cfg(feature = "raw_value")] +mod positioned; #[cfg(feature = "raw_value")] mod raw; + +#[cfg(feature = "raw_value")] +pub use positioned::Positioned; +#[cfg(feature = "raw_value")] +pub use read::Position; diff --git a/src/positioned.rs b/src/positioned.rs new file mode 100644 index 000000000..29a50a6d3 --- /dev/null +++ b/src/positioned.rs @@ -0,0 +1,76 @@ +use core::marker::PhantomData; + +use serde::{ + de::{SeqAccess, Visitor}, + Deserialize, Deserializer, Serialize, +}; + +use crate::read::Position; +#[cfg(feature = "raw_value")] +use crate::{de::StrRead, read::PositionedRead, value::RawValue}; + +pub const TOKEN: &str = "$serde_json::private::Positioned"; + +/// A value that is saved together with its position in the input. +pub struct Positioned { + /// The position in the input. + pub position: Position, + /// The actual deserialized value. + pub value: T, +} + +impl<'de, T> Deserialize<'de> for Positioned +where + T: Deserialize<'de>, +{ + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct PosVisitor(PhantomData); + + impl<'de, T: Deserialize<'de>> Visitor<'de> for PosVisitor { + type Value = Positioned; + fn expecting(&self, formatter: &mut alloc::fmt::Formatter) -> alloc::fmt::Result { + write!(formatter, "positioned value") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + Ok(Positioned { + position: seq.next_element()?.unwrap(), + value: seq.next_element()?.unwrap(), + }) + } + } + + deserializer.deserialize_tuple_struct(TOKEN, 2, PosVisitor(PhantomData)) + } +} + +impl Serialize for Positioned { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.value.serialize(serializer) + } +} + +#[cfg(all(feature = "raw_value", feature = "std"))] +impl Positioned> { + /// Read from a positioned RawValue. + pub fn read(&self) -> PositionedRead> { + PositionedRead::new(self.position.clone(), StrRead::new(self.value.get())) + } +} + +#[cfg(feature = "raw_value")] +impl<'a> Positioned<&'a RawValue> { + /// Read from a positioned RawValue. + pub fn read(&self) -> PositionedRead> { + PositionedRead::new(self.position.clone(), StrRead::new(self.value.get())) + } +} diff --git a/src/read.rs b/src/read.rs index fc3a3ca74..99bd05a60 100644 --- a/src/read.rs +++ b/src/read.rs @@ -4,6 +4,14 @@ use core::char; use core::cmp; use core::ops::Deref; use core::str; +#[cfg(feature = "raw_value")] +use serde::de::SeqAccess; +#[cfg(feature = "raw_value")] +use serde::ser::SerializeStruct; +#[cfg(feature = "raw_value")] +use serde::Deserialize; +#[cfg(feature = "raw_value")] +use serde::Serialize; #[cfg(feature = "std")] use crate::io; @@ -114,8 +122,12 @@ pub trait Read<'de>: private::Sealed { fn set_failed(&mut self, failed: &mut bool); } +/// The position in the input stream. +#[derive(Clone)] pub struct Position { + /// The current line number. pub line: usize, + /// The current column number. pub column: usize, } @@ -1002,3 +1014,145 @@ fn decode_hex_val(val: u8) -> Option { Some(n) } } + +#[cfg(feature = "raw_value")] +pub(crate) const TOKEN: &str = "$serde_json::private::Position"; + +#[cfg(feature = "raw_value")] +impl<'de> Deserialize<'de> for Position { + fn deserialize(deserializer: D) -> core::result::Result + where + D: serde::Deserializer<'de>, + { + struct PosVisitor; + + impl<'de> Visitor<'de> for PosVisitor { + type Value = Position; + + fn expecting(&self, f: &mut alloc::fmt::Formatter) -> alloc::fmt::Result { + write!(f, "position indicator") + } + + fn visit_seq(self, mut seq: A) -> core::result::Result + where + A: SeqAccess<'de>, + { + Ok(Position { + line: seq.next_element()?.unwrap(), + column: seq.next_element()?.unwrap(), + }) + } + } + + deserializer.deserialize_tuple_struct(TOKEN, 2, PosVisitor) + } +} + +#[cfg(feature = "raw_value")] +impl Serialize for Position { + fn serialize(&self, serializer: S) -> core::result::Result + where + S: serde::Serializer, + { + let mut s = serializer.serialize_struct("position", 2)?; + s.serialize_field("line", &self.line)?; + s.serialize_field("column", &self.column)?; + s.end() + } +} + +#[cfg(feature = "raw_value")] +pub struct PositionedRead { + start: Position, + read: R, +} + +#[cfg(feature = "raw_value")] +impl<'de, R: Read<'de>> private::Sealed for PositionedRead {} + +#[cfg(feature = "raw_value")] +impl<'de, R: Read<'de>> PositionedRead { + pub fn new(start: Position, read: R) -> Self { + Self { start, read } + } + + fn adjust_pos(&self, pos: Position) -> Position { + match pos.line == 1 { + true => Position { + line: self.start.line, + column: self.start.column + pos.column + 1, + }, + false => Position { + line: self.start.line + pos.line - 1, + column: pos.column, + }, + } + } +} + +#[cfg(feature = "raw_value")] +impl<'de, R: Read<'de>> Read<'de> for PositionedRead { + fn next(&mut self) -> Result> { + self.read.next() + } + + fn peek(&mut self) -> Result> { + self.read.peek() + } + + fn discard(&mut self) { + self.read.discard() + } + + fn position(&self) -> Position { + self.adjust_pos(self.read.position()) + } + + fn peek_position(&self) -> Position { + self.adjust_pos(self.read.peek_position()) + } + + fn byte_offset(&self) -> usize { + self.read.byte_offset() + } + + fn parse_str<'s>(&'s mut self, scratch: &'s mut Vec) -> Result> { + self.read.parse_str(scratch) + } + + fn parse_str_raw<'s>( + &'s mut self, + scratch: &'s mut Vec, + ) -> Result> { + self.read.parse_str_raw(scratch) + } + + fn ignore_str(&mut self) -> Result<()> { + self.read.ignore_str() + } + + fn decode_hex_escape(&mut self) -> Result { + self.read.decode_hex_escape() + } + + #[cfg(feature = "raw_value")] + #[doc(hidden)] + fn begin_raw_buffering(&mut self) { + self.read.begin_raw_buffering() + } + + #[cfg(feature = "raw_value")] + #[doc(hidden)] + fn end_raw_buffering(&mut self, visitor: V) -> Result + where + V: Visitor<'de>, + { + self.read.end_raw_buffering(visitor) + } + + const should_early_return_if_failed: bool = R::should_early_return_if_failed; + + fn set_failed(&mut self, failed: &mut bool) { + self.read.set_failed(failed) + } +}