Skip to content

Commit

Permalink
Merge pull request #12 from Lurk/transformations
Browse files Browse the repository at this point in the history
Transformations
  • Loading branch information
Lurk authored Oct 21, 2023
2 parents 50d8585 + b001a28 commit 1cc0e01
Show file tree
Hide file tree
Showing 9 changed files with 804 additions and 7 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ jobs:
# https://docs.github.com/en/actions/learn-github-actions/contexts#context-availability
strategy:
matrix:
msrv: [1.63.0] # due to openssl = { version = "0.10", features = ["vendored"] }
name: ubuntu / ${{ matrix.msrv }}
msrv: [1.65.0] # due to openssl-src v300.1.5+3.1.3
name: ubuntu / ${{ matrix.msrv }}
steps:
- uses: actions/checkout@v4
with:
Expand Down
6 changes: 4 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ name = "cloudinary"
description = "A Rust library for the Cloudinary API"
license = "MIT OR Apache-2.0"
keywords = ["cloudinary", "api", "image", "video", "upload"]
version = "0.2.0"
version = "0.2.1"
edition = "2021"
rust-version = "1.63.0" # due to openssl = { version = "0.10", features = ["vendored"] }
rust-version = "1.65.0" # due to openssl = { version = "0.10", features = ["vendored"] }

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

Expand All @@ -20,9 +20,11 @@ serde_json = "1.0.107"
sha1 = "0.10.6"
tokio = { version = "1.33.0", features = ["rt", "macros"] }
tokio-util = "0.7.9"
regex = "1.10.1"
log = "0.4.20"
openssl = { version = "0.10.57", features = ["vendored"] }
openssl-src = "111.28.0+1.1.1w" # due to minimal version fail
url = "2.4.1"

[dev-dependencies]
dotenv = "0.15.0"
28 changes: 25 additions & 3 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
# Cloudinary Rust API
# cloudinary

