fixed-bytes: add serde, document feature flags (#226)
Some checks are pending
Audit / audit (push) Waiting to run
CI / fmt (push) Waiting to run
CI / typo (push) Waiting to run
CI / ci (macos-latest, stable, bash) (push) Waiting to run
CI / ci (ubuntu-latest, stable, bash) (push) Waiting to run
CI / ci (windows-latest, stable-x86_64-pc-windows-gnu, msys2 {0}) (push) Waiting to run
Deny / audit (push) Waiting to run
Doc / build (push) Waiting to run
Doc / deploy (push) Blocked by required conditions

* fixed-bytes: add `serde`, document feature flags

* manual impl `serde::Deserialize`

* add serde tests
This commit is contained in:
hinto-janai 2024-07-10 21:00:47 -04:00 committed by GitHub
parent 5aeb8af4b4
commit 824651c8cf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 116 additions and 2 deletions

5
Cargo.lock generated
View file

@ -274,6 +274,9 @@ name = "bytes"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
dependencies = [
"serde",
]
[[package]]
name = "cc"
@ -641,6 +644,8 @@ name = "cuprate-fixed-bytes"
version = "0.1.0"
dependencies = [
"bytes",
"serde",
"serde_json",
"thiserror",
]

View file

@ -6,9 +6,14 @@ license = "MIT"
authors = ["Boog900"]
[features]
default = ["std"]
default = ["std", "serde"]
std = ["bytes/std", "dep:thiserror"]
serde = ["bytes/serde", "dep:serde"]
[dependencies]
thiserror = { workspace = true, optional = true }
bytes = { workspace = true }
bytes = { workspace = true }
serde = { workspace = true, features = ["derive"], optional = true }
[dev-dependencies]
serde_json = { workspace = true, features = ["std"] }

10
net/fixed-bytes/README.md Normal file
View file

@ -0,0 +1,10 @@
# `cuprate-fixed-bytes`
TODO
# Feature flags
| Feature flag | Does what |
|--------------|-----------|
| `std` | TODO
| `serde` | Enables `serde` on applicable types
`serde` is enabled by default.

View file

@ -1,3 +1,5 @@
#![doc = include_str!("../README.md")]
use core::{
fmt::{Debug, Formatter},
ops::{Deref, Index},
@ -5,7 +7,11 @@ use core::{
use bytes::{BufMut, Bytes, BytesMut};
#[cfg(feature = "serde")]
use serde::{Deserialize, Deserializer, Serialize};
#[cfg_attr(feature = "std", derive(thiserror::Error))]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub enum FixedByteError {
#[cfg_attr(
feature = "std",
@ -43,8 +49,30 @@ impl Debug for FixedByteError {
/// Internally this is just a wrapper around [`Bytes`], with the constructors checking that the length is equal to `N`.
/// This implements [`Deref`] with the target being `[u8; N]`.
#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
#[repr(transparent)]
pub struct ByteArray<const N: usize>(Bytes);
#[cfg(feature = "serde")]
impl<'de, const N: usize> Deserialize<'de> for ByteArray<N> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let bytes = Bytes::deserialize(deserializer)?;
let len = bytes.len();
if len == N {
Ok(Self(bytes))
} else {
Err(serde::de::Error::invalid_length(
len,
&N.to_string().as_str(),
))
}
}
}
impl<const N: usize> ByteArray<N> {
pub fn take_bytes(self) -> Bytes {
self.0
@ -88,8 +116,30 @@ impl<const N: usize> TryFrom<Vec<u8>> for ByteArray<N> {
}
#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
#[repr(transparent)]
pub struct ByteArrayVec<const N: usize>(Bytes);
#[cfg(feature = "serde")]
impl<'de, const N: usize> Deserialize<'de> for ByteArrayVec<N> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let bytes = Bytes::deserialize(deserializer)?;
let len = bytes.len();
if len % N == 0 {
Ok(Self(bytes))
} else {
Err(serde::de::Error::invalid_length(
len,
&N.to_string().as_str(),
))
}
}
}
impl<const N: usize> ByteArrayVec<N> {
pub fn len(&self) -> usize {
self.0.len() / N
@ -197,6 +247,8 @@ impl<const N: usize> Index<usize> for ByteArrayVec<N> {
#[cfg(test)]
mod tests {
use serde_json::{from_str, to_string};
use super::*;
#[test]
@ -207,4 +259,46 @@ mod tests {
assert_eq!(bytes.len(), 100);
let _ = bytes[99];
}
/// Tests that `serde` works on [`ByteArray`].
#[test]
#[cfg(feature = "serde")]
fn byte_array_serde() {
let b = ByteArray::from([1, 0, 0, 0, 1]);
let string = to_string(&b).unwrap();
assert_eq!(string, "[1,0,0,0,1]");
let b2 = from_str::<ByteArray<5>>(&string).unwrap();
assert_eq!(b, b2);
}
/// Tests that `serde` works on [`ByteArrayVec`].
#[test]
#[cfg(feature = "serde")]
fn byte_array_vec_serde() {
let b = ByteArrayVec::from([1, 0, 0, 0, 1]);
let string = to_string(&b).unwrap();
assert_eq!(string, "[1,0,0,0,1]");
let b2 = from_str::<ByteArrayVec<5>>(&string).unwrap();
assert_eq!(b, b2);
}
/// Tests that bad input `serde` fails on [`ByteArray`].
#[test]
#[cfg(feature = "serde")]
#[should_panic(
expected = r#"called `Result::unwrap()` on an `Err` value: Error("invalid length 4, expected 5", line: 0, column: 0)"#
)]
fn byte_array_bad_deserialize() {
from_str::<ByteArray<5>>("[1,0,0,0]").unwrap();
}
/// Tests that bad input `serde` fails on [`ByteArrayVec`].
#[test]
#[cfg(feature = "serde")]
#[should_panic(
expected = r#"called `Result::unwrap()` on an `Err` value: Error("invalid length 4, expected 5", line: 0, column: 0)"#
)]
fn byte_array_vec_bad_deserialize() {
from_str::<ByteArrayVec<5>>("[1,0,0,0]").unwrap();
}
}