Remove more boxes

This commit is contained in:
Aode (Lion) 2021-10-20 20:13:39 -05:00
parent 877390878b
commit 4aefc0403a
3 changed files with 172 additions and 144 deletions

View file

@ -382,14 +382,13 @@ async fn prepare_process(
operations operations
}; };
let chain = self::processor::build_chain(&operations);
let format = ext let format = ext
.parse::<Format>() .parse::<Format>()
.map_err(|_| UploadError::UnsupportedFormat)?; .map_err(|_| UploadError::UnsupportedFormat)?;
let processed_name = format!("{}.{}", name, ext); let processed_name = format!("{}.{}", name, ext);
let thumbnail_path = self::processor::build_path(&chain, processed_name);
let thumbnail_args = self::processor::build_args(&chain); let (thumbnail_path, thumbnail_args) =
self::processor::build_chain(&operations, processed_name);
Ok((format, name, thumbnail_path, thumbnail_args)) Ok((format, name, thumbnail_path, thumbnail_args))
} }

View file

@ -10,15 +10,9 @@ fn ptos(path: &Path) -> Result<String, Error> {
} }
pub(crate) trait Processor { pub(crate) trait Processor {
fn name() -> &'static str const NAME: &'static str;
where
Self: Sized;
fn is_processor(s: &str) -> bool fn parse(k: &str, v: &str) -> Option<Self>
where
Self: Sized;
fn parse(k: &str, v: &str) -> Option<Box<dyn Processor + Send>>
where where
Self: Sized; Self: Sized;
@ -29,25 +23,13 @@ pub(crate) trait Processor {
pub(crate) struct Identity; pub(crate) struct Identity;
impl Processor for Identity { impl Processor for Identity {
fn name() -> &'static str const NAME: &'static str = "identity";
where
Self: Sized,
{
"identity"
}
fn is_processor(s: &str) -> bool fn parse(_: &str, _: &str) -> Option<Self>
where where
Self: Sized, Self: Sized,
{ {
s == Self::name() Some(Identity)
}
fn parse(_: &str, _: &str) -> Option<Box<dyn Processor + Send>>
where
Self: Sized,
{
Some(Box::new(Identity))
} }
fn path(&self, path: PathBuf) -> PathBuf { fn path(&self, path: PathBuf) -> PathBuf {
@ -62,30 +44,18 @@ impl Processor for Identity {
pub(crate) struct Thumbnail(usize); pub(crate) struct Thumbnail(usize);
impl Processor for Thumbnail { impl Processor for Thumbnail {
fn name() -> &'static str const NAME: &'static str = "thumbnail";
where
Self: Sized,
{
"thumbnail"
}
fn is_processor(s: &str) -> bool fn parse(_: &str, v: &str) -> Option<Self>
where
Self: Sized,
{
s == Self::name()
}
fn parse(_: &str, v: &str) -> Option<Box<dyn Processor + Send>>
where where
Self: Sized, Self: Sized,
{ {
let size = v.parse().ok()?; let size = v.parse().ok()?;
Some(Box::new(Thumbnail(size))) Some(Thumbnail(size))
} }
fn path(&self, mut path: PathBuf) -> PathBuf { fn path(&self, mut path: PathBuf) -> PathBuf {
path.push(Self::name()); path.push(Self::NAME);
path.push(self.0.to_string()); path.push(self.0.to_string());
path path
} }
@ -100,30 +70,18 @@ impl Processor for Thumbnail {
pub(crate) struct Resize(usize); pub(crate) struct Resize(usize);
impl Processor for Resize { impl Processor for Resize {
fn name() -> &'static str const NAME: &'static str = "resize";
where
Self: Sized,
{
"resize"
}
fn is_processor(s: &str) -> bool fn parse(_: &str, v: &str) -> Option<Self>
where
Self: Sized,
{
s == Self::name()
}
fn parse(_: &str, v: &str) -> Option<Box<dyn Processor + Send>>
where where
Self: Sized, Self: Sized,
{ {
let size = v.parse().ok()?; let size = v.parse().ok()?;
Some(Box::new(Resize(size))) Some(Resize(size))
} }
fn path(&self, mut path: PathBuf) -> PathBuf { fn path(&self, mut path: PathBuf) -> PathBuf {
path.push(Self::name()); path.push(Self::NAME);
path.push(self.0.to_string()); path.push(self.0.to_string());
path path
} }
@ -143,18 +101,9 @@ impl Processor for Resize {
pub(crate) struct Crop(usize, usize); pub(crate) struct Crop(usize, usize);
impl Processor for Crop { impl Processor for Crop {
fn name() -> &'static str const NAME: &'static str = "crop";
where
Self: Sized,
{
"crop"
}
fn is_processor(s: &str) -> bool { fn parse(_: &str, v: &str) -> Option<Self> {
s == Self::name()
}
fn parse(_: &str, v: &str) -> Option<Box<dyn Processor + Send>> {
let mut iter = v.split('x'); let mut iter = v.split('x');
let first = iter.next()?; let first = iter.next()?;
let second = iter.next()?; let second = iter.next()?;
@ -170,11 +119,11 @@ impl Processor for Crop {
return None; return None;
} }
Some(Box::new(Crop(width, height))) Some(Crop(width, height))
} }
fn path(&self, mut path: PathBuf) -> PathBuf { fn path(&self, mut path: PathBuf) -> PathBuf {
path.push(Self::name()); path.push(Self::NAME);
path.push(format!("{}x{}", self.0, self.1)); path.push(format!("{}x{}", self.0, self.1));
path path
} }
@ -194,24 +143,15 @@ impl Processor for Crop {
pub(crate) struct Blur(f64); pub(crate) struct Blur(f64);
impl Processor for Blur { impl Processor for Blur {
fn name() -> &'static str const NAME: &'static str = "blur";
where
Self: Sized,
{
"blur"
}
fn is_processor(s: &str) -> bool { fn parse(_: &str, v: &str) -> Option<Self> {
s == Self::name()
}
fn parse(_: &str, v: &str) -> Option<Box<dyn Processor + Send>> {
let sigma = v.parse().ok()?; let sigma = v.parse().ok()?;
Some(Box::new(Blur(sigma))) Some(Blur(sigma))
} }
fn path(&self, mut path: PathBuf) -> PathBuf { fn path(&self, mut path: PathBuf) -> PathBuf {
path.push(Self::name()); path.push(Self::NAME);
path.push(self.0.to_string()); path.push(self.0.to_string());
path path
} }
@ -223,64 +163,147 @@ impl Processor for Blur {
} }
} }
macro_rules! parse { trait Process {
($x:ident, $k:expr, $v:expr) => {{ fn path(&self, path: PathBuf) -> PathBuf;
if $x::is_processor($k) {
return $x::parse($k, $v); fn command(&self, args: Vec<String>) -> Vec<String>;
}
}}; fn len(&self, len: u64) -> u64;
} }
pub(crate) struct ProcessChain { #[derive(Debug)]
inner: Vec<Box<dyn Processor + Send>>, struct Base;
#[derive(Debug)]
struct ProcessorNode<Inner, P> {
inner: Inner,
processor: P,
} }
impl std::fmt::Debug for ProcessChain { impl<Inner, P> ProcessorNode<Inner, P>
where
P: Processor,
{
fn new(inner: Inner, processor: P) -> Self {
ProcessorNode { inner, processor }
}
}
impl Process for Base {
fn path(&self, path: PathBuf) -> PathBuf {
path
}
fn command(&self, args: Vec<String>) -> Vec<String> {
args
}
fn len(&self, len: u64) -> u64 {
len
}
}
impl<Inner, P> Process for ProcessorNode<Inner, P>
where
Inner: Process,
P: Processor,
{
fn path(&self, path: PathBuf) -> PathBuf {
self.processor.path(self.inner.path(path))
}
fn command(&self, args: Vec<String>) -> Vec<String> {
self.processor.command(self.inner.command(args))
}
fn len(&self, len: u64) -> u64 {
self.inner.len(len + 1)
}
}
struct ProcessChain<P> {
inner: P,
}
impl<P> ProcessChain<P>
where
P: Process,
{
fn len(&self) -> u64 {
self.inner.len(0)
}
fn command(&self) -> Vec<String> {
self.inner.command(vec![])
}
fn path(&self) -> PathBuf {
self.inner.path(PathBuf::new())
}
}
impl<P> std::fmt::Debug for ProcessChain<P>
where
P: Process,
{
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("ProcessChain") f.debug_struct("ProcessChain")
.field("steps", &self.inner.len()) .field("path", &self.path())
.field("command", &self.command())
.field("steps", &self.len())
.finish() .finish()
} }
} }
#[instrument] #[instrument]
pub(crate) fn build_chain(args: &[(String, String)]) -> ProcessChain { pub(crate) fn build_chain(args: &[(String, String)], filename: String) -> (PathBuf, Vec<String>) {
let inner = args fn parse<P: Processor>(key: &str, value: &str) -> Option<P> {
.iter() if key == P::NAME {
.filter_map(|(k, v)| { return P::parse(key, value);
let k = k.as_str(); }
let v = v.as_str();
parse!(Identity, k, v); None
parse!(Thumbnail, k, v); }
parse!(Resize, k, v);
parse!(Crop, k, v);
parse!(Blur, k, v);
debug!("Skipping {}: {}, invalid", k, v); macro_rules! parse {
($inner:expr, $args:expr, $filename:expr, $x:ident, $k:expr, $v:expr) => {{
if let Some(processor) = parse::<$x>($k, $v) {
return build(
ProcessorNode::new($inner, processor),
&$args[1..],
$filename,
);
}
}};
}
None fn build(
}) inner: impl Process,
.collect(); args: &[(String, String)],
filename: String,
) -> (PathBuf, Vec<String>) {
if args.len() == 0 {
let chain = ProcessChain { inner };
ProcessChain { inner } debug!("built: {:?}", chain);
}
pub(crate) fn build_path(chain: &ProcessChain, filename: String) -> PathBuf { return (chain.path().join(filename), chain.command());
let mut path = chain }
.inner
.iter()
.fold(PathBuf::default(), |acc, processor| processor.path(acc));
path.push(filename); let (name, value) = &args[0];
path
}
pub(crate) fn build_args(chain: &ProcessChain) -> Vec<String> { parse!(inner, args, filename, Identity, name, value);
chain parse!(inner, args, filename, Thumbnail, name, value);
.inner parse!(inner, args, filename, Resize, name, value);
.iter() parse!(inner, args, filename, Crop, name, value);
.fold(Vec::new(), |acc, processor| processor.command(acc)) parse!(inner, args, filename, Blur, name, value);
debug!("Skipping {}: {}, invalid", name, value);
build(inner, &args[1..], filename)
}
build(Base, args, filename)
} }
fn is_motion(s: &str) -> bool { fn is_motion(s: &str) -> bool {

View file

@ -1,4 +1,6 @@
use crate::{config::Format, error::Error, ffmpeg::InputFormat, magick::ValidInputType}; use crate::{
config::Format, either::Either, error::Error, ffmpeg::InputFormat, magick::ValidInputType,
};
use actix_web::web::Bytes; use actix_web::web::Bytes;
use tokio::io::AsyncRead; use tokio::io::AsyncRead;
use tracing::instrument; use tracing::instrument;
@ -20,10 +22,6 @@ impl UnvalidatedBytes {
fn new(bytes: Bytes) -> Self { fn new(bytes: Bytes) -> Self {
UnvalidatedBytes { bytes, written: 0 } UnvalidatedBytes { bytes, written: 0 }
} }
fn boxed(self) -> Box<dyn AsyncRead + Unpin> {
Box::new(self)
}
} }
impl AsyncRead for UnvalidatedBytes { impl AsyncRead for UnvalidatedBytes {
@ -47,7 +45,7 @@ pub(crate) async fn validate_image_bytes(
bytes: Bytes, bytes: Bytes,
prescribed_format: Option<Format>, prescribed_format: Option<Format>,
validate: bool, validate: bool,
) -> Result<(mime::Mime, Box<dyn AsyncRead + Unpin>), Error> { ) -> Result<(mime::Mime, impl AsyncRead + Unpin), Error> {
let input_type = crate::magick::input_type_bytes(bytes.clone()).await?; let input_type = crate::magick::input_type_bytes(bytes.clone()).await?;
if !validate { if !validate {
@ -59,39 +57,47 @@ pub(crate) async fn validate_image_bytes(
ValidInputType::Webp => image_webp(), ValidInputType::Webp => image_webp(),
}; };
return Ok((mime_type, UnvalidatedBytes::new(bytes).boxed())); return Ok((mime_type, Either::left(UnvalidatedBytes::new(bytes))));
} }
match (prescribed_format, input_type) { match (prescribed_format, input_type) {
(_, ValidInputType::Gif) => Ok(( (_, ValidInputType::Gif) => Ok((
video_mp4(), video_mp4(),
Box::new(crate::ffmpeg::to_mp4_bytes(bytes, InputFormat::Gif)?) Either::right(Either::left(crate::ffmpeg::to_mp4_bytes(
as Box<dyn AsyncRead + Unpin>, bytes,
InputFormat::Gif,
)?)),
)), )),
(_, ValidInputType::Mp4) => Ok(( (_, ValidInputType::Mp4) => Ok((
video_mp4(), video_mp4(),
Box::new(crate::ffmpeg::to_mp4_bytes(bytes, InputFormat::Mp4)?) Either::right(Either::left(crate::ffmpeg::to_mp4_bytes(
as Box<dyn AsyncRead + Unpin>, bytes,
InputFormat::Mp4,
)?)),
)), )),
(Some(Format::Jpeg) | None, ValidInputType::Jpeg) => Ok(( (Some(Format::Jpeg) | None, ValidInputType::Jpeg) => Ok((
mime::IMAGE_JPEG, mime::IMAGE_JPEG,
Box::new(crate::exiftool::clear_metadata_bytes_read(bytes)?) Either::right(Either::right(Either::left(
as Box<dyn AsyncRead + Unpin>, crate::exiftool::clear_metadata_bytes_read(bytes)?,
))),
)), )),
(Some(Format::Png) | None, ValidInputType::Png) => Ok(( (Some(Format::Png) | None, ValidInputType::Png) => Ok((
mime::IMAGE_PNG, mime::IMAGE_PNG,
Box::new(crate::exiftool::clear_metadata_bytes_read(bytes)?) Either::right(Either::right(Either::left(
as Box<dyn AsyncRead + Unpin>, crate::exiftool::clear_metadata_bytes_read(bytes)?,
))),
)), )),
(Some(Format::Webp) | None, ValidInputType::Webp) => Ok(( (Some(Format::Webp) | None, ValidInputType::Webp) => Ok((
image_webp(), image_webp(),
Box::new(crate::magick::clear_metadata_bytes_read(bytes)?) Either::right(Either::right(Either::right(Either::left(
as Box<dyn AsyncRead + Unpin>, crate::magick::clear_metadata_bytes_read(bytes)?,
)))),
)), )),
(Some(format), _) => Ok(( (Some(format), _) => Ok((
format.to_mime(), format.to_mime(),
Box::new(crate::magick::convert_bytes_read(bytes, format)?) Either::right(Either::right(Either::right(Either::right(
as Box<dyn AsyncRead + Unpin>, crate::magick::convert_bytes_read(bytes, format)?,
)))),
)), )),
} }
} }