uptrakit_wire/
shared_types.rs1use std::fmt;
2
3use serde::{Deserialize, Serialize};
4use time::UtcDateTime;
5
6pub type Timestamp = i64;
8
9pub fn now_millis() -> Timestamp {
11 let now = UtcDateTime::now();
12 now.unix_timestamp() * 1000 + i64::from(now.millisecond())
13}
14
15#[non_exhaustive]
25#[derive(Debug, Clone, PartialEq, Eq)]
26#[cfg_attr(feature = "schema", derive(strum::EnumIter))]
27pub enum UpdateFinalStatus {
28 Completed,
29 Failed,
30 Other(String),
34}
35
36impl UpdateFinalStatus {
37 pub fn as_str(&self) -> &str {
41 match self {
42 Self::Completed => "completed",
43 Self::Failed => "failed",
44 Self::Other(s) => s.as_str(),
45 }
46 }
47}
48
49impl fmt::Display for UpdateFinalStatus {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 f.write_str(self.as_str())
52 }
53}
54
55impl From<String> for UpdateFinalStatus {
56 fn from(s: String) -> Self {
57 match s.as_str() {
58 "completed" => Self::Completed,
59 "failed" => Self::Failed,
60 _ => Self::Other(s),
61 }
62 }
63}
64
65impl Serialize for UpdateFinalStatus {
66 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
67 serializer.serialize_str(self.as_str())
68 }
69}
70
71impl<'de> Deserialize<'de> for UpdateFinalStatus {
72 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
73 String::deserialize(deserializer).map(UpdateFinalStatus::from)
74 }
75}
76
77pub const DEFAULT_UPDATE_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(7200);
79
80pub(crate) fn default_update_timeout() -> std::time::Duration {
82 DEFAULT_UPDATE_TIMEOUT
83}
84
85#[non_exhaustive]
94#[derive(Debug, Clone, PartialEq, Eq)]
95#[cfg_attr(feature = "schema", derive(strum::EnumIter))]
96pub enum DisconnectReason {
97 Shutdown,
99 Restart,
101 Other(String),
105}
106
107impl DisconnectReason {
108 pub fn as_str(&self) -> &str {
112 match self {
113 Self::Shutdown => "shutdown",
114 Self::Restart => "restart",
115 Self::Other(s) => s.as_str(),
116 }
117 }
118}
119
120impl fmt::Display for DisconnectReason {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 f.write_str(self.as_str())
123 }
124}
125
126impl From<String> for DisconnectReason {
127 fn from(s: String) -> Self {
128 match s.as_str() {
129 "shutdown" => Self::Shutdown,
130 "restart" => Self::Restart,
131 _ => Self::Other(s),
132 }
133 }
134}
135
136impl Serialize for DisconnectReason {
137 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
138 serializer.serialize_str(self.as_str())
139 }
140}
141
142impl<'de> Deserialize<'de> for DisconnectReason {
143 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
144 String::deserialize(deserializer).map(DisconnectReason::from)
145 }
146}
147
148#[cfg(feature = "schema")]
160impl schemars::JsonSchema for UpdateFinalStatus {
161 fn schema_name() -> std::borrow::Cow<'static, str> {
162 std::borrow::Cow::Borrowed("UpdateFinalStatus")
163 }
164
165 fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
166 use strum::IntoEnumIterator;
167 let known: Vec<String> = UpdateFinalStatus::iter()
168 .filter(|v| !matches!(v, Self::Other(_)))
169 .map(|v| v.as_str().to_string())
170 .collect();
171 schemars::json_schema!({
172 "type": "string",
173 "description": format!(
174 "Open wire string (unknown values are forward-compatible). Known values: {}.",
175 known.join(", ")
176 ),
177 })
178 }
179}
180
181#[cfg(feature = "schema")]
182impl schemars::JsonSchema for DisconnectReason {
183 fn schema_name() -> std::borrow::Cow<'static, str> {
184 std::borrow::Cow::Borrowed("DisconnectReason")
185 }
186
187 fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
188 use strum::IntoEnumIterator;
189 let known: Vec<String> = DisconnectReason::iter()
190 .filter(|v| !matches!(v, Self::Other(_)))
191 .map(|v| v.as_str().to_string())
192 .collect();
193 schemars::json_schema!({
194 "type": "string",
195 "description": format!(
196 "Open wire string (unknown values are forward-compatible). Known values: {}.",
197 known.join(", ")
198 ),
199 })
200 }
201}
202
203#[cfg(test)]
204mod tests {
205 #[cfg(feature = "schema")]
206 mod schema_tests {
207 use super::super::*;
208
209 fn assert_open_string_schema<T: schemars::JsonSchema>(known: &[&str]) {
210 let schema = schemars::schema_for!(T);
211 let value = serde_json::to_value(&schema).expect("schema to JSON");
212 assert_eq!(value["type"], "string");
213 assert!(
214 value.get("enum").is_none(),
215 "must be an open string schema, found closed enum list: {value}"
216 );
217 let desc = value["description"].as_str().expect("description present");
218 for k in known {
219 assert!(
220 desc.contains(k),
221 "known value {k} missing from description: {desc}"
222 );
223 }
224 }
225
226 #[test]
227 fn update_final_status_schema_is_open_string_with_known_values() {
228 assert_open_string_schema::<UpdateFinalStatus>(&["completed", "failed"]);
229 }
230
231 #[test]
232 fn disconnect_reason_schema_is_open_string_with_known_values() {
233 assert_open_string_schema::<DisconnectReason>(&["shutdown", "restart"]);
234 }
235 }
236}