pict-rs/src/processor.rs

194 lines
4.6 KiB
Rust
Raw Normal View History

2021-10-21 02:36:18 +00:00
use crate::error::{Error, UploadError};
use std::path::PathBuf;
use tracing::instrument;
pub(crate) trait Processor {
2021-10-21 01:13:39 +00:00
const NAME: &'static str;
2020-06-07 19:12:19 +00:00
2021-10-21 01:13:39 +00:00
fn parse(k: &str, v: &str) -> Option<Self>
2020-06-07 19:12:19 +00:00
where
Self: Sized;
fn path(&self, path: PathBuf) -> PathBuf;
fn command(&self, args: Vec<String>) -> Vec<String>;
}
pub(crate) struct Identity;
2021-10-23 04:48:56 +00:00
pub(crate) struct Thumbnail(usize);
pub(crate) struct Resize(usize);
pub(crate) struct Crop(usize, usize);
pub(crate) struct Blur(f64);
#[instrument]
pub(crate) fn build_chain(
args: &[(String, String)],
filename: String,
) -> Result<(PathBuf, Vec<String>), Error> {
fn parse<P: Processor>(key: &str, value: &str) -> Result<Option<P>, UploadError> {
if key == P::NAME {
return Ok(Some(P::parse(key, value).ok_or(UploadError::ParsePath)?));
}
Ok(None)
}
macro_rules! parse {
($inner:expr, $x:ident, $k:expr, $v:expr) => {{
if let Some(processor) = parse::<$x>($k, $v)? {
return Ok((processor.path($inner.0), processor.command($inner.1)));
};
}};
}
let (path, args) =
args.iter()
2021-10-23 04:48:56 +00:00
.fold(Ok((PathBuf::default(), vec![])), |inner, (name, value)| {
if let Ok(inner) = inner {
parse!(inner, Identity, name, value);
parse!(inner, Thumbnail, name, value);
parse!(inner, Resize, name, value);
parse!(inner, Crop, name, value);
parse!(inner, Blur, name, value);
Err(Error::from(UploadError::ParsePath))
} else {
inner
}
})?;
Ok((path.join(filename), args))
}
impl Processor for Identity {
2021-10-21 01:13:39 +00:00
const NAME: &'static str = "identity";
2020-06-07 19:12:19 +00:00
2021-10-21 01:13:39 +00:00
fn parse(_: &str, _: &str) -> Option<Self>
2020-06-07 19:12:19 +00:00
where
Self: Sized,
{
2021-10-21 01:13:39 +00:00
Some(Identity)
2020-06-07 19:12:19 +00:00
}
2020-06-07 18:03:36 +00:00
fn path(&self, path: PathBuf) -> PathBuf {
path
}
fn command(&self, args: Vec<String>) -> Vec<String> {
args
}
}
impl Processor for Thumbnail {
2021-10-21 01:13:39 +00:00
const NAME: &'static str = "thumbnail";
2020-06-07 19:12:19 +00:00
2021-10-21 01:13:39 +00:00
fn parse(_: &str, v: &str) -> Option<Self>
2020-06-07 19:12:19 +00:00
where
Self: Sized,
{
2020-06-24 16:58:46 +00:00
let size = v.parse().ok()?;
2021-10-21 01:13:39 +00:00
Some(Thumbnail(size))
2020-06-07 19:12:19 +00:00
}
fn path(&self, mut path: PathBuf) -> PathBuf {
2021-10-21 01:13:39 +00:00
path.push(Self::NAME);
path.push(self.0.to_string());
path
}
fn command(&self, mut args: Vec<String>) -> Vec<String> {
args.extend(["-sample".to_string(), format!("{}x{}>", self.0, self.0)]);
2020-06-17 02:05:06 +00:00
args
}
}
impl Processor for Resize {
2021-10-21 01:13:39 +00:00
const NAME: &'static str = "resize";
2021-10-21 01:13:39 +00:00
fn parse(_: &str, v: &str) -> Option<Self>
where
Self: Sized,
{
let size = v.parse().ok()?;
2021-10-21 01:13:39 +00:00
Some(Resize(size))
}
fn path(&self, mut path: PathBuf) -> PathBuf {
2021-10-21 01:13:39 +00:00
path.push(Self::NAME);
path.push(self.0.to_string());
path
}
fn command(&self, mut args: Vec<String>) -> Vec<String> {
args.extend([
"-filter".to_string(),
2021-08-29 04:15:29 +00:00
"Lanczos2".to_string(),
"-resize".to_string(),
format!("{}x{}>", self.0, self.0),
]);
args
}
}
2020-12-17 00:20:06 +00:00
impl Processor for Crop {
2021-10-21 01:13:39 +00:00
const NAME: &'static str = "crop";
2020-12-17 00:20:06 +00:00
2021-10-21 01:13:39 +00:00
fn parse(_: &str, v: &str) -> Option<Self> {
2020-12-17 00:20:06 +00:00
let mut iter = v.split('x');
let first = iter.next()?;
let second = iter.next()?;
let width = first.parse::<usize>().ok()?;
let height = second.parse::<usize>().ok()?;
if width == 0 || height == 0 {
return None;
}
if width > 20 || height > 20 {
return None;
}
2021-10-21 01:13:39 +00:00
Some(Crop(width, height))
2020-12-17 00:20:06 +00:00
}
fn path(&self, mut path: PathBuf) -> PathBuf {
2021-10-21 01:13:39 +00:00
path.push(Self::NAME);
2020-12-17 00:20:06 +00:00
path.push(format!("{}x{}", self.0, self.1));
path
}
fn command(&self, mut args: Vec<String>) -> Vec<String> {
args.extend([
"-gravity".to_string(),
"center".to_string(),
"-crop".to_string(),
format!("{}:{}+0+0", self.0, self.1),
]);
2020-12-17 00:20:06 +00:00
args
2020-12-17 00:20:06 +00:00
}
}
impl Processor for Blur {
2021-10-21 01:13:39 +00:00
const NAME: &'static str = "blur";
2020-06-07 19:12:19 +00:00
2021-10-21 01:13:39 +00:00
fn parse(_: &str, v: &str) -> Option<Self> {
2020-06-24 16:58:46 +00:00
let sigma = v.parse().ok()?;
2021-10-21 01:13:39 +00:00
Some(Blur(sigma))
2020-06-07 19:12:19 +00:00
}
fn path(&self, mut path: PathBuf) -> PathBuf {
2021-10-21 01:13:39 +00:00
path.push(Self::NAME);
path.push(self.0.to_string());
path
}
fn command(&self, mut args: Vec<String>) -> Vec<String> {
args.extend(["-gaussian-blur".to_string(), self.0.to_string()]);
2020-06-24 16:58:46 +00:00
args
}
}