use rand::Rng;
use crate::config::Config;
use crate::display::DisplayInfo;
use crate::{display, download};
const BASE_URL: &str = "https://wallhaven.cc/api/v1/search";
pub fn get_image_data(config: &Config, display_info: &DisplayInfo) -> Result<Vec<u8>, String> {
let image_url = get_image_url(config, display_info)?;
Ok(download::get_data(&image_url))
}
fn get_image_url(config: &Config, display_info: &DisplayInfo) -> Result<String, String> {
let request_url = build_request_url(config, display_info);
let json_string = download::get_string(&request_url);
let json_value: serde_json::Value = serde_json::from_str(json_string.as_str())
.map_err(|_| "Failed to parse Wallhaven API response".to_string())?;
if let Some(error) = json_value.get("error") {
return Err(format!(
"Wallhaven API error: {}",
error.as_str().unwrap_or("Unknown error")
));
}
let data_array = json_value
.get("data")
.ok_or("No 'data' field in Wallhaven response".to_string())?
.as_array()
.ok_or("'data' field is not an array".to_string())?;
if data_array.is_empty() {
return Err(format!(
"No wallpapers found for query '{}' with the specified resolution requirements. Try a different search term or check your internet connection.",
config.query
));
}
let images: Vec<&str> = data_array
.iter()
.filter_map(|item| item.get("path")?.as_str())
.collect();
if images.is_empty() {
return Err("No valid wallpaper URLs found in Wallhaven response".to_string());
}
let random_index = rand::rng().random_range(0..images.len());
Ok(images[random_index].to_string())
}
pub fn build_request_url(config: &Config, display_info: &DisplayInfo) -> String {
let target_width = if config.span {
display::get_width(&display_info.total_resolution)
} else {
display::get_width(&display_info.max_single_resolution)
};
let target_height = if config.span {
display::get_height(&display_info.total_resolution)
} else {
display::get_height(&display_info.max_single_resolution)
};
let mut request_url = BASE_URL.to_string();
let mut has_params = false;
if !config.query.is_empty() {
request_url.push_str("?q=");
request_url.push_str(&urlencoding::encode(&config.query));
has_params = true;
}
let separator = if has_params { "&" } else { "?" };
request_url.push_str(&format!(
"{}atleast={}x{}",
separator, target_width, target_height
));
request_url.push_str("&categories=111");
request_url.push_str("&purity=100");
request_url.push_str("&sorting=random");
if let Some(api_key) = &config.api_key {
request_url.push_str("&apikey=");
request_url.push_str(api_key);
}
request_url
}
#[cfg(test)]
pub fn parse_wallhaven_response(json_response: &str) -> Result<Vec<String>, String> {
let json_value: serde_json::Value = serde_json::from_str(json_response)
.map_err(|_| "Failed to parse JSON response".to_string())?;
if let Some(error) = json_value.get("error") {
return Err(format!(
"Wallhaven API error: {}",
error.as_str().unwrap_or("Unknown error")
));
}
let data_array = json_value
.get("data")
.ok_or("No 'data' field in response".to_string())?
.as_array()
.ok_or("'data' field is not an array".to_string())?;
if data_array.is_empty() {
return Err("No wallpapers found in response".to_string());
}
let images: Vec<String> = data_array
.iter()
.filter_map(|item| item.get("path")?.as_str().map(|s| s.to_string()))
.collect();
if images.is_empty() {
return Err("No valid wallpaper URLs found in response".to_string());
}
Ok(images)
}