1use core::marker::PhantomData;
41
42use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
43
44#[cfg(feature = "alloc")]
45use alloc::string::ToString;
46
47use crate::core_type::D38;
48
49impl<const SCALE: u32> Serialize for D38<SCALE> {
52 #[inline]
59 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
60 if serializer.is_human_readable() {
61 #[cfg(feature = "alloc")]
65 {
66 serializer.serialize_str(&self.0.to_string())
67 }
68 #[cfg(not(feature = "alloc"))]
73 {
74 let _ = serializer;
75 Err(serde::ser::Error::custom(
76 "decimal-scaled: human-readable serialisation requires the `alloc` feature",
77 ))
78 }
79 } else {
80 serializer.serialize_bytes(&self.0.to_le_bytes())
82 }
83 }
84}
85
86impl<'de, const SCALE: u32> Deserialize<'de> for D38<SCALE> {
89 #[inline]
102 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
103 let visitor = decimal_serde::DecimalVisitor::<SCALE>(PhantomData);
104 if deserializer.is_human_readable() {
105 deserializer.deserialize_any(visitor)
106 } else {
107 deserializer.deserialize_bytes(visitor)
108 }
109 }
110}
111
112pub mod decimal_serde {
134 use super::{Serializer, D38, Serialize, Deserializer, Deserialize, PhantomData, Visitor};
135
136 #[inline]
145 pub fn serialize<const SCALE: u32, S: Serializer>(
146 v: &D38<SCALE>,
147 s: S,
148 ) -> Result<S::Ok, S::Error> {
149 v.serialize(s)
150 }
151
152 #[inline]
161 pub fn deserialize<'de, const SCALE: u32, D: Deserializer<'de>>(
162 d: D,
163 ) -> Result<D38<SCALE>, D::Error> {
164 D38::<SCALE>::deserialize(d)
165 }
166
167 pub struct DecimalVisitor<const SCALE: u32>(pub PhantomData<()>);
181
182 impl<'de, const SCALE: u32> Visitor<'de> for DecimalVisitor<SCALE> {
183 type Value = D38<SCALE>;
184
185 fn expecting(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
186 f.write_str(
187 "a base-10 i128 integer string, 16 little-endian bytes, \
188 or a native integer",
189 )
190 }
191
192 fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
195 let bytes = v.as_bytes();
205 if bytes.is_empty() {
206 return Err(serde::de::Error::custom(
207 "decimal-scaled: empty string is not a valid i128 wire",
208 ));
209 }
210 if bytes[0] == b'+' {
211 return Err(serde::de::Error::custom(
212 "decimal-scaled: leading `+` is not part of the canonical wire format",
213 ));
214 }
215 v.parse::<i128>()
216 .map(D38::<SCALE>::from_bits)
217 .map_err(|_| {
218 serde::de::Error::custom(
219 "decimal-scaled: expected a base-10 i128 integer string",
220 )
221 })
222 }
223
224 fn visit_borrowed_str<E: serde::de::Error>(self, v: &'de str) -> Result<Self::Value, E> {
225 self.visit_str(v)
226 }
227
228 #[cfg(feature = "alloc")]
229 fn visit_string<E: serde::de::Error>(
230 self,
231 v: alloc::string::String,
232 ) -> Result<Self::Value, E> {
233 self.visit_str(&v)
234 }
235
236 fn visit_bytes<E: serde::de::Error>(self, v: &[u8]) -> Result<Self::Value, E> {
239 let arr: [u8; 16] = v.try_into().map_err(|_| {
241 serde::de::Error::invalid_length(
242 v.len(),
243 &"exactly 16 little-endian bytes for an i128",
244 )
245 })?;
246 Ok(D38::<SCALE>::from_bits(i128::from_le_bytes(arr)))
247 }
248
249 fn visit_borrowed_bytes<E: serde::de::Error>(
250 self,
251 v: &'de [u8],
252 ) -> Result<Self::Value, E> {
253 self.visit_bytes(v)
254 }
255
256 #[cfg(feature = "alloc")]
257 fn visit_byte_buf<E: serde::de::Error>(
258 self,
259 v: alloc::vec::Vec<u8>,
260 ) -> Result<Self::Value, E> {
261 self.visit_bytes(&v)
262 }
263
264 fn visit_i8<E: serde::de::Error>(self, v: i8) -> Result<Self::Value, E> {
273 Ok(D38::<SCALE>::from_bits(i128::from(v)))
274 }
275
276 fn visit_i16<E: serde::de::Error>(self, v: i16) -> Result<Self::Value, E> {
277 Ok(D38::<SCALE>::from_bits(i128::from(v)))
278 }
279
280 fn visit_i32<E: serde::de::Error>(self, v: i32) -> Result<Self::Value, E> {
281 Ok(D38::<SCALE>::from_bits(i128::from(v)))
282 }
283
284 fn visit_i64<E: serde::de::Error>(self, v: i64) -> Result<Self::Value, E> {
285 Ok(D38::<SCALE>::from_bits(i128::from(v)))
286 }
287
288 fn visit_i128<E: serde::de::Error>(self, v: i128) -> Result<Self::Value, E> {
289 Ok(D38::<SCALE>::from_bits(v))
290 }
291
292 fn visit_u8<E: serde::de::Error>(self, v: u8) -> Result<Self::Value, E> {
293 Ok(D38::<SCALE>::from_bits(i128::from(v)))
294 }
295
296 fn visit_u16<E: serde::de::Error>(self, v: u16) -> Result<Self::Value, E> {
297 Ok(D38::<SCALE>::from_bits(i128::from(v)))
298 }
299
300 fn visit_u32<E: serde::de::Error>(self, v: u32) -> Result<Self::Value, E> {
301 Ok(D38::<SCALE>::from_bits(i128::from(v)))
302 }
303
304 fn visit_u64<E: serde::de::Error>(self, v: u64) -> Result<Self::Value, E> {
305 Ok(D38::<SCALE>::from_bits(i128::from(v)))
306 }
307
308 fn visit_u128<E: serde::de::Error>(self, v: u128) -> Result<Self::Value, E> {
309 i128::try_from(v).map(D38::<SCALE>::from_bits).map_err(|_| {
312 serde::de::Error::custom(
313 "decimal-scaled: u128 value exceeds i128 storage range",
314 )
315 })
316 }
317
318 }
327}
328
329#[cfg(all(test, feature = "alloc", feature = "serde"))]
332mod tests {
333 use super::*;
334 use crate::core_type::{D38, D38s12};
335 use serde::de::value::{Error as DeError, StrDeserializer};
336 use serde::de::IntoDeserializer;
337 use alloc::format;
338
339 #[test]
343 fn deserialize_canonical_zero_string() {
344 let de: StrDeserializer<DeError> = "0".into_deserializer();
345 let v: D38s12 = D38s12::deserialize(de).unwrap();
346 assert_eq!(v, D38s12::ZERO);
347 }
348
349 #[test]
352 fn visitor_accepts_scaled_one_str() {
353 let visitor = decimal_serde::DecimalVisitor::<12>(PhantomData);
354 let v: D38s12 =
355 <_ as Visitor>::visit_str::<DeError>(visitor, "1000000000000").unwrap();
356 assert_eq!(v, D38s12::ONE);
357 }
358
359 #[test]
362 fn visitor_rejects_decimal_point_str() {
363 let visitor = decimal_serde::DecimalVisitor::<12>(PhantomData);
364 let res: Result<D38s12, _> =
365 <_ as Visitor>::visit_str::<DeError>(visitor, "1.5");
366 assert!(res.is_err(), "expected reject; got Ok({:?})", res);
367 }
368
369 #[test]
374 fn visitor_accepts_i64_as_storage() {
375 let visitor = decimal_serde::DecimalVisitor::<12>(PhantomData);
376 let v: D38s12 = <_ as Visitor>::visit_i64::<DeError>(visitor, -5).unwrap();
377 assert_eq!(v.to_bits(), -5);
378 }
379
380 #[test]
382 fn visitor_accepts_u64_max() {
383 let visitor = decimal_serde::DecimalVisitor::<12>(PhantomData);
384 let v: D38s12 =
385 <_ as Visitor>::visit_u64::<DeError>(visitor, u64::MAX).unwrap();
386 assert_eq!(v.to_bits(), u64::MAX as i128);
387 }
388
389 #[test]
392 fn visitor_rejects_u128_above_i128_max() {
393 let visitor = decimal_serde::DecimalVisitor::<12>(PhantomData);
394 let res: Result<D38s12, _> = <_ as Visitor>::visit_u128::<DeError>(
395 visitor,
396 (i128::MAX as u128) + 1,
397 );
398 assert!(res.is_err(), "expected overflow reject; got Ok({:?})", res);
399 }
400
401 #[test]
407 fn json_one_serialises_as_scaled_integer_string() {
408 let json = serde_json::to_string(&D38s12::ONE).unwrap();
409 assert_eq!(json, "\"1000000000000\"");
410 }
411
412 #[test]
413 fn json_zero_serialises_as_zero_string() {
414 let json = serde_json::to_string(&D38s12::ZERO).unwrap();
415 assert_eq!(json, "\"0\"");
416 }
417
418 #[test]
419 fn json_one_round_trips() {
420 let json = serde_json::to_string(&D38s12::ONE).unwrap();
421 let back: D38s12 = serde_json::from_str(&json).unwrap();
422 assert_eq!(back, D38s12::ONE);
423 }
424
425 #[test]
426 fn json_zero_round_trips() {
427 let json = serde_json::to_string(&D38s12::ZERO).unwrap();
428 let back: D38s12 = serde_json::from_str(&json).unwrap();
429 assert_eq!(back, D38s12::ZERO);
430 }
431
432 #[test]
435 fn json_negative_round_trips() {
436 let v = D38s12::from(-5_i32);
437 let json = serde_json::to_string(&v).unwrap();
438 assert_eq!(json, "\"-5000000000000\"");
439 let back: D38s12 = serde_json::from_str(&json).unwrap();
440 assert_eq!(back, v);
441 assert_eq!(back.to_bits(), -5_000_000_000_000_i128);
442 }
443
444 #[test]
447 fn json_max_round_trips() {
448 let json = serde_json::to_string(&D38s12::MAX).unwrap();
449 let back: D38s12 = serde_json::from_str(&json).unwrap();
450 assert_eq!(back, D38s12::MAX);
451 }
452
453 #[test]
454 fn json_min_round_trips() {
455 let json = serde_json::to_string(&D38s12::MIN).unwrap();
456 let back: D38s12 = serde_json::from_str(&json).unwrap();
457 assert_eq!(back, D38s12::MIN);
458 }
459
460 #[test]
464 fn json_string_matches_i128_to_string() {
465 let raw: i128 = -123_456_789_012_345_678_901_234_567_890_i128;
466 let v = D38s12::from_bits(raw);
467 let json = serde_json::to_string(&v).unwrap();
468 assert_eq!(json, format!("\"{}\"", raw));
469 }
470
471 #[test]
474 fn json_rejects_decimal_point_string() {
475 let res: Result<D38s12, _> = serde_json::from_str("\"1.5\"");
476 assert!(res.is_err(), "expected reject; got Ok({:?})", res);
477 }
478
479 #[test]
480 fn json_rejects_scientific_notation_string() {
481 let res: Result<D38s12, _> = serde_json::from_str("\"1e6\"");
482 assert!(res.is_err(), "expected reject; got Ok({:?})", res);
483 }
484
485 #[test]
486 fn json_rejects_not_a_number_string() {
487 let res: Result<D38s12, _> = serde_json::from_str("\"not-a-number\"");
488 assert!(res.is_err(), "expected reject; got Ok({:?})", res);
489 }
490
491 #[test]
492 fn json_rejects_empty_string() {
493 let res: Result<D38s12, _> = serde_json::from_str("\"\"");
494 assert!(res.is_err(), "expected reject; got Ok({:?})", res);
495 }
496
497 #[test]
498 fn json_rejects_leading_whitespace_string() {
499 let res: Result<D38s12, _> = serde_json::from_str("\" 42\"");
502 assert!(res.is_err(), "expected reject; got Ok({:?})", res);
503 }
504
505 #[test]
506 fn json_rejects_plus_prefix() {
507 let res: Result<D38s12, _> = serde_json::from_str("\"+42\"");
508 assert!(res.is_err(), "expected reject; got Ok({:?})", res);
509 }
510
511 #[test]
514 fn json_accepts_bare_integer_number_as_storage() {
515 let back: D38s12 = serde_json::from_str("42").unwrap();
516 assert_eq!(back.to_bits(), 42_i128);
517 }
518
519 #[test]
522 fn postcard_one_round_trips() {
523 let bytes: alloc::vec::Vec<u8> = postcard::to_allocvec(&D38s12::ONE).unwrap();
524 let raw = D38s12::ONE.to_bits().to_le_bytes();
527 assert!(bytes.windows(16).any(|w| w == raw));
528 let back: D38s12 = postcard::from_bytes(&bytes).unwrap();
529 assert_eq!(back, D38s12::ONE);
530 }
531
532 #[test]
533 fn postcard_zero_round_trips() {
534 let bytes: alloc::vec::Vec<u8> = postcard::to_allocvec(&D38s12::ZERO).unwrap();
535 let back: D38s12 = postcard::from_bytes(&bytes).unwrap();
536 assert_eq!(back, D38s12::ZERO);
537 }
538
539 #[test]
540 fn postcard_negative_round_trips() {
541 let v = D38s12::from(-5_i32);
542 let bytes: alloc::vec::Vec<u8> = postcard::to_allocvec(&v).unwrap();
543 let back: D38s12 = postcard::from_bytes(&bytes).unwrap();
544 assert_eq!(back, v);
545 }
546
547 #[test]
548 fn postcard_max_round_trips() {
549 let bytes: alloc::vec::Vec<u8> = postcard::to_allocvec(&D38s12::MAX).unwrap();
550 let back: D38s12 = postcard::from_bytes(&bytes).unwrap();
551 assert_eq!(back, D38s12::MAX);
552 }
553
554 #[test]
555 fn postcard_min_round_trips() {
556 let bytes: alloc::vec::Vec<u8> = postcard::to_allocvec(&D38s12::MIN).unwrap();
557 let back: D38s12 = postcard::from_bytes(&bytes).unwrap();
558 assert_eq!(back, D38s12::MIN);
559 }
560
561 #[test]
565 fn postcard_byte_order_matches_le() {
566 let v = D38s12::from_bits(0x0123_4567_89AB_CDEF_FEDC_BA98_7654_3210_i128);
567 let bytes: alloc::vec::Vec<u8> = postcard::to_allocvec(&v).unwrap();
568 let raw = v.to_bits().to_le_bytes();
569 let found = bytes.windows(16).position(|w| w == raw);
570 assert!(found.is_some(), "expected raw LE bytes embedded; got {:?}", bytes);
571 assert_eq!(raw[0], 0x10); assert_eq!(raw[15], 0x01); }
574
575 #[test]
580 fn cross_format_json_string_matches_le_bytes() {
581 let v = D38s12::from(42_i32);
582 let json = serde_json::to_string(&v).unwrap();
583 let inner = json.trim_matches('"');
584 let parsed: i128 = inner.parse().unwrap();
585 let json_bytes = parsed.to_le_bytes();
586 let direct_bytes = v.to_bits().to_le_bytes();
587 assert_eq!(json_bytes, direct_bytes);
588 }
589
590 #[test]
594 fn cross_scale_wire_is_storage_only() {
595 let raw: i128 = 1_500_000_000_000;
596 let v12 = D38::<12>::from_bits(raw);
597 let v6 = D38::<6>::from_bits(raw);
598 assert_eq!(serde_json::to_string(&v12).unwrap(), "\"1500000000000\"");
599 assert_eq!(serde_json::to_string(&v6).unwrap(), "\"1500000000000\"");
600 }
601
602 #[test]
607 fn decimal_serde_helper_round_trips() {
608 #[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq)]
609 struct Holder {
610 #[serde(with = "crate::serde_helpers::decimal_serde")]
611 length: D38<12>,
612 }
613
614 let h = Holder {
615 length: D38s12::from(7_i32),
616 };
617 let json = serde_json::to_string(&h).unwrap();
618 assert_eq!(json, r#"{"length":"7000000000000"}"#);
619 let back: Holder = serde_json::from_str(&json).unwrap();
620 assert_eq!(back, h);
621 }
622}
623
624#[cfg(any(feature = "d76", feature = "d153", feature = "d307", feature = "wide", feature = "x-wide"))]
637macro_rules! decl_wide_serde {
638 ($Type:ident, $Storage:ty, $bytes_len:literal) => {
639 impl<const SCALE: u32> Serialize for $crate::core_type::$Type<SCALE> {
640 #[inline]
644 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
645 if s.is_human_readable() {
646 #[cfg(feature = "alloc")]
647 {
648 s.serialize_str(&self.0.to_string())
649 }
650 #[cfg(not(feature = "alloc"))]
651 {
652 let _ = s;
653 Err(serde::ser::Error::custom(
654 "decimal-scaled: human-readable serialisation requires `alloc`",
655 ))
656 }
657 } else {
658 let mut bytes = [0u8; $bytes_len];
659 let limbs = self.0.limbs_le();
660 for (i, limb) in limbs.iter().enumerate() {
661 bytes[i * 16..(i + 1) * 16].copy_from_slice(&limb.to_le_bytes());
662 }
663 s.serialize_bytes(&bytes)
664 }
665 }
666 }
667
668 impl<'de, const SCALE: u32> Deserialize<'de> for $crate::core_type::$Type<SCALE> {
669 #[inline]
670 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
671 struct V<const S: u32>;
672 impl<'de, const S: u32> Visitor<'de> for V<S> {
673 type Value = $crate::core_type::$Type<S>;
674 fn expecting(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
675 f.write_str(concat!(
676 "a base-10 integer string or ",
677 stringify!($bytes_len),
678 " little-endian bytes for ",
679 stringify!($Type),
680 ))
681 }
682 fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
683 let parsed = <$Storage>::from_str_radix(v, 10).map_err(|_| {
684 serde::de::Error::custom(concat!(
685 stringify!($Type),
686 ": invalid base-10 integer string",
687 ))
688 })?;
689 Ok(<$crate::core_type::$Type<S>>::from_bits(parsed))
690 }
691 fn visit_borrowed_str<E: serde::de::Error>(self, v: &'de str) -> Result<Self::Value, E> {
692 self.visit_str(v)
693 }
694 #[cfg(feature = "alloc")]
695 fn visit_string<E: serde::de::Error>(self, v: alloc::string::String) -> Result<Self::Value, E> {
696 self.visit_str(&v)
697 }
698 fn visit_bytes<E: serde::de::Error>(self, v: &[u8]) -> Result<Self::Value, E> {
699 if v.len() != $bytes_len {
700 return Err(serde::de::Error::invalid_length($bytes_len, &self));
701 }
702 let mut limbs = [0u128; $bytes_len / 16];
703 for (i, limb) in limbs.iter_mut().enumerate() {
704 let mut buf = [0u8; 16];
705 buf.copy_from_slice(&v[i * 16..(i + 1) * 16]);
706 *limb = u128::from_le_bytes(buf);
707 }
708 Ok(<$crate::core_type::$Type<S>>::from_bits(<$Storage>::from_limbs_le(limbs)))
709 }
710 fn visit_borrowed_bytes<E: serde::de::Error>(self, v: &'de [u8]) -> Result<Self::Value, E> {
711 self.visit_bytes(v)
712 }
713 }
714 if d.is_human_readable() {
715 d.deserialize_str(V::<SCALE>)
716 } else {
717 d.deserialize_bytes(V::<SCALE>)
718 }
719 }
720 }
721 };
722}
723
724#[cfg(any(feature = "d76", feature = "wide"))]
725decl_wide_serde!(D76, crate::wide_int::Int256, 32);
726#[cfg(any(feature = "d153", feature = "wide"))]
727decl_wide_serde!(D153, crate::wide_int::Int512, 64);
728#[cfg(any(feature = "d307", feature = "wide"))]
729decl_wide_serde!(D307, crate::wide_int::Int1024, 128);
730
731#[cfg(all(test, feature = "wide"))]
732mod wide_serde_tests {
733 use crate::D76;
734
735 #[test]
736 fn d76_human_readable_round_trip() {
737 let v = D76::<12>::from_int(1_234_567_i128);
738 let json = serde_json::to_string(&v).unwrap();
739 let back: D76<12> = serde_json::from_str(&json).unwrap();
740 assert_eq!(back, v);
741 }
742
743 #[test]
744 fn d76_negative_human_readable_round_trip() {
745 let v = -D76::<12>::from_int(987_654_321_i128);
746 let json = serde_json::to_string(&v).unwrap();
747 let back: D76<12> = serde_json::from_str(&json).unwrap();
748 assert_eq!(back, v);
749 }
750
751 #[test]
752 fn d76_binary_round_trip() {
753 let v = D76::<12>::from_int(42_i128);
755 let bytes = postcard::to_allocvec(&v).unwrap();
756 let back: D76<12> = postcard::from_bytes(&bytes).unwrap();
757 assert_eq!(back, v);
758 }
759}