refactoring

This commit is contained in:
2023-04-03 04:29:27 +03:00
parent bf9f9f8c22
commit b7e83f921a
15 changed files with 309 additions and 56 deletions

View File

@ -1,19 +1,19 @@
use anyhow::Result;
use magick_rust::{magick_wand_genesis, MagickWand};
use std::sync::Once;
mod processors;
mod router;
mod routes;
mod utilities;
static START: Once = Once::new();
use dotenvy_macro::dotenv;
use router::create_router;
use std::net::SocketAddr;
pub fn liquid_rescale_image(image: &Vec<u8>) -> Result<Vec<u8>> {
START.call_once(|| {
magick_wand_genesis();
});
pub async fn run() {
let port = dotenv!("PORT");
let app = create_router();
let address = SocketAddr::from(([0, 0, 0, 0], port.parse().unwrap()));
let wand = MagickWand::new();
wand.read_image_blob(&image)?;
wand.fit(640, 640);
wand.liquid_rescale_image(320, 320, 1.0, 0.0)?;
wand.fit(640, 640);
let bytes = wand.write_image_blob("JPEG")?;
Ok(bytes)
axum::Server::bind(&address)
.serve(app.into_make_service())
.await
.unwrap();
}

View File

@ -1,44 +1,11 @@
use anyhow::Result;
use axum::body::Full;
use axum::extract::DefaultBodyLimit;
use axum::http::Response;
use axum::response::IntoResponse;
use axum::{extract::Multipart, routing::post, Router};
use liquid_rescale_api::liquid_rescale_image;
use env_logger::Env;
use liquid_rescale_api::run;
use log::info;
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/liquid", post(liquid_rescale_route))
.layer(DefaultBodyLimit::max(1024 * 10_000 /* 10 MiB */));
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
}
async fn liquid_rescale_route(multipart: Multipart) -> impl IntoResponse {
let image = parse_image(multipart).await.unwrap();
let image_data = liquid_rescale_image(&image).unwrap();
let body = Full::from(image_data);
Response::builder()
.header("Content-Type", "image/jpeg")
.body(body)
.unwrap()
}
async fn parse_image(mut multipart: Multipart) -> Result<Vec<u8>> {
while let Some(field) = multipart.next_field().await.unwrap() {
if let Some("file") = field.name() {
let bytes = field.bytes().await?;
let image_data = bytes.to_vec();
return Ok(image_data);
}
}
Err(anyhow::anyhow!("Missing file field"))
dotenvy::dotenv().ok();
env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
info!("Starting app on {}", dotenvy::var("PORT").unwrap());
run().await;
}

View File

@ -0,0 +1,26 @@
use axum::{extract::Multipart, http::StatusCode};
use log::error;
use crate::utilities::{
app_error::AppError, liquid_rescale::liquid_rescale_image, parse_image::parse_image,
};
pub async fn liquid_rescale_processor(multipart: Multipart) -> Result<Vec<u8>, AppError> {
let image = parse_image(multipart).await.map_err(|err| {
error!("Error parse image: {:?}", err);
AppError::new(
StatusCode::INTERNAL_SERVER_ERROR,
"There was a error parse your image",
)
})?;
let image_data = liquid_rescale_image(&image).map_err(|err| {
error!("Imagemagick parse image error: {:?}", err);
AppError::new(
StatusCode::INTERNAL_SERVER_ERROR,
"There was a error read your image",
)
})?;
Ok(image_data)
}

1
src/processors/mod.rs Normal file
View File

@ -0,0 +1 @@
pub mod liquid_rescale_processor;

9
src/router.rs Normal file
View File

@ -0,0 +1,9 @@
use axum::{extract::DefaultBodyLimit, routing::post, Router};
use crate::routes::liquid::liquid_rescale_route;
pub fn create_router() -> Router {
Router::new()
.route("/liquid", post(liquid_rescale_route))
.layer(DefaultBodyLimit::max(1024 * 10_000 /* 10 MiB */))
}

10
src/routes/liquid.rs Normal file
View File

@ -0,0 +1,10 @@
use axum::{extract::Multipart, Json};
use crate::{
processors::liquid_rescale_processor::liquid_rescale_processor, utilities::app_error::AppError,
};
pub async fn liquid_rescale_route(multipart: Multipart) -> Result<Json<Vec<u8>>, AppError> {
let image_data = liquid_rescale_processor(multipart).await?;
Ok(Json(image_data))
}

1
src/routes/mod.rs Normal file
View File

@ -0,0 +1 @@
pub mod liquid;

View File

@ -0,0 +1,33 @@
use axum::{http::StatusCode, response::IntoResponse, Json};
use serde::{Deserialize, Serialize};
pub struct AppError {
code: StatusCode,
message: String,
}
impl AppError {
pub fn new(code: StatusCode, message: impl Into<String>) -> Self {
Self {
code,
message: message.into(),
}
}
}
impl IntoResponse for AppError {
fn into_response(self) -> axum::response::Response {
(
self.code,
Json(ErrorResponse {
error: self.message.clone(),
}),
)
.into_response()
}
}
#[derive(Serialize, Deserialize)]
struct ErrorResponse {
error: String,
}

View File

@ -0,0 +1,20 @@
use std::sync::Once;
use anyhow::Result;
use magick_rust::{magick_wand_genesis, MagickWand};
static START: Once = Once::new();
pub fn liquid_rescale_image(image: &Vec<u8>) -> Result<Vec<u8>> {
START.call_once(|| {
magick_wand_genesis();
});
let wand = MagickWand::new();
wand.read_image_blob(&image)?;
wand.fit(640, 640);
wand.liquid_rescale_image(320, 320, 1.0, 0.0)?;
wand.fit(640, 640);
let bytes = wand.write_image_blob("JPEG")?;
Ok(bytes)
}

3
src/utilities/mod.rs Normal file
View File

@ -0,0 +1,3 @@
pub mod app_error;
pub mod liquid_rescale;
pub mod parse_image;

View File

@ -0,0 +1,14 @@
use anyhow::Result;
use axum::extract::Multipart;
pub async fn parse_image(mut multipart: Multipart) -> Result<Vec<u8>> {
while let Some(field) = multipart.next_field().await.unwrap() {
if let Some("file") = field.name() {
let bytes = field.bytes().await?;
let image_data = bytes.to_vec();
return Ok(image_data);
}
}
Err(anyhow::anyhow!("Missing file field"))
}