phytrace_sdk/transport/
traits.rs1use async_trait::async_trait;
6use std::fmt::Debug;
7
8use crate::error::PhyTraceResult;
9use crate::models::event::UdmEvent;
10
11#[async_trait]
19pub trait Transport: Send + Sync + Debug {
20 async fn send(&self, event: &UdmEvent) -> PhyTraceResult<SendResult>;
22
23 async fn send_batch(&self, events: &[UdmEvent]) -> PhyTraceResult<BatchResult>;
25
26 async fn is_connected(&self) -> bool;
28
29 async fn connect(&mut self) -> PhyTraceResult<()>;
31
32 async fn disconnect(&mut self) -> PhyTraceResult<()>;
34
35 fn stats(&self) -> TransportStats;
37
38 fn name(&self) -> &str;
40}
41
42#[derive(Debug, Clone)]
44pub struct SendResult {
45 pub success: bool,
47
48 pub status_code: Option<u16>,
50
51 pub message: Option<String>,
53
54 pub latency_ms: u64,
56
57 pub accepted: bool,
59}
60
61impl SendResult {
62 pub fn success(latency_ms: u64) -> Self {
64 Self {
65 success: true,
66 status_code: Some(200),
67 message: None,
68 latency_ms,
69 accepted: true,
70 }
71 }
72
73 pub fn failure(status_code: Option<u16>, message: impl Into<String>) -> Self {
75 Self {
76 success: false,
77 status_code,
78 message: Some(message.into()),
79 latency_ms: 0,
80 accepted: false,
81 }
82 }
83
84 pub fn is_retryable(&self) -> bool {
86 match self.status_code {
87 Some(code) => {
88 code >= 500 || code == 429 || code == 408
90 }
91 None => true, }
93 }
94}
95
96#[derive(Debug, Clone)]
98pub struct BatchResult {
99 pub total: usize,
101
102 pub succeeded: usize,
104
105 pub failed: usize,
107
108 pub latency_ms: u64,
110
111 pub individual_results: Option<Vec<SendResult>>,
113}
114
115impl BatchResult {
116 pub fn all_success(total: usize, latency_ms: u64) -> Self {
118 Self {
119 total,
120 succeeded: total,
121 failed: 0,
122 latency_ms,
123 individual_results: None,
124 }
125 }
126
127 pub fn all_failed(total: usize, _message: &str) -> Self {
129 Self {
130 total,
131 succeeded: 0,
132 failed: total,
133 latency_ms: 0,
134 individual_results: None,
135 }
136 }
137
138 pub fn all_succeeded(&self) -> bool {
140 self.succeeded == self.total
141 }
142
143 pub fn all_failed_check(&self) -> bool {
145 self.failed == self.total
146 }
147
148 pub fn success_rate(&self) -> f64 {
150 if self.total == 0 {
151 100.0
152 } else {
153 (self.succeeded as f64 / self.total as f64) * 100.0
154 }
155 }
156}
157
158#[derive(Debug, Clone, Default)]
160pub struct TransportStats {
161 pub events_sent: u64,
163
164 pub events_succeeded: u64,
166
167 pub events_failed: u64,
169
170 pub bytes_sent: u64,
172
173 pub total_latency_ms: u64,
175
176 pub retries: u64,
178
179 pub connections: u64,
181
182 pub connection_errors: u64,
184}
185
186impl TransportStats {
187 pub fn average_latency_ms(&self) -> f64 {
189 if self.events_succeeded == 0 {
190 0.0
191 } else {
192 self.total_latency_ms as f64 / self.events_succeeded as f64
193 }
194 }
195
196 pub fn success_rate(&self) -> f64 {
198 if self.events_sent == 0 {
199 100.0
200 } else {
201 (self.events_succeeded as f64 / self.events_sent as f64) * 100.0
202 }
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209
210 #[test]
211 fn test_send_result() {
212 let success = SendResult::success(50);
213 assert!(success.success);
214 assert!(success.accepted);
215
216 let failure = SendResult::failure(Some(500), "Server error");
217 assert!(!failure.success);
218 assert!(failure.is_retryable());
219
220 let not_retryable = SendResult::failure(Some(400), "Bad request");
221 assert!(!not_retryable.is_retryable());
222 }
223
224 #[test]
225 fn test_batch_result() {
226 let result = BatchResult::all_success(10, 100);
227 assert!(result.all_succeeded());
228 assert_eq!(result.success_rate(), 100.0);
229
230 let partial = BatchResult {
231 total: 10,
232 succeeded: 7,
233 failed: 3,
234 latency_ms: 150,
235 individual_results: None,
236 };
237 assert!(!partial.all_succeeded());
238 assert_eq!(partial.success_rate(), 70.0);
239 }
240
241 #[test]
242 fn test_transport_stats() {
243 let stats = TransportStats {
244 events_sent: 100,
245 events_succeeded: 95,
246 events_failed: 5,
247 total_latency_ms: 4750,
248 ..Default::default()
249 };
250
251 assert_eq!(stats.average_latency_ms(), 50.0);
252 assert_eq!(stats.success_rate(), 95.0);
253 }
254}