mirror of
https://git.asonix.dog/asonix/pict-rs
synced 2024-12-22 19:31:35 +00:00
Remove more boxes
This commit is contained in:
parent
877390878b
commit
4aefc0403a
3 changed files with 172 additions and 144 deletions
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
265
src/processor.rs
265
src/processor.rs
|
@ -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 {
|
||||||
|
|
|
@ -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)?,
|
||||||
|
)))),
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue