pub mod errors;
pub mod cssf;
pub mod classic_song;
pub mod song_yml;
pub mod ccli;
pub mod metadata;
use errors::CantaraFileDoesNotExistError;
use serde::{Deserialize, Serialize};
use crate::filetypes::FileType;
use crate::song::Song;
use std::error::Error;
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
pub struct SongFile {
pub file_path: PathBuf,
pub parsing_state: SongFileParsingState,
}
impl SongFile {
pub fn new(path: &str) -> Result<Self, CantaraFileDoesNotExistError> {
match Path::new(&path).exists() {
true => {
let file_path = Path::new(path).to_path_buf();
let parsing_state = SongFileParsingState::NotStarted;
Ok(
SongFile {
file_path,
parsing_state
}
)
},
false => {
Err(
CantaraFileDoesNotExistError
)
}
}
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
pub enum SongFileParsingState {
NotStarted,
FormatDetermined,
ClassicSongNotParsed,
ParsedCantaraSong(Song),
}
pub fn import_song_from_file(file_path: impl AsRef<Path>) -> Result<Song, Box<dyn Error>> {
let path = file_path.as_ref();
let file_type = FileType::from_path(path).ok_or_else(|| {
Box::new(errors::CantaraImportUnknownFileExtensionError {
file_extension: path
.extension()
.and_then(OsStr::to_str)
.unwrap_or("")
.to_string(),
})
})?;
let content = std::fs::read_to_string(path)?;
let mut song = match file_type {
FileType::SongYaml => song_yml::import_from_yml_string(&content)?,
FileType::ClassicSongFile => classic_song::import_song(&content)?,
FileType::CCLISongselectFile => ccli::import_from_ccli_string(&content)?,
FileType::CSSF => cssf::import_input_string(
content,
path.file_name()
.and_then(OsStr::to_str)
.unwrap_or("")
.to_string(),
)?,
};
if song.title.trim().is_empty()
&& let Some(stem) = path.file_stem().and_then(OsStr::to_str) {
song.title = stem.to_string();
}
Ok(song)
}
pub fn get_song_from_file_as_json(file_path: &str) -> Result<String, Box<dyn Error>> {
match import_song_from_file(file_path) {
Ok(song) => {
match serde_json::to_string_pretty(&song) {
Ok(string) => Ok(string),
Err(error) => Err(Box::new(error))
}
}
Err(error) => Err(error)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_import_song_with_title_tag_from_file() {
let song = import_song_from_file("tests/data/Amazing Grace.song").unwrap();
assert_eq!(song.title, "Amazing Grace");
}
#[test]
fn test_import_song_without_title_tag_from_file() {
let song = import_song_from_file("tests/data/What a friend we have in Jesus.song").unwrap();
assert_eq!(song.title, "What a friend we have in Jesus");
}
#[test]
fn test_import_song_with_unknown_file_extension_from_file() {
let result = import_song_from_file("tests/data/What a friend we have in Jesus.txt");
assert!(result.is_err());
let error: Box<dyn Error> = result.err().unwrap();
assert_eq!(error.to_string(), "Unknown file extension: txt");
}
#[test]
fn test_create_songfile_which_does_not_exist() {
let result = SongFile::new("tests/data/A Non Existing File.txt");
assert_eq!(result.unwrap_err(), CantaraFileDoesNotExistError);
}
}