Skip to content

Commit

Permalink
Document tuple impls as fake variadic
Browse files Browse the repository at this point in the history
  • Loading branch information
dtolnay committed May 25, 2024
1 parent 3761854 commit a45dbab
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 70 deletions.
133 changes: 77 additions & 56 deletions serde/src/de/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1393,82 +1393,103 @@ array_impls! {
macro_rules! tuple_impls {
($($len:tt => ($($n:tt $name:ident)+))+) => {
$(
impl<'de, $($name: Deserialize<'de>),+> Deserialize<'de> for ($($name,)+) {
#[cfg_attr(docsrs, doc(hidden))]
impl<'de, $($name),+> Deserialize<'de> for ($($name,)+)
where
$($name: Deserialize<'de>,)+
{
tuple_impl_body!($len => ($($n $name)+));
}
)+
};
}

macro_rules! tuple_impl_body {
($len:tt => ($($n:tt $name:ident)+)) => {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct TupleVisitor<$($name,)+> {
marker: PhantomData<($($name,)+)>,
}

impl<'de, $($name: Deserialize<'de>),+> Visitor<'de> for TupleVisitor<$($name,)+> {
type Value = ($($name,)+);

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str(concat!("a tuple of size ", $len))
}

#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
#[allow(non_snake_case)]
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
D: Deserializer<'de>,
A: SeqAccess<'de>,
{
struct TupleVisitor<$($name,)+> {
marker: PhantomData<($($name,)+)>,
}
$(
let $name = match tri!(seq.next_element()) {
Some(value) => value,
None => return Err(Error::invalid_length($n, &self)),
};
)+

impl<'de, $($name: Deserialize<'de>),+> Visitor<'de> for TupleVisitor<$($name,)+> {
type Value = ($($name,)+);
Ok(($($name,)+))
}
}

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str(concat!("a tuple of size ", $len))
}
deserializer.deserialize_tuple($len, TupleVisitor { marker: PhantomData })
}

#[inline]
#[allow(non_snake_case)]
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
$(
let $name = match tri!(seq.next_element()) {
Some(value) => value,
None => return Err(Error::invalid_length($n, &self)),
};
)+
#[inline]
fn deserialize_in_place<D>(deserializer: D, place: &mut Self) -> Result<(), D::Error>
where
D: Deserializer<'de>,
{
struct TupleInPlaceVisitor<'a, $($name: 'a,)+>(&'a mut ($($name,)+));

Ok(($($name,)+))
}
}
impl<'a, 'de, $($name: Deserialize<'de>),+> Visitor<'de> for TupleInPlaceVisitor<'a, $($name,)+> {
type Value = ();

deserializer.deserialize_tuple($len, TupleVisitor { marker: PhantomData })
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str(concat!("a tuple of size ", $len))
}

#[inline]
fn deserialize_in_place<D>(deserializer: D, place: &mut Self) -> Result<(), D::Error>
#[allow(non_snake_case)]
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
D: Deserializer<'de>,
A: SeqAccess<'de>,
{
struct TupleInPlaceVisitor<'a, $($name: 'a,)+>(&'a mut ($($name,)+));

impl<'a, 'de, $($name: Deserialize<'de>),+> Visitor<'de> for TupleInPlaceVisitor<'a, $($name,)+> {
type Value = ();

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str(concat!("a tuple of size ", $len))
}

#[inline]
#[allow(non_snake_case)]
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
$(
if tri!(seq.next_element_seed(InPlaceSeed(&mut (self.0).$n))).is_none() {
return Err(Error::invalid_length($n, &self));
}
)+

Ok(())
$(
if tri!(seq.next_element_seed(InPlaceSeed(&mut (self.0).$n))).is_none() {
return Err(Error::invalid_length($n, &self));
}
}
)+

deserializer.deserialize_tuple($len, TupleInPlaceVisitor(place))
Ok(())
}
}
)+
}

deserializer.deserialize_tuple($len, TupleInPlaceVisitor(place))
}
};
}

#[cfg_attr(docsrs, doc(fake_variadic))]
#[cfg_attr(
docsrs,
doc = "This trait is implemented for tuples up to 16 items long."
)]
impl<'de, T> Deserialize<'de> for (T,)
where
T: Deserialize<'de>,
{
tuple_impl_body!(1 => (0 T));
}

tuple_impls! {
1 => (0 T0)
2 => (0 T0 1 T1)
3 => (0 T0 1 T1 2 T2)
4 => (0 T0 1 T1 2 T2 3 T3)
Expand Down
3 changes: 2 additions & 1 deletion serde/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@
// Support using Serde without the standard library!
#![cfg_attr(not(feature = "std"), no_std)]
// Show which crate feature enables conditionally compiled APIs in documentation.
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(docsrs, feature(doc_cfg, rustdoc_internals))]
#![cfg_attr(docsrs, allow(internal_features))]
// Unstable functionality only if the user asks for it. For tracking and
// discussion of these features please refer to this issue:
//
Expand Down
44 changes: 31 additions & 13 deletions serde/src/ser/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -386,28 +386,46 @@ impl Serialize for ! {
macro_rules! tuple_impls {
($($len:expr => ($($n:tt $name:ident)+))+) => {
$(
#[cfg_attr(docsrs, doc(hidden))]
impl<$($name),+> Serialize for ($($name,)+)
where
$($name: Serialize,)+
{
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut tuple = tri!(serializer.serialize_tuple($len));
$(
tri!(tuple.serialize_element(&self.$n));
)+
tuple.end()
}
tuple_impl_body!($len => ($($n)+));
}
)+
}
};
}

macro_rules! tuple_impl_body {
($len:expr => ($($n:tt)+)) => {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut tuple = tri!(serializer.serialize_tuple($len));
$(
tri!(tuple.serialize_element(&self.$n));
)+
tuple.end()
}
};
}

#[cfg_attr(docsrs, doc(fake_variadic))]
#[cfg_attr(
docsrs,
doc = "This trait is implemented for tuples up to 16 items long."
)]
impl<T> Serialize for (T,)
where
T: Serialize,
{
tuple_impl_body!(1 => (0));
}

tuple_impls! {
1 => (0 T0)
2 => (0 T0 1 T1)
3 => (0 T0 1 T1 2 T2)
4 => (0 T0 1 T1 2 T2 3 T3)
Expand Down

0 comments on commit a45dbab

Please sign in to comment.