diff --git a/Cargo.toml b/Cargo.toml index 90d7ade..7b02d7f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,4 +17,5 @@ serde = {version = "1.0", features = ["derive"] } tokio = { version = "1.32", features = ["full"] } bytes = "1.5" futures = "0.3.28" -mime = "0.3" \ No newline at end of file +mime = "0.3" +async-trait = "0.1.73" \ No newline at end of file diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..010d04e --- /dev/null +++ b/src/error.rs @@ -0,0 +1,20 @@ +use image::ImageError; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum Error { + #[error("Storage error: {0}")] + Storage(#[from] object_store::Error), + #[error("Invalid path: {0}")] + Path(#[from] object_store::path::Error), + #[error("Configuration error: {0}")] + Config(#[from] config::ConfigError), + #[error("Image error: {0}")] + Image(#[from] ImageError), + #[error("Image format not supported")] + NotSupported, + #[error("Utf-8 error")] + Utf, +} + +pub type ThumbsResult = Result; diff --git a/src/gcs.rs b/src/gcs.rs index 6b2864e..dcfd0a5 100644 --- a/src/gcs.rs +++ b/src/gcs.rs @@ -1,7 +1,6 @@ -use crate::{ImageThumbs, ThumbsResult}; -use config::Config; use object_store::gcp::{GoogleCloudStorage, GoogleCloudStorageBuilder}; -use object_store::ClientOptions; + +use crate::{ImageThumbs, ThumbsResult}; impl ImageThumbs { /// Create new ImageThumbs instance connected to Google Cloud Storage using the environment @@ -14,26 +13,13 @@ impl ImageThumbs { /// # Arguments /// * config - Path to the config file from the crate root (`.yaml` may be omitted) pub async fn new(config: &str) -> ThumbsResult { - let settings = Config::builder() - .add_source(config::File::with_name(config)) - .build()? - .get("thumbs")?; - - #[allow(unused_mut)] - let mut client_options = ClientOptions::new() - .with_content_type_for_suffix("jpg", mime::IMAGE_JPEG.to_string()) - .with_content_type_for_suffix("jpeg", mime::IMAGE_JPEG.to_string()) - .with_content_type_for_suffix("png", mime::IMAGE_PNG.to_string()); - - #[cfg(test)] - { - client_options = client_options.with_allow_http(true); - } - let client = GoogleCloudStorageBuilder::from_env() - .with_client_options(client_options) + .with_client_options(Self::client_options()) .build()?; - Ok(Self { client, settings }) + Ok(Self { + client, + settings: Self::settings(config)?, + }) } } diff --git a/src/image.rs b/src/image.rs index fcdb6e0..5fe78cf 100644 --- a/src/image.rs +++ b/src/image.rs @@ -8,7 +8,8 @@ use image::{load_from_memory_with_format, ImageFormat}; use object_store::path::Path; use object_store::ObjectStore; -use crate::{Error, ImageDetails, ImageThumbs, Mode, ThumbsResult}; +use crate::model::{ImageDetails, Mode}; +use crate::{Error, ImageThumbs, ThumbsResult}; impl ImageThumbs { pub(crate) async fn create_thumbs_from_bytes( diff --git a/src/lib.rs b/src/lib.rs index 335adf2..4e43ac7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,60 +1,20 @@ -use ::image::{ImageError, ImageFormat}; +use ::image::ImageFormat; +use config::Config; use object_store::path::Path; use object_store::ObjectStore; -use serde::Deserialize; use thiserror::Error; +pub use crate::error::Error; +pub use crate::error::ThumbsResult; +pub use crate::model::ImageThumbs; +use crate::model::Params; + +mod error; mod gcs; mod image; +mod model; mod storage; -#[derive(Error, Debug)] -pub enum Error { - #[error("Storage error: {0}")] - Storage(#[from] object_store::Error), - #[error("Invalid path: {0}")] - Path(#[from] object_store::path::Error), - #[error("Configuration error: {0}")] - Config(#[from] config::ConfigError), - #[error("Image error: {0}")] - Image(#[from] ImageError), - #[error("Image format not supported")] - NotSupported, - #[error("Utf-8 error")] - Utf, -} - -pub type ThumbsResult = Result; - -#[derive(Debug)] -pub struct ImageThumbs { - client: T, - settings: Vec, -} - -#[derive(Deserialize, Debug, Clone)] -struct Params { - name: String, - quality: u8, - size: (u32, u32), - mode: Mode, -} - -#[derive(Deserialize, Debug, Clone)] -#[serde(rename_all = "snake_case")] -enum Mode { - Fit, - Crop, -} - -#[derive(Debug)] -struct ImageDetails { - stem: String, - format: ImageFormat, - path: Path, - bytes: Vec, -} - impl ImageThumbs { /// Get image from object storage, create thumbnails, and put them in the `dest_dir` directory pub async fn create_thumbs_dir( @@ -111,18 +71,25 @@ impl ImageThumbs { .await?; self.upload_thumbs(thumbs).await } + + fn settings(config: &str) -> ThumbsResult> { + Ok(Config::builder() + .add_source(config::File::with_name(config)) + .build()? + .get("thumbs")?) + } } #[cfg(test)] mod tests { + use crate::model::ImageDetails; + use crate::ImageThumbs; use image::ImageFormat; use object_store::path::Path; use object_store::ObjectStore; use tokio::fs::File; use tokio::io::{AsyncReadExt, BufReader}; - use crate::{ImageDetails, ImageThumbs}; - #[tokio::test] async fn from_cloud() { let client = ImageThumbs::new("src/test/image_thumbs").await.unwrap(); diff --git a/src/model.rs b/src/model.rs new file mode 100644 index 0000000..f074bfe --- /dev/null +++ b/src/model.rs @@ -0,0 +1,32 @@ +use image::ImageFormat; +use object_store::path::Path; +use serde::Deserialize; + +#[derive(Debug)] +pub struct ImageThumbs { + pub(crate) client: T, + pub(crate) settings: Vec, +} + +#[derive(Deserialize, Debug, Clone)] +pub(crate) struct Params { + pub(crate) name: String, + pub(crate) quality: u8, + pub(crate) size: (u32, u32), + pub(crate) mode: Mode, +} + +#[derive(Deserialize, Debug, Clone)] +#[serde(rename_all = "snake_case")] +pub(crate) enum Mode { + Fit, + Crop, +} + +#[derive(Debug)] +pub(crate) struct ImageDetails { + pub(crate) stem: String, + pub(crate) format: ImageFormat, + pub(crate) path: Path, + pub(crate) bytes: Vec, +} diff --git a/src/storage.rs b/src/storage.rs index dcc8099..4ecc61f 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -1,11 +1,26 @@ use image::ImageFormat; use object_store::path::{Path, PathPart}; -use object_store::{ObjectMeta, ObjectStore}; +use object_store::{ClientOptions, ObjectMeta, ObjectStore}; +use crate::model::ImageDetails; use crate::Error::NotSupported; -use crate::{ImageDetails, ImageThumbs, ThumbsResult}; +use crate::{ImageThumbs, ThumbsResult}; impl ImageThumbs { + pub(crate) fn client_options() -> ClientOptions { + #[allow(unused_mut)] + let mut client_options = ClientOptions::new() + .with_content_type_for_suffix("jpg", mime::IMAGE_JPEG.to_string()) + .with_content_type_for_suffix("jpeg", mime::IMAGE_JPEG.to_string()) + .with_content_type_for_suffix("png", mime::IMAGE_PNG.to_string()); + + #[cfg(test)] + { + client_options = client_options.with_allow_http(true); + } + client_options + } + pub(crate) async fn upload_thumbs(&self, images: Vec) -> ThumbsResult<()> { for image in images { let path = Self::generate_path(&image.path, &image.stem, &image.format);