2
0
Fork 0
mirror of https://git.asonix.dog/asonix/pict-rs synced 2024-11-10 06:25:00 +00:00
pict-rs/src/processor.rs

216 lines
4.8 KiB
Rust
Raw Normal View History

use crate::error::UploadError;
use actix_web::web;
use image::{DynamicImage, GenericImageView};
2020-06-07 19:12:19 +00:00
use std::{collections::HashSet, path::PathBuf};
2020-06-14 15:07:31 +00:00
use tracing::{debug, instrument, Span};
pub(crate) trait Processor {
2020-06-07 19:12:19 +00:00
fn name() -> &'static str
where
Self: Sized;
fn is_processor(s: &str) -> bool
where
Self: Sized;
fn parse(s: &str) -> Option<Box<dyn Processor + Send>>
where
Self: Sized;
fn path(&self, path: PathBuf) -> PathBuf;
fn process(&self, img: DynamicImage) -> Result<(DynamicImage, bool), UploadError>;
2020-06-07 19:12:19 +00:00
fn is_whitelisted(whitelist: Option<&HashSet<String>>) -> bool
where
Self: Sized,
{
whitelist
.map(|wl| wl.contains(Self::name()))
.unwrap_or(true)
}
}
pub(crate) struct Identity;
impl Processor for Identity {
2020-06-07 19:12:19 +00:00
fn name() -> &'static str
where
Self: Sized,
{
"identity"
}
fn is_processor(s: &str) -> bool
where
Self: Sized,
{
s == Self::name()
}
fn parse(_: &str) -> Option<Box<dyn Processor + Send>>
where
Self: Sized,
{
2020-06-14 15:07:31 +00:00
debug!("Identity");
2020-06-07 19:12:19 +00:00
Some(Box::new(Identity))
}
2020-06-07 18:03:36 +00:00
fn path(&self, path: PathBuf) -> PathBuf {
path
}
fn process(&self, img: DynamicImage) -> Result<(DynamicImage, bool), UploadError> {
Ok((img, false))
}
}
pub(crate) struct Thumbnail(u32);
impl Processor for Thumbnail {
2020-06-07 19:12:19 +00:00
fn name() -> &'static str
where
Self: Sized,
{
"thumbnail"
}
fn is_processor(s: &str) -> bool
where
Self: Sized,
{
s.starts_with(Self::name())
}
fn parse(s: &str) -> Option<Box<dyn Processor + Send>>
where
Self: Sized,
{
let size = s.trim_start_matches(Self::name()).parse().ok()?;
Some(Box::new(Thumbnail(size)))
}
fn path(&self, mut path: PathBuf) -> PathBuf {
2020-06-07 19:12:19 +00:00
path.push(Self::name());
path.push(self.0.to_string());
path
}
fn process(&self, img: DynamicImage) -> Result<(DynamicImage, bool), UploadError> {
2020-06-14 15:07:31 +00:00
debug!("Thumbnail");
if img.width() > self.0 || img.height() > self.0 {
Ok((img.thumbnail(self.0, self.0), true))
} else {
Ok((img, false))
}
}
}
pub(crate) struct Blur(f32);
impl Processor for Blur {
2020-06-07 19:12:19 +00:00
fn name() -> &'static str
where
Self: Sized,
{
"blur"
}
fn is_processor(s: &str) -> bool {
s.starts_with(Self::name())
}
fn parse(s: &str) -> Option<Box<dyn Processor + Send>> {
let sigma = s.trim_start_matches(Self::name()).parse().ok()?;
Some(Box::new(Blur(sigma)))
}
fn path(&self, mut path: PathBuf) -> PathBuf {
2020-06-07 19:12:19 +00:00
path.push(Self::name());
path.push(self.0.to_string());
path
}
fn process(&self, img: DynamicImage) -> Result<(DynamicImage, bool), UploadError> {
2020-06-14 15:07:31 +00:00
debug!("Blur");
if self.0 > 0.0 {
Ok((img.blur(self.0), true))
} else {
Ok((img, false))
}
}
}
2020-06-07 19:12:19 +00:00
macro_rules! parse {
($x:ident, $y:expr, $z:expr) => {{
if $x::is_processor($y) && $x::is_whitelisted($z) {
return $x::parse($y);
}
}};
}
pub(crate) struct ProcessChain {
inner: Vec<Box<dyn Processor + Send>>,
}
impl std::fmt::Debug for ProcessChain {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("ProcessChain")
2020-06-14 15:07:31 +00:00
.field("steps", &self.inner.len())
.finish()
}
}
#[instrument]
pub(crate) fn build_chain(args: &[String], whitelist: Option<&HashSet<String>>) -> ProcessChain {
let inner = args
.into_iter()
2020-06-07 19:12:19 +00:00
.filter_map(|arg| {
parse!(Identity, arg.as_str(), whitelist);
parse!(Thumbnail, arg.as_str(), whitelist);
parse!(Blur, arg.as_str(), whitelist);
debug!("Skipping {}, invalid or whitelisted", arg);
None
})
.collect();
ProcessChain { inner }
}
pub(crate) fn build_path(base: PathBuf, chain: &ProcessChain, filename: String) -> PathBuf {
let mut path = chain
.inner
.iter()
.fold(base, |acc, processor| processor.path(acc));
path.push(filename);
path
}
2020-06-14 15:07:31 +00:00
#[instrument(skip(img))]
pub(crate) async fn process_image(
chain: ProcessChain,
2020-06-14 15:07:31 +00:00
mut img: DynamicImage,
) -> Result<(DynamicImage, bool), UploadError> {
let mut changed = false;
for processor in chain.inner.into_iter() {
2020-06-14 15:07:31 +00:00
debug!("Step");
let span = Span::current();
let tup = web::block(move || {
2020-06-14 15:07:31 +00:00
let entered = span.enter();
let res = processor.process(img);
drop(entered);
res
})
.await?;
debug!("Step complete");
img = tup.0;
changed |= tup.1;
}
Ok((img, changed))
}