1use crate::grades;
7use crate::profiles::UserProfile;
8use crate::task_runner::TestRunResult;
9use crate::types::{ClientLocation, PhaseResult, ServerInfo, TestPhases, TestResult};
10
11pub type Report = TestResult;
13
14pub struct TestResultBuilder {
16 server: ServerInfo,
17 ping: Option<f64>,
18 jitter: Option<f64>,
19 packet_loss: Option<f64>,
20 ping_samples: Vec<f64>,
21 download_result: Option<TestRunResult>,
22 upload_result: Option<TestRunResult>,
23 client_ip: Option<String>,
24 client_location: Option<ClientLocation>,
25}
26
27impl TestResultBuilder {
28 pub fn new(server: ServerInfo) -> Self {
29 Self {
30 server,
31 ping: None,
32 jitter: None,
33 packet_loss: None,
34 ping_samples: Vec::new(),
35 download_result: None,
36 upload_result: None,
37 client_ip: None,
38 client_location: None,
39 }
40 }
41
42 pub fn with_ping(
43 mut self,
44 latency: f64,
45 jitter: f64,
46 packet_loss: f64,
47 samples: Vec<f64>,
48 ) -> Self {
49 self.ping = Some(latency);
50 self.jitter = Some(jitter);
51 self.packet_loss = Some(packet_loss);
52 self.ping_samples = samples;
53 self
54 }
55
56 pub fn with_download(mut self, result: TestRunResult) -> Self {
57 self.download_result = Some(result);
58 self
59 }
60
61 pub fn with_upload(mut self, result: TestRunResult) -> Self {
62 self.upload_result = Some(result);
63 self
64 }
65
66 pub fn with_client_ip(mut self, ip: String) -> Self {
67 self.client_ip = Some(ip);
68 self
69 }
70
71 pub fn with_client_location(mut self, location: ClientLocation) -> Self {
72 self.client_location = Some(location);
73 self
74 }
75
76 pub fn build(self) -> TestResult {
77 let default_dl = TestRunResult::default();
78 let default_ul = TestRunResult::default();
79 let dl = self.download_result.as_ref().unwrap_or(&default_dl);
80 let ul = self.upload_result.as_ref().unwrap_or(&default_ul);
81
82 TestResult::from_test_runs(
83 self.server,
84 self.ping,
85 self.jitter,
86 self.packet_loss,
87 &self.ping_samples,
88 dl,
89 ul,
90 self.client_ip,
91 self.client_location,
92 )
93 }
94}
95
96impl Default for TestResult {
97 fn default() -> Self {
98 Self {
99 status: String::new(),
100 version: String::new(),
101 test_id: None,
102 server: ServerInfo {
103 id: String::new(),
104 name: String::new(),
105 sponsor: String::new(),
106 country: String::new(),
107 distance: 0.0,
108 },
109 ping: None,
110 jitter: None,
111 packet_loss: None,
112 download: None,
113 download_peak: None,
114 download_cv: None,
115 upload: None,
116 upload_peak: None,
117 upload_cv: None,
118 download_ci_95: None,
119 upload_ci_95: None,
120 latency_download: None,
121 latency_upload: None,
122 download_samples: None,
123 upload_samples: None,
124 ping_samples: None,
125 timestamp: String::new(),
126 client_ip: None,
127 client_location: None,
128 overall_grade: None,
129 download_grade: None,
130 upload_grade: None,
131 connection_rating: None,
132 phases: TestPhases {
133 ping: PhaseResult::completed(),
134 download: PhaseResult::completed(),
135 upload: PhaseResult::completed(),
136 },
137 }
138 }
139}
140
141pub fn compute_overall_grade(
142 ping: Option<f64>,
143 jitter: Option<f64>,
144 download: Option<f64>,
145 upload: Option<f64>,
146 profile: UserProfile,
147) -> String {
148 grades::grade_overall(ping, jitter, download, upload, profile)
149 .as_str()
150 .to_string()
151}
152
153pub fn compute_download_grade(download_mbps: f64, profile: UserProfile) -> String {
154 grades::grade_download(download_mbps, profile)
155 .as_str()
156 .to_string()
157}
158
159pub fn compute_upload_grade(upload_mbps: f64, profile: UserProfile) -> String {
160 grades::grade_upload(upload_mbps, profile)
161 .as_str()
162 .to_string()
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168
169 #[test]
170 fn test_result_builder_default() {
171 let result = TestResult::default();
172 assert!(result.server.id.is_empty());
173 }
174
175 #[test]
176 fn test_result_builder_basic() {
177 let server = ServerInfo {
178 id: "123".to_string(),
179 name: "Test Server".to_string(),
180 sponsor: "Test ISP".to_string(),
181 country: "US".to_string(),
182 distance: 100.0,
183 };
184
185 let builder = TestResultBuilder::new(server);
186 let result = builder.build();
187
188 assert_eq!(result.server.id, "123");
189 assert_eq!(result.status, "ok");
190 }
191}