nntp_proxy/types/config/
timeout.rs1use std::time::Duration;
7
8macro_rules! timeout_newtype {
9 (
10 $(#[$meta:meta])*
11 $vis:vis struct $name:ident($default_secs:expr);
12 ) => {
13 $(#[$meta])*
14 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
15 $vis struct $name(Duration);
16
17 impl $name {
18 pub const DEFAULT: Self = Self(Duration::from_secs($default_secs));
20
21 #[inline]
23 pub const fn new(duration: Duration) -> Self {
24 Self(duration)
25 }
26
27 #[inline]
29 #[must_use]
30 pub const fn as_duration(self) -> Duration {
31 self.0
32 }
33
34 #[inline]
36 #[must_use]
37 pub const fn as_secs(self) -> u64 {
38 self.0.as_secs()
39 }
40 }
41
42 impl From<Duration> for $name {
43 fn from(duration: Duration) -> Self {
44 Self(duration)
45 }
46 }
47
48 impl From<$name> for Duration {
49 fn from(timeout: $name) -> Self {
50 timeout.0
51 }
52 }
53 };
54}
55
56timeout_newtype! {
57 pub struct BackendReadTimeout(30);
63}
64
65timeout_newtype! {
66 pub struct ConnectionTimeout(10);
71}
72
73timeout_newtype! {
74 pub struct CommandExecutionTimeout(60);
79}
80
81timeout_newtype! {
82 pub struct HealthCheckTimeout(2);
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92 use proptest::prelude::*;
93
94 proptest! {
96 #[test]
97 fn backend_read_timeout_roundtrip(secs in 0u64..86400u64) {
98 let duration = Duration::from_secs(secs);
99 let timeout = BackendReadTimeout::new(duration);
100 prop_assert_eq!(timeout.as_duration(), duration);
101 prop_assert_eq!(timeout.as_secs(), secs);
102 }
103
104 #[test]
105 fn connection_timeout_roundtrip(secs in 0u64..86400u64) {
106 let duration = Duration::from_secs(secs);
107 let timeout = ConnectionTimeout::new(duration);
108 prop_assert_eq!(timeout.as_duration(), duration);
109 prop_assert_eq!(timeout.as_secs(), secs);
110 }
111
112 #[test]
113 fn command_execution_timeout_roundtrip(secs in 0u64..86400u64) {
114 let duration = Duration::from_secs(secs);
115 let timeout = CommandExecutionTimeout::new(duration);
116 prop_assert_eq!(timeout.as_duration(), duration);
117 prop_assert_eq!(timeout.as_secs(), secs);
118 }
119
120 #[test]
121 fn health_check_timeout_roundtrip(secs in 0u64..86400u64) {
122 let duration = Duration::from_secs(secs);
123 let timeout = HealthCheckTimeout::new(duration);
124 prop_assert_eq!(timeout.as_duration(), duration);
125 prop_assert_eq!(timeout.as_secs(), secs);
126 }
127
128 #[test]
129 fn timeout_from_into_duration(secs in 0u64..86400u64) {
130 let duration = Duration::from_secs(secs);
131 let timeout = BackendReadTimeout::from(duration);
132 let back: Duration = timeout.into();
133 prop_assert_eq!(back, duration);
134 }
135
136 #[test]
137 fn timeout_ordering_property(a in 0u64..1000u64, b in 0u64..1000u64) {
138 let t1 = ConnectionTimeout::new(Duration::from_secs(a));
139 let t2 = ConnectionTimeout::new(Duration::from_secs(b));
140 prop_assert_eq!(t1.cmp(&t2), a.cmp(&b));
141 }
142
143 #[test]
144 fn timeout_clone_equality(secs in 0u64..86400u64) {
145 let timeout = BackendReadTimeout::new(Duration::from_secs(secs));
146 let cloned = timeout;
147 prop_assert_eq!(timeout, cloned);
148 }
149
150 #[test]
151 fn timeout_debug_contains_name(secs in 0u64..100u64) {
152 let timeout = BackendReadTimeout::new(Duration::from_secs(secs));
153 let debug_str = format!("{timeout:?}");
154 prop_assert!(debug_str.contains("BackendReadTimeout"));
155 }
156 }
157
158 #[test]
160 fn all_default_constants_correct() {
161 assert_eq!(BackendReadTimeout::DEFAULT.as_secs(), 30);
162 assert_eq!(ConnectionTimeout::DEFAULT.as_secs(), 10);
163 assert_eq!(CommandExecutionTimeout::DEFAULT.as_secs(), 60);
164 assert_eq!(HealthCheckTimeout::DEFAULT.as_secs(), 2);
165 }
166
167 #[test]
169 fn zero_timeout_is_valid() {
170 let timeout = BackendReadTimeout::new(Duration::from_secs(0));
171 assert_eq!(timeout.as_secs(), 0);
172 }
173
174 #[test]
175 fn large_timeout_one_day() {
176 let large = crate::constants::duration_polyfill::from_hours(24);
177 let timeout = CommandExecutionTimeout::new(large);
178 assert_eq!(timeout.as_secs(), 86400);
179 }
180
181 #[test]
182 fn timeout_hash_works() {
183 use std::collections::HashSet;
184 let mut set = HashSet::new();
185 set.insert(BackendReadTimeout::DEFAULT);
186 assert!(set.contains(&BackendReadTimeout::DEFAULT));
187 }
188}