use super::get_client_builder;
use std::time::Duration;
use futures::future::{ready, TryFutureExt};
use serde::Deserialize;
use url::Url;
use crate::{Error, Result, services::which_service};
#[derive(Debug, Deserialize)]
struct DrApiResp {
url: Option<String>,
}
fn extract_slug(u: &str) -> Option<String> {
let parsed = Url::parse(u).ok()?;
let host = parsed.host_str()?.to_ascii_lowercase();
let segments: Vec<&str> = parsed
.path_segments()
.map(|it| it.filter(|s| !s.is_empty()).collect())
.unwrap_or_else(Vec::new);
if segments.is_empty() {
return None;
}
if host == "encurtador.dev" || host == "www.encurtador.dev" {
if let Some(i) = segments.iter().position(|s| *s == "redirecionamento") {
return segments.get(i + 1..)?.last().map(|s| (*s).to_string());
}
return None;
}
if which_service(u).is_some() {
return segments.last().map(|s| (*s).to_string());
}
None
}
async fn resolve_via_dr_api(
client_no_redirect: &reqwest::Client,
slug: &str,
) -> Result<String> {
let api_url = format!("https://dr-api.encurtador.dev/encurtamentos/{}", slug);
let resp = client_no_redirect
.get(&api_url)
.header(reqwest::header::ACCEPT, "application/json,*/*")
.send()
.await?;
if resp.status().is_redirection() {
if let Some(loc) = resp.headers().get(reqwest::header::LOCATION) {
let s = loc.to_str().unwrap_or("").trim();
if !s.is_empty() {
return Ok(s.to_string());
}
}
}
if resp.status().is_success() {
let data: DrApiResp = resp.json().await?;
if let Some(u) = data.url {
let u = u.trim().to_string();
if !u.is_empty() {
return Ok(u);
}
}
}
Err(Error::Reqwest("dr-api could not resolve slug".to_string()))
}
pub(crate) async fn unshort(url: &str, timeout: Option<Duration>) -> Result<String> {
ready(get_client_builder(timeout).build())
.map_err(Error::from)
.and_then(|client| async move {
let client_no_redirect = get_client_builder(timeout)
.redirect(reqwest::redirect::Policy::none())
.build()
.map_err(Error::from)?;
let resp = client.get(url).send().await.map_err(Error::from)?;
let final_url = resp.url().to_string();
let slug = extract_slug(&final_url)
.or_else(|| extract_slug(url))
.ok_or(Error::NoString)?;
let resolved = resolve_via_dr_api(&client_no_redirect, &slug)
.await
.map_err(Error::from)?;
Ok(resolved)
})
.await
}