use std::fs::{File, self}; use std::io::{BufReader, BufWriter}; use std::path::{Path, PathBuf}; use std::error::Error; use walkdir::WalkDir; use file_format::{FileFormat, Kind}; use snap; use deunicode::deunicode_with_tofu; use super::library::{AlbumArt, URI}; pub(super) fn normalize(input_string: &str) -> String { // Normalize the string to latin characters... this needs a lot of work let mut normalized = deunicode_with_tofu(input_string, " "); // Remove non alphanumeric characters normalized.retain(|c| c.is_alphanumeric()); normalized = normalized.to_ascii_lowercase(); normalized } /// Write any data structure which implements [serde::Serialize] /// out to a [bincode] encoded file compressed using [snap] pub(super) fn write_file( library: T, path: PathBuf, ) -> Result<(), Box> { // Create a temporary name for writing out let mut writer_name = path.clone(); writer_name.set_extension("tmp"); // Create a new BufWriter on the file and a snap frame encoder let writer = BufWriter::new(File::create(&writer_name)?); let mut e = snap::write::FrameEncoder::new(writer); // Write out the data bincode::serde::encode_into_std_write( library, &mut e, bincode::config::standard() .with_little_endian() .with_variable_int_encoding(), )?; fs::rename(writer_name, &path)?; Ok(()) } /// Read a file serialized out with [write_file] and turn it into /// the desired structure pub(super) fn read_file serde::Deserialize<'de>>(path: PathBuf) -> Result> { // Create a new snap reader over the file let file_reader = BufReader::new(File::open(path)?); let mut d = snap::read::FrameDecoder::new(file_reader); // Decode the library from the serialized data into the vec let library: T = bincode::serde::decode_from_std_read( &mut d, bincode::config::standard() .with_little_endian() .with_variable_int_encoding(), )?; Ok(library) } pub fn find_images(song_path: &Path) -> Result, Box> { let mut images: Vec = Vec::new(); let song_dir = song_path.parent().ok_or("")?; for target_file in WalkDir::new(song_dir) .follow_links(true) .into_iter() .filter_map(|e| e.ok()) { if target_file.depth() >= 3 { // Don't recurse very deep break; } let path = target_file.path(); if !path.is_file() { continue; } let format = FileFormat::from_file(path)?.kind(); if format != Kind::Image { break; } let image_uri = URI::Local(path.to_path_buf().canonicalize()?); images.push(AlbumArt::External(image_uri)); } Ok(images) }