1use std::time::Duration;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
9pub enum Expiration {
10 #[default]
12 Never,
13 Seconds(u32),
15 Unix(u32),
17}
18
19const RELATIVE_MAX_SECONDS: u32 = 60 * 60 * 24 * 30;
21
22impl Expiration {
23 pub fn to_wire(self) -> u32 {
25 match self {
26 Expiration::Never => 0,
27 Expiration::Seconds(secs) => secs,
28 Expiration::Unix(ts) => ts,
29 }
30 }
31}
32
33impl From<u32> for Expiration {
34 fn from(secs: u32) -> Self {
38 if secs == 0 {
39 Expiration::Never
40 } else if secs <= RELATIVE_MAX_SECONDS {
41 Expiration::Seconds(secs)
42 } else {
43 Expiration::Unix(secs)
44 }
45 }
46}
47
48impl From<Duration> for Expiration {
49 fn from(duration: Duration) -> Self {
50 Expiration::from(duration.as_secs().min(u32::MAX as u64) as u32)
51 }
52}
53
54impl From<Option<Duration>> for Expiration {
55 fn from(duration: Option<Duration>) -> Self {
56 duration.map_or(Expiration::Never, Expiration::from)
57 }
58}
59
60#[cfg(test)]
61mod tests {
62 use super::*;
63
64 #[test]
65 fn zero_seconds_is_never() {
66 assert_eq!(Expiration::from(0u32), Expiration::Never);
67 assert_eq!(Expiration::from(0u32).to_wire(), 0);
68 }
69
70 #[test]
71 fn relative_and_absolute_boundary() {
72 assert_eq!(Expiration::from(60u32), Expiration::Seconds(60));
73 let over = RELATIVE_MAX_SECONDS + 1;
74 assert_eq!(Expiration::from(over), Expiration::Unix(over));
75 }
76
77 #[test]
78 fn duration_converts_to_seconds() {
79 assert_eq!(
80 Expiration::from(Duration::from_secs(90)),
81 Expiration::Seconds(90)
82 );
83 }
84}