1use core::marker::PhantomData;
44
45use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Visitor};
46
47#[cfg(feature = "alloc")]
48use alloc::string::ToString;
49
50impl<const SCALE: u32> Serialize for crate::D<crate::int::types::Int<2>, SCALE> {
53 #[inline]
60 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
61 if serializer.is_human_readable() {
62 #[cfg(feature = "alloc")]
66 {
67 serializer.serialize_str(&self.0.to_string())
68 }
69 #[cfg(not(feature = "alloc"))]
74 {
75 let _ = serializer;
76 Err(serde::ser::Error::custom(
77 "decimal-scaled: human-readable serialisation requires the `alloc` feature",
78 ))
79 }
80 } else {
81 serializer.serialize_bytes(&self.0.as_i128().to_le_bytes())
83 }
84 }
85}
86
87impl<'de, const SCALE: u32> Deserialize<'de> for crate::D<crate::int::types::Int<2>, SCALE> {
90 #[inline]
103 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
104 let visitor = decimal_serde::DecimalVisitor::<SCALE>(PhantomData);
105 if deserializer.is_human_readable() {
106 deserializer.deserialize_any(visitor)
107 } else {
108 deserializer.deserialize_bytes(visitor)
109 }
110 }
111}
112
113pub mod decimal_serde {
135 use super::{Deserialize, Deserializer, PhantomData, Serialize, Serializer, Visitor};
136
137 #[inline]
146 pub fn serialize<const SCALE: u32, S: Serializer>(
147 v: &crate::D<crate::int::types::Int<2>, SCALE>,
148 s: S,
149 ) -> Result<S::Ok, S::Error> {
150 v.serialize(s)
151 }
152
153 #[inline]
162 pub fn deserialize<'de, const SCALE: u32, D: Deserializer<'de>>(
163 d: D,
164 ) -> Result<crate::D<crate::int::types::Int<2>, SCALE>, D::Error> {
165 crate::D::<crate::int::types::Int<2>, SCALE>::deserialize(d)
166 }
167
168 pub struct DecimalVisitor<const SCALE: u32>(pub PhantomData<()>);
182
183 impl<'de, const SCALE: u32> Visitor<'de> for DecimalVisitor<SCALE> {
184 type Value = crate::D<crate::int::types::Int<2>, SCALE>;
185
186 fn expecting(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
187 f.write_str(
188 "a base-10 i128 integer string, 16 little-endian bytes, \
189 or a native integer",
190 )
191 }
192
193 fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
196 let bytes = v.as_bytes();
206 if bytes.is_empty() {
207 return Err(serde::de::Error::custom(
208 "decimal-scaled: empty string is not a valid i128 wire",
209 ));
210 }
211 if bytes[0] == b'+' {
212 return Err(serde::de::Error::custom(
213 "decimal-scaled: leading `+` is not part of the canonical wire format",
214 ));
215 }
216 v.parse::<i128>().map(|n| crate::D::<crate::int::types::Int<2>, SCALE>::from_bits(crate::int::types::Int::<2>::from_i128(n))).map_err(|_| {
217 serde::de::Error::custom("decimal-scaled: expected a base-10 i128 integer string")
218 })
219 }
220
221 fn visit_borrowed_str<E: serde::de::Error>(self, v: &'de str) -> Result<Self::Value, E> {
222 self.visit_str(v)
223 }
224
225 #[cfg(feature = "alloc")]
226 fn visit_string<E: serde::de::Error>(
227 self,
228 v: alloc::string::String,
229 ) -> Result<Self::Value, E> {
230 self.visit_str(&v)
231 }
232
233 fn visit_bytes<E: serde::de::Error>(self, v: &[u8]) -> Result<Self::Value, E> {
236 let arr: [u8; 16] = v.try_into().map_err(|_| {
238 serde::de::Error::invalid_length(
239 v.len(),
240 &"exactly 16 little-endian bytes for an i128",
241 )
242 })?;
243 Ok(crate::D::<crate::int::types::Int<2>, SCALE>::from_bits(crate::int::types::Int::<2>::from_i128(i128::from_le_bytes(arr))))
244 }
245
246 fn visit_borrowed_bytes<E: serde::de::Error>(self, v: &'de [u8]) -> Result<Self::Value, E> {
247 self.visit_bytes(v)
248 }
249
250 #[cfg(feature = "alloc")]
251 fn visit_byte_buf<E: serde::de::Error>(
252 self,
253 v: alloc::vec::Vec<u8>,
254 ) -> Result<Self::Value, E> {
255 self.visit_bytes(&v)
256 }
257
258 fn visit_i8<E: serde::de::Error>(self, v: i8) -> Result<Self::Value, E> {
267 Ok(crate::D::<crate::int::types::Int<2>, SCALE>::from_bits(crate::int::types::Int::<2>::from_i128(i128::from(v))))
268 }
269
270 fn visit_i16<E: serde::de::Error>(self, v: i16) -> Result<Self::Value, E> {
271 Ok(crate::D::<crate::int::types::Int<2>, SCALE>::from_bits(crate::int::types::Int::<2>::from_i128(i128::from(v))))
272 }
273
274 fn visit_i32<E: serde::de::Error>(self, v: i32) -> Result<Self::Value, E> {
275 Ok(crate::D::<crate::int::types::Int<2>, SCALE>::from_bits(crate::int::types::Int::<2>::from_i128(i128::from(v))))
276 }
277
278 fn visit_i64<E: serde::de::Error>(self, v: i64) -> Result<Self::Value, E> {
279 Ok(crate::D::<crate::int::types::Int<2>, SCALE>::from_bits(crate::int::types::Int::<2>::from_i128(i128::from(v))))
280 }
281
282 fn visit_i128<E: serde::de::Error>(self, v: i128) -> Result<Self::Value, E> {
283 Ok(crate::D::<crate::int::types::Int<2>, SCALE>::from_bits(crate::int::types::Int::<2>::from_i128(v)))
284 }
285
286 fn visit_u8<E: serde::de::Error>(self, v: u8) -> Result<Self::Value, E> {
287 Ok(crate::D::<crate::int::types::Int<2>, SCALE>::from_bits(crate::int::types::Int::<2>::from_i128(i128::from(v))))
288 }
289
290 fn visit_u16<E: serde::de::Error>(self, v: u16) -> Result<Self::Value, E> {
291 Ok(crate::D::<crate::int::types::Int<2>, SCALE>::from_bits(crate::int::types::Int::<2>::from_i128(i128::from(v))))
292 }
293
294 fn visit_u32<E: serde::de::Error>(self, v: u32) -> Result<Self::Value, E> {
295 Ok(crate::D::<crate::int::types::Int<2>, SCALE>::from_bits(crate::int::types::Int::<2>::from_i128(i128::from(v))))
296 }
297
298 fn visit_u64<E: serde::de::Error>(self, v: u64) -> Result<Self::Value, E> {
299 Ok(crate::D::<crate::int::types::Int<2>, SCALE>::from_bits(crate::int::types::Int::<2>::from_i128(i128::from(v))))
300 }
301
302 fn visit_u128<E: serde::de::Error>(self, v: u128) -> Result<Self::Value, E> {
303 i128::try_from(v).map(|n| crate::D::<crate::int::types::Int<2>, SCALE>::from_bits(crate::int::types::Int::<2>::from_i128(n))).map_err(|_| {
306 serde::de::Error::custom("decimal-scaled: u128 value exceeds i128 storage range")
307 })
308 }
309
310 }
319}
320
321#[cfg(all(test, feature = "alloc", feature = "serde"))]
324mod tests {
325 use super::*;
326 use crate::types::widths::D38s12;
327 use alloc::format;
328 use serde::de::IntoDeserializer;
329 use serde::de::value::{Error as DeError, StrDeserializer};
330
331 #[test]
335 fn deserialize_canonical_zero_string() {
336 let de: StrDeserializer<DeError> = "0".into_deserializer();
337 let v: D38s12 = D38s12::deserialize(de).unwrap();
338 assert_eq!(v, D38s12::ZERO);
339 }
340
341 #[test]
344 fn visitor_accepts_scaled_one_str() {
345 let visitor = decimal_serde::DecimalVisitor::<12>(PhantomData);
346 let v: D38s12 = <_ as Visitor>::visit_str::<DeError>(visitor, "1000000000000").unwrap();
347 assert_eq!(v, D38s12::ONE);
348 }
349
350 #[test]
353 fn visitor_rejects_decimal_point_str() {
354 let visitor = decimal_serde::DecimalVisitor::<12>(PhantomData);
355 let res: Result<D38s12, _> = <_ as Visitor>::visit_str::<DeError>(visitor, "1.5");
356 assert!(res.is_err(), "expected reject; got Ok({:?})", res);
357 }
358
359 #[test]
364 fn visitor_accepts_i64_as_storage() {
365 let visitor = decimal_serde::DecimalVisitor::<12>(PhantomData);
366 let v: D38s12 = <_ as Visitor>::visit_i64::<DeError>(visitor, -5).unwrap();
367 assert_eq!(v.to_bits(), -5);
368 }
369
370 #[test]
372 fn visitor_accepts_u64_max() {
373 let visitor = decimal_serde::DecimalVisitor::<12>(PhantomData);
374 let v: D38s12 = <_ as Visitor>::visit_u64::<DeError>(visitor, u64::MAX).unwrap();
375 assert_eq!(v.to_bits(), u64::MAX as i128);
376 }
377
378 #[test]
381 fn visitor_rejects_u128_above_i128_max() {
382 let visitor = decimal_serde::DecimalVisitor::<12>(PhantomData);
383 let res: Result<D38s12, _> =
384 <_ as Visitor>::visit_u128::<DeError>(visitor, (i128::MAX as u128) + 1);
385 assert!(res.is_err(), "expected overflow reject; got Ok({:?})", res);
386 }
387
388 #[test]
394 fn json_one_serialises_as_scaled_integer_string() {
395 let json = serde_json::to_string(&D38s12::ONE).unwrap();
396 assert_eq!(json, "\"1000000000000\"");
397 }
398
399 #[test]
400 fn json_zero_serialises_as_zero_string() {
401 let json = serde_json::to_string(&D38s12::ZERO).unwrap();
402 assert_eq!(json, "\"0\"");
403 }
404
405 #[test]
406 fn json_one_round_trips() {
407 let json = serde_json::to_string(&D38s12::ONE).unwrap();
408 let back: D38s12 = serde_json::from_str(&json).unwrap();
409 assert_eq!(back, D38s12::ONE);
410 }
411
412 #[test]
413 fn json_zero_round_trips() {
414 let json = serde_json::to_string(&D38s12::ZERO).unwrap();
415 let back: D38s12 = serde_json::from_str(&json).unwrap();
416 assert_eq!(back, D38s12::ZERO);
417 }
418
419 #[test]
422 fn json_negative_round_trips() {
423 let v = D38s12::try_from(-5_i32).unwrap();
424 let json = serde_json::to_string(&v).unwrap();
425 assert_eq!(json, "\"-5000000000000\"");
426 let back: D38s12 = serde_json::from_str(&json).unwrap();
427 assert_eq!(back, v);
428 assert_eq!(back.to_bits(), -5_000_000_000_000_i128);
429 }
430
431 #[test]
434 fn json_max_round_trips() {
435 let json = serde_json::to_string(&D38s12::MAX).unwrap();
436 let back: D38s12 = serde_json::from_str(&json).unwrap();
437 assert_eq!(back, D38s12::MAX);
438 }
439
440 #[test]
441 fn json_min_round_trips() {
442 let json = serde_json::to_string(&D38s12::MIN).unwrap();
443 let back: D38s12 = serde_json::from_str(&json).unwrap();
444 assert_eq!(back, D38s12::MIN);
445 }
446
447 #[test]
451 fn json_string_matches_i128_to_string() {
452 let raw: i128 = -123_456_789_012_345_678_901_234_567_890_i128;
453 let v = D38s12::from_bits(crate::int::types::Int::<2>::from_i128(raw));
454 let json = serde_json::to_string(&v).unwrap();
455 assert_eq!(json, format!("\"{}\"", raw));
456 }
457
458 #[test]
461 fn json_rejects_decimal_point_string() {
462 let res: Result<D38s12, _> = serde_json::from_str("\"1.5\"");
463 assert!(res.is_err(), "expected reject; got Ok({:?})", res);
464 }
465
466 #[test]
467 fn json_rejects_scientific_notation_string() {
468 let res: Result<D38s12, _> = serde_json::from_str("\"1e6\"");
469 assert!(res.is_err(), "expected reject; got Ok({:?})", res);
470 }
471
472 #[test]
473 fn json_rejects_not_a_number_string() {
474 let res: Result<D38s12, _> = serde_json::from_str("\"not-a-number\"");
475 assert!(res.is_err(), "expected reject; got Ok({:?})", res);
476 }
477
478 #[test]
479 fn json_rejects_empty_string() {
480 let res: Result<D38s12, _> = serde_json::from_str("\"\"");
481 assert!(res.is_err(), "expected reject; got Ok({:?})", res);
482 }
483
484 #[test]
485 fn json_rejects_leading_whitespace_string() {
486 let res: Result<D38s12, _> = serde_json::from_str("\" 42\"");
489 assert!(res.is_err(), "expected reject; got Ok({:?})", res);
490 }
491
492 #[test]
493 fn json_rejects_plus_prefix() {
494 let res: Result<D38s12, _> = serde_json::from_str("\"+42\"");
495 assert!(res.is_err(), "expected reject; got Ok({:?})", res);
496 }
497
498 #[test]
501 fn json_accepts_bare_integer_number_as_storage() {
502 let back: D38s12 = serde_json::from_str("42").unwrap();
503 assert_eq!(back.to_bits(), 42_i128);
504 }
505
506 #[test]
509 fn postcard_one_round_trips() {
510 let bytes: alloc::vec::Vec<u8> = postcard::to_allocvec(&D38s12::ONE).unwrap();
511 let raw = D38s12::ONE.to_bits().as_i128().to_le_bytes();
514 assert!(bytes.windows(16).any(|w| w == raw));
515 let back: D38s12 = postcard::from_bytes(&bytes).unwrap();
516 assert_eq!(back, D38s12::ONE);
517 }
518
519 #[test]
520 fn postcard_zero_round_trips() {
521 let bytes: alloc::vec::Vec<u8> = postcard::to_allocvec(&D38s12::ZERO).unwrap();
522 let back: D38s12 = postcard::from_bytes(&bytes).unwrap();
523 assert_eq!(back, D38s12::ZERO);
524 }
525
526 #[test]
527 fn postcard_negative_round_trips() {
528 let v = D38s12::try_from(-5_i32).unwrap();
529 let bytes: alloc::vec::Vec<u8> = postcard::to_allocvec(&v).unwrap();
530 let back: D38s12 = postcard::from_bytes(&bytes).unwrap();
531 assert_eq!(back, v);
532 }
533
534 #[test]
535 fn postcard_max_round_trips() {
536 let bytes: alloc::vec::Vec<u8> = postcard::to_allocvec(&D38s12::MAX).unwrap();
537 let back: D38s12 = postcard::from_bytes(&bytes).unwrap();
538 assert_eq!(back, D38s12::MAX);
539 }
540
541 #[test]
542 fn postcard_min_round_trips() {
543 let bytes: alloc::vec::Vec<u8> = postcard::to_allocvec(&D38s12::MIN).unwrap();
544 let back: D38s12 = postcard::from_bytes(&bytes).unwrap();
545 assert_eq!(back, D38s12::MIN);
546 }
547
548 #[test]
552 fn postcard_byte_order_matches_le() {
553 let v = D38s12::from_bits(crate::int::types::Int::<2>::from_i128(0x0123_4567_89AB_CDEF_FEDC_BA98_7654_3210_i128));
554 let bytes: alloc::vec::Vec<u8> = postcard::to_allocvec(&v).unwrap();
555 let raw = v.to_bits().as_i128().to_le_bytes();
556 let found = bytes.windows(16).position(|w| w == raw);
557 assert!(
558 found.is_some(),
559 "expected raw LE bytes embedded; got {:?}",
560 bytes
561 );
562 assert_eq!(raw[0], 0x10); assert_eq!(raw[15], 0x01); }
565
566 #[test]
571 fn cross_format_json_string_matches_le_bytes() {
572 let v = D38s12::try_from(42_i32).unwrap();
573 let json = serde_json::to_string(&v).unwrap();
574 let inner = json.trim_matches('"');
575 let parsed: i128 = inner.parse().unwrap();
576 let json_bytes = parsed.to_le_bytes();
577 let direct_bytes = v.to_bits().as_i128().to_le_bytes();
578 assert_eq!(json_bytes, direct_bytes);
579 }
580
581 #[test]
585 fn cross_scale_wire_is_storage_only() {
586 let raw: i128 = 1_500_000_000_000;
587 let v12 = crate::D::<crate::int::types::Int<2>, 12>::from_bits(crate::int::types::Int::<2>::from_i128(raw));
588 let v6 = crate::D::<crate::int::types::Int<2>, 6>::from_bits(crate::int::types::Int::<2>::from_i128(raw));
589 assert_eq!(serde_json::to_string(&v12).unwrap(), "\"1500000000000\"");
590 assert_eq!(serde_json::to_string(&v6).unwrap(), "\"1500000000000\"");
591 }
592
593 #[test]
598 fn decimal_serde_helper_round_trips() {
599 #[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq)]
600 struct Holder {
601 #[serde(with = "crate::serde_helpers::decimal_serde")]
602 length: crate::D<crate::int::types::Int<2>, 12>,
603 }
604
605 let h = Holder {
606 length: D38s12::try_from(7_i32).unwrap(),
607 };
608 let json = serde_json::to_string(&h).unwrap();
609 assert_eq!(json, r#"{"length":"7000000000000"}"#);
610 let back: Holder = serde_json::from_str(&json).unwrap();
611 assert_eq!(back, h);
612 }
613}
614
615#[cfg(feature = "_wide-support")]
628macro_rules! decl_wide_serde {
629 ($Type:ident, $Storage:ty, $bytes_len:literal) => {
630 impl<const SCALE: u32> Serialize for $crate::types::widths::$Type<SCALE> {
631 #[inline]
635 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
636 if s.is_human_readable() {
637 #[cfg(feature = "alloc")]
638 {
639 s.serialize_str(&self.0.to_string())
640 }
641 #[cfg(not(feature = "alloc"))]
642 {
643 let _ = s;
644 Err(serde::ser::Error::custom(
645 "decimal-scaled: human-readable serialisation requires `alloc`",
646 ))
647 }
648 } else {
649 let mut bytes = [0u8; $bytes_len];
650 let limbs = self.0.limbs_le();
651 for (i, limb) in limbs.iter().enumerate() {
656 bytes[i * 8..(i + 1) * 8].copy_from_slice(&limb.to_le_bytes());
657 }
658 s.serialize_bytes(&bytes)
659 }
660 }
661 }
662
663 impl<'de, const SCALE: u32> Deserialize<'de> for $crate::types::widths::$Type<SCALE> {
664 #[inline]
665 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
666 struct V<const S: u32>;
667 impl<'de, const S: u32> Visitor<'de> for V<S> {
668 type Value = $crate::types::widths::$Type<S>;
669 fn expecting(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
670 f.write_str(concat!(
671 "a base-10 integer string or ",
672 stringify!($bytes_len),
673 " little-endian bytes for ",
674 stringify!($Type),
675 ))
676 }
677 fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
678 let parsed = <$Storage>::from_str_radix(v, 10).map_err(|_| {
679 serde::de::Error::custom(concat!(
680 stringify!($Type),
681 ": invalid base-10 integer string",
682 ))
683 })?;
684 Ok(<$crate::types::widths::$Type<S>>::from_bits(parsed))
685 }
686 fn visit_borrowed_str<E: serde::de::Error>(
687 self,
688 v: &'de str,
689 ) -> Result<Self::Value, E> {
690 self.visit_str(v)
691 }
692 #[cfg(feature = "alloc")]
693 fn visit_string<E: serde::de::Error>(
694 self,
695 v: alloc::string::String,
696 ) -> Result<Self::Value, E> {
697 self.visit_str(&v)
698 }
699 fn visit_bytes<E: serde::de::Error>(self, v: &[u8]) -> Result<Self::Value, E> {
700 if v.len() != $bytes_len {
701 return Err(serde::de::Error::invalid_length($bytes_len, &self));
702 }
703 let mut limbs = [0u64; $bytes_len / 8];
705 for (i, limb) in limbs.iter_mut().enumerate() {
706 let mut buf = [0u8; 8];
707 buf.copy_from_slice(&v[i * 8..(i + 1) * 8]);
708 *limb = u64::from_le_bytes(buf);
709 }
710 Ok(<$crate::types::widths::$Type<S>>::from_bits(
711 <$Storage>::from_limbs_le(limbs),
712 ))
713 }
714 fn visit_borrowed_bytes<E: serde::de::Error>(
715 self,
716 v: &'de [u8],
717 ) -> Result<Self::Value, E> {
718 self.visit_bytes(v)
719 }
720 }
721 if d.is_human_readable() {
722 d.deserialize_str(V::<SCALE>)
723 } else {
724 d.deserialize_bytes(V::<SCALE>)
725 }
726 }
727 }
728 };
729}
730
731#[cfg(any(feature = "d57", feature = "wide"))]
732decl_wide_serde!(D57, crate::int::types::Int<3>, 24);
733#[cfg(any(feature = "d76", feature = "wide"))]
734decl_wide_serde!(D76, crate::int::types::Int<4>, 32);
735#[cfg(any(feature = "d115", feature = "wide"))]
736decl_wide_serde!(D115, crate::int::types::Int<6>, 48);
737#[cfg(any(feature = "d153", feature = "wide"))]
738decl_wide_serde!(D153, crate::int::types::Int<8>, 64);
739#[cfg(any(feature = "d230", feature = "wide"))]
740decl_wide_serde!(D230, crate::int::types::Int<12>, 96);
741#[cfg(any(feature = "d307", feature = "wide", feature = "x-wide"))]
742decl_wide_serde!(D307, crate::int::types::Int<16>, 128);
743#[cfg(any(feature = "d462", feature = "x-wide"))]
744decl_wide_serde!(D462, crate::int::types::Int<24>, 192);
745#[cfg(any(feature = "d616", feature = "x-wide"))]
746decl_wide_serde!(D616, crate::int::types::Int<32>, 256);
747#[cfg(any(feature = "d924", feature = "xx-wide"))]
748decl_wide_serde!(D924, crate::int::types::Int<48>, 384);
749#[cfg(any(feature = "d1232", feature = "xx-wide"))]
750decl_wide_serde!(D1232, crate::int::types::Int<64>, 512);
751
752#[cfg(all(test, feature = "wide"))]
753mod wide_serde_tests {
754
755 #[test]
756 fn d76_human_readable_round_trip() {
757 let v = crate::D::<crate::int::types::Int<4>, 12>::try_from(1_234_567_i128).unwrap();
758 let json = serde_json::to_string(&v).unwrap();
759 let back: crate::D<crate::int::types::Int<4>, 12> = serde_json::from_str(&json).unwrap();
760 assert_eq!(back, v);
761 }
762
763 #[test]
764 fn d76_negative_human_readable_round_trip() {
765 let v = -crate::D::<crate::int::types::Int<4>, 12>::try_from(987_654_321_i128).unwrap();
766 let json = serde_json::to_string(&v).unwrap();
767 let back: crate::D<crate::int::types::Int<4>, 12> = serde_json::from_str(&json).unwrap();
768 assert_eq!(back, v);
769 }
770
771 #[test]
772 fn d76_binary_round_trip() {
773 let v = crate::D::<crate::int::types::Int<4>, 12>::try_from(42_i128).unwrap();
775 let bytes = postcard::to_allocvec(&v).unwrap();
776 let back: crate::D<crate::int::types::Int<4>, 12> = postcard::from_bytes(&bytes).unwrap();
777 assert_eq!(back, v);
778 }
779}