use std::path::Path;
use std::path::PathBuf;
pub use iced::image::Handle as ImageHandle;
use iced_native::image::Data;
use indexmap::IndexMap;
use ruma::api::exports::http::Uri;
pub fn make_content_path(content_url: &Uri) -> Option<PathBuf> {
let dir = make_content_folder(content_url)?;
make_content_filename(content_url).map(|filename| dir.join(filename))
}
pub fn make_content_filename(content_url: &Uri) -> Option<PathBuf> {
if !content_url.path().is_empty() {
let filename = content_url.path()[1..].to_string();
Some(PathBuf::from(filename))
} else {
None
}
}
pub fn make_content_folder(content_url: &Uri) -> Option<PathBuf> {
content_url.authority().map(|authority| {
PathBuf::from(format!(
"{}content/{}",
crate::data_dir!(),
authority.as_str().replace('.', "_")
))
})
}
pub fn content_exists(content_url: &Uri) -> bool {
make_content_path(content_url)
.map(|p| p.exists())
.unwrap_or(false)
}
pub fn infer_mimetype_from_bytes(data: &[u8]) -> String {
infer::get(&data)
.map(|filetype| filetype.mime_type().to_string())
.unwrap_or_else(|| String::from("application/octet-stream"))
}
pub fn get_filename(path: impl AsRef<Path>) -> String {
path.as_ref()
.file_name()
.map(|s| s.to_string_lossy().to_string())
.unwrap_or_else(|| String::from("unknown"))
}
pub fn get_in_memory(handle: &ImageHandle) -> &[u8] {
match handle.data() {
Data::Bytes(raw) => raw.as_slice(),
_ => unreachable!("We don't use path or pixel data for images, how did this happen?"),
}
}
const MAX_CACHE_SIZE: usize = 1000 * 1000 * 100; pub struct ThumbnailStore(IndexMap<Uri, ImageHandle>);
impl ThumbnailStore {
pub fn new() -> Self {
Self(IndexMap::new())
}
pub fn put_thumbnail(&mut self, thumbnail_url: Uri, thumbnail: ImageHandle) {
let cache_size: usize = self.0.values().map(|h| get_in_memory(h).len()).sum();
let thumbnail_size = get_in_memory(&thumbnail).len();
if cache_size + thumbnail_size > MAX_CACHE_SIZE {
let mut current_size = 0;
let mut remove_upto = 0;
for (index, size) in self.0.values().map(|h| get_in_memory(h).len()).enumerate() {
if current_size >= thumbnail_size {
remove_upto = index + 1;
break;
}
current_size += size;
}
for index in 0..remove_upto {
self.0.shift_remove_index(index);
}
} else {
self.0.insert(thumbnail_url, thumbnail);
}
}
pub fn has_thumbnail(&self, thumbnail_url: &Uri) -> bool {
self.0.contains_key(thumbnail_url)
}
pub fn get_thumbnail(&self, thumbnail_url: &Uri) -> Option<&ImageHandle> {
self.0.get(thumbnail_url)
}
pub fn invalidate_thumbnail(&mut self, thumbnail_url: &Uri) {
self.0.remove(thumbnail_url);
}
}
#[derive(Debug, Clone)]
pub enum ContentType {
Image,
Audio,
Video,
Other,
}
impl ContentType {
pub fn new(mimetype: &str) -> Self {
use ContentType::*;
if let Some(filetype) = mimetype.split('/').next() {
match filetype {
"image" => Image,
"audio" => Audio,
"video" => Video,
_ => Other,
}
} else {
Other
}
}
}
impl From<&str> for ContentType {
fn from(other: &str) -> Self {
ContentType::new(other)
}
}