refactoring

This commit is contained in:
Timofey Gelazoniya 2023-04-03 04:29:27 +03:00
parent bf9f9f8c22
commit b7e83f921a
Signed by: zeldon
GPG Key ID: 047886915281DD2A
15 changed files with 309 additions and 56 deletions

2
.env.sample Normal file
View File

@ -0,0 +1,2 @@
PORT=3021
RUST_LOG=info

1
.gitignore vendored
View File

@ -1 +1,2 @@
/target
.env

163
Cargo.lock generated
View File

@ -2,6 +2,15 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "0.7.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
dependencies = [
"memchr",
]
[[package]]
name = "anyhow"
version = "1.0.70"
@ -122,6 +131,12 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
[[package]]
name = "cc"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
[[package]]
name = "cexpr"
version = "0.6.0"
@ -148,6 +163,24 @@ dependencies = [
"libloading",
]
[[package]]
name = "dotenvy"
version = "0.15.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
[[package]]
name = "dotenvy_macro"
version = "0.15.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb0235d912a8c749f4e0c9f18ca253b4c28cfefc1d2518096016d6e3230b6424"
dependencies = [
"dotenvy",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "either"
version = "1.8.1"
@ -163,6 +196,40 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "env_logger"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
dependencies = [
"humantime",
"is-terminal",
"log",
"regex",
"termcolor",
]
[[package]]
name = "errno"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0"
dependencies = [
"errno-dragonfly",
"libc",
"windows-sys",
]
[[package]]
name = "errno-dragonfly"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
"cc",
"libc",
]
[[package]]
name = "fnv"
version = "1.0.7"
@ -232,6 +299,12 @@ dependencies = [
"libc",
]
[[package]]
name = "hermit-abi"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
[[package]]
name = "http"
version = "0.2.9"
@ -266,6 +339,12 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "hyper"
version = "0.14.25"
@ -289,6 +368,29 @@ dependencies = [
"want",
]
[[package]]
name = "io-lifetimes"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb"
dependencies = [
"hermit-abi 0.3.1",
"libc",
"windows-sys",
]
[[package]]
name = "is-terminal"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "256017f749ab3117e93acb91063009e1f1bb56d03965b14c2c8df4eb02c524d8"
dependencies = [
"hermit-abi 0.3.1",
"io-lifetimes",
"rustix",
"windows-sys",
]
[[package]]
name = "itoa"
version = "1.0.6"
@ -323,13 +425,24 @@ dependencies = [
"winapi",
]
[[package]]
name = "linux-raw-sys"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f"
[[package]]
name = "liquid-rescale-api"
version = "0.1.0"
dependencies = [
"anyhow",
"axum",
"dotenvy",
"dotenvy_macro",
"env_logger",
"log",
"magick_rust",
"serde",
"tokio",
]
@ -423,7 +536,7 @@ version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
dependencies = [
"hermit-abi",
"hermit-abi 0.2.6",
"libc",
]
@ -507,6 +620,8 @@ version = "1.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
@ -522,6 +637,20 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustix"
version = "0.37.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d097081ed288dfe45699b72f5b5d648e5f15d64d900c7080273baa20c16a6849"
dependencies = [
"bitflags",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "rustversion"
version = "1.0.12"
@ -539,6 +668,20 @@ name = "serde"
version = "1.0.159"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.159"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.13",
]
[[package]]
name = "serde_json"
@ -622,6 +765,15 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
[[package]]
name = "termcolor"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
dependencies = [
"winapi-util",
]
[[package]]
name = "tokio"
version = "1.27.0"
@ -759,6 +911,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"

View File

@ -8,5 +8,10 @@ version = "0.1.0"
[dependencies]
anyhow = "1.0.70"
axum = {version = "0.6", features = ["multipart", "macros"]}
dotenvy = "0.15.7"
dotenvy_macro = "0.15.7"
env_logger = "0.10.0"
log = "0.4.17"
magick_rust = "0.17.0"
serde = { version = "1.0.159", features = ["derive"] }
tokio = {version = "1", features = ["macros", "rt-multi-thread"]}

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"))
}