[![codecov](https://codecov.io/gh/Lurk/cloudinary_rs/branch/main/graph/badge.svg?token=K8H5DLTSX4)](https://codecov.io/gh/Lurk/cloudinary_rs)
[![crates.io](https://img.shields.io/crates/v/cloudinary.svg)](https://crates.io/crates/cloudinary)
[![Released API docs](https://docs.rs/cloudinary/badge.svg)](https://docs.rs/cloudinary)

At the moment, there is only half-backed upload functionality, but if you need more, please let me know.
At the moment, there is only half-backed upload and transformation functionality, but if you need more, please let
me know.

## Usage
## Upload an image

```rust
use cloudinary::{Source, Cloudinary};
Expand All @@ -15,3 +16,24 @@ let options = UploadOptions::new().set_public_id("file.jpg".to_string());
let cloudinary = Cloudinary::new("api_key".to_string(), "cloud_name".to_string(), "api_secret".to_string() );
let result = cloudinary.upload_image(Source::Path("./image.jpg".into()), &options);
```

## Transform an image

```rust
use cloudinary::transformation::{
Transformations::Resize, resize_mode::ResizeMode::ScaleByWidth, Image, aspect_ratio::AspectRatio
};

let image = Image::new("test".into(), "path/name.png".into())
.add_transformation(Resize(ScaleByWidth{ width:100, ar: None, liquid:None}));
assert_eq!(
image.to_string(),
"https://res.cloudinary.com/test/image/upload/c_scale,w_100/path/name.png"
);
```
## Minimum supported Rust version

The minimum supported Rust version for this crate is 1.65


License: MIT OR Apache-2.0
36 changes: 36 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,39 @@
//! [![codecov](https://codecov.io/gh/Lurk/cloudinary_rs/branch/main/graph/badge.svg?token=K8H5DLTSX4)](https://codecov.io/gh/Lurk/cloudinary_rs)
//! [![crates.io](https://img.shields.io/crates/v/cloudinary.svg)](https://crates.io/crates/cloudinary)
//! [![Released API docs](https://docs.rs/cloudinary/badge.svg)](https://docs.rs/cloudinary)
//!
//! At the moment, there is only half-backed upload and transformation functionality, but if you need more, please let
//! me know.
//!
//! # Upload an image
//!
//! ```rust
//! use cloudinary::{Source, Cloudinary};
//! use cloudinary::upload::{UploadOptions};
//! let options = UploadOptions::new().set_public_id("file.jpg".to_string());
//! let cloudinary = Cloudinary::new("api_key".to_string(), "cloud_name".to_string(), "api_secret".to_string() );
//! let result = cloudinary.upload_image(Source::Path("./image.jpg".into()), &options);
//! ```
//!
//! # Transform an image
//!
//! ```rust
//! use cloudinary::transformation::{
//! Transformations::Resize, resize_mode::ResizeMode::ScaleByWidth, Image, aspect_ratio::AspectRatio
//! };
//!
//! let image = Image::new("test".into(), "path/name.png".into())
//! .add_transformation(Resize(ScaleByWidth{ width:100, ar: None, liquid:None}));
//! assert_eq!(
//! image.to_string(),
//! "https://res.cloudinary.com/test/image/upload/c_scale,w_100/path/name.png"
//! );
//! ```
//! # Minimum supported Rust version
//!
//! The minimum supported Rust version for this crate is 1.65
//!
pub mod transformation;
pub mod upload;

use anyhow::{Context, Result};
Expand Down
21 changes: 21 additions & 0 deletions src/transformation/aspect_ratio.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use std::fmt::{Display, Formatter};

#[derive(Debug, Clone)]
pub enum AspectRatio {
/// Ignore the aspect ratio of the input and stretch to exactly the given width or height values.
Ignore,
/// The usual way to represent aspect ratio is by using a colon, e.g. 4:3.
Sides(u32, u32),
/// A decimal value representing the width divided by the height (e.g., 0.5).
Result(f32),
}

impl Display for AspectRatio {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
AspectRatio::Sides(width, height) => write!(f, "ar_{}:{}", width, height),
AspectRatio::Result(result) => write!(f, "ar_{}", result),
AspectRatio::Ignore => write!(f, "fl_ignore_aspect_ratio"),
}
}
}
174 changes: 174 additions & 0 deletions src/transformation/crop_mode.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
use std::fmt::Display;

use super::{aspect_ratio::AspectRatio, gravity::Gravity};

#[derive(Debug, Clone)]
pub enum CropMode {
/// Creates an asset with the exact specified width and AspectRatio without distorting the asset. This option first
/// scales as much as needed to fill the specified dimensions. If the requested aspect ratio is
/// different than the original, cropping will occur on the dimension that exceeds the requested size after
/// scaling. You can specify which part of the original asset you want to keep if cropping occurs using the
/// gravity (set to 'center' by default).
FillByWidth {
width: u32,
ar: Option<AspectRatio>,
gravity: Option<Gravity>,
},
/// Same as FillByWidth, but uses the original height instead of the original width to calculate the aspect ratio.
FillByHeight {
height: u32,
ar: Option<AspectRatio>,
gravity: Option<Gravity>,
},
/// Creates an asset with the exact specified width and height without distorting the asset. This option first
/// scales as much as needed to fill the specified dimensions. If the requested aspect ratio is
/// different than the original, cropping will occur on the dimension that exceeds the requested size after
/// scaling. You can specify which part of the original asset you want to keep if cropping occurs using the
/// gravity (set to 'center' by default).
Fill {
width: u32,
height: u32,
gravity: Option<Gravity>,
},
}

impl Display for CropMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CropMode::FillByWidth { width, ar, gravity } => write!(
f,
"{}c_fill{},w_{}",
ar.as_ref()
.map(|ar| format!("{},", ar))
.unwrap_or("".into()),
gravity
.as_ref()
.map(|g| format!(",{}", g))
.unwrap_or("".into()),
width,
),
CropMode::FillByHeight {
height,
ar,
gravity,
} => write!(
f,
"{}c_fill{},h_{}",
ar.as_ref()
.map(|ar| format!("{},", ar))
.unwrap_or("".into()),
gravity
.as_ref()
.map(|g| format!(",{}", g))
.unwrap_or("".into()),
height,
),
CropMode::Fill {
width,
height,
gravity,
} => write!(
f,
"c_fill{},w_{},h_{}",
gravity
.as_ref()
.map(|g| format!(",{}", g))
.unwrap_or("".into()),
width,
height,
),
}
}
}

#[cfg(test)]
mod tests {

use super::*;

#[test]
fn test_fill_by_width() {
assert_eq!(
CropMode::FillByWidth {
width: 100,
ar: None,
gravity: None,
}
.to_string(),
"c_fill,w_100"
);
assert_eq!(
CropMode::FillByWidth {
width: 100,
ar: Some(AspectRatio::Ignore),
gravity: None,
}
.to_string(),
"fl_ignore_aspect_ratio,c_fill,w_100"
);

assert_eq!(
CropMode::FillByWidth {
width: 100,
ar: Some(AspectRatio::Sides(16, 9)),
gravity: Some(Gravity::North),
}
.to_string(),
"ar_16:9,c_fill,g_north,w_100"
);
}

#[test]
fn test_fill_by_height() {
assert_eq!(
CropMode::FillByHeight {
height: 100,
ar: None,
gravity: None,
}
.to_string(),
"c_fill,h_100"
);
assert_eq!(
CropMode::FillByHeight {
height: 100,
ar: Some(AspectRatio::Ignore),
gravity: None,
}
.to_string(),
"fl_ignore_aspect_ratio,c_fill,h_100"
);

assert_eq!(
CropMode::FillByHeight {
height: 100,
ar: Some(AspectRatio::Sides(16, 9)),
gravity: Some(Gravity::North),
}
.to_string(),
"ar_16:9,c_fill,g_north,h_100"
);
}

#[test]
fn test_fill() {
assert_eq!(
CropMode::Fill {
width: 100,
height: 100,
gravity: None,
}
.to_string(),
"c_fill,w_100,h_100"
);
assert_eq!(
CropMode::Fill {
width: 100,
height: 100,
gravity: Some(Gravity::AutoClassic),
}
.to_string(),
"c_fill,g_auto:classic,w_100,h_100"
);
}
}
Loading

0 comments on commit 1cc0e01

Please sign in to comment.