use std::{path::PathBuf, sync::Arc};
use axum::{
RequestExt, RequestPartsExt,
extract::{FromRequest, FromRequestParts, Multipart, Path, Query, Request},
};
use crate::{
error::ImageEndpointError,
get_image_options::{FolderName, GetImageOptions},
helpers::get_image_path_hash,
image_app_options::{MODIFIED, ORIGINAL},
image_app_state::ImageAppState,
multipart::ImageMultipart,
};
#[derive(Debug, Clone)]
pub struct Folder {
folder: String,
state: Arc<ImageAppState>,
}
impl Folder {
pub fn original_subfolder(&self) -> PathBuf {
self.state.original_image_path.join(self.folder.as_str())
}
pub fn original_subfolder_file_path(&self, image_name: &str) -> PathBuf {
self.original_subfolder().join(image_name)
}
}
#[derive(Debug, Clone)]
pub struct FolderAndFileName {
folder: String,
filename: String,
axum_path: String,
get_image_options: GetImageOptions,
state: Arc<ImageAppState>,
}
impl FolderAndFileName {
pub fn get_image_options(&self) -> &GetImageOptions {
&self.get_image_options
}
pub fn original_filename(&self) -> PathBuf {
self.state
.original_image_path
.join(&self.folder)
.join(&self.filename)
}
pub fn modified_filepath(&self, hash: &str) -> PathBuf {
let foldername = self.modified_folder(hash);
let filepath = foldername.join(&self.filename);
filepath
}
pub fn modified_folder(&self, hash: &str) -> PathBuf {
let foldername = self
.state
.modified_image_path
.join(hash)
.join(self.get_image_options.folder_name());
foldername
}
pub fn hash(&self) -> String {
let hash = get_image_path_hash(&self.axum_path);
hash
}
pub fn modified_redirect_path(&self, hash: &str) -> String {
let redirect_to = format!(
"{}{}/{}/{}/{}",
&self.state.image_route,
MODIFIED,
hash,
self.get_image_options.folder_name(),
self.filename
);
redirect_to
}
pub fn does_original_file_exist(&self) -> bool {
let original_filename = self.original_filename();
std::path::Path::exists(&original_filename)
}
pub fn no_options_set(&self) -> bool {
self.get_image_options.no_options_set()
}
pub(crate) fn original_redirect_path(&self) -> String {
let redirect_to = format!(
"{}{}/{}/{}",
&self.state.image_route, ORIGINAL, self.folder, self.filename
);
redirect_to
}
}
impl FromRequestParts<Arc<ImageAppState>> for Folder {
type Rejection = ImageEndpointError;
async fn from_request_parts(
parts: &mut http::request::Parts,
state: &Arc<ImageAppState>,
) -> Result<Self, Self::Rejection> {
let Path(path) = parts
.extract::<Path<String>>()
.await
.map_err(|_| ImageEndpointError::Generic("Error while extracting the path".into()))?;
let folder = Folder {
folder: path,
state: Arc::clone(state),
};
Ok(folder)
}
}
impl FromRequestParts<Arc<ImageAppState>> for FolderAndFileName {
type Rejection = ImageEndpointError;
async fn from_request_parts(
parts: &mut http::request::Parts,
state: &Arc<ImageAppState>,
) -> Result<Self, Self::Rejection> {
let Path(path) = parts
.extract::<Path<String>>()
.await
.map_err(|_| ImageEndpointError::Generic("Error while extracting the path".into()))?;
let Query(get_image_options) =
parts
.extract::<Query<GetImageOptions>>()
.await
.map_err(|_| {
ImageEndpointError::Generic("Was not able to extract query params".into())
})?;
let parts = path.rsplit_once('/');
match parts {
Some((folder, file)) => Ok(FolderAndFileName {
folder: folder.into(),
filename: file.into(),
axum_path: path,
state: state.clone(),
get_image_options,
}),
None => Err(ImageEndpointError::Generic(
"Was not able to read folder and filenamd from the path".into(),
)),
}
}
}
impl<S> FromRequest<S> for ImageMultipart
where
S: Send + Sync,
{
type Rejection = ImageEndpointError;
async fn from_request(req: Request, _state: &S) -> std::result::Result<Self, Self::Rejection> {
let multipart: Multipart = req.extract::<Multipart, _>().await?;
let image_multipart = ImageMultipart::try_from_multipart(multipart).await?;
Ok(image_multipart)
}
}