Skip to content

Commit 3df563c

Browse files
authored
serde support (#88)
Support for serializing `Array<T, U>` as a serde tuple. Unfortunately `serde` lacks first-class support for arrays, so this is the best we can do other than a length-prefixed slice-like serialization, which might also be worth considering. This seems like the most sensible place to start, though. Closes #73
1 parent 544f75b commit 3df563c

File tree

5 files changed

+182
-0
lines changed

5 files changed

+182
-0
lines changed

.github/workflows/hybrid-array.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ jobs:
3838
targets: ${{ matrix.target }}
3939
- run: cargo build --no-default-features --target ${{ matrix.target }}
4040
- run: cargo build --no-default-features --target ${{ matrix.target }} --features extra-sizes
41+
- run: cargo build --no-default-features --target ${{ matrix.target }} --features serde
4142

4243
careful:
4344
runs-on: ubuntu-latest
@@ -105,4 +106,5 @@ jobs:
105106
with:
106107
toolchain: ${{ matrix.toolchain }}
107108
- run: cargo test
109+
- run: cargo test --features serde
108110
- run: cargo test --all-features

Cargo.lock

Lines changed: 66 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,14 @@ rust-version = "1.81"
1818

1919
[dependencies]
2020
typenum = { version = "1.17", features = ["const-generics"] }
21+
22+
# optional dependencies
23+
serde = { version = "1", optional = true, default-features = false }
2124
zeroize = { version = "1.8", optional = true, default-features = false }
2225

26+
[dev-dependencies]
27+
bincode = "1"
28+
2329
[features]
2430
extra-sizes = []
2531

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ mod from_fn;
9090
mod iter;
9191
mod traits;
9292

93+
#[cfg(feature = "serde")]
94+
mod serde;
95+
9396
pub use crate::{iter::TryFromIteratorError, traits::*};
9497
pub use typenum;
9598

src/serde.rs

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
//! Support for serializing and deserializing `Array` using `serde`.
2+
3+
use crate::{Array, ArraySize};
4+
use core::{fmt, marker::PhantomData};
5+
use serde::{
6+
de::{self, Deserialize, Deserializer, SeqAccess, Visitor},
7+
ser::{Serialize, SerializeTuple, Serializer},
8+
};
9+
10+
impl<'de, T, U> Deserialize<'de> for Array<T, U>
11+
where
12+
T: Deserialize<'de>,
13+
U: ArraySize,
14+
{
15+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
16+
where
17+
D: Deserializer<'de>,
18+
T: Deserialize<'de>,
19+
{
20+
struct ArrayVisitor<T> {
21+
element: PhantomData<T>,
22+
}
23+
24+
impl<'de, T, U> Visitor<'de> for ArrayVisitor<Array<T, U>>
25+
where
26+
T: Deserialize<'de>,
27+
U: ArraySize,
28+
{
29+
type Value = Array<T, U>;
30+
31+
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
32+
write!(formatter, "an array of length {}", U::USIZE)
33+
}
34+
35+
fn visit_seq<A>(self, mut seq: A) -> Result<Array<T, U>, A::Error>
36+
where
37+
A: SeqAccess<'de>,
38+
{
39+
Array::<T, U>::try_from_fn(|i| {
40+
seq.next_element()?
41+
.ok_or_else(|| de::Error::invalid_length(i, &self))
42+
})
43+
}
44+
}
45+
46+
let visitor = ArrayVisitor {
47+
element: PhantomData,
48+
};
49+
50+
deserializer.deserialize_tuple(U::USIZE, visitor)
51+
}
52+
}
53+
54+
impl<T, U> Serialize for Array<T, U>
55+
where
56+
T: Serialize,
57+
U: ArraySize,
58+
{
59+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
60+
where
61+
S: Serializer,
62+
{
63+
let mut seq = serializer.serialize_tuple(U::USIZE)?;
64+
65+
for elem in self {
66+
seq.serialize_element(elem)?;
67+
}
68+
69+
seq.end()
70+
}
71+
}
72+
73+
#[cfg(test)]
74+
mod tests {
75+
const INTEGER_ARRAY_EXAMPLE: [u64; 4] = [1, 2, 3, 4];
76+
use crate::{
77+
sizes::{U4, U5},
78+
Array,
79+
};
80+
81+
#[test]
82+
fn deserialize_integer_array() {
83+
let serialized = bincode::serialize(&INTEGER_ARRAY_EXAMPLE).unwrap();
84+
let deserialized: Array<u64, U4> = bincode::deserialize(&serialized).unwrap();
85+
assert_eq!(deserialized, INTEGER_ARRAY_EXAMPLE);
86+
}
87+
88+
#[test]
89+
fn deserialize_too_short() {
90+
let serialized = bincode::serialize(&INTEGER_ARRAY_EXAMPLE).unwrap();
91+
let deserialized: Result<Array<u64, U5>, bincode::Error> =
92+
bincode::deserialize(&serialized);
93+
94+
// TODO(tarcieri): check for more specific error type
95+
assert!(deserialized.is_err())
96+
}
97+
98+
#[test]
99+
fn serialize_integer_array() {
100+
let example: Array<u64, U4> = Array(INTEGER_ARRAY_EXAMPLE);
101+
let serialized = bincode::serialize(&example).unwrap();
102+
let deserialized: Array<u64, U4> = bincode::deserialize(&serialized).unwrap();
103+
assert_eq!(example, deserialized);
104+
}
105+
}

0 commit comments

Comments
 (0)