diff --git a/Cargo.lock b/Cargo.lock index 3354901..5bd15fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -142,7 +142,7 @@ checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" [[package]] name = "icarus_models" -version = "0.5.6" +version = "0.6.3" dependencies = [ "josekit", "rand", diff --git a/Cargo.toml b/Cargo.toml index 3ddb8f5..6379891 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "icarus_models" -version = "0.5.6" +version = "0.6.3" edition = "2024" rust-version = "1.88" description = "models used for the icarus project" diff --git a/src/coverart.rs b/src/coverart.rs index 35c607b..edb5de4 100644 --- a/src/coverart.rs +++ b/src/coverart.rs @@ -37,6 +37,9 @@ impl CoverArt { Err(err) => Err(err), } } + + // TODO: Add method to save to filesystem + // TODO: Add method to remove from filesystem } #[cfg(test)] diff --git a/src/song.rs b/src/song.rs index 2fbd3d9..183d08b 100644 --- a/src/song.rs +++ b/src/song.rs @@ -1,11 +1,14 @@ -use std::io::Read; +use std::io::Write; + +use rand::Rng; +use serde::{Deserialize, Serialize}; use crate::constants; use crate::init; use crate::types; -use rand::Rng; -use serde::{Deserialize, Serialize}; +/// Length of characters of a filename to be generated +const FILENAME_LENGTH: i32 = 16; #[derive(Clone, Debug, Default, Deserialize, Serialize, utoipa::ToSchema)] pub struct Song { @@ -90,10 +93,114 @@ impl Song { } } - pub fn to_data(&self) -> Result, std::io::Error> { - let path_result = self.song_path(); + /// Saves the song to the filesystem using the song's data + pub fn save_to_filesystem(&self) -> Result<(), std::io::Error> { + match self.song_path() { + Ok(song_path) => match std::fs::File::create(&song_path) { + Ok(mut file) => match file.write_all(&self.data) { + Ok(_res) => Ok(()), + Err(err) => Err(err), + }, + Err(err) => Err(err), + }, + Err(err) => Err(err), + } + } - match path_result { + /// Removes the song from the filesystem + pub fn remove_from_filesystem(&self) -> Result<(), std::io::Error> { + match self.song_path() { + Ok(song_path) => { + let p = std::path::Path::new(&song_path); + if p.exists() { + match std::fs::remove_file(&p) { + Ok(_) => Ok(()), + Err(err) => Err(err), + } + } else { + Ok(()) + } + } + Err(err) => Err(err), + } + } +} + +/// Generates a filename. In order to save a song to the filesystem, the song must have +/// a directory and filename +pub fn generate_filename(typ: types::MusicTypes, randomize: bool) -> String { + let file_extension = match typ { + types::MusicTypes::DefaultMusicExtension => { + String::from(constants::file_extensions::audio::DEFAULTMUSICEXTENSION) + } + + types::MusicTypes::WavExtension => { + String::from(constants::file_extensions::audio::WAVEXTENSION) + } + types::MusicTypes::FlacExtension => { + String::from(constants::file_extensions::audio::FLACEXTENSION) + } + types::MusicTypes::MPThreeExtension => { + String::from(constants::file_extensions::audio::MPTHREEEXTENSION) + } + }; + + if randomize { + let mut filename: String = String::new(); + let some_chars: String = String::from("abcdefghij0123456789"); + let mut rng = rand::rng(); + + for _ in 0..FILENAME_LENGTH { + let index = rng.random_range(0..=19); + let rando_char = some_chars.chars().nth(index); + + if let Some(c) = rando_char { + filename.push(c); + } + } + filename + &file_extension + } else { + "track-output".to_string() + &file_extension + } +} + +/// I/O operations for songs +pub mod io { + use std::io::Read; + + /// Copies a song using the source song's data + pub fn copy_song( + song_source: &super::Song, + song_target: &mut super::Song, + ) -> Result<(), std::io::Error> { + match song_target.song_path() { + Ok(songpath) => { + let p = std::path::Path::new(&songpath); + if p.exists() { + Err(std::io::Error::other( + "Cannot copy song over to one that already exists", + )) + } else { + if song_target.data.is_empty() { + song_target.data = song_source.data.clone(); + } else { + song_target.data.clear(); + song_target.data = song_source.data.clone(); + } + + match song_target.save_to_filesystem() { + Ok(_) => Ok(()), + Err(err) => Err(err), + } + } + } + Err(err) => Err(err), + } + } + + /// Gets the raw file data of a song from the filesystem + pub fn to_data(song: &super::Song) -> Result, std::io::Error> { + match song.song_path() { Ok(path) => { let mut file = std::fs::File::open(path)?; let mut buffer: Vec = Vec::new(); @@ -108,46 +215,4 @@ impl Song { Err(er) => Err(er), } } - - pub fn generate_filename(&self, typ: types::MusicTypes, randomize: bool) -> String { - let mut filename: String = String::new(); - let filename_len = 10; - - let file_extension = match typ { - types::MusicTypes::DefaultMusicExtension => { - String::from(constants::file_extensions::audio::DEFAULTMUSICEXTENSION) - } - - types::MusicTypes::WavExtension => { - String::from(constants::file_extensions::audio::WAVEXTENSION) - } - types::MusicTypes::FlacExtension => { - String::from(constants::file_extensions::audio::FLACEXTENSION) - } - types::MusicTypes::MPThreeExtension => { - String::from(constants::file_extensions::audio::MPTHREEEXTENSION) - } - }; - - if randomize { - let some_chars: String = String::from("abcdefghij0123456789"); - let mut rng = rand::rng(); - - for _i in 0..filename_len { - let random_number: i32 = rng.random_range(0..=19); - let index = random_number as usize; - let rando_char = some_chars.chars().nth(index); - - if let Some(c) = rando_char { - filename.push(c); - } - } - } else { - filename += "track-output"; - } - - filename += &file_extension; - - filename - } } diff --git a/tests/tests.rs b/tests/tests.rs index f281ea7..386940b 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -30,16 +30,12 @@ mod utils { #[cfg(test)] mod song_tests { - use std::fs::File; - use std::io::Write; - use tempfile::tempdir; + use crate::utils; use icarus_models::song; use icarus_models::types; - use crate::utils; - #[test] fn test_song_to_data() { println!("Test"); @@ -65,7 +61,7 @@ mod song_tests { Ok(buffer) => { assert_eq!(buffer.is_empty(), false); - match song.to_data() { + match song::io::to_data(&song) { Ok(song_data) => { println!("Both files match"); assert_eq!(buffer, song_data); @@ -103,50 +99,47 @@ mod song_tests { song.directory = utils::get_tests_directory(); song.filename = String::from("track01.flac"); - match song.song_path() { - Ok(songpath) => match utils::extract_data_from_file(&songpath) { - Ok(buffer) => { - let mut song_cpy = song.clone(); - let temp_dir = tempdir().expect("Failed to create temp dir"); - song_cpy.directory = match temp_dir.path().to_str() { - Some(s) => String::from(s), - None => String::new(), - }; + let mut song_cpy = song.clone(); + let temp_dir = tempdir().expect("Failed to create temp dir"); + song_cpy.directory = match temp_dir.path().to_str() { + Some(s) => String::from(s), + None => String::new(), + }; - assert_eq!(song.directory.is_empty(), false); - song_cpy.filename = - song.generate_filename(types::MusicTypes::FlacExtension, true); - println!("Directory: {:?}", song_cpy.directory); - println!("File to be created: {:?}", song_cpy.filename); + assert_eq!(song.directory.is_empty(), false); + song_cpy.filename = song::generate_filename(types::MusicTypes::FlacExtension, true); + println!("Directory: {:?}", song_cpy.directory); + println!("File to be created: {:?}", song_cpy.filename); - let path = match song_cpy.song_path() { - Ok(s_path) => s_path, - Err(err) => { - assert!(false, "Error: {:?}", err); - String::new() - } - }; + match song::io::copy_song(&song, &mut song_cpy) { + Ok(_) => {} + Err(err) => { + assert!(false, "Error copying song: Error: {err:?}") + } + } + } - match File::create(path) { - Ok(mut file_cpy) => match file_cpy.write_all(&buffer) { - Ok(success) => { - println!("Success: {:?}", success); - } - Err(err) => { - assert!(false, "Error saving file: {:?}", err); - } - }, - Err(err) => { - assert!(false, "Error: {:?}", err); - } - }; - } + #[test] + fn test_save_song_to_filesystem_and_remove() { + let mut song = song::Song::default(); + song.directory = utils::get_tests_directory(); + song.filename = String::from("track02.flac"); + + let mut copied_song = song::Song { + directory: utils::get_tests_directory(), + filename: String::from("track02-coppied.flac"), + ..Default::default() + }; + + match song::io::copy_song(&song, &mut copied_song) { + Ok(_) => match copied_song.remove_from_filesystem() { + Ok(_) => {} Err(err) => { - assert!(false, "Error: {:?}", err); + assert!(false, "Error: {err:?}") } }, Err(err) => { - assert!(false, "Error extracting song data: {:?}", err); + assert!(false, "Error: {err:?}") } } } @@ -154,7 +147,6 @@ mod song_tests { #[cfg(test)] mod album_tests { - use crate::utils; use icarus_models::album;