diff --git a/alox-48-derive/Cargo.toml b/alox-48-derive/Cargo.toml index f1a71f5..a4b09ce 100644 --- a/alox-48-derive/Cargo.toml +++ b/alox-48-derive/Cargo.toml @@ -6,7 +6,17 @@ authors = ["Melody Madeline Lyons"] repository = "https://github.com/Speak2Erase/alox-48" license = "MPL-2.0" description = "ruby marshal data deserializer" -keywords = ["ruby", "serde", "deserialization", "marshal", "ruby-marshal"] +keywords = [ + "ruby", + "deserialization", + "marshal", + "ruby-marshal", + "data-format", + "parser", + "deserializer", + "serialization", + "serializer", +] categories = ["parser-implementations"] [lib] diff --git a/alox-48/src/de/impls.rs b/alox-48/src/de/impls.rs index 0673f4f..755cb3a 100644 --- a/alox-48/src/de/impls.rs +++ b/alox-48/src/de/impls.rs @@ -10,11 +10,12 @@ use std::{ collections::{BTreeMap, BTreeSet, HashMap, HashSet, LinkedList, VecDeque}, hash::{BuildHasher, Hash}, marker::PhantomData, + num::*, }; use super::{ traits::VisitorOption, ArrayAccess, Deserialize, DeserializerTrait, Error, HashAccess, Result, - Visitor, + Unexpected, Visitor, }; use crate::Sym; @@ -50,8 +51,59 @@ macro_rules! primitive_int_impl { }; } +struct NonZeroIntVisitor; + +impl<'de> Visitor<'de> for NonZeroIntVisitor { + type Value = std::num::NonZeroI32; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("a non-zero integer") + } + + fn visit_i32(self, v: i32) -> Result { + std::num::NonZeroI32::new(v) + .ok_or_else(|| Error::invalid_value(Unexpected::Integer(v), &self)) + } + + fn visit_f64(self, v: f64) -> Result { + std::num::NonZeroI32::new(v as i32) + .ok_or_else(|| Error::invalid_value(Unexpected::Integer(v as i32), &self)) + } +} + primitive_int_impl!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize); +macro_rules! nonzero_int_impl { + ($($primitive:ty),*) => { + $(impl<'de> Deserialize<'de> for $primitive { + fn deserialize(deserializer: D) -> Result + where + D: DeserializerTrait<'de>, + { + let i = deserializer.deserialize(NonZeroIntVisitor)?.get(); + // we've already asserted that it's non-zero simply by the fact that NonZeroIntVisitor returns a NonZeroI32. + // so this new_unchecked is safe + Ok(unsafe { <$primitive>::new_unchecked(i as _) }) + } + })* + }; +} + +nonzero_int_impl!( + NonZeroU8, + NonZeroU16, + NonZeroU32, + NonZeroU64, + NonZeroU128, + NonZeroUsize, + NonZeroI8, + NonZeroI16, + NonZeroI32, + NonZeroI64, + NonZeroI128, + NonZeroIsize +); + struct UnitVisitor; impl<'de> Visitor<'de> for UnitVisitor {