use async_trait::async_trait;
use serde::Deserialize;
use super::{Provider, ServerInfo};
pub struct LibrespeedProvider {
servers: Vec<LibrespeedServer>,
selected_server: Option<LibrespeedServer>,
}
#[derive(Debug, Clone, Deserialize)]
#[allow(dead_code)]
struct LibrespeedServer {
id: String,
name: String,
server: String,
#[serde(rename = "dlURL")]
dl_url: String,
#[serde(rename = "ulURL")]
ul_url: String,
#[serde(rename = "pingURL")]
ping_url: String,
#[serde(rename = "getIpURL")]
get_ip_url: Option<String>,
}
impl LibrespeedProvider {
pub fn new() -> Self {
Self {
servers: get_default_servers(),
selected_server: None,
}
}
#[allow(dead_code)]
pub fn with_server(server_url: &str) -> Self {
let server = LibrespeedServer {
id: "custom".to_string(),
name: "Custom Server".to_string(),
server: server_url.to_string(),
dl_url: format!("{}/garbage.php", server_url),
ul_url: format!("{}/empty.php", server_url),
ping_url: format!("{}/empty.php", server_url),
get_ip_url: Some(format!("{}/getIP.php", server_url)),
};
Self {
servers: vec![server.clone()],
selected_server: Some(server),
}
}
}
impl Default for LibrespeedProvider {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl Provider for LibrespeedProvider {
fn name(&self) -> &str {
"LibreSpeed"
}
fn get_download_url(&self) -> String {
self.selected_server
.as_ref()
.map(|s| s.dl_url.clone())
.unwrap_or_else(|| self.servers[0].dl_url.clone())
}
fn get_upload_url(&self) -> String {
self.selected_server
.as_ref()
.map(|s| s.ul_url.clone())
.unwrap_or_else(|| self.servers[0].ul_url.clone())
}
fn get_ping_url(&self) -> String {
self.selected_server
.as_ref()
.map(|s| s.ping_url.clone())
.unwrap_or_else(|| self.servers[0].ping_url.clone())
}
async fn get_servers(&self) -> anyhow::Result<Vec<ServerInfo>> {
Ok(self
.servers
.iter()
.map(|s| ServerInfo {
id: s.id.clone(),
name: s.name.clone(),
location: s.name.clone(),
country: "".to_string(),
host: s.server.clone(),
latency_ms: None,
})
.collect())
}
async fn select_best_server(&self) -> anyhow::Result<Option<ServerInfo>> {
let mut best_server: Option<(ServerInfo, f64)> = None;
for server in &self.servers {
if let Ok(latency) = crate::network::ping::single_ping(&server.ping_url).await {
let server_info = ServerInfo {
id: server.id.clone(),
name: server.name.clone(),
location: server.name.clone(),
country: "".to_string(),
host: server.server.clone(),
latency_ms: Some(latency),
};
match &best_server {
None => best_server = Some((server_info, latency)),
Some((_, best_latency)) if latency < *best_latency => {
best_server = Some((server_info, latency));
}
_ => {}
}
}
}
Ok(best_server.map(|(s, _)| s))
}
}
fn get_default_servers() -> Vec<LibrespeedServer> {
vec![LibrespeedServer {
id: "librespeed-backend".to_string(),
name: "LibreSpeed Backend".to_string(),
server: "https://librespeed.org".to_string(),
dl_url: "https://librespeed.org/backend/garbage.php".to_string(),
ul_url: "https://librespeed.org/backend/empty.php".to_string(),
ping_url: "https://librespeed.org/backend/empty.php".to_string(),
get_ip_url: Some("https://librespeed.org/backend/getIP.php".to_string()),
}]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_librespeed_provider() {
let provider = LibrespeedProvider::new();
assert!(!provider.get_download_url().is_empty());
assert!(!provider.get_upload_url().is_empty());
}
#[tokio::test]
async fn test_get_servers() {
let provider = LibrespeedProvider::new();
let servers = provider.get_servers().await.unwrap();
assert!(!servers.is_empty());
}
}