diff --git a/src/lib.rs b/src/lib.rs index e3225c7..d948237 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -67,6 +67,25 @@ //! # Ok::<(), serde_json::Error>(()) //! ``` //! +//! Even nested arrays are supported: +//! +//! ``` +//! # use serde::{Serialize, Deserialize}; +//! # use serde_json; +//! #[derive(Serialize, Debug, PartialEq, Eq)] +//! struct NestedArray { +//! #[serde(with = "serde_arrays")] +//! arr: [[u32; 64]; 64], +//! #[serde(with = "serde_arrays")] +//! vec: Vec<[u32; 96]>, +//! } +//! # let data = NestedArray{ arr: [[1; 64]; 64], vec: vec![[2; 96]; 37], }; +//! # let json = serde_json::to_string(&data)?; +//! # //let de_data = serde_json::from_str(&json)?; +//! # //assert_eq!(data, de_data); +//! # Ok::<(), serde_json::Error>(()) +//! ``` +//! //! # MSRV //! //! This library relies on the const generics feature introduced in Rust 1.51.0. @@ -74,39 +93,39 @@ //! # Relevant links //! //! * The [Serde issue](https://github.com/serde-rs/serde/issues/1937) for const generics support -//! * [serde-big-array](https://crates.io/crates/serde-big-array) is a similar crate, but it -//! depends on `unsafe` code (whether its use of such is safe or not is beyond this scope) +//! * [serde-big-array](https://crates.io/crates/serde-big-array) is a similar crate for large +//! arrays and const generic arrays +//! * [serde_with](https://crates.io/crates/serde_with/) is a much more flexible and powerful +//! crate, but with arguably more complex ergonomics //! //! [Serde]: https://serde.rs/ use serde::{ de::{self, Deserialize, Deserializer, SeqAccess, Visitor}, - ser::{Serialize, SerializeTuple, Serializer}, + ser::{Serialize, Serializer}, }; use std::{fmt, marker::PhantomData, mem::MaybeUninit}; -pub mod nested; +#[doc(hidden)] +pub mod serializable; +mod wrapper; +pub use serializable::Serializable; /// Serialize const generic or arbitrarily-large arrays /// -/// For any array up to length `usize::MAX`, this function will allow Serde to properly serialize -/// it, provided of course that the type `T` is itself serializable. +/// Types must implement the [`Serializable`] trait; while this requirement sharply limits how +/// composable the final result is, the simple ergonomics make up for it. /// -/// This implementation is adapted from the [Serde documentataion][serialize_map] +/// For greater flexibility see [`serde_with`][serde_with]. /// -/// [serialize_map]: https://serde.rs/impl-serialize.html#serializing-a-sequence-or-map -pub fn serialize(data: &[T; N], ser: S) -> Result +/// [serde_with]: https://crates.io/crates/serde_with/ +pub fn serialize(data: &A, ser: S) -> Result where + A: Serializable, S: Serializer, T: Serialize, { - // Fixed-length structures, including arrays, are supported in Serde as tuples - // See: https://serde.rs/impl-serialize.html#serializing-a-tuple - let mut s = ser.serialize_tuple(N)?; - for item in data { - s.serialize_element(item)?; - } - s.end() + data.serialize(ser) } /// A Serde Deserializer `Visitor` for [T; N] arrays diff --git a/src/nested.rs b/src/nested.rs deleted file mode 100644 index b323608..0000000 --- a/src/nested.rs +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2021 Travis Veazey -// -// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be -// copied, modified, or distributed except according to those terms. - -//! Serialize const generic or large arrays nested within arrays or `Vec`s -//! -//! This module extends the functionality of [`serde_arrays`][crate] to additionally support const generic -//! and large arrays that are nested within const generic or large arrays, or `Vec`s. -//! -//! ``` -//! use serde::{Serialize}; -//! use serde_json; -//! -//! #[derive(Serialize, Debug, PartialEq, Eq)] -//! struct NestedArray { -//! #[serde(with = "serde_arrays::nested")] -//! arr: [[u32; N]; M], -//! } -//! -//! let data = NestedArray{ arr: [[1; 16]; 64] }; -//! let json = serde_json::to_string(&data)?; -//! # //let de_data = serde_json::from_str(&json)?; -//! -//! # //assert_eq!(data, de_data); -//! # Ok::<(), serde_json::Error>(()) -//! ``` -//! - -use serde::ser::{Serialize, SerializeSeq, SerializeTuple, Serializer}; - -struct ArrayWrap<'a, T: Serialize, const N: usize> { - inner: &'a [T; N], -} - -impl<'a, T: Serialize, const N: usize> ArrayWrap<'a, T, N> { - pub fn new(array: &'a [T; N]) -> ArrayWrap<'a, T, N> { - ArrayWrap { inner: array } - } -} - -impl<'a, T: Serialize, const N: usize> Serialize for ArrayWrap<'a, T, N> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - super::serialize(self.inner, serializer) - } -} - -pub trait SerializeArray { - fn serialize(&self, ser: S) -> Result - where - S: Serializer; -} -impl SerializeArray for [[T; N]; M] { - fn serialize(&self, ser: S) -> Result - where - S: Serializer, - { - // Fixed-length structures, including arrays, are supported in Serde as tuples - // See: https://serde.rs/impl-serialize.html#serializing-a-tuple - let mut s = ser.serialize_tuple(N)?; - for item in self { - let wrapped = ArrayWrap::new(item); - s.serialize_element(&wrapped)?; - } - s.end() - } -} -impl SerializeArray for Vec<[T; N]> { - fn serialize(&self, ser: S) -> Result - where - S: Serializer, - { - let mut s = ser.serialize_seq(Some(self.len()))?; - for item in self { - let wrapped = ArrayWrap::new(item); - s.serialize_element(&wrapped)?; - } - s.end() - } -} -impl SerializeArray for [T; N] { - fn serialize(&self, ser: S) -> Result - where - S: Serializer, - { - super::serialize(self, ser) - } -} - -pub fn serialize(data: &A, ser: S) -> Result -where - A: SerializeArray, - S: Serializer, - T: Serialize, -{ - data.serialize(ser) -} diff --git a/src/serializable.rs b/src/serializable.rs new file mode 100644 index 0000000..cadd2c5 --- /dev/null +++ b/src/serializable.rs @@ -0,0 +1,79 @@ +// Copyright 2021 Travis Veazey +// +// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use crate::wrapper::ArrayWrap; +use serde::ser::{Serialize, SerializeSeq, SerializeTuple, Serializer}; + +/// Trait for types serializable using `serde_arrays` +/// +/// In order to serialize data using this crate, the type needs to implement this trait. While this +/// approach has limitations in what can be supported (namely it limits support to only those types +/// this trait is explicitly implemented on), the trade off is a significant increase in ergonomics. +/// +/// If the greater flexibility lost by this approach is needed, see [`serde_with`][serde_with]. +/// +/// [serde_with]: https://crates.io/crates/serde_with/ +pub trait Serializable { + fn serialize(&self, ser: S) -> Result + where + S: Serializer; +} + +impl Serializable for [[T; N]; M] { + fn serialize(&self, ser: S) -> Result + where + S: Serializer, + { + // Fixed-length structures, including arrays, are supported in Serde as tuples + // See: https://serde.rs/impl-serialize.html#serializing-a-tuple + let mut s = ser.serialize_tuple(N)?; + for item in self { + let wrapped = ArrayWrap::new(item); + s.serialize_element(&wrapped)?; + } + s.end() + } +} + +impl Serializable for Vec<[T; N]> { + fn serialize(&self, ser: S) -> Result + where + S: Serializer, + { + let mut s = ser.serialize_seq(Some(self.len()))?; + for item in self { + let wrapped = ArrayWrap::new(item); + s.serialize_element(&wrapped)?; + } + s.end() + } +} + +impl Serializable for [T; N] { + fn serialize(&self, ser: S) -> Result + where + S: Serializer, + { + serialize_as_tuple(self, ser) + } +} + +/// Serialize an array +/// +/// In Serde arrays (and other fixed-length structures) are supported as tuples +fn serialize_as_tuple(data: &[T; N], ser: S) -> Result +where + S: Serializer, + T: Serialize, +{ + // See: https://serde.rs/impl-serialize.html#serializing-a-tuple + let mut s = ser.serialize_tuple(N)?; + for item in data { + s.serialize_element(item)?; + } + s.end() +} diff --git a/src/wrapper.rs b/src/wrapper.rs new file mode 100644 index 0000000..6f15ca3 --- /dev/null +++ b/src/wrapper.rs @@ -0,0 +1,27 @@ +// Copyright 2021 Travis Veazey +// +// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +use serde::ser::{Serialize, Serializer}; + +pub struct ArrayWrap<'a, T: Serialize, const N: usize> { + inner: &'a [T; N], +} + +impl<'a, T: Serialize, const N: usize> ArrayWrap<'a, T, N> { + pub fn new(array: &'a [T; N]) -> ArrayWrap<'a, T, N> { + ArrayWrap { inner: array } + } +} + +impl<'a, T: Serialize, const N: usize> Serialize for ArrayWrap<'a, T, N> { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + super::serialize(self.inner, serializer) + } +} diff --git a/tests/common/nested.rs b/tests/common/nested.rs index 8fd82f1..6a934ff 100644 --- a/tests/common/nested.rs +++ b/tests/common/nested.rs @@ -9,24 +9,18 @@ use serde::Serialize; #[derive(Serialize, Debug, PartialEq, Eq)] pub struct NestedArray { - #[serde(with = "serde_arrays::nested")] + #[serde(with = "serde_arrays")] pub arr: [[u32; N]; 2], } #[derive(Serialize, Debug, PartialEq, Eq)] pub struct GenericNestedArray { - #[serde(with = "serde_arrays::nested")] + #[serde(with = "serde_arrays")] pub arr: [[u32; N]; M], } #[derive(Serialize, Debug, PartialEq, Eq)] pub struct VecArray { - #[serde(with = "serde_arrays::nested")] + #[serde(with = "serde_arrays")] pub arr: Vec<[u32; N]>, } - -#[derive(Serialize, Debug, PartialEq, Eq)] -pub struct FlatArray { - #[serde(with = "serde_arrays::nested")] - pub arr: [u32; N], -} diff --git a/tests/serialize_nested.rs b/tests/serialize_nested.rs index 8dabde4..7aa28b8 100644 --- a/tests/serialize_nested.rs +++ b/tests/serialize_nested.rs @@ -25,10 +25,3 @@ fn serialize_nested_array() { assert_eq!(json, &j_generic); assert_eq!(json, &j_vecced); } - -#[test] -fn serialize_flat_as_nested() { - let flat = FlatArray { arr: [1; 3] }; - let j_flat = serde_json::to_string(&flat).unwrap(); - assert_eq!("{\"arr\":[1,1,1]}", &j_flat); -}