Skip to main content

uptrakit_wire/
shared_types.rs

1use std::fmt;
2
3use serde::{Deserialize, Serialize};
4use time::UtcDateTime;
5
6/// Unix epoch timestamp in milliseconds.
7pub type Timestamp = i64;
8
9/// Returns the current time as Unix epoch milliseconds.
10pub fn now_millis() -> Timestamp {
11    let now = UtcDateTime::now();
12    now.unix_timestamp() * 1000 + i64::from(now.millisecond())
13}
14
15/// Final status of an update execution.
16///
17/// # Wire forward-compatibility
18///
19/// `Other(String)` is a catch-all for status strings received from a newer
20/// agent that this build does not yet recognise. Serde deserialization is
21/// infallible: an unknown string becomes `Other(...)` rather than a parse
22/// error, allowing older controllers to survive rolling upgrades without
23/// dropping the enclosing `UpdateResult` message.
24#[non_exhaustive]
25#[derive(Debug, Clone, PartialEq, Eq)]
26pub enum UpdateFinalStatus {
27    Completed,
28    Failed,
29    /// An unknown status received from a newer peer.
30    ///
31    /// The inner string is the raw snake_case value as it appeared on the wire.
32    Other(String),
33}
34
35impl UpdateFinalStatus {
36    /// Returns the string representation.
37    ///
38    /// For [`UpdateFinalStatus::Other`], returns the inner string as-is.
39    pub fn as_str(&self) -> &str {
40        match self {
41            Self::Completed => "completed",
42            Self::Failed => "failed",
43            Self::Other(s) => s.as_str(),
44        }
45    }
46}
47
48impl fmt::Display for UpdateFinalStatus {
49    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50        f.write_str(self.as_str())
51    }
52}
53
54impl From<String> for UpdateFinalStatus {
55    fn from(s: String) -> Self {
56        match s.as_str() {
57            "completed" => Self::Completed,
58            "failed" => Self::Failed,
59            _ => Self::Other(s),
60        }
61    }
62}
63
64impl Serialize for UpdateFinalStatus {
65    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
66        serializer.serialize_str(self.as_str())
67    }
68}
69
70impl<'de> Deserialize<'de> for UpdateFinalStatus {
71    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
72        String::deserialize(deserializer).map(UpdateFinalStatus::from)
73    }
74}
75
76/// Default timeout for update execution (2 hours).
77pub const DEFAULT_UPDATE_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(7200);
78
79/// Default timeout for update execution.
80pub(crate) fn default_update_timeout() -> std::time::Duration {
81    DEFAULT_UPDATE_TIMEOUT
82}
83
84/// Reason for service disconnection.
85///
86/// # Wire forward-compatibility
87///
88/// `Other(String)` is a catch-all for reason strings received from a newer
89/// peer that this build does not yet recognise. Serde deserialization is
90/// infallible: an unknown string becomes `Other(...)` rather than a parse
91/// error, allowing rolling upgrades without dropping the `Disconnecting` message.
92#[non_exhaustive]
93#[derive(Debug, Clone, PartialEq, Eq)]
94pub enum DisconnectReason {
95    /// SIGTERM/SIGINT - clean exit.
96    Shutdown,
97    /// SIGHUP - will reconnect after external restart.
98    Restart,
99    /// An unknown reason received from a newer peer.
100    ///
101    /// The inner string is the raw snake_case value as it appeared on the wire.
102    Other(String),
103}
104
105impl DisconnectReason {
106    /// Returns the string representation.
107    ///
108    /// For [`DisconnectReason::Other`], returns the inner string as-is.
109    pub fn as_str(&self) -> &str {
110        match self {
111            Self::Shutdown => "shutdown",
112            Self::Restart => "restart",
113            Self::Other(s) => s.as_str(),
114        }
115    }
116}
117
118impl fmt::Display for DisconnectReason {
119    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120        f.write_str(self.as_str())
121    }
122}
123
124impl From<String> for DisconnectReason {
125    fn from(s: String) -> Self {
126        match s.as_str() {
127            "shutdown" => Self::Shutdown,
128            "restart" => Self::Restart,
129            _ => Self::Other(s),
130        }
131    }
132}
133
134impl Serialize for DisconnectReason {
135    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
136        serializer.serialize_str(self.as_str())
137    }
138}
139
140impl<'de> Deserialize<'de> for DisconnectReason {
141    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
142        String::deserialize(deserializer).map(DisconnectReason::from)
143    }
144}