1use std::{fmt, marker::PhantomData, str::FromStr, time::Duration};
4
5use serde::{
6 Deserialize, Deserializer,
7 de::{self, EnumAccess, Error as DeError, VariantAccess},
8};
9
10use crate::{
11 ByteSize, EtherAmount,
12 de::{CustomKnownOption, DeserializeContext, DeserializeParam, Optional, WellKnown},
13 error::ErrorWithOrigin,
14 metadata::{BasicTypes, ParamMetadata, SizeUnit, TimeUnit, TypeDescription, TypeSuffixes},
15 utils::{Decimal, FromStrStart},
16 value::Value,
17};
18
19impl TimeUnit {
20 fn overflow_err(self, raw_val: Decimal) -> serde_json::Error {
21 let plural = self.plural();
22 DeError::custom(format!(
23 "{raw_val} {plural} does not fit into `u64` when converted to milliseconds"
24 ))
25 }
26
27 fn into_duration(self, raw_value: Decimal) -> Result<Duration, serde_json::Error> {
28 let millis_in_unit = match self {
29 Self::Millis => Decimal::from(1),
30 Self::Seconds => Decimal::new(1, 3),
31 Self::Minutes => Decimal::new(60, 3),
32 Self::Hours => Decimal::new(3_600, 3),
33 Self::Days => Decimal::new(86_400, 3),
34 Self::Weeks => Decimal::new(7 * 86_400, 3),
35 };
36 let millis = raw_value
37 .checked_mul(millis_in_unit)
38 .ok_or_else(|| self.overflow_err(raw_value))?;
39 let millis = millis
40 .to_int()
41 .ok_or_else(|| self.overflow_err(raw_value))?;
42
43 u64::try_from(millis)
44 .map(Duration::from_millis)
45 .or_else(|_| {
46 let secs =
49 u64::try_from(millis / 1_000).map_err(|_| self.overflow_err(raw_value))?;
50 Ok(Duration::from_secs(secs))
51 })
52 }
53}
54
55impl DeserializeParam<Duration> for TimeUnit {
76 const EXPECTING: BasicTypes = BasicTypes::INTEGER;
77
78 fn describe(&self, description: &mut TypeDescription) {
79 description
80 .set_details("time duration")
81 .set_unit((*self).into());
82 }
83
84 fn deserialize_param(
85 &self,
86 ctx: DeserializeContext<'_>,
87 param: &'static ParamMetadata,
88 ) -> Result<Duration, ErrorWithOrigin> {
89 let deserializer = ctx.current_value_deserializer(param.name)?;
90 let raw_value = Decimal::deserialize(deserializer)?;
91 self.into_duration(raw_value)
92 .map_err(|err| deserializer.enrich_err(err))
93 }
94
95 fn serialize_param(&self, param: &Duration) -> serde_json::Value {
96 match self {
97 Self::Millis => serde_json::to_value(param.as_millis()).unwrap(),
98 Self::Seconds => param.as_secs().into(),
99 Self::Minutes => (param.as_secs() / 60).into(),
100 Self::Hours => (param.as_secs() / 3_600).into(),
101 Self::Days => (param.as_secs() / 86_400).into(),
102 Self::Weeks => (param.as_secs() / 86_400 / 7).into(),
103 }
104 }
105}
106
107impl DeserializeParam<ByteSize> for SizeUnit {
128 const EXPECTING: BasicTypes = BasicTypes::INTEGER;
129
130 fn describe(&self, description: &mut TypeDescription) {
131 description
132 .set_details("byte size")
133 .set_unit((*self).into());
134 }
135
136 fn deserialize_param(
137 &self,
138 ctx: DeserializeContext<'_>,
139 param: &'static ParamMetadata,
140 ) -> Result<ByteSize, ErrorWithOrigin> {
141 let deserializer = ctx.current_value_deserializer(param.name)?;
142 let raw_value = u64::deserialize(deserializer)?;
143 ByteSize::checked(raw_value, *self).ok_or_else(|| {
144 let err = DeError::custom(format!(
145 "{raw_value} {unit} does not fit into `u64`",
146 unit = self.as_str()
147 ));
148 deserializer.enrich_err(err)
149 })
150 }
151
152 fn serialize_param(&self, param: &ByteSize) -> serde_json::Value {
153 match self {
154 Self::Bytes => param.0.into(),
155 Self::KiB => (param.0 >> 10).into(),
156 Self::MiB => (param.0 >> 20).into(),
157 Self::GiB => (param.0 >> 30).into(),
158 }
159 }
160}
161
162#[derive(Debug, Clone, Copy)]
224pub struct WithUnit;
225
226impl WithUnit {
227 const EXPECTED_TYPES: BasicTypes = BasicTypes::STRING.or(BasicTypes::OBJECT);
228
229 fn deserialize<Raw, T>(
230 ctx: &DeserializeContext<'_>,
231 param: &'static ParamMetadata,
232 ) -> Result<T, ErrorWithOrigin>
233 where
234 Raw: EnumWithUnit + TryInto<T, Error = serde_json::Error>,
235 {
236 let deserializer = ctx.current_value_deserializer(param.name)?;
237 let raw = if let Value::String(s) = deserializer.value() {
238 s.expose()
239 .parse::<Raw>()
240 .map_err(|err| deserializer.enrich_err(err))?
241 } else {
242 deserializer.deserialize_enum("Raw", Raw::VARIANTS, EnumVisitor(PhantomData::<Raw>))?
243 };
244 raw.try_into().map_err(|err| deserializer.enrich_err(err))
245 }
246
247 fn deserialize_opt<Raw, T>(
250 ctx: &DeserializeContext<'_>,
251 param: &'static ParamMetadata,
252 ) -> Result<Option<T>, ErrorWithOrigin>
253 where
254 Raw: EnumWithUnit + TryInto<T, Error = serde_json::Error>,
255 {
256 let deserializer = ctx.current_value_deserializer(param.name)?;
257 let raw = if let Value::String(s) = deserializer.value() {
258 Some(
259 s.expose()
260 .parse::<Raw>()
261 .map_err(|err| deserializer.enrich_err(err))?,
262 )
263 } else {
264 deserializer.deserialize_enum(
265 "Raw",
266 Raw::VARIANTS,
267 EnumVisitor(PhantomData::<Option<Raw>>),
268 )?
269 };
270 let Some(raw) = raw else {
271 return Ok(None);
272 };
273 raw.try_into()
274 .map(Some)
275 .map_err(|err| deserializer.enrich_err(err))
276 }
277}
278
279trait EnumWithUnit: FromStr<Err = serde_json::Error> {
281 type Value: FromStrStart + de::DeserializeOwned;
282
283 const EXPECTING: &'static str;
284 const VARIANTS: &'static [&'static str];
285
286 fn extract_variant(unit: &str) -> Option<fn(Self::Value) -> Self>;
287
288 fn parse<E: de::Error>(unit: &str, value: Self::Value) -> Result<Self, E> {
289 let variant_mapper = Self::extract_variant(unit)
290 .ok_or_else(|| DeError::unknown_variant(unit, Self::VARIANTS))?;
291 Ok(variant_mapper(value))
292 }
293
294 fn parse_opt<E: de::Error>(unit: &str, value: Option<Self::Value>) -> Result<Option<Self>, E> {
295 let variant_mapper = Self::extract_variant(unit)
296 .ok_or_else(|| DeError::unknown_variant(unit, Self::VARIANTS))?;
297 Ok(value.map(variant_mapper))
299 }
300
301 fn from_unit_str(s: &str, lowercase_unit: bool) -> Result<Self, serde_json::Error> {
302 let (value, rem) = <Self::Value as FromStrStart>::from_str_start(s)?;
303 let value =
304 value.ok_or_else(|| DeError::invalid_type(de::Unexpected::Str(s), &Self::EXPECTING))?;
305
306 let mut unit = rem.trim();
307 if unit.is_empty() {
308 return Err(DeError::invalid_type(
309 de::Unexpected::Str(s),
310 &Self::EXPECTING,
311 ));
312 }
313
314 let lowercase_unit_string;
315 if lowercase_unit {
316 lowercase_unit_string = unit.to_lowercase();
317 unit = &lowercase_unit_string;
318 }
319 Self::parse(unit, value)
320 }
321}
322
323#[derive(Debug)]
324struct EnumVisitor<T>(PhantomData<T>);
325
326impl<'v, T> de::Visitor<'v> for EnumVisitor<T>
327where
328 T: EnumWithUnit<Value: de::DeserializeOwned>,
329{
330 type Value = T;
331
332 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
333 write!(formatter, "enum with one of {:?} variants", T::VARIANTS)
334 }
335
336 fn visit_enum<A: EnumAccess<'v>>(self, data: A) -> Result<Self::Value, A::Error> {
337 let (tag, payload) = data.variant::<String>()?;
338 let value = payload.newtype_variant()?;
339 let unit = tag.strip_prefix("in_").unwrap_or(&tag);
340 T::parse(unit, value)
341 }
342}
343
344impl<'v, T: EnumWithUnit> de::Visitor<'v> for EnumVisitor<Option<T>> {
345 type Value = Option<T>;
346
347 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
348 write!(formatter, "enum with one of {:?} variants", T::VARIANTS)
349 }
350
351 fn visit_enum<A: EnumAccess<'v>>(self, data: A) -> Result<Self::Value, A::Error> {
352 let (tag, payload) = data.variant::<String>()?;
353 let value = payload.newtype_variant()?;
354 let unit = tag.strip_prefix("in_").unwrap_or(&tag);
355 T::parse_opt(unit, value)
356 }
357}
358
359#[derive(Debug)]
361#[cfg_attr(test, derive(PartialEq))]
362enum RawDuration {
363 Millis(Decimal),
364 Seconds(Decimal),
365 Minutes(Decimal),
366 Hours(Decimal),
367 Days(Decimal),
368 Weeks(Decimal),
369}
370
371macro_rules! impl_enum_with_unit {
372 ($($($name:tt)|+ => $func:expr,)+) => {
373 const VARIANTS: &'static [&'static str] = &[$($($name,)+)+];
374
375 fn extract_variant(unit: &str) -> Option<fn(Self::Value) -> Self> {
376 Some(match unit {
377 $($($name )|+ => $func,)+
378 _ => return None,
379 })
380 }
381 };
382}
383
384impl EnumWithUnit for RawDuration {
385 type Value = Decimal;
386
387 const EXPECTING: &'static str = "value with unit, like '10 ms'";
388
389 impl_enum_with_unit!(
390 "milliseconds" | "millis" | "ms" => Self::Millis,
391 "seconds" | "second" | "secs" | "sec" | "s" => Self::Seconds,
392 "minutes" | "minute" | "mins" | "min" | "m" => Self::Minutes,
393 "hours" | "hour" | "hr" | "h" => Self::Hours,
394 "days" | "day" | "d" => Self::Days,
395 "weeks" | "week" | "w" => Self::Weeks,
396 );
397}
398
399impl FromStr for RawDuration {
400 type Err = serde_json::Error;
401
402 fn from_str(s: &str) -> Result<Self, Self::Err> {
403 Self::from_unit_str(s, false)
404 }
405}
406
407impl TryFrom<RawDuration> for Duration {
408 type Error = serde_json::Error;
409
410 fn try_from(value: RawDuration) -> Result<Self, Self::Error> {
411 let (unit, raw_value) = match value {
412 RawDuration::Millis(val) => (TimeUnit::Millis, val),
413 RawDuration::Seconds(val) => (TimeUnit::Seconds, val),
414 RawDuration::Minutes(val) => (TimeUnit::Minutes, val),
415 RawDuration::Hours(val) => (TimeUnit::Hours, val),
416 RawDuration::Days(val) => (TimeUnit::Days, val),
417 RawDuration::Weeks(val) => (TimeUnit::Weeks, val),
418 };
419 unit.into_duration(raw_value)
420 }
421}
422
423impl DeserializeParam<Duration> for WithUnit {
424 const EXPECTING: BasicTypes = Self::EXPECTED_TYPES;
425
426 fn describe(&self, description: &mut TypeDescription) {
427 description.set_details("duration with unit, or object with single unit key");
428 description.set_suffixes(TypeSuffixes::DurationUnits);
429 }
430
431 fn deserialize_param(
432 &self,
433 ctx: DeserializeContext<'_>,
434 param: &'static ParamMetadata,
435 ) -> Result<Duration, ErrorWithOrigin> {
436 Self::deserialize::<RawDuration, _>(&ctx, param)
437 }
438
439 fn serialize_param(&self, param: &Duration) -> serde_json::Value {
440 if param.is_zero() {
441 return "0s".into();
443 }
444
445 let duration_string = if param.subsec_millis() != 0 {
446 format!("{}ms", param.as_millis())
447 } else {
448 let seconds = param.as_secs();
449 if seconds % 60 != 0 {
450 format!("{seconds}s")
451 } else if seconds % 3_600 != 0 {
452 format!("{}min", seconds / 60)
453 } else if seconds % 86_400 != 0 {
454 format!("{}h", seconds / 3_600)
455 } else if seconds % (86_400 * 7) != 0 {
456 format!("{}d", seconds / 86_400)
457 } else {
458 format!("{}w", seconds / (86_400 * 7))
459 }
460 };
461 duration_string.into()
462 }
463}
464
465macro_rules! impl_deserialize_opt_param {
466 ($ty:ty => $raw:ty) => {
467 impl DeserializeParam<Option<$ty>> for WithUnit {
468 const EXPECTING: BasicTypes = Self::EXPECTED_TYPES;
469
470 fn describe(&self, description: &mut TypeDescription) {
471 <Self as DeserializeParam<$ty>>::describe(self, description);
472 }
473
474 fn deserialize_param(
475 &self,
476 ctx: DeserializeContext<'_>,
477 param: &'static ParamMetadata,
478 ) -> Result<Option<$ty>, ErrorWithOrigin> {
479 Self::deserialize_opt::<$raw, _>(&ctx, param)
480 }
481
482 fn serialize_param(&self, param: &Option<$ty>) -> serde_json::Value {
483 match param {
484 Some(val) => self.serialize_param(val),
485 None => serde_json::Value::Null,
486 }
487 }
488 }
489 };
490}
491
492impl_deserialize_opt_param!(Duration => RawDuration);
493
494macro_rules! impl_well_known_with_unit {
495 ($ty:ty) => {
496 impl WellKnown for $ty {
497 type Deserializer = WithUnit;
498 const DE: Self::Deserializer = WithUnit;
499 }
500
501 impl CustomKnownOption for $ty {
502 type OptDeserializer = Optional<WithUnit, true>;
503 const OPT_DE: Self::OptDeserializer = Optional(WithUnit);
504 }
505 };
506}
507
508impl_well_known_with_unit!(Duration);
509
510#[derive(Debug)]
511#[cfg_attr(test, derive(PartialEq))]
512enum RawByteSize {
513 Bytes(u64),
514 Kilobytes(u64),
515 Megabytes(u64),
516 Gigabytes(u64),
517}
518
519impl EnumWithUnit for RawByteSize {
520 type Value = u64;
521
522 const EXPECTING: &'static str = "value with unit, like '32 MB'";
523
524 impl_enum_with_unit!(
525 "bytes" | "b" => Self::Bytes,
526 "kilobytes" | "kb" | "kib" => Self::Kilobytes,
527 "megabytes" | "mb" | "mib" => Self::Megabytes,
528 "gigabytes" | "gb" | "gib" => Self::Gigabytes,
529 );
530}
531
532impl TryFrom<RawByteSize> for ByteSize {
533 type Error = serde_json::Error;
534
535 fn try_from(value: RawByteSize) -> Result<Self, Self::Error> {
536 let (unit, raw_value) = match value {
537 RawByteSize::Bytes(val) => (SizeUnit::Bytes, val),
538 RawByteSize::Kilobytes(val) => (SizeUnit::KiB, val),
539 RawByteSize::Megabytes(val) => (SizeUnit::MiB, val),
540 RawByteSize::Gigabytes(val) => (SizeUnit::GiB, val),
541 };
542 ByteSize::checked(raw_value, unit).ok_or_else(|| {
543 DeError::custom(format!(
544 "{raw_value} {unit} does not fit into `u64`",
545 unit = unit.as_str()
546 ))
547 })
548 }
549}
550
551macro_rules! impl_deserialize_param {
553 ($ty:ty, raw: $raw:ty, name: $name:tt, units: $units:ident) => {
554 impl<'de> Deserialize<'de> for $raw {
555 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
556 deserializer.deserialize_enum(
557 stringify!($raw),
558 Self::VARIANTS,
559 EnumVisitor(PhantomData::<Self>),
560 )
561 }
562 }
563
564 impl FromStr for $raw {
565 type Err = serde_json::Error;
566
567 fn from_str(s: &str) -> Result<Self, Self::Err> {
568 Self::from_unit_str(s, true)
569 }
570 }
571
572 impl FromStr for $ty {
573 type Err = serde_json::Error;
574
575 fn from_str(s: &str) -> Result<Self, Self::Err> {
576 <$raw>::from_unit_str(s, true)?.try_into()
577 }
578 }
579
580 impl DeserializeParam<$ty> for WithUnit {
581 const EXPECTING: BasicTypes = Self::EXPECTED_TYPES;
582
583 fn describe(&self, description: &mut TypeDescription) {
584 description
585 .set_details(concat!($name, " with unit, or object with single unit key"));
586 description.set_suffixes(TypeSuffixes::$units);
587 }
588
589 fn deserialize_param(
590 &self,
591 ctx: DeserializeContext<'_>,
592 param: &'static ParamMetadata,
593 ) -> Result<$ty, ErrorWithOrigin> {
594 Self::deserialize::<$raw, _>(&ctx, param)
595 }
596
597 fn serialize_param(&self, param: &$ty) -> serde_json::Value {
598 param.to_string().into()
599 }
600 }
601 };
602}
603
604impl_deserialize_param!(ByteSize, raw: RawByteSize, name: "size", units: SizeUnits);
605impl_deserialize_opt_param!(ByteSize => RawByteSize);
606impl_well_known_with_unit!(ByteSize);
607
608impl TypeSuffixes {
609 pub(crate) fn contains(self, suffix: &str) -> bool {
610 match self {
611 Self::All => true,
612 Self::DurationUnits => {
613 let suffix = suffix.strip_prefix("in_").unwrap_or(suffix);
614 RawDuration::VARIANTS.contains(&suffix)
615 }
616 Self::SizeUnits => {
617 let suffix = suffix.strip_prefix("in_").unwrap_or(suffix);
618 RawByteSize::VARIANTS.contains(&suffix)
619 }
620 Self::EtherUnits => {
621 let suffix = suffix.strip_prefix("in_").unwrap_or(suffix);
622 RawEtherAmount::VARIANTS.contains(&suffix)
623 }
624 }
625 }
626}
627
628#[derive(Debug)]
629#[cfg_attr(test, derive(PartialEq))]
630enum RawEtherAmount {
631 Wei(Decimal),
632 Gwei(Decimal),
633 Ether(Decimal),
634}
635
636impl EnumWithUnit for RawEtherAmount {
637 type Value = Decimal;
638
639 const EXPECTING: &'static str = "value with unit, like '100 gwei'";
640
641 impl_enum_with_unit!(
642 "wei" => Self::Wei,
643 "gwei" => Self::Gwei,
644 "ether" => Self::Ether,
645 );
646}
647
648impl TryFrom<RawEtherAmount> for EtherAmount {
649 type Error = serde_json::Error;
650
651 fn try_from(value: RawEtherAmount) -> Result<Self, Self::Error> {
652 let (scale, raw_value) = match value {
653 RawEtherAmount::Wei(val) => (0, val),
654 RawEtherAmount::Gwei(val) => (9, val),
655 RawEtherAmount::Ether(val) => (18, val),
656 };
657 let value = raw_value.scale(scale)?;
658 Ok(Self(value))
659 }
660}
661
662impl_deserialize_param!(EtherAmount, raw: RawEtherAmount, name: "amount", units: EtherUnits);
663impl_deserialize_opt_param!(EtherAmount => RawEtherAmount);
664impl_well_known_with_unit!(EtherAmount);
665
666#[cfg(test)]
667mod tests {
668 use super::*;
669
670 #[test]
671 fn parsing_time_string() {
672 let duration: RawDuration = "10ms".parse().unwrap();
673 assert_eq!(duration, RawDuration::Millis(10.into()));
674 let duration: RawDuration = "50 seconds".parse().unwrap();
675 assert_eq!(duration, RawDuration::Seconds(50.into()));
676 let duration: RawDuration = "40s".parse().unwrap();
677 assert_eq!(duration, RawDuration::Seconds(40.into()));
678 let duration: RawDuration = "10 min".parse().unwrap();
679 assert_eq!(duration, RawDuration::Minutes(10.into()));
680 let duration: RawDuration = "10m".parse().unwrap();
681 assert_eq!(duration, RawDuration::Minutes(10.into()));
682 let duration: RawDuration = "12 hours".parse().unwrap();
683 assert_eq!(duration, RawDuration::Hours(12.into()));
684 let duration: RawDuration = "12h".parse().unwrap();
685 assert_eq!(duration, RawDuration::Hours(12.into()));
686 let duration: RawDuration = "30d".parse().unwrap();
687 assert_eq!(duration, RawDuration::Days(30.into()));
688 let duration: RawDuration = "1 day".parse().unwrap();
689 assert_eq!(duration, RawDuration::Days(1.into()));
690 let duration: RawDuration = "2 weeks".parse().unwrap();
691 assert_eq!(duration, RawDuration::Weeks(2.into()));
692 let duration: RawDuration = "3w".parse().unwrap();
693 assert_eq!(duration, RawDuration::Weeks(3.into()));
694 }
695
696 #[test]
697 fn parsing_fractional_time_string() {
698 let duration: RawDuration = "10.0ms".parse().unwrap();
699 assert_eq!(duration, RawDuration::Millis(10.into()));
700 let duration: RawDuration = "0.2s".parse().unwrap();
701 assert_eq!(duration, RawDuration::Seconds(Decimal::new(2, -1)));
702 let duration: RawDuration = "0.33 days".parse().unwrap();
703 assert_eq!(duration, RawDuration::Days(Decimal::new(33, -2)));
704 let duration: RawDuration = "1.7e+3 hours".parse().unwrap();
705 assert_eq!(duration, RawDuration::Hours(Decimal::new(17, 2)));
706 }
707
708 #[test]
709 fn parsing_time_string_errors() {
710 let err = "".parse::<RawDuration>().unwrap_err().to_string();
711 assert!(err.starts_with("invalid type"), "{err}");
712 let err = "???".parse::<RawDuration>().unwrap_err().to_string();
713 assert!(err.starts_with("invalid type"), "{err}");
714 let err = "10".parse::<RawDuration>().unwrap_err().to_string();
715 assert!(err.starts_with("invalid type"), "{err}");
716 let err = "hours".parse::<RawDuration>().unwrap_err().to_string();
717 assert!(err.starts_with("invalid type"), "{err}");
718
719 let err = "111111111111111111111111111111111111111111s"
720 .parse::<RawDuration>()
721 .unwrap_err()
722 .to_string();
723 assert!(err.contains("too many digits"), "{err}");
724
725 let err = "10 months".parse::<RawDuration>().unwrap_err().to_string();
726 assert!(err.starts_with("unknown variant"), "{err}");
727 }
728
729 #[test]
730 fn parsing_byte_size_string() {
731 let size: RawByteSize = "16bytes".parse().unwrap();
732 assert_eq!(size, RawByteSize::Bytes(16));
733 let size: RawByteSize = "128 KiB".parse().unwrap();
734 assert_eq!(size, RawByteSize::Kilobytes(128));
735 let size: RawByteSize = "16 kb".parse().unwrap();
736 assert_eq!(size, RawByteSize::Kilobytes(16));
737 let size: RawByteSize = "4MB".parse().unwrap();
738 assert_eq!(size, RawByteSize::Megabytes(4));
739 let size: RawByteSize = "1 GB".parse().unwrap();
740 assert_eq!(size, RawByteSize::Gigabytes(1));
741 }
742
743 #[test]
744 fn parsing_ether_amount_string() {
745 let amount: RawEtherAmount = "1wei".parse().unwrap();
746 assert_eq!(amount, RawEtherAmount::Wei(1.into()));
747 let amount: EtherAmount = amount.try_into().unwrap();
748 assert_eq!(amount, EtherAmount(1));
749
750 let amount: RawEtherAmount = "123 wei".parse().unwrap();
751 assert_eq!(amount, RawEtherAmount::Wei(123.into()));
752 let amount: EtherAmount = amount.try_into().unwrap();
753 assert_eq!(amount, EtherAmount(123));
754
755 let amount: RawEtherAmount = "1.5 gwei".parse().unwrap();
756 assert_eq!(amount, RawEtherAmount::Gwei(Decimal::new(15, -1)));
757 let amount: EtherAmount = amount.try_into().unwrap();
758 assert_eq!(amount, EtherAmount(1_500_000_000));
759
760 for input in [
761 "0.0015 ether",
762 "0.001_5ether",
763 "0.0015ether",
764 "1.5e-3 ether",
765 "15e-4ether",
766 ".015e-1 ether",
767 ] {
768 let amount: RawEtherAmount = input.parse().unwrap();
769 assert_eq!(amount, RawEtherAmount::Ether(Decimal::new(15, -4)));
770 let amount: EtherAmount = amount.try_into().unwrap();
771 assert_eq!(amount, EtherAmount(1_500_000_000_000_000));
772 }
773 }
774
775 #[test]
776 fn serializing_with_time_unit() {
777 let val = TimeUnit::Millis.serialize_param(&Duration::from_millis(10));
778 assert_eq!(val, 10_u32);
779 let val = TimeUnit::Millis.serialize_param(&Duration::from_secs(10));
780 assert_eq!(val, 10_000_u32);
781 let val = TimeUnit::Seconds.serialize_param(&Duration::from_secs(10));
782 assert_eq!(val, 10_u32);
783 let val = TimeUnit::Minutes.serialize_param(&Duration::from_secs(10));
784 assert_eq!(val, 0_u32);
785 let val = TimeUnit::Minutes.serialize_param(&Duration::from_secs(120));
786 assert_eq!(val, 2_u32);
787 }
788
789 #[test]
790 fn serializing_with_size_unit() {
791 let val = SizeUnit::Bytes.serialize_param(&ByteSize(128));
792 assert_eq!(val, 128_u32);
793 let val = SizeUnit::Bytes.serialize_param(&ByteSize(1 << 16));
794 assert_eq!(val, 1_u32 << 16);
795 let val = SizeUnit::KiB.serialize_param(&ByteSize(1 << 16));
796 assert_eq!(val, 1_u32 << 6);
797 let val = SizeUnit::MiB.serialize_param(&ByteSize(1 << 16));
798 assert_eq!(val, 0_u32);
799 let val = SizeUnit::MiB.serialize_param(&ByteSize::new(3, SizeUnit::MiB));
800 assert_eq!(val, 3_u32);
801 }
802
803 #[test]
804 fn serializing_with_duration() {
805 let val = WithUnit.serialize_param(&Duration::ZERO);
806 assert_eq!(val, "0s");
807 let val = WithUnit.serialize_param(&Duration::from_millis(10));
808 assert_eq!(val, "10ms");
809 let val = WithUnit.serialize_param(&Duration::from_secs(5));
810 assert_eq!(val, "5s");
811 let val = WithUnit.serialize_param(&Duration::from_millis(5_050));
812 assert_eq!(val, "5050ms");
813 let val = WithUnit.serialize_param(&Duration::from_secs(300));
814 assert_eq!(val, "5min");
815 let val = WithUnit.serialize_param(&Duration::from_secs(7_200));
816 assert_eq!(val, "2h");
817 let val = WithUnit.serialize_param(&Duration::from_secs(86_400));
818 assert_eq!(val, "1d");
819 }
820
821 #[test]
822 fn serializing_with_byte_size() {
823 let val = WithUnit.serialize_param(&ByteSize(0));
824 assert_eq!(val, "0 B");
825 let val = WithUnit.serialize_param(&ByteSize(128));
826 assert_eq!(val, "128 B");
827 let val = WithUnit.serialize_param(&ByteSize(32 << 10));
828 assert_eq!(val, "32 KiB");
829 let val = WithUnit.serialize_param(&ByteSize(3 << 20));
830 assert_eq!(val, "3 MiB");
831 }
832}