use crate::error::{FreesoundError, Result};
use crate::models::{SearchResponse, Sound};
use reqwest;
pub const DEFAULT_BASE_URL: &str = "https://freesound.org/apiv2";
#[derive(Debug, Clone)]
pub struct FreesoundClient {
client: reqwest::Client,
api_key: String,
base_url: String,
}
impl FreesoundClient {
pub fn new(api_key: String, base_url: Option<String>) -> Self {
Self {
client: reqwest::Client::new(),
api_key,
base_url: base_url.unwrap_or_else(|| DEFAULT_BASE_URL.to_string()),
}
}
pub fn api_key(&self) -> &str {
&self.api_key
}
pub fn base_url(&self) -> &str {
&self.base_url
}
pub fn request(&self, method: reqwest::Method, path: &str) -> reqwest::RequestBuilder {
let url = format!("{}/{}", self.base_url, path.trim_start_matches('/'));
self.client
.request(method, url)
.query(&[("token", &self.api_key)])
}
pub async fn test_api_key(&self) -> Result<()> {
let response = self
.request(reqwest::Method::GET, "sounds/794253")
.send()
.await
.map_err(FreesoundError::from)?;
let status = response.status();
if status == reqwest::StatusCode::UNAUTHORIZED {
return Err(FreesoundError::AuthError("Invalid API key".to_string()));
}
if !status.is_success() {
let body = response.text().await.map_err(FreesoundError::from)?;
return Err(FreesoundError::ApiError(format!(
"API request failed: {status} - {body}"
)));
}
let body = response.text().await.map_err(FreesoundError::from)?;
match serde_json::from_str::<serde_json::Value>(&body) {
Ok(json) => {
if json.get("id").is_some() {
Ok(())
} else {
Err(FreesoundError::ApiError(format!(
"Response missing sound ID: {body}"
)))
}
}
Err(e) => Err(FreesoundError::ApiError(format!(
"Invalid JSON response: {e} - {body}"
))),
}
}
pub async fn search(&self, query: &[(String, String)]) -> Result<SearchResponse> {
let response = self
.request(reqwest::Method::GET, "search/text")
.query(query)
.send()
.await
.map_err(FreesoundError::from)?;
let status = response.status();
if !status.is_success() {
let body = response.text().await.map_err(FreesoundError::from)?;
return Err(FreesoundError::ApiError(format!(
"API request failed: {status} - {body}"
)));
}
response
.json::<SearchResponse>()
.await
.map_err(FreesoundError::from)
}
pub async fn get_sound(
&self,
sound_id: i32,
descriptors: Option<&[&str]>,
normalized: Option<bool>,
) -> Result<Sound> {
let mut request = self.request(reqwest::Method::GET, &format!("sounds/{}", sound_id));
if let Some(desc) = descriptors {
request = request.query(&[("descriptors", desc.join(","))]);
}
if let Some(norm) = normalized {
request = request.query(&[("normalized", if norm { "1" } else { "0" })]);
}
let response = request.send().await.map_err(FreesoundError::from)?;
let status = response.status();
if !status.is_success() {
let body = response.text().await.map_err(FreesoundError::from)?;
return Err(FreesoundError::ApiError(format!(
"API request failed: {status} - {body}"
)));
}
response.json::<Sound>().await.map_err(FreesoundError::from)
}
}