phytrace_sdk/transport/
traits.rs

1//! Transport trait definitions.
2//!
3//! Defines the common interface for all transport implementations.
4
5use async_trait::async_trait;
6use std::fmt::Debug;
7
8use crate::error::PhyTraceResult;
9use crate::models::event::UdmEvent;
10
11/// Transport layer trait for sending events.
12///
13/// Implementations should handle:
14/// - Connection management
15/// - Authentication
16/// - Serialization
17/// - Error handling and retries (optional, can be handled by reliability layer)
18#[async_trait]
19pub trait Transport: Send + Sync + Debug {
20    /// Send a single event.
21    async fn send(&self, event: &UdmEvent) -> PhyTraceResult<SendResult>;
22
23    /// Send a batch of events.
24    async fn send_batch(&self, events: &[UdmEvent]) -> PhyTraceResult<BatchResult>;
25
26    /// Check if the transport is connected/ready.
27    async fn is_connected(&self) -> bool;
28
29    /// Connect to the remote endpoint.
30    async fn connect(&mut self) -> PhyTraceResult<()>;
31
32    /// Disconnect from the remote endpoint.
33    async fn disconnect(&mut self) -> PhyTraceResult<()>;
34
35    /// Get transport statistics.
36    fn stats(&self) -> TransportStats;
37
38    /// Get transport name for logging.
39    fn name(&self) -> &str;
40}
41
42/// Result of sending a single event.
43#[derive(Debug, Clone)]
44pub struct SendResult {
45    /// Whether the send was successful.
46    pub success: bool,
47
48    /// HTTP status code (if applicable).
49    pub status_code: Option<u16>,
50
51    /// Response message.
52    pub message: Option<String>,
53
54    /// Time taken in milliseconds.
55    pub latency_ms: u64,
56
57    /// Whether the event was accepted by the server.
58    pub accepted: bool,
59}
60
61impl SendResult {
62    /// Create a successful result.
63    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    /// Create a failed result.
74    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    /// Check if the error is retryable.
85    pub fn is_retryable(&self) -> bool {
86        match self.status_code {
87            Some(code) => {
88                // Retry on server errors (5xx) and some client errors
89                code >= 500 || code == 429 || code == 408
90            }
91            None => true, // Network errors are typically retryable
92        }
93    }
94}
95
96/// Result of sending a batch of events.
97#[derive(Debug, Clone)]
98pub struct BatchResult {
99    /// Total events in batch.
100    pub total: usize,
101
102    /// Successfully sent events.
103    pub succeeded: usize,
104
105    /// Failed events.
106    pub failed: usize,
107
108    /// Time taken in milliseconds.
109    pub latency_ms: u64,
110
111    /// Individual results (if available).
112    pub individual_results: Option<Vec<SendResult>>,
113}
114
115impl BatchResult {
116    /// Create a fully successful batch result.
117    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    /// Create a fully failed batch result.
128    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    /// Check if all events succeeded.
139    pub fn all_succeeded(&self) -> bool {
140        self.succeeded == self.total
141    }
142
143    /// Check if all events failed.
144    pub fn all_failed_check(&self) -> bool {
145        self.failed == self.total
146    }
147
148    /// Get success rate as a percentage.
149    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/// Transport statistics.
159#[derive(Debug, Clone, Default)]
160pub struct TransportStats {
161    /// Total events sent.
162    pub events_sent: u64,
163
164    /// Successful sends.
165    pub events_succeeded: u64,
166
167    /// Failed sends.
168    pub events_failed: u64,
169
170    /// Bytes sent.
171    pub bytes_sent: u64,
172
173    /// Total latency in milliseconds.
174    pub total_latency_ms: u64,
175
176    /// Number of retries.
177    pub retries: u64,
178
179    /// Number of connections made.
180    pub connections: u64,
181
182    /// Number of connection errors.
183    pub connection_errors: u64,
184}
185
186impl TransportStats {
187    /// Calculate average latency.
188    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    /// Calculate success rate.
197    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}