Skip to content

Commit

Permalink
Add serde support (#38)
Browse files Browse the repository at this point in the history
* Add serde support

* Don't pin serde patch version
  • Loading branch information
widberg authored Oct 22, 2023
1 parent 2f4e076 commit 493f0fd
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

- Support `Step` so that arbitrary-int can be used in a range expression, e.g. `for n in u3::MIN..=u3::MAX { println!("{n}") }`. Note this trait is currently unstable, and so is only usable in nightly. Enable this feature with `step_trait`.
- Support formatting via [defmt](https://crates.io/crates/defmt). Enable the option `defmt` feature
- Support serializing and deserializing via [serde](https://crates.io/crates/serde). Enable the option `serde` feature

## arbitrary-int 1.2.6

Expand Down
7 changes: 7 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ step_trait = []
# Supports defmt
defmt = ["dep:defmt"]

# Supports serde
serde = ["dep:serde"]

[dependencies]
num-traits = { version = "0.2.15", default-features = false, optional = true }
defmt = { version = "0.3.5", optional = true }
serde = { version = "1.0", optional = true, default-features = false}

[dev-dependencies]
serde_test = "1.0"
54 changes: 54 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ use core::ops::{
Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not, Shl,
ShlAssign, Shr, ShrAssign, Sub, SubAssign,
};
#[cfg(feature = "serde")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct TryNewError;
Expand Down Expand Up @@ -722,6 +724,58 @@ where
}
}

#[cfg(feature = "serde")]
impl<T, const BITS: usize> Serialize for UInt<T, BITS>
where
T: Serialize,
{
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.value.serialize(serializer)
}
}

// Serde's invalid_value error (https://rust-lang.github.io/hashbrown/serde/de/trait.Error.html#method.invalid_value)
// takes an Unexpected (https://rust-lang.github.io/hashbrown/serde/de/enum.Unexpected.html) which only accepts a 64 bit
// unsigned integer. This is a problem for us because we want to support 128 bit unsigned integers. To work around this
// we define our own error type using the UInt's underlying type which implements Display and then use
// serde::de::Error::custom to create an error with our custom type.
#[cfg(feature = "serde")]
struct InvalidUIntValueError<T: Display> {
value: T,
max: T,
}

#[cfg(feature = "serde")]
impl<T: Display> Display for InvalidUIntValueError<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(
f,
"invalid value: integer `{}`, expected a value between `0` and `{}`",
self.value, self.max
)
}
}

#[cfg(feature = "serde")]
impl<'de, T: Display, const BITS: usize> Deserialize<'de> for UInt<T, BITS>
where
Self: Number,
T: Deserialize<'de> + PartialOrd,
{
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let value = T::deserialize(deserializer)?;

if value <= Self::MAX.value {
Ok(Self { value })
} else {
Err(serde::de::Error::custom(InvalidUIntValueError {
value,
max: Self::MAX.value,
}))
}
}
}

impl<T, const BITS: usize> Hash for UInt<T, BITS>
where
T: Hash,
Expand Down
26 changes: 26 additions & 0 deletions tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1373,3 +1373,29 @@ fn steps_between() {
Step::steps_between(&u125::new(0x7), &u125::new(0x1_0000_0000_0000_0007))
);
}

#[cfg(feature = "serde")]
#[test]
fn serde() {
use serde_test::{assert_de_tokens_error, assert_tokens, Token};

let a = u7::new(0b0101_0101);
assert_tokens(&a, &[Token::U8(0b0101_0101)]);

let b = u63::new(0x1234_5678_9ABC_DEFE);
assert_tokens(&b, &[Token::U64(0x1234_5678_9ABC_DEFE)]);

// This requires https://github.com/serde-rs/test/issues/18 (Add Token::I128 and Token::U128 to serde_test)
// let c = u127::new(0x1234_5678_9ABC_DEFE_DCBA_9876_5432_1010);
// assert_tokens(&c, &[Token::U128(0x1234_5678_9ABC_DEFE_DCBA_9876_5432_1010)]);

assert_de_tokens_error::<u2>(
&[Token::U8(0b0101_0101)],
"invalid value: integer `85`, expected a value between `0` and `3`",
);

assert_de_tokens_error::<u100>(
&[Token::I64(-1)],
"invalid value: integer `-1`, expected u128",
);
}

0 comments on commit 493f0fd

Please sign in to comment.