refactoring
This commit is contained in:
parent
bf9f9f8c22
commit
b7e83f921a
2
.env.sample
Normal file
2
.env.sample
Normal file
@ -0,0 +1,2 @@
|
||||
PORT=3021
|
||||
RUST_LOG=info
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
||||
/target
|
||||
.env
|
163
Cargo.lock
generated
163
Cargo.lock
generated
@ -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"
|
||||
|
@ -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"]}
|
||||
|
30
src/lib.rs
30
src/lib.rs
@ -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();
|
||||
}
|
||||
|
47
src/main.rs
47
src/main.rs
@ -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;
|
||||
}
|
||||
|
26
src/processors/liquid_rescale_processor.rs
Normal file
26
src/processors/liquid_rescale_processor.rs
Normal 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
1
src/processors/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod liquid_rescale_processor;
|
9
src/router.rs
Normal file
9
src/router.rs
Normal 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
10
src/routes/liquid.rs
Normal 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
1
src/routes/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod liquid;
|
33
src/utilities/app_error.rs
Normal file
33
src/utilities/app_error.rs
Normal 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,
|
||||
}
|
20
src/utilities/liquid_rescale.rs
Normal file
20
src/utilities/liquid_rescale.rs
Normal 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
3
src/utilities/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
pub mod app_error;
|
||||
pub mod liquid_rescale;
|
||||
pub mod parse_image;
|
14
src/utilities/parse_image.rs
Normal file
14
src/utilities/parse_image.rs
Normal 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"))
|
||||
}
|
Loading…
Reference in New Issue
Block a user