use std::path::PathBuf;
#[derive(Debug, Clone)]
pub enum ImageSource {
File(PathBuf),
Url(String),
Base64(String),
Bytes {
data: Vec<u8>,
format: Option<ImageFormat>,
},
Emoji {
emoji: String,
size: f32,
},
Rgba {
data: Vec<u8>,
width: u32,
height: u32,
},
}
impl ImageSource {
pub fn file(path: impl Into<PathBuf>) -> Self {
Self::File(path.into())
}
pub fn url(url: impl Into<String>) -> Self {
Self::Url(url.into())
}
pub fn base64(data: impl Into<String>) -> Self {
Self::Base64(data.into())
}
pub fn bytes(data: Vec<u8>) -> Self {
Self::Bytes { data, format: None }
}
pub fn bytes_with_format(data: Vec<u8>, format: ImageFormat) -> Self {
Self::Bytes {
data,
format: Some(format),
}
}
pub fn emoji(emoji: impl Into<String>, size: f32) -> Self {
Self::Emoji {
emoji: emoji.into(),
size,
}
}
pub fn rgba(data: Vec<u8>, width: u32, height: u32) -> Self {
Self::Rgba {
data,
width,
height,
}
}
pub fn from_uri(uri: &str) -> Self {
if uri.starts_with("data:") {
Self::Base64(uri.to_string())
} else if uri.starts_with("http://") || uri.starts_with("https://") {
Self::Url(uri.to_string())
} else if uri.starts_with("emoji://") {
let rest = uri.strip_prefix("emoji://").unwrap_or("");
let (emoji, size) = if let Some(idx) = rest.find('?') {
let emoji_part = &rest[..idx];
let query = &rest[idx + 1..];
let size = query
.split('&')
.find_map(|param| {
let mut parts = param.splitn(2, '=');
if parts.next() == Some("size") {
parts.next().and_then(|v| v.parse::<f32>().ok())
} else {
None
}
})
.unwrap_or(64.0);
(emoji_part.to_string(), size)
} else {
(rest.to_string(), 64.0)
};
Self::Emoji { emoji, size }
} else if uri.starts_with("file://") {
let path = uri.strip_prefix("file://").unwrap_or(uri);
Self::File(PathBuf::from(path))
} else {
Self::File(PathBuf::from(uri))
}
}
}
impl From<&str> for ImageSource {
fn from(s: &str) -> Self {
Self::from_uri(s)
}
}
impl From<String> for ImageSource {
fn from(s: String) -> Self {
Self::from_uri(&s)
}
}
impl From<PathBuf> for ImageSource {
fn from(path: PathBuf) -> Self {
Self::File(path)
}
}
impl From<&std::path::Path> for ImageSource {
fn from(path: &std::path::Path) -> Self {
Self::File(path.to_path_buf())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ImageFormat {
Png,
Jpeg,
Gif,
WebP,
Bmp,
}
impl ImageFormat {
pub fn from_extension(ext: &str) -> Option<Self> {
match ext.to_lowercase().as_str() {
"png" => Some(Self::Png),
"jpg" | "jpeg" => Some(Self::Jpeg),
"gif" => Some(Self::Gif),
"webp" => Some(Self::WebP),
"bmp" => Some(Self::Bmp),
_ => None,
}
}
pub fn from_mime(mime: &str) -> Option<Self> {
match mime {
"image/png" => Some(Self::Png),
"image/jpeg" | "image/jpg" => Some(Self::Jpeg),
"image/gif" => Some(Self::Gif),
"image/webp" => Some(Self::WebP),
"image/bmp" => Some(Self::Bmp),
_ => None,
}
}
}