use std::path::PathBuf;
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum FetchError {
#[error("hf-hub API error: {0}")]
Api(#[from] hf_hub::api::tokio::ApiError),
#[error("I/O error at {path}: {source}")]
Io {
path: PathBuf,
source: std::io::Error,
},
#[error("repository not found: {repo_id}")]
RepoNotFound {
repo_id: String,
},
#[error("authentication failed: {reason}")]
Auth {
reason: String,
},
#[error("invalid glob pattern: {pattern}: {reason}")]
InvalidPattern {
pattern: String,
reason: String,
},
#[error("checksum mismatch for {filename}: expected {expected}, got {actual}")]
Checksum {
filename: String,
expected: String,
actual: String,
},
#[error("timeout downloading {filename} after {seconds}s")]
Timeout {
filename: String,
seconds: u64,
},
#[error("{} file(s) failed to download:{}", failures.len(), format_failures(failures))]
PartialDownload {
path: Option<PathBuf>,
failures: Vec<FileFailure>,
},
#[error("chunked download failed for {filename}: {reason}")]
ChunkedDownload {
filename: String,
reason: String,
},
#[error("HTTP error: {0}")]
Http(String),
#[error("{0}")]
InvalidArgument(String),
#[error("no files matched in repository {repo_id}")]
NoFilesMatched {
repo_id: String,
},
#[error("safetensors header error for {filename}: {reason}")]
SafetensorsHeader {
filename: String,
reason: String,
},
}
#[derive(Debug, Clone)]
pub struct FileFailure {
pub filename: String,
pub reason: String,
pub retryable: bool,
}
impl std::fmt::Display for FileFailure {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}: {} (retryable: {})",
self.filename, self.reason, self.retryable
)
}
}
fn format_failures(failures: &[FileFailure]) -> String {
let mut s = String::new();
for f in failures {
s.push_str("\n - ");
s.push_str(f.filename.as_str());
s.push_str(": ");
s.push_str(f.reason.as_str());
}
s
}