Refactor grouping
This commit is contained in:
100
crates/pile-value/src/extract/blob/image/mod.rs
Normal file
100
crates/pile-value/src/extract/blob/image/mod.rs
Normal file
@@ -0,0 +1,100 @@
|
||||
use image::ImageFormat;
|
||||
use mime::Mime;
|
||||
use pile_config::Label;
|
||||
use pile_io::AsyncReader;
|
||||
use std::{io::Cursor, str::FromStr, sync::Arc};
|
||||
use tracing::trace;
|
||||
use transform::{CropTransformer, ImageTransformer, MaxDimTransformer};
|
||||
|
||||
mod transform;
|
||||
|
||||
use crate::{
|
||||
extract::traits::{ExtractState, ObjectExtractor},
|
||||
value::{ArcBytes, BinaryPileValue, PileValue},
|
||||
};
|
||||
|
||||
pub struct ImageExtractor {
|
||||
item: BinaryPileValue,
|
||||
}
|
||||
|
||||
impl ImageExtractor {
|
||||
pub fn new(item: &BinaryPileValue) -> Self {
|
||||
Self { item: item.clone() }
|
||||
}
|
||||
|
||||
async fn apply<T: ImageTransformer + Send + 'static>(
|
||||
&self,
|
||||
args: &str,
|
||||
) -> Result<Option<PileValue>, std::io::Error> {
|
||||
let transformer = match T::parse_args(args) {
|
||||
Ok(t) => t,
|
||||
Err(_) => return Ok(None),
|
||||
};
|
||||
|
||||
let mime = self.item.mime().clone();
|
||||
let bytes = self.item.read().await?.read_to_end().await?;
|
||||
|
||||
let Some(format) = ImageFormat::from_mime_type(&mime) else {
|
||||
return Ok(Some(PileValue::Binary(BinaryPileValue::Blob {
|
||||
mime,
|
||||
bytes: ArcBytes(Arc::new(bytes)),
|
||||
})));
|
||||
};
|
||||
|
||||
let bytes_for_closure = bytes.clone();
|
||||
let result = tokio::task::spawn_blocking(move || {
|
||||
let mut img = image::load_from_memory_with_format(&bytes_for_closure, format)?;
|
||||
transformer.transform(&mut img);
|
||||
|
||||
let mut out = Cursor::new(Vec::new());
|
||||
img.write_to(&mut out, format)?;
|
||||
|
||||
let out_mime =
|
||||
Mime::from_str(format.to_mime_type()).unwrap_or(mime::APPLICATION_OCTET_STREAM);
|
||||
Ok::<_, image::ImageError>((out_mime, out.into_inner()))
|
||||
})
|
||||
.await?;
|
||||
|
||||
match result {
|
||||
Ok((out_mime, out_bytes)) => Ok(Some(PileValue::Binary(BinaryPileValue::Blob {
|
||||
mime: out_mime,
|
||||
bytes: ArcBytes(Arc::new(out_bytes)),
|
||||
}))),
|
||||
|
||||
Err(_) => Ok(Some(PileValue::Binary(BinaryPileValue::Blob {
|
||||
mime,
|
||||
bytes: ArcBytes(Arc::new(bytes)),
|
||||
}))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ObjectExtractor for ImageExtractor {
|
||||
async fn field(
|
||||
&self,
|
||||
_state: &ExtractState,
|
||||
name: &Label,
|
||||
args: Option<&str>,
|
||||
) -> Result<Option<PileValue>, std::io::Error> {
|
||||
let Some(args) = args else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
trace!(?args, "Getting field {name:?} from ImageExtractor",);
|
||||
|
||||
match name.as_str() {
|
||||
"maxdim" => self.apply::<MaxDimTransformer>(args).await,
|
||||
"crop" => self.apply::<CropTransformer>(args).await,
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(clippy::unwrap_used)]
|
||||
async fn fields(&self) -> Result<Vec<Label>, std::io::Error> {
|
||||
Ok(vec![
|
||||
Label::new("maxdim").unwrap(),
|
||||
Label::new("crop").unwrap(),
|
||||
])
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user