chuchi_postgres_types/time/
timeout.rs

1use std::time::{Duration, SystemTime};
2
3#[derive(Debug, Clone)]
4pub struct Timeout {
5	inner: SystemTime,
6}
7
8impl Timeout {
9	pub fn new(dur: Duration) -> Self {
10		Self {
11			inner: SystemTime::now() + dur,
12		}
13	}
14
15	pub fn now() -> Self {
16		Self {
17			inner: SystemTime::now(),
18		}
19	}
20
21	pub fn has_elapsed(&self) -> bool {
22		SystemTime::now() > self.inner
23	}
24
25	/// Returns None if the Duration is negative
26	pub fn remaining(&self) -> Option<Duration> {
27		self.inner.duration_since(SystemTime::now()).ok()
28	}
29
30	/// returns the time from UNIX_EPOCH
31	pub fn as_secs(&self) -> u64 {
32		self.inner
33			.duration_since(SystemTime::UNIX_EPOCH)
34			.expect("Welcome to the past!")
35			.as_secs()
36	}
37
38	pub fn from_secs(s: u64) -> Option<Self> {
39		SystemTime::UNIX_EPOCH
40			.checked_add(Duration::from_secs(s))
41			.map(|c| Timeout { inner: c })
42	}
43}
44
45#[cfg(feature = "serde")]
46mod impl_serde {
47	use super::*;
48
49	use serde::de::{Deserializer, Error};
50	use serde::ser::Serializer;
51	use serde::{Deserialize, Serialize};
52
53	impl Serialize for Timeout {
54		fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
55		where
56			S: Serializer,
57		{
58			serializer.serialize_u64(self.as_secs())
59		}
60	}
61
62	impl<'de> Deserialize<'de> for Timeout {
63		fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
64		where
65			D: Deserializer<'de>,
66		{
67			let num: u64 = Deserialize::deserialize(deserializer)?;
68			Self::from_secs(num).ok_or(D::Error::custom("timeout to big"))
69		}
70	}
71}
72
73#[cfg(feature = "postgres")]
74mod postgres {
75	use super::*;
76	use bytes::BytesMut;
77	use postgres_types::{to_sql_checked, FromSql, IsNull, ToSql, Type};
78
79	impl ToSql for Timeout {
80		fn to_sql(
81			&self,
82			ty: &Type,
83			out: &mut BytesMut,
84		) -> Result<IsNull, Box<dyn std::error::Error + Sync + Send>> {
85			let secs: i64 = self.as_secs().try_into()?;
86
87			secs.to_sql(ty, out)
88		}
89
90		fn accepts(ty: &Type) -> bool
91		where
92			Self: Sized,
93		{
94			<i64 as ToSql>::accepts(ty)
95		}
96
97		to_sql_checked!();
98	}
99
100	impl<'a> FromSql<'a> for Timeout {
101		fn from_sql(
102			ty: &Type,
103			raw: &'a [u8],
104		) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
105			let secs: u64 = <i64 as FromSql>::from_sql(ty, raw)?.try_into()?;
106
107			Self::from_secs(secs).ok_or_else(|| "timeout to large".into())
108		}
109
110		fn accepts(ty: &Type) -> bool {
111			<i64 as FromSql>::accepts(ty)
112		}
113	}
114}