1mod encoding;
2
3use serde::{Deserialize, Serialize};
4use std::fmt::Display;
5
6#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
8pub struct HeaderPackage {
9 pub bytes: usize,
10}
11
12impl Into<Vec<u8>> for HeaderPackage {
13 fn into(self) -> Vec<u8> {
14 match encoding::serialize(&self) {
15 Ok(bytes) => {
16 let mut padded = vec![0u8; 8];
17 padded[..bytes.len()].copy_from_slice(&bytes);
18 padded
19 },
20 Err(err) => {
21 eprintln!("could not serialize {self:?}\nerror = {err}");
22 vec![]
23 }
24 }
25 }
26}
27
28impl HeaderPackage {
29 pub fn try_parse(bytes: &Vec<u8>) -> Option<Self> {
30 encoding::deserialize(bytes).ok()
31 }
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
36pub struct RequestPackage {
37 pub command: Command,
39}
40
41impl Into<Vec<u8>> for RequestPackage {
42 fn into(self) -> Vec<u8> {
43 match encoding::serialize(&self) {
44 Ok(bytes) => bytes,
45 Err(err) => {
46 eprintln!("could not serialize {self:?}\nerror = {err}");
47 vec![]
48 }
49 }
50 }
51}
52
53impl From<Vec<u8>> for RequestPackage {
54 fn from(bytes: Vec<u8>) -> Self {
55 match encoding::deserialize::<RequestPackage>(&bytes) {
56 Ok(value) => value,
57 Err(err) => {
58 eprintln!("could not deserialize bytes to RequestPackage\nerror = {err}");
59 RequestPackage { command: Command::NoOp }
60 }
61 }
62 }
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
67pub struct ResponsePackage {
68 pub status: Status,
70}
71
72impl Into<Vec<u8>> for ResponsePackage {
73 fn into(self) -> Vec<u8> {
74 match encoding::serialize(&self) {
75 Ok(bytes) => bytes,
76 Err(err) => {
77 eprintln!("could not serialize {self:?}\nerror = {err}");
78 vec![]
79 }
80 }
81 }
82}
83
84impl From<Vec<u8>> for ResponsePackage {
85 fn from(bytes: Vec<u8>) -> Self {
86 match encoding::deserialize::<ResponsePackage>(&bytes) {
87 Ok(value) => value,
88 Err(err) => {
89 eprintln!("could not deserialize bytes to ResponsePackage\nerror = {err}");
90 ResponsePackage {
91 status: Status::ParseErr,
92 }
93 }
94 }
95 }
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
99pub enum Status {
100 Ok,
102
103 NoCmd,
106
107 Err(String),
109
110 ParseErr,
112
113 AuthErr,
115
116 RateLimit,
118
119 TooLarge,
121
122 Blocked,
125}
126
127impl Display for Status {
128 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129 write!(f, "{self:?}")
130 }
131}
132
133#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
134pub enum Command {
135 NoOp,
137
138 Login(AuthToken),
140
141 Log(LogMessage),
143}
144
145pub type AuthToken = String;
147
148#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
149pub struct LogMessage {
150 pub service: String,
152
153 pub target: String,
155
156 pub level: String,
158
159 pub timestamp: String,
161
162 pub source: String,
164
165 pub message: String,
167}
168
169impl LogMessage {
170 #[cfg(all(feature = "chrono", feature = "log"))]
174 pub fn from_log_record(record: &log::Record, service: &str) -> Self {
175 LogMessage {
176 service: service.to_owned(),
177 target: record.target().to_string(),
178 level: record.level().to_string(),
179 timestamp: chrono::Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Millis, true),
180 source: format!("{}:{}", record.file().unwrap_or("-"), record.line().unwrap_or(0)),
181 message: record.args().to_string(),
182 }
183 }
184}
185
186impl Display for LogMessage {
187 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
188 write!(
189 f,
190 "{ts} {lvl} [{srv}] ({trg}@{src}) {msg}",
191 ts = self.timestamp,
192 srv = self.service,
193 lvl = self.level,
194 trg = self.target,
195 src = self.source,
196 msg = self.message
197 )
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 use super::*;
204
205 #[test]
206 #[cfg(all(feature = "chrono", feature = "log"))]
207 fn test_from_log_record_log_message() {
208 let service = "service";
209 let target = "target";
210 let level = "DEBUG";
211 let source_file = file!();
212 let source_line = line!();
213 let source = format!("{source_file}:{source_line}");
214
215 let record = log::Record::builder()
216 .target(target)
217 .level(log::Level::Debug)
218 .file(Some(source_file))
219 .line(Some(source_line))
220 .args(format_args!("foo bar baz"))
221 .build();
222
223 let actual = LogMessage::from_log_record(&record, service);
224
225 assert_eq!(service, actual.service);
226 assert_eq!(target, actual.target);
227 assert_eq!(level, actual.level);
228 assert_eq!(source, actual.source);
229 assert_eq!("foo bar baz", actual.message);
230 }
231
232 #[test]
233 fn test_display_log_message() {
234 let actual: String = format!(
235 "{}",
236 LogMessage {
237 service: "service".to_string(),
238 target: "target".to_string(),
239 level: "level".to_string(),
240 timestamp: "timestamp".to_string(),
241 source: "source".to_string(),
242 message: "message".to_string(),
243 }
244 );
245
246 assert!(actual.is_ascii());
247 }
248
249 #[test]
250 fn test_serde_request_package() {
251 let expected = RequestPackage {
252 command: Command::Log(LogMessage {
253 service: "service".to_string(),
254 target: "target".to_string(),
255 level: "level".to_string(),
256 timestamp: "timestamp".to_string(),
257 source: "source".to_string(),
258 message: "message".to_string(),
259 }),
260 };
261 let encoded: Vec<u8> = expected.clone().into();
262 let actual: RequestPackage = encoded.into();
263 assert_eq!(expected, actual);
264 }
265
266 #[test]
267 fn test_serde_response_package() {
268 let expected = ResponsePackage {
269 status: Status::Err("unknown exception".to_string()),
270 };
271 let encoded: Vec<u8> = expected.clone().into();
272 let actual: ResponsePackage = encoded.into();
273 assert_eq!(expected, actual);
274 }
275}