#![doc = include_str!("../examples/image_thumbs.yaml")]
use ::image::ImageFormat;
use config::Config;
use object_store::ObjectStore;
use object_store::path::Path;
use thiserror::Error;
pub use crate::error::Error;
pub use crate::error::ThumbsResult;
pub use crate::model::ImageThumbs;
pub use model::Params;
pub use object_store::gcp::GoogleCloudStorage;
mod error;
mod gcs;
mod image;
mod model;
mod storage;
impl<T: ObjectStore> ImageThumbs<T> {
pub async fn create_thumbs_dir(
&self,
directory: Option<&str>,
dest_dir: &str,
force_override: bool,
) -> ThumbsResult<()> {
let prefix = match directory {
Some(p) => Some(Path::parse(p)?),
None => None,
};
let mut names = self.list_folder(prefix.as_ref()).await?;
if force_override {
let existent_thumbs = self.list_folder(Some(&Path::parse(dest_dir)?)).await?;
names = self.filter_existent_thumbs(names, &existent_thumbs)?;
}
for name in names {
self.create_thumbs(name.as_ref(), dest_dir, force_override)
.await?;
}
Ok(())
}
pub async fn create_thumbs(
&self,
file: &str,
dest_dir: &str,
force_override: bool,
) -> ThumbsResult<()> {
let image = self.download_image(file).await?;
self.create_thumbs_from_bytes(
image.bytes,
dest_dir,
&image.stem,
image.format,
force_override,
(0.5, 0.5),
)
.await
}
pub async fn create_thumbs_man_center(
&self,
file: &str,
dest_dir: &str,
force_override: bool,
center: (f32, f32),
) -> ThumbsResult<()> {
let image = self.download_image(file).await?;
self.create_thumbs_from_bytes(
image.bytes,
dest_dir,
&image.stem,
image.format,
force_override,
center,
)
.await
}
pub async fn create_thumbs_from_bytes(
&self,
bytes: Vec<u8>,
dest_dir: &str,
image_name: &str,
format: ImageFormat,
force_override: bool,
center: (f32, f32),
) -> ThumbsResult<()> {
let dest_dir = Path::parse(dest_dir)?;
let thumbs = self
.create_thumb_images_from_bytes(
bytes,
dest_dir,
image_name,
format,
force_override,
center,
)
.await?;
self.upload_thumbs(thumbs).await
}
#[doc = include_str!("../examples/image_thumbs.yaml")]
fn settings(config: &str) -> ThumbsResult<Vec<Params>> {
Ok(Config::builder()
.add_source(config::File::with_name(config))
.build()?
.get("thumbs")?)
}
}
#[cfg(test)]
mod tests {
use image::ImageFormat;
use object_store::path::Path;
use sequential_test::sequential;
use tokio::fs::File;
use tokio::io::{AsyncReadExt, BufReader};
use crate::ImageThumbs;
use crate::model::ImageDetails;
#[tokio::test]
#[ignore]
#[sequential]
async fn create_thumbs() {
let client = ImageThumbs::new("src/test/image_thumbs").unwrap();
client
.create_thumbs("penguin.jpg", "/test_dir", false)
.await
.unwrap();
client
.create_thumbs("penguin.png", "/test_dir", false)
.await
.unwrap();
client
.download_image("test_dir/penguin_standard.jpg")
.await
.unwrap();
client
.download_image("test_dir/penguin_mini.jpg")
.await
.unwrap();
client
.download_image("test_dir/penguin_standard.png")
.await
.unwrap();
client
.download_image("test_dir/penguin_mini.png")
.await
.unwrap();
client
.delete("test_dir/penguin_standard.jpg")
.await
.unwrap();
client.delete("test_dir/penguin_mini.jpg").await.unwrap();
client
.delete("test_dir/penguin_standard.png")
.await
.unwrap();
client.delete("test_dir/penguin_mini.png").await.unwrap();
}
#[tokio::test]
#[ignore]
#[sequential]
async fn create_thumbs_dir() {
let client = ImageThumbs::new("src/test/image_thumbs").unwrap();
client
.create_thumbs_dir(None, "thumbs", false)
.await
.unwrap();
client
.download_image("thumbs/penguin_standard.jpg")
.await
.unwrap();
client
.download_image("thumbs/penguin_mini.jpg")
.await
.unwrap();
client
.download_image("thumbs/penguin_standard.png")
.await
.unwrap();
client
.download_image("thumbs/penguin_mini.png")
.await
.unwrap();
client.delete("thumbs/penguin_standard.jpg").await.unwrap();
client.delete("thumbs/penguin_mini.jpg").await.unwrap();
client.delete("thumbs/penguin_standard.png").await.unwrap();
client.delete("thumbs/penguin_mini.png").await.unwrap();
client
.create_thumbs_dir(Some("/"), "thumbs", false)
.await
.unwrap();
client
.download_image("thumbs/penguin_standard.jpg")
.await
.unwrap();
client
.download_image("thumbs/penguin_mini.jpg")
.await
.unwrap();
client
.download_image("thumbs/penguin_standard.png")
.await
.unwrap();
client
.download_image("thumbs/penguin_mini.png")
.await
.unwrap();
client.delete("thumbs/penguin_standard.jpg").await.unwrap();
client.delete("thumbs/penguin_mini.jpg").await.unwrap();
client.delete("thumbs/penguin_standard.png").await.unwrap();
client.delete("thumbs/penguin_mini.png").await.unwrap();
}
#[tokio::test]
#[ignore]
#[sequential]
async fn create_thumbs_from_bytes() {
let client = ImageThumbs::new("src/test/image_thumbs").unwrap();
{
let test_jpg = File::open("src/test/mock_data/testBucket/penguin.jpg")
.await
.unwrap();
let mut reader = BufReader::new(test_jpg);
let mut buffer = Vec::new();
reader.read_to_end(&mut buffer).await.unwrap();
client
.create_thumbs_from_bytes(
buffer,
"/from_bytes_test",
"penguin",
ImageFormat::Jpeg,
false,
(0.5, 0.5),
)
.await
.unwrap();
}
{
let test_png = File::open("src/test/mock_data/testBucket/penguin.png")
.await
.unwrap();
let mut reader = BufReader::new(test_png);
let mut buffer = Vec::new();
reader.read_to_end(&mut buffer).await.unwrap();
client
.create_thumbs_from_bytes(
buffer,
"/from_bytes_test",
"penguin",
ImageFormat::Png,
false,
(0.5, 0.5),
)
.await
.unwrap();
}
client
.download_image("from_bytes_test/penguin_standard.png")
.await
.unwrap();
client
.download_image("from_bytes_test/penguin_mini.png")
.await
.unwrap();
client
.download_image("from_bytes_test/penguin_standard.png")
.await
.unwrap();
client
.download_image("from_bytes_test/penguin_mini.png")
.await
.unwrap();
client
.delete("from_bytes_test/penguin_standard.jpg")
.await
.unwrap();
client
.delete("from_bytes_test/penguin_mini.jpg")
.await
.unwrap();
client
.delete("from_bytes_test/penguin_standard.png")
.await
.unwrap();
client
.delete("from_bytes_test/penguin_mini.png")
.await
.unwrap();
}
#[tokio::test]
#[ignore]
#[sequential]
async fn override_behaviour() {
let client = ImageThumbs::new("src/test/image_thumbs").unwrap();
let broken_thumb = ImageDetails {
stem: "penguin_standard".to_string(),
format: ImageFormat::Png,
path: Path::parse("/thumbs").unwrap(),
bytes: vec![1, 2, 3, 4, 5, 6, 7, 8, 9],
};
client.upload_thumbs(vec![broken_thumb]).await.unwrap();
client
.create_thumbs_dir(Some("/"), "thumbs", false)
.await
.unwrap();
client
.download_image("thumbs/penguin_standard.jpg")
.await
.unwrap();
client
.download_image("thumbs/penguin_mini.jpg")
.await
.unwrap();
assert!(
client
.download_image("thumbs/penguin_standard.png")
.await
.is_err(),
"This image should not be overwritten"
);
client
.download_image("thumbs/penguin_mini.png")
.await
.unwrap();
client
.create_thumbs_dir(Some("/"), "thumbs", true)
.await
.unwrap();
assert_ne!(
client
.download_image("thumbs/penguin_standard.png")
.await
.unwrap()
.bytes,
vec![1, 2, 3, 4, 5, 6, 7, 8, 9],
"The image should have been overwritten"
);
client.delete("thumbs/penguin_standard.jpg").await.unwrap();
client.delete("thumbs/penguin_mini.jpg").await.unwrap();
client.delete("thumbs/penguin_standard.png").await.unwrap();
client.delete("thumbs/penguin_mini.png").await.unwrap();
}
}