use reqwest::StatusCode;
use typed_builder::TypedBuilder;
use crate::error::Error;
#[derive(Debug, Clone, PartialEq)]
pub enum UrlStatus {
Valid,
Expired(ExpiredReason),
Unknown,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ExpiredReason {
Forbidden,
NotFound,
Gone,
Unauthorized,
Other(String),
}
impl UrlStatus {
pub fn is_expired(&self) -> bool {
matches!(self, UrlStatus::Expired(_))
}
pub fn expired_reason(&self) -> Option<&ExpiredReason> {
match self {
UrlStatus::Expired(reason) => Some(reason),
_ => None,
}
}
}
impl std::fmt::Display for UrlStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Valid => f.write_str("Valid"),
Self::Expired(reason) => write!(f, "Expired(reason={})", reason),
Self::Unknown => f.write_str("Unknown"),
}
}
}
impl std::fmt::Display for ExpiredReason {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Forbidden => f.write_str("Forbidden"),
Self::NotFound => f.write_str("NotFound"),
Self::Gone => f.write_str("Gone"),
Self::Unauthorized => f.write_str("Unauthorized"),
Self::Other(msg) => write!(f, "Other(msg={})", msg),
}
}
}
pub fn check_http_status(status: StatusCode) -> UrlStatus {
let result = match status {
StatusCode::FORBIDDEN => UrlStatus::Expired(ExpiredReason::Forbidden),
StatusCode::NOT_FOUND => UrlStatus::Expired(ExpiredReason::NotFound),
StatusCode::GONE => UrlStatus::Expired(ExpiredReason::Gone),
StatusCode::UNAUTHORIZED => UrlStatus::Expired(ExpiredReason::Unauthorized),
_ if status.is_success() => UrlStatus::Valid,
_ => UrlStatus::Unknown,
};
tracing::debug!(status = status.as_u16(), result = %result, "⚙️ Checked HTTP status for URL expiry");
result
}
pub fn check_error(error: &reqwest::Error) -> UrlStatus {
if let Some(status) = error.status() {
check_http_status(status)
} else {
UrlStatus::Unknown
}
}
pub fn check_download_error(error: &Error) -> UrlStatus {
match error {
Error::Http { source, .. } => check_error(source),
_ => UrlStatus::Unknown,
}
}
pub fn should_refresh_url(error: &Error) -> bool {
let should_refresh = check_download_error(error).is_expired();
tracing::debug!(should_refresh = should_refresh, "⚙️ Checked if URL should be refreshed");
should_refresh
}
pub fn should_refresh_url_from_http_error(error: &reqwest::Error) -> bool {
check_error(error).is_expired()
}
#[derive(Debug, Clone, TypedBuilder)]
pub struct ExpiryConfig {
#[builder(default = 2)]
pub max_refresh_attempts: usize,
#[builder(default = true)]
pub auto_refresh: bool,
}
impl ExpiryConfig {
pub fn new() -> Self {
Self::default()
}
}
impl Default for ExpiryConfig {
fn default() -> Self {
Self {
max_refresh_attempts: 2,
auto_refresh: true,
}
}
}
impl std::fmt::Display for ExpiryConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"ExpiryConfig(max_refresh={}, auto={})",
self.max_refresh_attempts, self.auto_refresh
)
}
}