HIGH: fix watch_medium data-loss bug, standardize error handling on ApiError, fix dep direction (rss/template-askama no longer dep on application), extract ImageFetcher port (remove reqwest from app layer), move event construction from save_review to use case, extract infra-wiring crate (DbPool/EventBusBackend dedup), deduplicate presentation helpers (encode_error, export streaming, multipart parsing) MEDIUM: split LocalApContentQuery god-trait 10→3 methods, dedup movie resolution orchestration, add RemoteActorDto/PersonDto mappers, move AppConfig to infra-wiring, fix SocialQueryPort Uuid→UserId, replace stringly-typed api-types with domain enums, move count_reviews_in_year to StatsRepository, dedup event publisher cfg blocks, extract should_enrich, move group_by_month to application, dedup count_local_posts, add FederationFlags Default, TUI input helper + ShowError rename + typed auth errors, api-types cleanup (UserSettingsDto/UserProfileBase/PreviewRowData) 102 files changed, -681 lines net
73 lines
2.1 KiB
Rust
73 lines
2.1 KiB
Rust
mod config;
|
|
pub use config::PosterFetcherConfig;
|
|
|
|
use std::time::Duration;
|
|
|
|
use async_trait::async_trait;
|
|
use domain::{
|
|
errors::DomainError,
|
|
ports::{ImageFetcher, PosterFetcherClient},
|
|
value_objects::PosterUrl,
|
|
};
|
|
|
|
pub struct ReqwestPosterFetcher {
|
|
client: reqwest::Client,
|
|
}
|
|
|
|
impl ReqwestPosterFetcher {
|
|
pub fn new(config: PosterFetcherConfig) -> anyhow::Result<Self> {
|
|
let client = reqwest::Client::builder()
|
|
.timeout(Duration::from_secs(config.timeout_seconds))
|
|
.build()?;
|
|
Ok(Self { client })
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl PosterFetcherClient for ReqwestPosterFetcher {
|
|
async fn fetch_poster_bytes(&self, poster_url: &PosterUrl) -> Result<Vec<u8>, DomainError> {
|
|
let bytes = self
|
|
.client
|
|
.get(poster_url.value())
|
|
.send()
|
|
.await
|
|
.map_err(|e| DomainError::InfrastructureError(e.to_string()))?
|
|
.error_for_status()
|
|
.map_err(|e| DomainError::InfrastructureError(e.to_string()))?
|
|
.bytes()
|
|
.await
|
|
.map_err(|e| DomainError::InfrastructureError(e.to_string()))?;
|
|
Ok(bytes.to_vec())
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl ImageFetcher for ReqwestPosterFetcher {
|
|
async fn fetch_image(&self, url: &str) -> Result<Vec<u8>, DomainError> {
|
|
let bytes = self
|
|
.client
|
|
.get(url)
|
|
.send()
|
|
.await
|
|
.map_err(|e| DomainError::InfrastructureError(e.to_string()))?
|
|
.error_for_status()
|
|
.map_err(|e| DomainError::InfrastructureError(e.to_string()))?
|
|
.bytes()
|
|
.await
|
|
.map_err(|e| DomainError::InfrastructureError(e.to_string()))?;
|
|
Ok(bytes.to_vec())
|
|
}
|
|
}
|
|
|
|
pub fn create() -> anyhow::Result<std::sync::Arc<dyn domain::ports::PosterFetcherClient>> {
|
|
Ok(std::sync::Arc::new(ReqwestPosterFetcher::new(
|
|
PosterFetcherConfig::from_env(),
|
|
)?))
|
|
}
|
|
|
|
pub fn create_image_fetcher() -> anyhow::Result<std::sync::Arc<dyn domain::ports::ImageFetcher>> {
|
|
Ok(std::sync::Arc::new(ReqwestPosterFetcher::new(
|
|
PosterFetcherConfig::from_env(),
|
|
)?))
|
|
}
|