1use std::ops::Index;
2use linked_hash_map::LinkedHashMap;
3
4#[derive(Debug, Clone, PartialEq)]
67pub enum Hocon {
68 Real(f64),
70 Integer(i64),
72 String(String),
74 Boolean(bool),
76 Array(Vec<Hocon>),
78 Hash(LinkedHashMap<String, Hocon>),
80 Null,
82 BadValue(crate::Error),
84}
85
86static NOT_FOUND: Hocon = Hocon::BadValue(crate::Error::MissingKey);
87static INVALID_KEY: Hocon = Hocon::BadValue(crate::Error::InvalidKey);
88
89impl<'a> Index<&'a str> for Hocon {
90 type Output = Hocon;
91
92 fn index(&self, idx: &'a str) -> &Self::Output {
93 match self {
94 Hocon::Hash(hash) => hash.get(idx).unwrap_or(&NOT_FOUND),
95 _ => &INVALID_KEY,
96 }
97 }
98}
99impl Index<usize> for Hocon {
100 type Output = Hocon;
101
102 fn index(&self, idx: usize) -> &Self::Output {
103 match self {
104 Hocon::Array(vec) => vec.get(idx).unwrap_or(&NOT_FOUND),
105 Hocon::Hash(hash) => {
106 let mut keys_as_usize = hash
107 .keys()
108 .filter_map(|k| k.parse::<usize>().ok().map(|v| (k, v)))
109 .collect::<Vec<_>>();
110 keys_as_usize.sort_by(|(_, v0), (_, v1)| v0.cmp(v1));
111 keys_as_usize
112 .get(idx)
113 .and_then(|(k, _)| hash.get(*k))
114 .unwrap_or(&INVALID_KEY)
115 }
116 _ => &INVALID_KEY,
117 }
118 }
119}
120
121impl Hocon {
122 pub fn as_f64(&self) -> Option<f64> {
124 match *self {
125 Hocon::Real(ref v) => Some(*v),
126 Hocon::Integer(ref v) => Some(*v as f64),
127 Hocon::String(ref v) => v.parse::<f64>().ok(),
128 _ => None,
129 }
130 }
131
132 pub fn as_i64(&self) -> Option<i64> {
134 match *self {
135 Hocon::Integer(ref v) => Some(*v),
136 Hocon::String(ref v) => v.parse::<i64>().ok(),
137 _ => None,
138 }
139 }
140
141 pub fn as_string(&self) -> Option<String> {
143 match *self {
144 Hocon::String(ref v) => Some(v.to_string()),
145 Hocon::Boolean(true) => Some("true".to_string()),
146 Hocon::Boolean(false) => Some("false".to_string()),
147 Hocon::Integer(i) => Some(i.to_string()),
148 Hocon::Real(f) => Some(f.to_string()),
149 _ => None,
150 }
151 }
152
153 pub(crate) fn as_internal_string(&self) -> Option<String> {
154 match *self {
155 Hocon::String(ref v) => Some(v.to_string()),
156 Hocon::Boolean(true) => Some("true".to_string()),
157 Hocon::Boolean(false) => Some("false".to_string()),
158 Hocon::Integer(i) => Some(i.to_string()),
159 Hocon::Real(f) => Some(f.to_string()),
160 Hocon::Null => Some("null".to_string()),
161 _ => None,
162 }
163 }
164
165 pub fn as_bool(&self) -> Option<bool> {
167 match *self {
168 Hocon::Boolean(ref v) => Some(*v),
169 Hocon::String(ref v) if v == "yes" || v == "true" || v == "on" => Some(true),
170 Hocon::String(ref v) if v == "no" || v == "false" || v == "off" => Some(false),
171 _ => None,
172 }
173 }
174}
175
176mod unit_format {
177 use nom::*;
178
179 named!(
180 parse_float<types::CompleteStr, f64>,
181 complete!(flat_map!(recognize_float, parse_to!(f64)))
182 );
183
184 pub(crate) fn value_and_unit(s: &str) -> Option<(f64, &str)> {
185 match parse_float(types::CompleteStr(s)) {
186 Ok((remaining, float)) => Some((float, &remaining)),
187 _ => None,
188 }
189 }
190}
191
192macro_rules! units {
193 ( match $input:expr, $( $first_unit:expr, $( $unit:expr ),* => $scale:expr ),* ) => {
194 match $input {
195 $(
196 Some((value, $first_unit))
197 $(
198 | Some((value, $unit))
199 )* => Some(value * $scale),
200 )*
201 _ => None
202 }
203 };
204}
205
206impl Hocon {
207 pub fn as_bytes(&self) -> Option<f64> {
226 match *self {
227 Hocon::Integer(ref i) => Some(*i as f64),
228 Hocon::Real(ref f) => Some(*f),
229 Hocon::String(ref s) => units!(
230 match unit_format::value_and_unit(s).map(|(value, unit)| (value, unit.trim())),
231 "", "B", "b", "byte", "bytes" => 1.0,
232 "kB", "kilobyte", "kilobytes" => 10.0f64.powf(3.0),
233 "MB", "megabyte", "megabytes" => 10.0f64.powf(6.0),
234 "GB", "gigabyte", "gigabytes" => 10.0f64.powf(9.0),
235 "TB", "terabyte", "terabytes" => 10.0f64.powf(12.0),
236 "PB", "petabyte", "petabytes" => 10.0f64.powf(15.0),
237 "EB", "exabyte", "exabytes" => 10.0f64.powf(18.0),
238 "ZB", "zettabyte", "zettabytes" => 10.0f64.powf(21.0),
239 "YB", "yottabyte", "yottabytes" => 10.0f64.powf(24.0),
240 "K", "k", "Ki", "KiB", "kibibyte", "kibibytes" => 2.0f64.powf(10.0),
241 "M", "m", "Mi", "MiB", "mebibyte", "mebibytes" => 2.0f64.powf(20.0),
242 "G", "g", "Gi", "GiB", "gibibyte", "gibibytes" => 2.0f64.powf(30.0),
243 "T", "t", "Ti", "TiB", "tebibyte", "tebibytes" => 2.0f64.powf(40.0),
244 "P", "p", "Pi", "PiB", "pebibyte", "pebibytes" => 2.0f64.powf(50.0),
245 "E", "e", "Ei", "EiB", "exbibyte", "exbibytes" => 2.0f64.powf(60.0),
246 "Z", "z", "Zi", "ZiB", "zebibyte", "zebibytes" => 2.0f64.powf(70.0),
247 "Y", "y", "Yi", "YiB", "yobibyte", "yobibytes" => 2.0f64.powf(80.0)
248 ),
249 _ => None,
250 }
251 }
252
253 pub fn as_milliseconds(&self) -> Option<f64> {
273 match *self {
274 Hocon::Integer(ref i) => Some(*i as f64),
275 Hocon::Real(ref f) => Some(*f),
276 Hocon::String(ref s) => Self::str_as_milliseconds(s),
277 _ => None,
278 }
279 }
280
281 pub(crate) fn str_as_milliseconds(s: &str) -> Option<f64> {
282 units!(
283 match unit_format::value_and_unit(s).map(|(value, unit)| (value, unit.trim())),
284 "ns", "nano", "nanos", "nanosecond", "nanoseconds" => 10.0f64.powf(-6.0),
285 "us", "micro", "micros", "microsecond", "microseconds" => 10.0f64.powf(-3.0),
286 "", "ms", "milli", "millis", "millisecond", "milliseconds" => 1.0,
287 "s", "second", "seconds" => 1_000.0,
288 "m", "minute", "minutes" => 1_000.0 * 60.0,
289 "h", "hour", "hours" => 1_000.0 * 60.0 * 60.0,
290 "d", "day", "days" => 1_000.0 * 60.0 * 60.0 * 24.0,
291 "w", "week", "weeks" => 1_000.0 * 60.0 * 60.0 * 24.0 * 7.0,
292 "mo", "month", "months" => 1_000.0 * 60.0 * 60.0 * 24.0 * 30.0,
293 "y", "year", "years" => 1_000.0 * 60.0 * 60.0 * 24.0 * 365.0
294 )
295 }
296
297 pub fn as_nanoseconds(&self) -> Option<f64> {
317 self.as_milliseconds().map(|v| v * 10.0f64.powf(6.0))
318 }
319
320 pub fn as_microseconds(&self) -> Option<f64> {
340 self.as_milliseconds().map(|v| v * 10.0f64.powf(3.0))
341 }
342
343 pub fn as_seconds(&self) -> Option<f64> {
363 self.as_milliseconds().map(|v| v * 10.0f64.powf(-3.0))
364 }
365
366 pub fn as_minutes(&self) -> Option<f64> {
386 self.as_milliseconds()
387 .map(|v| v * 10.0f64.powf(-3.0) / 60.0)
388 }
389
390 pub fn as_hours(&self) -> Option<f64> {
410 self.as_milliseconds()
411 .map(|v| v * 10.0f64.powf(-3.0) / 60.0 / 60.0)
412 }
413
414 pub fn as_days(&self) -> Option<f64> {
434 self.as_milliseconds()
435 .map(|v| v * 10.0f64.powf(-3.0) / 60.0 / 60.0 / 24.0)
436 }
437
438 pub fn as_weeks(&self) -> Option<f64> {
458 self.as_milliseconds()
459 .map(|v| v * 10.0f64.powf(-3.0) / 60.0 / 60.0 / 24.0 / 7.0)
460 }
461
462 pub fn as_months(&self) -> Option<f64> {
482 self.as_milliseconds()
483 .map(|v| v * 10.0f64.powf(-3.0) / 60.0 / 60.0 / 24.0 / 30.0)
484 }
485
486 pub fn as_years(&self) -> Option<f64> {
506 self.as_milliseconds()
507 .map(|v| v * 10.0f64.powf(-3.0) / 60.0 / 60.0 / 24.0 / 365.0)
508 }
509
510 pub fn as_duration(&self) -> Option<std::time::Duration> {
530 self.as_nanoseconds()
531 .map(|v| std::time::Duration::from_nanos(v as u64))
532 }
533}
534
535impl Hocon {
536 #[cfg(feature = "serde-support")]
552 pub fn resolve<'de, T>(self) -> Result<T, crate::Error>
553 where
554 T: ::serde::Deserialize<'de>,
555 {
556 crate::serde::from_hocon(self).map_err(|err| crate::Error::Deserialization {
557 message: err.message,
558 })
559 }
560}
561
562#[cfg(test)]
563mod tests {
564 use super::*;
565
566 #[test]
567 fn access_on_string() {
568 let val = Hocon::String(String::from("test"));
569
570 assert_eq!(val.as_bool(), None);
571 assert_eq!(val.as_f64(), None);
572 assert_eq!(val.as_i64(), None);
573 assert_eq!(val.as_string(), Some(String::from("test")));
574 assert_eq!(val[0], INVALID_KEY);
575 assert_eq!(val["a"], INVALID_KEY);
576 }
577
578 #[test]
579 fn access_on_real() {
580 let val = Hocon::Real(5.6);
581
582 assert_eq!(val.as_bool(), None);
583 assert_eq!(val.as_f64(), Some(5.6));
584 assert_eq!(val.as_i64(), None);
585 assert_eq!(val.as_string(), Some(String::from("5.6")));
586 assert_eq!(val[0], INVALID_KEY);
587 assert_eq!(val["a"], INVALID_KEY);
588 }
589
590 #[test]
591 fn access_on_integer() {
592 let val = Hocon::Integer(5);
593
594 assert_eq!(val.as_bool(), None);
595 assert_eq!(val.as_f64(), Some(5.0));
596 assert_eq!(val.as_i64(), Some(5));
597 assert_eq!(val.as_string(), Some(String::from("5")));
598 assert_eq!(val[0], INVALID_KEY);
599 assert_eq!(val["a"], INVALID_KEY);
600 }
601
602 #[test]
603 fn access_on_boolean_false() {
604 let val = Hocon::Boolean(false);
605
606 assert_eq!(val.as_bool(), Some(false));
607 assert_eq!(val.as_f64(), None);
608 assert_eq!(val.as_i64(), None);
609 assert_eq!(val.as_string(), Some(String::from("false")));
610 assert_eq!(val[0], INVALID_KEY);
611 assert_eq!(val["a"], INVALID_KEY);
612 }
613
614 #[test]
615 fn access_on_boolean_true() {
616 let val = Hocon::Boolean(true);
617
618 assert_eq!(val.as_bool(), Some(true));
619 assert_eq!(val.as_f64(), None);
620 assert_eq!(val.as_i64(), None);
621 assert_eq!(val.as_string(), Some(String::from("true")));
622 assert_eq!(val[0], INVALID_KEY);
623 assert_eq!(val["a"], INVALID_KEY);
624 }
625
626 #[test]
627 fn access_on_null() {
628 let val = Hocon::Null;
629
630 assert_eq!(val.as_bool(), None);
631 assert_eq!(val.as_f64(), None);
632 assert_eq!(val.as_i64(), None);
633 assert_eq!(val.as_string(), None);
634 assert_eq!(val[0], INVALID_KEY);
635 assert_eq!(val["a"], INVALID_KEY);
636 }
637
638 #[test]
639 fn access_on_bad_value() {
640 let val = Hocon::BadValue(crate::Error::DisabledExternalUrl);
641
642 assert_eq!(val.as_bool(), None);
643 assert_eq!(val.as_f64(), None);
644 assert_eq!(val.as_i64(), None);
645 assert_eq!(val.as_string(), None);
646 assert_eq!(val[0], INVALID_KEY);
647 assert_eq!(val["a"], INVALID_KEY);
648 }
649
650 #[test]
651 fn access_on_array() {
652 let val = Hocon::Array(vec![Hocon::Integer(5), Hocon::Integer(6)]);
653
654 assert_eq!(val.as_bool(), None);
655 assert_eq!(val.as_f64(), None);
656 assert_eq!(val.as_i64(), None);
657 assert_eq!(val.as_string(), None);
658 assert_eq!(val[0], Hocon::Integer(5));
659 assert_eq!(val[1], Hocon::Integer(6));
660 assert_eq!(val[2], NOT_FOUND);
661 assert_eq!(val["a"], INVALID_KEY);
662 }
663
664 #[test]
665 fn access_on_hash() {
666 let mut hm = LinkedHashMap::new();
667 hm.insert(String::from("a"), Hocon::Integer(5));
668 hm.insert(String::from("b"), Hocon::Integer(6));
669 let val = Hocon::Hash(hm);
670
671 assert_eq!(val.as_bool(), None);
672 assert_eq!(val.as_f64(), None);
673 assert_eq!(val.as_i64(), None);
674 assert_eq!(val.as_string(), None);
675 assert_eq!(val[0], INVALID_KEY);
676 assert_eq!(val["a"], Hocon::Integer(5));
677 assert_eq!(val["b"], Hocon::Integer(6));
678 assert_eq!(val["c"], NOT_FOUND);
679 }
680
681 #[test]
682 fn cast_string() {
683 assert_eq!(Hocon::String(String::from("true")).as_bool(), Some(true));
684 assert_eq!(Hocon::String(String::from("yes")).as_bool(), Some(true));
685 assert_eq!(Hocon::String(String::from("on")).as_bool(), Some(true));
686 assert_eq!(Hocon::String(String::from("false")).as_bool(), Some(false));
687 assert_eq!(Hocon::String(String::from("no")).as_bool(), Some(false));
688 assert_eq!(Hocon::String(String::from("off")).as_bool(), Some(false));
689
690 assert_eq!(Hocon::String(String::from("5.6")).as_f64(), Some(5.6));
691 assert_eq!(Hocon::String(String::from("5.6")).as_i64(), None);
692 assert_eq!(Hocon::String(String::from("5")).as_f64(), Some(5.0));
693 assert_eq!(Hocon::String(String::from("5")).as_i64(), Some(5));
694 }
695
696 #[test]
697 fn access_hash_as_array() {
698 let mut hm = LinkedHashMap::new();
699 hm.insert(String::from("0"), Hocon::Integer(5));
700 hm.insert(String::from("a"), Hocon::Integer(6));
701 hm.insert(String::from("2"), Hocon::Integer(7));
702 let val = Hocon::Hash(hm);
703
704 assert_eq!(val.as_bool(), None);
705 assert_eq!(val.as_f64(), None);
706 assert_eq!(val.as_i64(), None);
707 assert_eq!(val.as_string(), None);
708 assert_eq!(val[0], Hocon::Integer(5));
709 assert_eq!(val[1], Hocon::Integer(7));
710 assert_eq!(val[2], INVALID_KEY);
711 assert_eq!(val["0"], Hocon::Integer(5));
712 assert_eq!(val["a"], Hocon::Integer(6));
713 assert_eq!(val["2"], Hocon::Integer(7));
714 }
715
716 #[test]
717 fn access_on_bytes() {
718 let val = Hocon::Array(vec![
719 Hocon::Integer(5),
720 Hocon::Real(6.5),
721 Hocon::String(String::from("7")),
722 Hocon::String(String::from("8kB")),
723 Hocon::String(String::from("9 EB")),
724 Hocon::String(String::from("10.5MiB")),
725 Hocon::String(String::from("5unit")),
726 Hocon::Boolean(false),
727 ]);
728
729 assert_eq!(val[0].as_bytes(), Some(5.0));
730 assert_eq!(val[1].as_bytes(), Some(6.5));
731 assert_eq!(val[2].as_bytes(), Some(7.0));
732 assert_eq!(val[3].as_bytes(), Some(8.0 * 1_000.0));
733 assert_eq!(val[4].as_bytes(), Some(9.0 * 10.0f64.powf(18.0)));
734 assert_eq!(val[5].as_bytes(), Some(10.5 * 2.0f64.powf(20.0)));
735 assert_eq!(val[6].as_bytes(), None);
736 assert_eq!(val[7].as_bytes(), None);
737 }
738
739 #[test]
740 fn access_on_bytes_all_bytes_units() {
741 for unit in vec!["B", "b", "byte", "bytes"] {
742 let val = Hocon::Array(vec![Hocon::String(format!("8{}", unit))]);
743 assert_eq!(dbg!(val)[0].as_bytes(), Some(8.0));
744 }
745
746 for unit in vec!["kB", "kilobyte", "kilobytes"] {
747 let val = Hocon::Array(vec![Hocon::String(format!("8{}", unit))]);
748 assert_eq!(dbg!(val)[0].as_bytes(), Some(8.0 * 10.0f64.powf(3.0)));
749 }
750 for unit in vec!["MB", "megabyte", "megabytes"] {
751 let val = Hocon::Array(vec![Hocon::String(format!("8{}", unit))]);
752 assert_eq!(dbg!(val)[0].as_bytes(), Some(8.0 * 10.0f64.powf(6.0)));
753 }
754 for unit in vec!["GB", "gigabyte", "gigabytes"] {
755 let val = Hocon::Array(vec![Hocon::String(format!("8{}", unit))]);
756 assert_eq!(dbg!(val)[0].as_bytes(), Some(8.0 * 10.0f64.powf(9.0)));
757 }
758 for unit in vec!["TB", "terabyte", "terabytes"] {
759 let val = Hocon::Array(vec![Hocon::String(format!("8{}", unit))]);
760 assert_eq!(dbg!(val)[0].as_bytes(), Some(8.0 * 10.0f64.powf(12.0)));
761 }
762 for unit in vec!["PB", "petabyte", "petabytes"] {
763 let val = Hocon::Array(vec![Hocon::String(format!("8{}", unit))]);
764 assert_eq!(dbg!(val)[0].as_bytes(), Some(8.0 * 10.0f64.powf(15.0)));
765 }
766 for unit in vec!["EB", "exabyte", "exabytes"] {
767 let val = Hocon::Array(vec![Hocon::String(format!("8{}", unit))]);
768 assert_eq!(dbg!(val)[0].as_bytes(), Some(8.0 * 10.0f64.powf(18.0)));
769 }
770 for unit in vec!["ZB", "zettabyte", "zettabytes"] {
771 let val = Hocon::Array(vec![Hocon::String(format!("8{}", unit))]);
772 assert_eq!(dbg!(val)[0].as_bytes(), Some(8.0 * 10.0f64.powf(21.0)));
773 }
774 for unit in vec!["YB", "yottabyte", "yottabytes"] {
775 let val = Hocon::Array(vec![Hocon::String(format!("8{}", unit))]);
776 assert_eq!(dbg!(val)[0].as_bytes(), Some(8.0 * 10.0f64.powf(24.0)));
777 }
778
779 for unit in vec!["K", "k", "Ki", "KiB", "kibibyte", "kibibytes"] {
780 let val = Hocon::Array(vec![Hocon::String(format!("8{}", unit))]);
781 assert_eq!(dbg!(val)[0].as_bytes(), Some(8.0 * 2.0f64.powf(10.0)));
782 }
783 for unit in vec!["M", "m", "Mi", "MiB", "mebibyte", "mebibytes"] {
784 let val = Hocon::Array(vec![Hocon::String(format!("8{}", unit))]);
785 assert_eq!(dbg!(val)[0].as_bytes(), Some(8.0 * 2.0f64.powf(20.0)));
786 }
787 for unit in vec!["G", "g", "Gi", "GiB", "gibibyte", "gibibytes"] {
788 let val = Hocon::Array(vec![Hocon::String(format!("8{}", unit))]);
789 assert_eq!(dbg!(val)[0].as_bytes(), Some(8.0 * 2.0f64.powf(30.0)));
790 }
791 for unit in vec!["T", "t", "Ti", "TiB", "tebibyte", "tebibytes"] {
792 let val = Hocon::Array(vec![Hocon::String(format!("8{}", unit))]);
793 assert_eq!(dbg!(val)[0].as_bytes(), Some(8.0 * 2.0f64.powf(40.0)));
794 }
795 for unit in vec!["P", "p", "Pi", "PiB", "pebibyte", "pebibytes"] {
796 let val = Hocon::Array(vec![Hocon::String(format!("8{}", unit))]);
797 assert_eq!(dbg!(val)[0].as_bytes(), Some(8.0 * 2.0f64.powf(50.0)));
798 }
799 for unit in vec!["E", "e", "Ei", "EiB", "exbibyte", "exbibytes"] {
800 let val = Hocon::Array(vec![Hocon::String(format!("8{}", unit))]);
801 assert_eq!(dbg!(val)[0].as_bytes(), Some(8.0 * 2.0f64.powf(60.0)));
802 }
803 for unit in vec!["Z", "z", "Zi", "ZiB", "zebibyte", "zebibytes"] {
804 let val = Hocon::Array(vec![Hocon::String(format!("8{}", unit))]);
805 assert_eq!(dbg!(val)[0].as_bytes(), Some(8.0 * 2.0f64.powf(70.0)));
806 }
807 for unit in vec!["Y", "y", "Yi", "YiB", "yobibyte", "yobibytes"] {
808 let val = Hocon::Array(vec![Hocon::String(format!("8{}", unit))]);
809 assert_eq!(dbg!(val)[0].as_bytes(), Some(8.0 * 2.0f64.powf(80.0)));
810 }
811 }
812
813 #[test]
814 fn access_on_duration() {
815 let mut hm = LinkedHashMap::new();
816 hm.insert(String::from("ns"), Hocon::String(String::from("1ns")));
817 hm.insert(String::from("us"), Hocon::String(String::from("1us")));
818 hm.insert(String::from("ms"), Hocon::String(String::from("1ms")));
819 hm.insert(String::from("s"), Hocon::String(String::from("1s")));
820 hm.insert(String::from("m"), Hocon::String(String::from("1m")));
821 hm.insert(String::from("h"), Hocon::String(String::from("1h")));
822 hm.insert(String::from("d"), Hocon::String(String::from("1d")));
823 hm.insert(String::from("w"), Hocon::String(String::from("1w")));
824 hm.insert(String::from("mo"), Hocon::String(String::from("1mo")));
825 hm.insert(String::from("y"), Hocon::String(String::from("1y")));
826 let val = Hocon::Hash(hm);
827
828 assert_eq!(val["ns"].as_nanoseconds(), Some(1.0));
829 assert_eq!(
830 val["ns"].as_duration(),
831 Some(std::time::Duration::from_nanos(1))
832 );
833 assert_eq!(val["us"].as_microseconds(), Some(1.0));
834 assert_eq!(
835 val["us"].as_duration(),
836 Some(std::time::Duration::from_micros(1))
837 );
838 assert_eq!(val["ms"].as_milliseconds(), Some(1.0));
839 assert_eq!(
840 val["ms"].as_duration(),
841 Some(std::time::Duration::from_millis(1))
842 );
843 assert_eq!(val["s"].as_seconds(), Some(1.0));
844 assert_eq!(
845 val["s"].as_duration(),
846 Some(std::time::Duration::from_secs(1))
847 );
848 assert_eq!(val["m"].as_minutes(), Some(1.0));
849 assert_eq!(
850 val["m"].as_duration(),
851 Some(std::time::Duration::from_secs(60))
852 );
853 assert_eq!(val["h"].as_hours(), Some(1.0));
854 assert_eq!(
855 val["h"].as_duration(),
856 Some(std::time::Duration::from_secs(60 * 60))
857 );
858 assert_eq!(val["d"].as_days(), Some(1.0));
859 assert_eq!(
860 val["d"].as_duration(),
861 Some(std::time::Duration::from_secs(60 * 60 * 24))
862 );
863 assert_eq!(val["w"].as_weeks(), Some(1.0));
864 assert_eq!(
865 val["w"].as_duration(),
866 Some(std::time::Duration::from_secs(60 * 60 * 24 * 7))
867 );
868 assert_eq!(val["mo"].as_months(), Some(1.0));
869 assert_eq!(
870 val["mo"].as_duration(),
871 Some(std::time::Duration::from_secs(60 * 60 * 24 * 30))
872 );
873 assert_eq!(val["y"].as_years(), Some(1.0));
874 assert_eq!(
875 val["y"].as_duration(),
876 Some(std::time::Duration::from_secs(60 * 60 * 24 * 365))
877 );
878 }
879}