use bytes::Bytes;
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
#[derive(Debug, Clone)]
pub enum FileInput {
Url(String),
Path(PathBuf),
Bytes {
data: Bytes,
filename: Option<String>,
content_type: Option<String>,
},
}
impl FileInput {
pub fn from_url(url: impl Into<String>) -> Self {
Self::Url(url.into())
}
pub fn from_path(path: impl AsRef<Path>) -> Self {
Self::Path(path.as_ref().to_path_buf())
}
pub fn from_bytes(data: impl Into<Bytes>) -> Self {
Self::Bytes {
data: data.into(),
filename: None,
content_type: None,
}
}
pub fn from_bytes_with_metadata(
data: impl Into<Bytes>,
filename: Option<String>,
content_type: Option<String>,
) -> Self {
Self::Bytes {
data: data.into(),
filename,
content_type,
}
}
pub fn is_url(&self) -> bool {
matches!(self, Self::Url(_))
}
pub fn is_path(&self) -> bool {
matches!(self, Self::Path(_))
}
pub fn is_bytes(&self) -> bool {
matches!(self, Self::Bytes { .. })
}
pub fn as_url(&self) -> Option<&str> {
match self {
Self::Url(url) => Some(url),
_ => None,
}
}
pub fn as_path(&self) -> Option<&Path> {
match self {
Self::Path(path) => Some(path),
_ => None,
}
}
}
impl From<String> for FileInput {
fn from(s: String) -> Self {
if s.starts_with("http://") || s.starts_with("https://") {
Self::Url(s)
} else {
Self::Path(PathBuf::from(s))
}
}
}
impl From<&str> for FileInput {
fn from(s: &str) -> Self {
Self::from(s.to_string())
}
}
impl From<PathBuf> for FileInput {
fn from(path: PathBuf) -> Self {
Self::Path(path)
}
}
impl From<&Path> for FileInput {
fn from(path: &Path) -> Self {
Self::Path(path.to_path_buf())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FileOutput {
pub url: String,
pub filename: Option<String>,
pub content_type: Option<String>,
pub size: Option<u64>,
}
impl FileOutput {
pub fn new(url: impl Into<String>) -> Self {
Self {
url: url.into(),
filename: None,
content_type: None,
size: None,
}
}
pub fn with_filename(mut self, filename: impl Into<String>) -> Self {
self.filename = Some(filename.into());
self
}
pub fn with_content_type(mut self, content_type: impl Into<String>) -> Self {
self.content_type = Some(content_type.into());
self
}
pub fn with_size(mut self, size: u64) -> Self {
self.size = Some(size);
self
}
pub async fn download(&self) -> crate::Result<Bytes> {
let response = reqwest::get(&self.url).await?;
let bytes = response.bytes().await?;
Ok(bytes)
}
pub async fn save_to_path(&self, path: impl AsRef<Path>) -> crate::Result<()> {
let bytes = self.download().await?;
tokio::fs::write(path, bytes).await?;
Ok(())
}
}
impl From<String> for FileOutput {
fn from(url: String) -> Self {
Self::new(url)
}
}
impl From<&str> for FileOutput {
fn from(url: &str) -> Self {
Self::new(url)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum FileEncodingStrategy {
Base64DataUrl,
Multipart,
}
impl Default for FileEncodingStrategy {
fn default() -> Self {
Self::Multipart
}
}