Skip to main content

netspeed_cli/
endpoints.rs

1//! Speedtest server endpoint derivation.
2//!
3//! The speedtest.net XML feed commonly publishes server URLs ending in
4//! `upload.php`. Runtime endpoints such as `latency.txt` and `random*.jpg`
5//! live in the same directory. This module centralizes that URL knowledge.
6
7/// Canonical endpoint set derived from a speedtest server URL.
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct ServerEndpoints {
10    raw: String,
11    base: String,
12    upload: String,
13}
14
15impl ServerEndpoints {
16    /// Derive endpoints from a speedtest server URL.
17    #[must_use]
18    pub fn from_server_url(url: &str) -> Self {
19        let trimmed = url.trim_end_matches('/');
20        let base = trimmed
21            .strip_suffix("/upload.php")
22            .or_else(|| trimmed.strip_suffix("/upload"))
23            .unwrap_or(trimmed)
24            .to_string();
25        let upload = if trimmed.ends_with("/upload.php") || trimmed.ends_with("/upload") {
26            trimmed.to_string()
27        } else {
28            format!("{trimmed}/upload.php")
29        };
30
31        Self {
32            raw: trimmed.to_string(),
33            base,
34            upload,
35        }
36    }
37
38    /// Original normalized server URL.
39    #[must_use]
40    pub fn raw(&self) -> &str {
41        &self.raw
42    }
43
44    /// Base directory containing the speedtest assets.
45    #[must_use]
46    pub fn base(&self) -> &str {
47        &self.base
48    }
49
50    /// Upload endpoint.
51    #[must_use]
52    pub fn upload(&self) -> &str {
53        &self.upload
54    }
55
56    /// Latency probe endpoint.
57    #[must_use]
58    pub fn latency(&self) -> String {
59        format!("{}/latency.txt", self.base)
60    }
61
62    /// Download asset endpoint for a specific test asset name.
63    #[must_use]
64    pub fn download_asset(&self, asset_name: &str) -> String {
65        format!("{}/{}", self.base, asset_name.trim_start_matches('/'))
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    use super::ServerEndpoints;
72
73    #[test]
74    fn test_from_upload_php_url() {
75        let endpoints = ServerEndpoints::from_server_url("http://example.com/speedtest/upload.php");
76        assert_eq!(endpoints.base(), "http://example.com/speedtest");
77        assert_eq!(
78            endpoints.upload(),
79            "http://example.com/speedtest/upload.php"
80        );
81        assert_eq!(
82            endpoints.latency(),
83            "http://example.com/speedtest/latency.txt"
84        );
85    }
86
87    #[test]
88    fn test_from_base_url() {
89        let endpoints = ServerEndpoints::from_server_url("http://example.com/speedtest");
90        assert_eq!(endpoints.base(), "http://example.com/speedtest");
91        assert_eq!(
92            endpoints.upload(),
93            "http://example.com/speedtest/upload.php"
94        );
95    }
96
97    #[test]
98    fn test_download_asset() {
99        let endpoints = ServerEndpoints::from_server_url("https://cdn.example.net/upload.php");
100        assert_eq!(
101            endpoints.download_asset("random3500x3500.jpg"),
102            "https://cdn.example.net/random3500x3500.jpg"
103        );
104    }
105}