From dced4416a7c150780867708b16fba7d56782a5ee Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 12 Jan 2017 15:31:40 +0100 Subject: [PATCH] add wrapper to serde that allows serializing iterators as sequences --- serde/src/lib.rs | 2 +- serde/src/ser/impls.rs | 28 ++++++++++++++++++++++++++ serde/src/ser/mod.rs | 20 +++++++++++++++++++ testing/tests/test_ser.rs | 41 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 1 deletion(-) diff --git a/serde/src/lib.rs b/serde/src/lib.rs index 81a13620..522cf094 100644 --- a/serde/src/lib.rs +++ b/serde/src/lib.rs @@ -30,7 +30,7 @@ extern crate alloc; #[cfg(feature = "std")] mod core { pub use std::{ops, hash, fmt, cmp, marker, mem, i8, i16, i32, i64, u8, u16, u32, u64, isize, - usize, f32, f64, char, str, num, slice, iter}; + usize, f32, f64, char, str, num, slice, iter, cell}; #[cfg(feature = "unstable")] extern crate core; #[cfg(feature = "unstable")] diff --git a/serde/src/ser/impls.rs b/serde/src/ser/impls.rs index 5653a717..0705af86 100644 --- a/serde/src/ser/impls.rs +++ b/serde/src/ser/impls.rs @@ -70,6 +70,7 @@ use super::{ Error, Serialize, Serializer, + IteratorSerializer, }; /////////////////////////////////////////////////////////////////////////////// @@ -221,6 +222,33 @@ array_impls!(32); /////////////////////////////////////////////////////////////////////////////// +#[cfg(feature = "unstable")] +impl<'a, I> Serialize for IteratorSerializer + where I: Iterator, ::Item: Serialize +{ + #[inline] + fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> + where S: Serializer, + { + // FIXME: use specialization to prevent invalidating the object in case of clonable iterators? + let iter = match self.0.borrow_mut().take() { + Some(iter) => iter, + None => return Err(S::Error::custom("IteratorSerializer used twice")), + }; + let size = match iter.size_hint() { + (lo, Some(hi)) if lo == hi => Some(lo), + _ => None, + }; + let mut state = try!(serializer.serialize_seq(size)); + for e in iter { + try!(serializer.serialize_seq_elt(&mut state, e)); + } + serializer.serialize_seq_end(state) + } +} + +/////////////////////////////////////////////////////////////////////////////// + macro_rules! serialize_seq { () => { #[inline] diff --git a/serde/src/ser/mod.rs b/serde/src/ser/mod.rs index ab903b2f..ab6d08fa 100644 --- a/serde/src/ser/mod.rs +++ b/serde/src/ser/mod.rs @@ -18,6 +18,9 @@ use error; #[cfg(all(feature = "collections", not(feature = "std")))] use collections::String; +use core::marker::PhantomData; +use core::cell::RefCell; + pub mod impls; /////////////////////////////////////////////////////////////////////////////// @@ -410,3 +413,20 @@ pub trait Serializer { state: Self::StructVariantState, ) -> Result<(), Self::Error>; } + + +/// A wrapper type for iterators that implements `Serialize` for iterators whose items implement +/// `Serialize`. Don't use multiple times. Create new versions of this with the `iterator` function +/// every time you want to serialize an iterator. +pub struct IteratorSerializer(RefCell>) + where ::Item: Serialize, + I: Iterator; + +/// Creates a temporary type that can be passed to any function expecting a `Serialize` and will +/// serialize the given iterator as a sequence +pub fn iterator(iter: I) -> IteratorSerializer + where ::Item: Serialize, + I: Iterator +{ + IteratorSerializer(RefCell::new(Some(iter))) +} diff --git a/testing/tests/test_ser.rs b/testing/tests/test_ser.rs index 3a5a5748..02943fab 100644 --- a/testing/tests/test_ser.rs +++ b/testing/tests/test_ser.rs @@ -15,6 +15,8 @@ use self::serde_test::{ extern crate fnv; use self::fnv::FnvHasher; +use serde::ser::iterator; + ////////////////////////////////////////////////////////////////////////// #[derive(Serialize)] @@ -98,6 +100,45 @@ declare_ser_tests! { Token::I32(1), ], } + test_iterator { + iterator([0; 0].iter()) => &[ + Token::SeqStart(Some(0)), + Token::SeqEnd, + ], + iterator([1, 2, 3].iter()) => &[ + Token::SeqStart(Some(3)), + Token::SeqSep, + Token::I32(1), + + Token::SeqSep, + Token::I32(2), + + Token::SeqSep, + Token::I32(3), + Token::SeqEnd, + ], + iterator([1, 2, 3].iter().map(|x| x * 2)) => &[ + Token::SeqStart(Some(3)), + Token::SeqSep, + Token::I32(2), + + Token::SeqSep, + Token::I32(4), + + Token::SeqSep, + Token::I32(6), + Token::SeqEnd, + ], + iterator([1, 2, 3].iter().filter(|&x| x % 2 != 0)) => &[ + Token::SeqStart(None), + Token::SeqSep, + Token::I32(1), + + Token::SeqSep, + Token::I32(3), + Token::SeqEnd, + ], + } test_slice { &[0][..0] => &[ Token::SeqStart(Some(0)),