#[cfg(feature = "download")]
use crate::v1::error::APIError;
#[cfg(feature = "download")]
use crate::v1::helpers::generate_file_name;
use crate::v1::resources::shared::FileUpload;
#[cfg(feature = "download")]
use base64::{engine::general_purpose, Engine as _};
use derive_builder::Builder;
#[cfg(feature = "download")]
use futures::future;
use serde::{Deserialize, Serialize};
use std::fmt::Display;
#[derive(Serialize, Deserialize, Debug, Default, Builder, Clone, PartialEq)]
#[builder(name = "CreateImageParametersBuilder")]
#[builder(setter(into, strip_option), default)]
pub struct CreateImageParameters {
pub prompt: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub model: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub n: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub quality: Option<ImageQuality>,
#[serde(skip_serializing_if = "Option::is_none")]
pub response_format: Option<ResponseFormat>,
#[serde(skip_serializing_if = "Option::is_none")]
pub size: Option<ImageSize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub style: Option<ImageStyle>,
#[serde(skip_serializing_if = "Option::is_none")]
pub user: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Default, Builder, Clone, PartialEq)]
#[builder(name = "EditImageParametersBuilder")]
#[builder(setter(into, strip_option), default)]
pub struct EditImageParameters {
pub image: FileUpload,
pub prompt: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub background: Option<BackgroundStyle>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mask: Option<FileUpload>,
#[serde(skip_serializing_if = "Option::is_none")]
pub quality: Option<ImageQuality>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mime_type: Option<MimeType>,
#[serde(skip_serializing_if = "Option::is_none")]
pub model: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub n: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub size: Option<ImageSize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub response_format: Option<ResponseFormat>,
#[serde(skip_serializing_if = "Option::is_none")]
pub user: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Default, Builder, Clone, PartialEq)]
#[builder(name = "CreateImageVariationParametersBuilder")]
#[builder(setter(into, strip_option), default)]
pub struct CreateImageVariationParameters {
pub image: FileUpload,
#[serde(skip_serializing_if = "Option::is_none")]
pub model: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub n: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub response_format: Option<ResponseFormat>,
#[serde(skip_serializing_if = "Option::is_none")]
pub size: Option<ImageSize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub user: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct ImageResponse {
pub created: u32,
pub data: Vec<ImageData>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub enum ImageSize {
#[serde(rename = "256x256")]
Size256X256,
#[serde(rename = "512x512")]
Size512X512,
#[serde(rename = "1024x1024")]
Size1024X1024,
#[serde(rename = "1024x1536")]
Size1024X1536,
#[serde(rename = "1536x1024")]
Size1536X1024,
#[serde(rename = "1792x1024")]
Size1792X1024,
#[serde(rename = "1024x1792")]
Size1024X1792,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum BackgroundStyle {
Transparent,
Opaque,
Auto,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum ImageQuality {
Standard,
Hd,
High,
Medium,
Low,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum MimeType {
#[serde(rename = "image/png")]
Png,
#[serde(rename = "image/jpeg")]
Jpeg,
#[serde(rename = "image/webp")]
Webp,
#[serde(rename = "application/octet-stream")]
OctetStream,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum ImageStyle {
Vivid,
Natural,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum ResponseFormat {
Url,
B64Json,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(untagged)]
pub enum ImageData {
Url {
url: String,
#[serde(skip_serializing_if = "Option::is_none")]
revised_prompt: Option<String>,
},
B64Json {
b64_json: String,
#[serde(skip_serializing_if = "Option::is_none")]
revised_prompt: Option<String>,
},
}
impl ImageResponse {
#[cfg(feature = "download")]
pub async fn save(&self, path: &str) -> Result<Vec<String>, APIError> {
let mut files = vec![];
let mut handles = vec![];
for item in self.data.clone() {
let path = path.to_owned();
handles.push(tokio::spawn(async move { item.save_to_disk(&path).await }));
}
let results = future::join_all(handles).await;
for result in results {
match result {
Ok(path) => match path {
Ok(item) => files.push(item),
Err(_error) => (),
},
Err(_error) => (),
}
}
Ok(files)
}
}
impl ImageData {
#[cfg(feature = "download")]
pub async fn save_to_disk(&self, path: &str) -> Result<String, APIError> {
match self {
ImageData::Url { url, .. } => self.download_image_from_url(url, path).await,
ImageData::B64Json { b64_json, .. } => {
self.download_b64_json_image(b64_json, path).await
}
}
}
#[cfg(feature = "download")]
async fn download_image_from_url(&self, url: &str, path: &str) -> Result<String, APIError> {
let response = reqwest::get(url)
.await
.map_err(|error| APIError::FileError(error.to_string()))?;
let full_path = generate_file_name(path, 16, "png");
tokio::fs::write(
&full_path,
response
.bytes()
.await
.map_err(|error| APIError::FileError(error.to_string()))?,
)
.await
.map_err(|error| APIError::FileError(error.to_string()))?;
Ok(full_path)
}
#[cfg(feature = "download")]
async fn download_b64_json_image(
&self,
b64_json: &str,
path: &str,
) -> Result<String, APIError> {
let full_path = generate_file_name(path, 16, "png");
let bytes = general_purpose::STANDARD.decode(b64_json).unwrap();
tokio::fs::write(&full_path, bytes)
.await
.map_err(|error| APIError::FileError(error.to_string()))?;
Ok(full_path)
}
}
impl Display for BackgroundStyle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
BackgroundStyle::Transparent => "transparent",
BackgroundStyle::Opaque => "opaque",
BackgroundStyle::Auto => "auto",
}
)
}
}
impl Display for ImageQuality {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
ImageQuality::Standard => "standard",
ImageQuality::Hd => "hd",
ImageQuality::High => "high",
ImageQuality::Medium => "medium",
ImageQuality::Low => "low",
}
)
}
}
impl Display for ImageSize {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
ImageSize::Size256X256 => "256x256",
ImageSize::Size512X512 => "512x512",
ImageSize::Size1024X1024 => "1024x1024",
ImageSize::Size1536X1024 => "1536x1024",
ImageSize::Size1024X1536 => "1024x1536",
ImageSize::Size1792X1024 => "1792x1024",
ImageSize::Size1024X1792 => "1024x1792",
}
)
}
}
impl Display for MimeType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
MimeType::Png => "image/png",
MimeType::Jpeg => "image/jpeg",
MimeType::Webp => "image/webp",
MimeType::OctetStream => "application/octet-stream",
}
)
}
}
impl Display for ResponseFormat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
ResponseFormat::Url => "url",
ResponseFormat::B64Json => "b64_json",
}
)
}
}