Skip to content

Instantly share code, notes, and snippets.

@giuseppe998e
Last active March 25, 2024 14:36
Show Gist options
  • Save giuseppe998e/0b4f7d92de772e081a90b8003c986110 to your computer and use it in GitHub Desktop.
Save giuseppe998e/0b4f7d92de772e081a90b8003c986110 to your computer and use it in GitHub Desktop.
Wrappers for peeking into Serde's "MapAccess" and "SeqAccess" implementations without advancing the iterator. The "PeekableMapAccess" struct allows peeking at the next key and/or value in a map, while "PeekableSeqAccess" provides similar functionality for sequences.
//! Licensed under either of:
//! - Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
//! - MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
//!
//! This file may not be copied, modified, or distributed except according to those terms.
use std::marker::PhantomData;
use serde::{__private::de as private_de, de};
/// Wraps around a Serde's MapAccess, providing the ability
/// to peek at the next key and/or value without consuming it.
pub struct PeekableMapAccess<'de, A> {
map: A,
peeked_key: Option<Option<private_de::Content<'de>>>,
peeked_value: Option<private_de::Content<'de>>,
}
impl<'de, A> PeekableMapAccess<'de, A>
where
A: de::MapAccess<'de>,
{
/// Peeks at the next key in the map without consuming it.
pub fn peek_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, A::Error>
where
K: de::DeserializeSeed<'de>,
{
let key_ref = match self.peeked_key.as_ref() {
Some(key_ref) => key_ref,
None => {
self.peeked_key = Some(self.map.next_key::<private_de::Content<'de>>()?);
self.peeked_value = None; // Clears the previous peeked value
// SAFETY: a `None` variant for `self` would have been replaced by a `Some`
// variant in the code above.
unsafe { self.peeked_key.as_ref().unwrap_unchecked() }
}
};
match key_ref {
Some(key_ref) => {
let deserializer = private_de::ContentRefDeserializer::new(key_ref);
seed.deserialize(deserializer).map(Some)
}
None => Ok(None),
}
}
/// Peeks at the next value in the map without consuming it.
fn peek_value_seed<V>(&mut self, seed: V) -> Result<V::Value, A::Error>
where
V: de::DeserializeSeed<'de>,
{
let value_ref = match self.peeked_value.as_ref() {
Some(value_ref) => value_ref,
None => {
self.peeked_value = Some(self.map.next_value::<private_de::Content<'de>>()?);
// SAFETY: a `None` variant for `self` would have been replaced by a `Some`
// variant in the code above.
unsafe { self.peeked_value.as_ref().unwrap_unchecked() }
}
};
let deserializer = private_de::ContentRefDeserializer::new(value_ref);
seed.deserialize(deserializer)
}
/// Peeks at the next key in the map without consuming it.
///
/// This method exists as a convenience for `Deserialize` implementations.
#[inline]
pub fn peek_key<K>(&mut self) -> Result<Option<K>, A::Error>
where
K: de::Deserialize<'de>,
{
self.peek_key_seed(PhantomData)
}
/// Peeks at the next value in the map without consuming it.
///
/// This method exists as a convenience for `Deserialize` implementations.
#[inline]
pub fn peek_value<V>(&mut self) -> Result<V, A::Error>
where
V: de::Deserialize<'de>,
{
self.peek_value_seed(PhantomData)
}
}
impl<'de, A> de::MapAccess<'de> for PeekableMapAccess<'de, A>
where
A: de::MapAccess<'de>,
{
type Error = A::Error;
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error>
where
K: de::DeserializeSeed<'de>,
{
match self.peeked_key.take() {
Some(Some(key)) => {
let deserializer = private_de::ContentDeserializer::new(key);
seed.deserialize(deserializer).map(Some)
}
Some(None) => Ok(None),
None => self.map.next_key_seed(seed),
}
}
fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Self::Error>
where
V: de::DeserializeSeed<'de>,
{
self.peeked_key = None; // Clears the previous peeked key
match self.peeked_value.take() {
Some(value) => {
let deserializer = private_de::ContentDeserializer::new(value);
seed.deserialize(deserializer)
}
None => self.map.next_value_seed(seed),
}
}
fn size_hint(&self) -> Option<usize> {
self.map.size_hint()
}
}
impl<'de, A> From<A> for PeekableMapAccess<'de, A>
where
A: de::MapAccess<'de>,
{
fn from(map: A) -> Self {
Self {
map,
peeked_key: None,
peeked_value: None,
}
}
}
/// Wraps around a Serde's SeqAccess, providing the ability
/// to peek at the next element without consuming it.
pub struct PeekableSeqAccess<'de, S> {
seq: S,
peeked: Option<Option<private_de::Content<'de>>>,
}
impl<'de, S> PeekableSeqAccess<'de, S>
where
S: de::SeqAccess<'de>,
{
/// Peeks at the next element in the sequence without consuming it.
pub fn peek_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, S::Error>
where
T: de::DeserializeSeed<'de>,
{
let elem_ref = match self.peeked.as_ref() {
Some(elem_ref) => elem_ref,
None => {
self.peeked = Some(self.seq.next_element::<private_de::Content<'de>>()?);
// SAFETY: a `None` variant for `self` would have been replaced by a `Some`
// variant in the code above.
unsafe { self.peeked.as_ref().unwrap_unchecked() }
}
};
match elem_ref {
Some(elem_ref) => {
let deserializer = private_de::ContentRefDeserializer::new(elem_ref);
seed.deserialize(deserializer).map(Some)
}
None => Ok(None),
}
}
/// Peeks at the next element in the sequence without consuming it.
///
/// This method exists as a convenience for `Deserialize` implementations.
#[inline]
pub fn peek_element<T>(&mut self) -> Result<Option<T>, S::Error>
where
T: de::Deserialize<'de>,
{
self.peek_element_seed(PhantomData)
}
}
impl<'de, S> de::SeqAccess<'de> for PeekableSeqAccess<'de, S>
where
S: de::SeqAccess<'de>,
{
type Error = S::Error;
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
where
T: de::DeserializeSeed<'de>,
{
match self.peeked.take() {
None => self.seq.next_element_seed(seed),
Some(Some(elem)) => {
let deserializer = private_de::ContentDeserializer::new(elem);
seed.deserialize(deserializer).map(Some)
}
Some(None) => Ok(None),
}
}
fn size_hint(&self) -> Option<usize> {
self.seq.size_hint()
}
}
impl<'de, S> From<S> for PeekableSeqAccess<'de, S>
where
S: de::SeqAccess<'de>,
{
fn from(seq: S) -> Self {
Self { seq, peeked: None }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment